You are building Cairn, a markdown-first knowledge base with an MCP interface on Cloudflare Workers. This is a greenfield project with comprehensive specs already written.
-
Read all four docs before writing any code:
docs/requirements.md— what we're buildingdocs/architecture.md— system design, data flow, DO/R2 splitdocs/mcp-spec.md— all 8 MCP tool definitions with schemasdocs/implementation-plan.md— sequenced build plan (this is your roadmap)
-
Bootstrap the project following the instructions in
docs/implementation-plan.md→ Bootstrap section. Scaffold the Cloudflare template into a temp directory, merge into this repo, install dependencies. -
Read the generated template code thoroughly before making changes. Understand:
- How
OAuthProviderwraps the worker fetch handler - How
McpAgent<Env, State, Props>receives auth context viathis.props - How Durable Object bindings and migrations work in
wrangler.jsonc - Where Google OAuth handler lives and how it populates user identity
- How
-
Follow the phases in
docs/implementation-plan.mdsequentially. Phase 0 → Phase 7. Do not skip ahead.
Each phase must end with:
- Working verification — the phase's stated verification steps pass. Use MCP Inspector (
npx @modelcontextprotocol/inspector),curl, or browser as appropriate. - Git commit — clean commit with message like
phase 0: scaffold and verify baseline. Commit working code only. If a phase is large, commit incrementally within it, but always leave the repo in a working state. - No regressions — before committing, verify that previous phases still work. Run
npm run devand confirm existing tools respond correctly.
wrangler devis your primary development environment. It emulates R2, KV, and Durable Objects locally.- MCP Inspector (
npx @modelcontextprotocol/inspector) is the primary tool testing interface. Connect tohttp://localhost:8788/mcp(orhttp://localhost:8788/{workspaceId}/mcponce workspace routing is added). curlfor REST API endpoints.- Browser for OAuth flow and frontend.
- Test the happy path AND error cases (missing notes, bad params, unauthorized access).
- If something isn't working, read the wrangler dev console output carefully — Durable Object errors and R2 failures show up there.
- TypeScript strict mode. Define types for all data structures (frontmatter, tool params, DO RPC payloads).
- Keep files focused and small. One tool per file in
src/mcp/tools/. - Pure functions where possible (frontmatter parsing, markdown manipulation, wikilink extraction). These are easy to reason about and test.
- No external dependencies unless necessary. Check Workers runtime compatibility before adding anything.
js-yamlandzodare fine. Avoid heavy packages.
- Template doesn't behave as expected: Read the
workers-oauth-providersource and Cloudflare Agents SDK docs. Don't guess at the API — checknode_modules/agents/andnode_modules/workers-oauth-provider/for type definitions. - DO communication issues: Remember that Durable Object methods called via RPC must be
asyncand return serialisable values. File content (large strings) should NOT cross the RPC boundary — extract metadata in the worker, send only structured data to the DO. - R2 quirks: R2 custom metadata values must be strings.
tagsshould be comma-separated, not JSON.list()returns max 1000 objects — always handle thetruncatedflag andcursorfor pagination. - OAuth flow not completing: The OAuth paths (
/authorize,/callback,/token,/.well-known/oauth-authorization-server) are handled by theOAuthProviderwrapper BEFORE your router sees the request. Don't try to handle them yourself.
These are non-negotiable. Do not deviate:
-
R2 for content, DO SQLite for indexes. Notes go in R2. All metadata, backlinks, search terms, and aliases go in WorkspaceIndex DO's SQLite. No JSON index files in R2.
-
Lightweight RPC. Worker reads/writes R2 directly. Only send extracted metadata to the DO (title, type, tags, aliases, links array, modified timestamp). Never send full note content over the RPC boundary.
-
MCP-native errors. All tool errors return
{ content: [{ type: "text", text: "error_type: message" }], isError: true }. No custom error formats. -
Workspace-scoped from Phase 1. All storage functions take
workspaceIdas a parameter. Don't hardcode a workspace and plan to refactor later. -
Both write and patch update indexes. Every code path that modifies note content must: update R2 custom metadata AND call
WorkspaceIndex.noteUpdated(). -
Section params use heading text without
#prefix."Meetings"not"## Meetings". -
Don't rewrite the OAuth plumbing. Adapt the template's auth flow. Don't replace it.
Start with Phase 0. Keep going through every phase until the project is complete. Don't stop to ask — if a decision needs to be made and the specs are ambiguous, make the simplest choice that's consistent with the architecture docs, note it in a commit message, and move on.
Full send. Ship it.