Skip to content

feat: add read-only MCP server for AI-spend data#112

Open
studert wants to merge 2 commits into
mainfrom
claude/mcp-capabilities-overview-D6RMz
Open

feat: add read-only MCP server for AI-spend data#112
studert wants to merge 2 commits into
mainfrom
claude/mcp-capabilities-overview-D6RMz

Conversation

@studert
Copy link
Copy Markdown
Member

@studert studert commented Jun 3, 2026

Summary

Adds a read-only Model Context Protocol (MCP) server so MCP clients (Claude Desktop/Code, Cursor, etc.) can query the Hub's AI-spend data in natural language. It reuses the existing, already-tested read layer rather than duplicating logic, and is dormant by default (off until MCP_SERVER_SECRET is set).

  • Endpoint: POST /api/mcp/mcp (Streamable HTTP via mcp-handler + official MCP SDK)
  • Auth: shared-secret bearer (MCP_SERVER_SECRET), constant-time compare, via withMcpAuth
  • Access: read-only — no mutations, no decrypted API keys, no password hashes

Tools (7)

Tool Backed by
list_ai_tools aiTools + accessTiers
get_user_cost_profile fetchProfileDataInternal
get_claude_spend_summary loadDashboardKpis
list_claude_workspaces loadWorkspaceList
get_budget_status getActiveBudget + getBudgetWithCosts + buildBudgetForecast
get_copilot_usage_summary copilotUsageMetrics + copilotBillingSnapshots
list_recent_sync_events syncEvents + loadSyncStatus

All monetary fields are returned as both integer cents (*Cents) and derived USD (*Usd).

Architecture

  • src/app/api/mcp/[transport]/route.ts — thin route (createMcpHandler + withMcpAuth)
  • src/lib/mcp/{auth,tools,data,format}.ts — auth, tool registration, data assembly, pure helpers
  • Excluded /api/mcp from the NextAuth middleware matcher (clean 401 instead of a /login redirect) and added it to the nighthawk agent deny-list as defense-in-depth, mirroring /api/sync
  • MCP_SERVER_SECRET added to env.ts (optional, .min(16)), matching PROFILE_API_SECRET
  • Centralized centsToUsd in utils.ts (now reused by formatCurrency)

Design notes / decisions

  • SDK compatibility: pinned @modelcontextprotocol/sdk@1.26.0 to satisfy mcp-handler@1.1.0's exact peer; SDK 1.26 supports the app's Zod v4 via Standard Schema. Tool inputs are kept to flat strings/numbers/optionals to avoid known Zod-v4 quirks (discriminated unions, description propagation).
  • Decoupling: all domain logic lives in src/lib/mcp/* and is unit-tested independently of mcp-handler, so swapping transports later is a route-file change, not a rewrite.
  • A detailed plan with self-critique is in docs/mcp-implementation-plan.html; operator/client setup is in docs/mcp-server.md.

Testing / QA

  • pnpm typecheck ✅ · pnpm lint (zero warnings) ✅ · pnpm test431 passed (incl. new format/auth/data/tools suites)
  • pnpm build: compiles and type-checks ✅. Static prerender of the unrelated /reports/budget page fails only because this sandbox has no DATABASE_URL (that page does an unguarded DB call at prerender time); not caused by this change. The MCP route is force-dynamic and never prerendered.

Configuration

{
  "mcpServers": {
    "ai-developer-hub": {
      "type": "http",
      "url": "https://<your-hub-host>/api/mcp/mcp",
      "headers": { "Authorization": "Bearer <MCP_SERVER_SECRET>" }
    }
  }
}

https://claude.ai/code/session_01JbYxs25bCVAFyzayB3barx


Generated by Claude Code

Expose the Hub's read-only data via a Model Context Protocol server so MCP
clients (Claude Desktop/Code, Cursor, etc.) can query AI spend directly.

- Mount mcp-handler at /api/mcp/mcp (Streamable HTTP) with withMcpAuth
- Shared-secret auth (MCP_SERVER_SECRET), dormant-by-default when unset
- 7 read-only tools delegating to the existing tested read layer:
  list_ai_tools, get_user_cost_profile, get_claude_spend_summary,
  list_claude_workspaces, get_budget_status, get_copilot_usage_summary,
  list_recent_sync_events
- Exclude /api/mcp from NextAuth middleware; add to agent deny-list
- Centralize centsToUsd in utils.ts (reused by formatCurrency)
- Unit tests for format, auth, data shaping, and tool registration
- Docs: docs/mcp-server.md and docs/mcp-implementation-plan.html

https://claude.ai/code/session_01JbYxs25bCVAFyzayB3barx
Copilot AI review requested due to automatic review settings June 3, 2026 20:35
@vercel
Copy link
Copy Markdown

vercel Bot commented Jun 3, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
ai-developer-hub Ready Ready Preview, Comment Jun 4, 2026 7:19am

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a read-only Model Context Protocol (MCP) server endpoint to the AI Developer Hub so MCP clients can query AI spend/budget/sync health data via a shared-secret bearer token, reusing existing read-layer logic and keeping the feature dormant unless MCP_SERVER_SECRET is configured.

Changes:

  • Introduces an MCP server route (/api/mcp/mcp) with shared-secret auth and a set of 7 read-only tools.
  • Adds MCP data/format/tool registration helpers under src/lib/mcp/*, plus unit tests covering auth, formatting, data assembly, and tool wiring.
  • Wires configuration/security hardening (middleware exclusion for /api/mcp, agent deny-list entry, optional env var, and centralized centsToUsd).

Reviewed changes

Copilot reviewed 16 out of 17 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/mcp/tools.test.ts Unit tests validating tool registration, arg forwarding, and error degradation behavior.
tests/unit/mcp/format.test.ts Unit tests for cents→USD conversion and MCP result envelopes.
tests/unit/mcp/data.test.ts Unit tests for each MCP tool’s data assembly with mocked DB/loaders.
tests/unit/mcp/auth.test.ts Unit tests for shared-secret validation and constant-time comparison behavior.
src/middleware.ts Excludes /api/mcp from NextAuth middleware matching to avoid login redirects for MCP clients.
src/lib/utils.ts Adds centsToUsd helper and reuses it in formatCurrency.
src/lib/mcp/tools.ts Registers the 7 MCP tools with Zod input schemas and safeJsonResult wrapping.
src/lib/mcp/format.ts Adds pure MCP formatting/result helpers and re-exports centsToUsd.
src/lib/mcp/data.ts Implements read-only tool backing functions by delegating to existing read layers and DB reads.
src/lib/mcp/auth.ts Implements shared-secret bearer auth with constant-time comparison and “dormant by default” behavior.
src/lib/env.ts Adds optional MCP_SERVER_SECRET with minimum length validation and reformats schema declarations.
src/lib/agent-auth.ts Adds /api/mcp to the built-in deny list for the agent as defense-in-depth.
src/app/api/mcp/[transport]/route.ts Adds the MCP endpoint route using mcp-handler + withMcpAuth.
package.json Adds pinned MCP dependencies (@modelcontextprotocol/sdk@1.26.0, mcp-handler@1.1.0).
pnpm-lock.yaml Lockfile updates to include MCP dependencies and their transitive packages.
docs/mcp-server.md Operator/client setup docs for the MCP server and tool list.
docs/mcp-implementation-plan.html Detailed implementation plan and rationale document committed to the repo.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/mcp/data.ts Outdated
Comment thread src/lib/mcp/data.ts
- Require copilotSyncEnabled on the GitHub connection, matching
  getActiveConnection() in copilot-data.ts, so a connection without
  Copilot sync isn't reported as connected with stale/absent data
- Default the since/until range using UTC (formatUtcDateOnly) instead of
  date-fns local-timezone format(), keeping the YYYY-MM-DD day boundary
  timezone-stable like the rest of the codebase

https://claude.ai/code/session_01JbYxs25bCVAFyzayB3barx
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants