Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# AGENTS.md — Rainkeeper

Rainkeeper is Simon's own open-source MCP server for Raindrop.io, described here for any agentic coding tool operating on this repo. It fixes two bugs in existing community alternatives: it works on free Raindrop accounts (the official MCP requires Pro), and collection moves actually work (correct `{"collection": {"$id": n}}` request body format). MIT licence.

**Language:** Python
**Framework:** FastMCP
**Repo:** `~/git/rainkeeper`
**Registered as:** `rainkeeper` in Claude Desktop and CLI (`mcp__rainkeeper__` tool prefix)

## Scope
This repo's delta over generic Python/FastMCP competence: the Raindrop-specific API bug fixes, and the mandatory Superpowers development methodology for this project. Generic Python/FastMCP/MCP-server knowledge is assumed, not repeated.

## Precedence
This file is authoritative for all rainkeeper work — no parent AGENTS.md exists inside this repo. Global developer and tool conventions apply where not overridden here; this file overrides defaults on rainkeeper-specific technical and process matters (e.g. mandatory TDD) regardless of recency.

## Instructions

**Commands:**
- Branch naming: `feat/`, `fix/`, `docs/`, `chore/`. All work on a feature branch, PR into `main` — never commit directly to `main`.
- Mandatory workflow for any new feature or significant change: `/brainstorming` (before any implementation, no exceptions for perceived simplicity) → `/writing-plans` (plan written to `docs/superpowers/specs/` and committed before coding) → `/test-driven-development` (no production code without a failing test first) → `/verification-before-completion` (before marking any task done) → `/finishing-a-development-branch` (before raising a PR).
- For bug fixes: write the failing test that reproduces the bug first, then fix. No exception.

**Conventions:**
- Always call `list_collections` first in any Raindrop session — all move/file operations require numeric collection IDs, not names.
- The Superpowers plugin methodology (referenced via `/brainstorming`, `/writing-plans`, `/test-driven-development`, `/verification-before-completion`, `/finishing-a-development-branch`) is mandatory for all work on this project.

## Negatives
Never skip a Superpowers hard gate without explicit instruction from Simon. Never write production code before a failing test exists — delete any code written before tests existed and start fresh, no exceptions. Exception: throwaway spikes for exploration/prototyping are exempt from TDD, but must be deleted before production implementation begins — do not adapt spike code into production code.

## Expiry
The critical Raindrop API fix (`{"collection": {"$id": n}}` required for moves; `{"collectionId": n}` returns 200 OK but silently does nothing) and the 19-tool count across collections/raindrops/bulk-operations/search/tags — owner: Simon, last-verified: 2026-07-25, refresh interval: whenever Raindrop's API changes or tools are added/removed. Connects to Simon's Raindrop.io account (`simon-ives-au`).

---
[[Rainkeeper]] · [[Side Projects CLAUDE]]
34 changes: 34 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# CLAUDE.md — Rainkeeper

Rainkeeper is Simon's own open-source MCP server for Raindrop.io. It fixes two bugs in existing community alternatives: it works on free Raindrop accounts (the official MCP requires Pro), and collection moves actually work (correct `{"collection": {"$id": n}}` request body format). MIT licence.

**Language:** Python
**Framework:** FastMCP
**Repo:** `~/git/rainkeeper`
**Registered as:** `rainkeeper` in Claude Desktop and CLI (`mcp__rainkeeper__` tool prefix)

## Scope
This repo's delta over generic Python/FastMCP competence: the Raindrop-specific API bug fixes, and the mandatory Superpowers development methodology for this project. Generic Python/FastMCP/MCP-server knowledge is assumed, not repeated.

## Precedence
This file is authoritative for all rainkeeper work — no parent CLAUDE.md exists inside this repo. Global `~/.claude/CLAUDE.md` DOCTRINE and preferences apply where not overridden here; this file overrides the global defaults on rainkeeper-specific technical and process matters (e.g. mandatory TDD) regardless of recency.

## Instructions

**Commands:**
- Branch naming: `feat/`, `fix/`, `docs/`, `chore/`. All work on a feature branch, PR into `main` — never commit directly to `main`.
- Mandatory workflow for any new feature or significant change: `/brainstorming` (before any implementation, no exceptions for perceived simplicity) → `/writing-plans` (plan written to `docs/superpowers/specs/` and committed before coding) → `/test-driven-development` (no production code without a failing test first) → `/verification-before-completion` (before marking any task done) → `/finishing-a-development-branch` (before raising a PR).
- For bug fixes: write the failing test that reproduces the bug first, then fix. No exception.

**Conventions:**
- Always call `list_collections` first in any Raindrop session — all move/file operations require numeric collection IDs, not names.
- The Superpowers plugin (`superpowers@claude-plugins-official`) methodology is mandatory for all work on this project.

## Negatives
Never skip a Superpowers hard gate without explicit instruction from Simon. Never write production code before a failing test exists — delete any code written before tests existed and start fresh, no exceptions. Exception: throwaway spikes for exploration/prototyping are exempt from TDD, but must be deleted before production implementation begins — do not adapt spike code into production code.

## Expiry
The critical Raindrop API fix (`{"collection": {"$id": n}}` required for moves; `{"collectionId": n}` returns 200 OK but silently does nothing) and the 19-tool count across collections/raindrops/bulk-operations/search/tags — owner: Simon, last-verified: 2026-07-25, refresh interval: whenever Raindrop's API changes or tools are added/removed. Connects to Simon's Raindrop.io account (`simon-ives-au`).

---
[[Rainkeeper]] · [[Side Projects CLAUDE]]
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM python:3.12-slim AS app
WORKDIR /app

COPY pyproject.toml README.md ./
COPY src/ ./src/
RUN pip install --no-cache-dir . uvicorn

EXPOSE 8001
CMD ["python", "-m", "rainkeeper.http_server"]
34 changes: 34 additions & 0 deletions GEMINI.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# GEMINI.md — Rainkeeper

Rainkeeper is Simon's own open-source MCP server for Raindrop.io, described here for Gemini operating directly on this repo via Antigravity. It fixes two bugs in existing community alternatives: it works on free Raindrop accounts (the official MCP requires Pro), and collection moves actually work (correct `{"collection": {"$id": n}}` request body format). MIT licence.

**Language:** Python
**Framework:** FastMCP
**Repo:** `~/git/rainkeeper`
**Registered as:** `rainkeeper` in Claude Desktop and CLI (`mcp__rainkeeper__` tool prefix)

## Scope
This repo's delta over generic Python/FastMCP competence: the Raindrop-specific API bug fixes, and the mandatory Superpowers development methodology for this project. Generic Python/FastMCP/MCP-server knowledge is assumed, not repeated.

## Precedence
This file is authoritative for all rainkeeper work — no parent GEMINI.md exists inside this repo. General developer and tool conventions apply where not overridden here; this file overrides defaults on rainkeeper-specific technical and process matters (e.g. mandatory TDD) regardless of recency.

## Instructions

**Commands:**
- Branch naming: `feat/`, `fix/`, `docs/`, `chore/`. All work on a feature branch, PR into `main` — never commit directly to `main`.
- Mandatory workflow for any new feature or significant change: `/brainstorming` (before any implementation, no exceptions for perceived simplicity) → `/writing-plans` (plan written to `docs/superpowers/specs/` and committed before coding) → `/test-driven-development` (no production code without a failing test first) → `/verification-before-completion` (before marking any task done) → `/finishing-a-development-branch` (before raising a PR).
- For bug fixes: write the failing test that reproduces the bug first, then fix. No exception.

**Conventions:**
- Always call `list_collections` first in any Raindrop session — all move/file operations require numeric collection IDs, not names.
- The Superpowers plugin methodology (referenced via `/brainstorming`, `/writing-plans`, `/test-driven-development`, `/verification-before-completion`, `/finishing-a-development-branch`) is mandatory for all work on this project.

## Negatives
Never skip a Superpowers hard gate without explicit instruction from Simon. Never write production code before a failing test exists — delete any code written before tests existed and start fresh, no exceptions. Exception: throwaway spikes for exploration/prototyping are exempt from TDD, but must be deleted before production implementation begins — do not adapt spike code into production code.

## Expiry
The critical Raindrop API fix (`{"collection": {"$id": n}}` required for moves; `{"collectionId": n}` returns 200 OK but silently does nothing) and the 19-tool count across collections/raindrops/bulk-operations/search/tags — owner: Simon, last-verified: 2026-07-25, refresh interval: whenever Raindrop's API changes or tools are added/removed. Connects to Simon's Raindrop.io account (`simon-ives-au`).

---
[[Rainkeeper]] · [[Side Projects CLAUDE]]
2 changes: 2 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ dependencies = [
"fastmcp>=0.4.0",
"httpx>=0.27.0",
"python-dotenv>=1.0.0",
"uvicorn>=0.30.0",
]

[project.scripts]
rainkeeper = "rainkeeper.server:main"
rainkeeper-http = "rainkeeper.http_server:main"

[tool.hatch.build.targets.wheel]
packages = ["src/rainkeeper"]
54 changes: 54 additions & 0 deletions src/rainkeeper/http_server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
"""HTTP transport entry point for cia hosting, guarded by X-API-Key.

Mirrors reed's ApiKeyMiddleware pattern (src/reed/mcp_server.py in the
reed repo): a pure-ASGI wrapper around FastMCP's own http_app(), since
the mounted MCP app is a Starlette sub-app rather than routes that can
take a Depends().
"""
from __future__ import annotations

import os
import secrets

from starlette.responses import JSONResponse
from starlette.types import ASGIApp, Receive, Scope, Send

from .server import mcp

API_KEY = os.environ.get("RAINKEEPER_API_KEY", "")


class ApiKeyMiddleware:
def __init__(self, app: ASGIApp) -> None:
self.app = app

async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
if scope["type"] != "http":
await self.app(scope, receive, send)
return
if API_KEY:
headers = dict(scope["headers"])
key = headers.get(b"x-api-key", b"").decode()
if not secrets.compare_digest(key, API_KEY):
response = JSONResponse({"error": "Invalid API key"}, status_code=401)
await response(scope, receive, send)
return
await self.app(scope, receive, send)


app = ApiKeyMiddleware(mcp.http_app(path="/mcp"))


def main() -> None:
import uvicorn

uvicorn.run(
app,
host=os.environ.get("RAINKEEPER_HOST", "0.0.0.0"),
port=int(os.environ.get("RAINKEEPER_PORT", "8001")),
log_level="info",
)


if __name__ == "__main__":
main()
Loading