Skip to content

feat: add MCP server with OAuth 2.1 authentication#56

Merged
volod-vana merged 3 commits intomainfrom
feat/mcp
Mar 2, 2026
Merged

feat: add MCP server with OAuth 2.1 authentication#56
volod-vana merged 3 commits intomainfrom
feat/mcp

Conversation

@volod-vana
Copy link
Member

@volod-vana volod-vana commented Feb 26, 2026

Summary

  • Adds MCP (Model Context Protocol) server for AI tool integration — exposes user data as resources and tools
  • Adds OAuth 2.1 (PKCE + RFC 7591 dynamic client registration) to protect the /mcp endpoint
  • Identity verification delegates to Account Portal (account.vana.org) — user signs "vana-master-key-v1", server recovers wallet via EIP-191 and checks ownership before issuing tokens

MCP capabilities

  • Resources: list scopes, read scope data (latest or specific version)
  • Tools: search_files (search across scopes), list_versions (version history)

OAuth endpoints

  • /.well-known/oauth-authorization-server — discovery metadata
  • /.well-known/oauth-protected-resource — resource metadata
  • POST /register — dynamic client registration
  • GET /authorize → redirects to Account Portal
  • GET /oauth/callback — receives wallet proof, issues auth code
  • POST /token — code exchange (PKCE) and refresh

How auth works

  1. MCP client hits /mcp → gets 401 with WWW-Authenticate
  2. Discovers OAuth, registers dynamically, initiates PKCE flow
  3. User authenticates via Account Portal (Privy), signs masterKeySig
  4. Server recovers wallet, checks ownership, issues tokens
  5. Bearer token on all /mcp requests

Security

  • PKCE required (S256 only), auth codes single-use (5min TTL)
  • Access tokens 1h expiry, refresh token rotation
  • Wallet verified via EIP-191 recovery of "vana-master-key-v1"
  • Owner wallet → full access; unknown → denied

Test plan

  • npm run build passes
  • All 462 existing tests pass
  • curl POST /mcp → 401 with WWW-Authenticate
  • GET /.well-known/oauth-authorization-server → valid metadata
  • End-to-end with Claude custom connector

Depends on: vana-com/vana-connect PR for Account Portal HTTPS redirect support

🤖 Generated with Claude Code

Add a new `packages/mcp` package that exposes personal server data through
the Model Context Protocol, enabling integration with AI tools like Claude
and ChatGPT.

Resources: vana://files, vana://file/{scope}, vana://file/{scope}/metadata,
vana://grants, vana://schemas

Tools: list_files, get_file (with dot-path extraction), search_files

The MCP server is mounted at /mcp on the existing HTTP server using
Streamable HTTP transport (stateless, one transport per request). A stdio
entry point is also provided for Claude Desktop usage.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@github-actions github-actions bot added dependencies Pull requests that update a dependency file server labels Feb 26, 2026
@github-actions
Copy link

Codex Review

Findings

  1. Security: MCP endpoint exposes all personal data without auth.
    /mcp is mounted whenever serverOwner is present, but there is no auth guard at all. Anyone who can reach the server can call MCP tools/resources (get_file, list_files, search_files, vana://grants, etc.) and exfiltrate data. CORS is *, so this is also callable from any browser origin. Add the same owner/dev-token auth used on /v1/*, or require a signed token/header for MCP requests.
    Files: packages/server/src/app.ts:134, packages/server/src/routes/mcp.ts:13, packages/mcp/src/resources/files.ts:10, packages/mcp/src/resources/grants.ts:5, packages/mcp/src/tools/get-file.ts:22.

  2. Correctness/DoS risk: search_files reads full JSON for up to 500 scopes per request.
    The tool loads and stringifies entire latest payloads in-process, per scope, until it finds maxResults. On large datasets this can be very slow, block the event loop, and allow resource exhaustion. Consider enforcing a tighter scan limit, adding size/time caps, or streaming/indexing for search.
    File: packages/mcp/src/tools/search-files.ts:33.

  3. Protocol correctness risk: new MCP server instance per request.
    mcpRoute constructs a new McpServer and WebStandardStreamableHTTPServerTransport on every HTTP request. If MCP expects session continuity (e.g., follow-up calls by mcp-session-id), you will lose state and potentially break multi-step interactions. Consider caching per session or keeping a single server instance bound to the route.
    File: packages/server/src/routes/mcp.ts:13.

Missing tests

  1. Auth-required access for /mcp (unauthenticated should be denied).
    Files: packages/server/src/app.ts:134, packages/server/src/routes/mcp.ts:13.

  2. MCP session continuity across multiple requests (initialize → tool call).
    File: packages/server/src/routes/mcp.ts:13.

  3. search_files behavior/perf on large datasets (limits, timeouts, or graceful failure).
    File: packages/mcp/src/tools/search-files.ts:33.

If you want, I can propose an auth middleware placement and minimal test cases for MCP.

Protect the /mcp endpoint with OAuth 2.1 (PKCE, RFC 7591 dynamic
client registration). Identity verification delegates to Account Portal
(account.vana.org) — user signs "vana-master-key-v1", server recovers
wallet via EIP-191 and checks ownership before issuing tokens.

New endpoints: /.well-known/oauth-authorization-server,
/.well-known/oauth-protected-resource, /register, /authorize,
/oauth/callback, /token.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@volod-vana volod-vana changed the title feat: add MCP server for AI tool integration feat: add MCP server with OAuth 2.1 authentication Mar 1, 2026
The tunnel (frpc → Cloudflare) terminates TLS before reaching the
personal server, so `new URL(c.req.url).origin` returned `http://`.
OAuth clients like Claude rejected the metadata because endpoints
used HTTP while the server was accessed via HTTPS.

Use the existing `serverOrigin` function (which returns the tunnel
HTTPS URL once connected) for all OAuth discovery endpoints and the
WWW-Authenticate header in the MCP route.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
@volod-vana
Copy link
Member Author

Codex review raised 3 concerns, heres where we landed:

  1. No auth on MCP endpoint - addressed. Full OAuth 2.1 with PKCE, wallet-based identity verification via EIP-191 signature recovery. Bearer token required for all MCP requests when server owner is configured.
  2. search_files DoS risk (500 scopes full scan) - not addressed, acceptable for now. Personal server handles small datasets, this wont be a problem in practice. Will revisit if we see performance issues.
  3. New MCP server instance per request - by design. Using stateless WebStandardStreamableHTTPServerTransport without session continuity. Fine for current use case where we dont need subscriptions or server-to-client notifications.

Minor things to follow up on separately:

  • client_secret is generated but never verified at /token endpoint - not exploitable because PKCE is mandatory but its misleading. Should either validate it or change auth method to "none"
  • In-memory OAuth state (tokens, clients) doesnt survive server restart - clients need to re-auth. Acceptable for now
  • No cleanup for expired tokens / stale pending auths - minor memory leak over long-running sessions

@volod-vana volod-vana merged commit 93bdda2 into main Mar 2, 2026
7 of 8 checks passed
@volod-vana volod-vana deleted the feat/mcp branch March 2, 2026 04:18
volod-vana added a commit to vana-com/data-connect that referenced this pull request Mar 2, 2026
## Summary
- Update `@opendatalabs/personal-server-ts-core` and `server` from
`0.0.1-canary.93673d7` to `0.0.1-canary.92ee4d6`
- Add new `@opendatalabs/personal-server-ts-mcp` package
- Add transitive deps (`hono`, `@modelcontextprotocol/sdk`) required by
the MCP server

This picks up the MCP server + OAuth 2.1 changes from [personal-server
PR #56](vana-com/personal-server#56).

## Test plan
- [ ] Binary builds successfully
- [ ] Personal server starts and MCP endpoint responds at `/mcp`
- [ ] OAuth flow works end-to-end with Claude AI

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Volod <[email protected]>
Co-authored-by: Claude Opus 4.6 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file server

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant