Authenticated MCP read route + read resources + llms.txt (#18)#53
Conversation
Joins #17's API tokens and #15's deterministic serializer into an authenticated MCP server: an agent presents a bearer token, reads the architecture as deterministic markdown, scoped to its owner's projects. - resolveActorFromToken: re-derives the keyed HMAC (ADR-0020) and resolves a token to an Actor{via:"token"}; missing/unknown/revoked/expired all collapse to one null -> 401 (non-disclosure, ADR-0002). - export.service refactored into a three-layer split (ADR-0017 refined): pure serializeGraph -> shared serializeProjectScope -> two front doors, the slug-grant exportMarkdown (web) and owner-gated exportMarkdownForActor (MCP). Golden byte-equality test unchanged. - src/server/mcp/: resource catalog (index/project/subtree, additive for #38), SDK registration with owner-scoped resources/list, withMcpAuth verifier, and a not-found-equals-forbidden error mapper. - Route at /api/mcp (Streamable HTTP via mcp-handler, SSE disabled so no Redis/config, runtime=nodejs); generated /llms.txt rendered from the catalog. - 13 service tests (resolver, owner-gated read incl. cross-owner, list isolation). ADR-0022; CONTEXT.md future->present + MCP path/resource/ llms.txt/Agent entries. Read-only: write tools (#19/#20), Flow resources (#38), and scope enforcement (ADR-0021) stay out of scope by design. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughImplements an authenticated MCP read surface: token→Actor resolution, owner-gated MCP resources (index/project/subtree), shared serializeProjectScope core for deterministic markdown, Next.js route wiring with Node runtime and SSE disabled, dynamic /llms.txt discovery, and tests for auth and read isolation. ChangesAuthenticated MCP Read Surface
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related issues
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/adr/0022-authenticated-mcp-read-surface.md`:
- Around line 16-17: The two occurrences that begin with the literal tokens
`#17` and `#18` are being interpreted as ATX headings by Markdown linters;
update the ADR text so those references are escaped or rephrased (for example
use backticks, add a non-heading-safe prefix like "issue " or enclose in
parentheses) wherever the document currently starts a line with `#17` or `#18`
to avoid MD018 lint failures; search for the exact tokens `#17` and `#18` in the
ADR content and replace them with the escaped or reworded forms.
- Around line 82-84: Update the sentence that says
`withMcpAuth(resolveActorFromToken, { required: true })` to reflect that the
actual adapter seam wraps the resolver via `makeVerifyMcpToken(db)` before
wiring to `withMcpAuth`; i.e., mention that `resolveActorFromToken` is invoked
through the `makeVerifyMcpToken` verifier (used by `createMcpHandler`) rather
than being passed directly to `withMcpAuth`, so the doc matches the real call
chain.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 02666434-5818-449c-8d77-537980e75026
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (14)
CONTEXT.mddocs/adr/0022-authenticated-mcp-read-surface.mdpackage.jsonsrc/app/api/[transport]/route.tssrc/app/llms.txt/route.tssrc/lib/schemas.tssrc/server/architecture/__tests__/mcp-read.service.test.tssrc/server/architecture/export.service.tssrc/server/architecture/token.service.tssrc/server/mcp/auth.tssrc/server/mcp/catalog.tssrc/server/mcp/errors.tssrc/server/mcp/handler.tssrc/server/mcp/resources.ts
Fixes Applied SuccessfullyFixed 1 file based on 2 CodeRabbit feedback item(s). Files modified:
Changes:
Commit: The latest autofix changes are on the |
Closes #18.
Joins #17's API tokens and #15's deterministic markdown serializer into an authenticated MCP server: an agent presents a bearer token and reads the architecture as deterministic markdown, scoped to its owner's projects. Read-only by design.
What's here
resolveActorFromToken,token.service.ts): re-derives the keyed HMAC (ADR-0020), looks the token up bytokenHash, and resolves it to anActor{via:"token"}. Missing / unknown / revoked / expired all collapse to onenull→ a single 401 — which check failed is never disclosed (ADR-0002).export.service.ts): refactored into a three-layer split that refines ADR-0017 — pureserializeGraph→ sharedserializeProjectScope→ two front doors: the slug-grantexportMarkdown(web, unchanged) and the new owner-gatedexportMarkdownForActor(MCP, addressed byprojectId, authorized viaassertCanRead). The two grant postures share fetch, not authz.src/server/mcp/): a data-driven resource catalog (index/project/subtree— the MCP-addressable face of the serializer's three modes), SDK registration with an owner-scopedresources/list(reuseslistProjects), thewithMcpAuthverifier, and a not-found≡forbidden error mapper./api/mcpover Streamable HTTP viamcp-handler(SSE disabled → no Redis, no new env var;runtime="nodejs"). Generated/llms.txtrendered from the same catalog so it can't drift fromresources/list.MCP path/MCP resource/llms.txt/Agententries.Acceptance criteria
llms.txtserved describing endpoint, auth, and resourcesValidation
pnpm checkclean (eslint + tsc).pnpm test: 235/235 — includes the ADR-0017 golden byte-equality test, so the export refactor is provably byte-identical.pnpm checkcan't catch):/llms.txt→200; tokenless/bogus→401;initialize→capabilities;resources/list→owner's projects only;resources/readof project & subtree→correct markdown; inaccessible project→non-disclosing "not found". Synthetic seed cleaned up after.Scope boundaries (held by design)
flow/:id,flow-route/:id) +llms.txtFlow vocabulary — Slice 1: Flows on Components — schema, service, spec paste #34 / Slice 5: Aggregated parent rendering + markdown export for Flows #38 (catalog is append-only)New dependencies
mcp-handler@1.1.0+@modelcontextprotocol/sdk@1.26.0(the SDK ismcp-handler's required peer). Chosen over hand-rolling Streamable HTTP for protocol/Inspector compatibility; the resolver and registry are transport-agnostic so swapping to the SDK transport directly would be localized.🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Documentation