Skip to content

MCP Server API

The Nucleus MCP server is a standalone Cloudflare Worker at mcp.nucleus.fast. It implements the Model Context Protocol Streamable HTTP transport and OAuth 2.0 with PKCE.

See the MCP Setup guide for connection instructions.


AI Client (Claude Desktop, Claude Code, Cursor…)
│ OAuth 2.0 / PKCE
MCP Worker (mcp.nucleus.fast) — apps/mcp/
│ CF Access JWT forwarded on every call
Nucleus API (app.nucleus.fast/api/*) — apps/app/worker/
│ Permission checks + business logic
Cloudflare D1

The MCP Worker never touches D1 directly. All permission enforcement is in the existing API layer.

Transport: WebStandardStreamableHTTPServerTransport (stateless). Each request creates a fresh McpServer + transport — no Durable Objects needed.

Auth: OAuth 2.0 with PKCE. The bearer token maps to a CF Access JWT stored in KV. Every API proxy call forwards that JWT as Cf-Access-Jwt-Assertion.


PropertyValue
Package@nucleus/mcp
Pathapps/mcp/
Deployed URLhttps://mcp.nucleus.fast
Dev port8790

All OAuth endpoints are public (no CF Access policy on the domain — auth is handled in-band by the /oauth/authorize flow).

GET /.well-known/oauth-authorization-server
GET /.well-known/oauth-protected-resource

RFC 8414 OAuth server metadata. Clients use these to discover auth URLs automatically.

POST /oauth/clients
Content-Type: application/json
{
"client_name": "Claude Desktop",
"redirect_uris": ["claude://mcp/auth/callback"]
}

Response 201:

{
"client_id": "<generated>",
"client_id_issued_at": 1234567890,
"redirect_uris": ["claude://mcp/auth/callback"],
"grant_types": ["authorization_code", "refresh_token"],
"response_types": ["code"],
"token_endpoint_auth_method": "none"
}
GET /oauth/authorize
?response_type=code
&client_id=<id>
&redirect_uri=<uri>
&code_challenge=<S256_challenge>
&code_challenge_method=S256
&state=<state>

Checks for a CF_Authorization cookie. If missing, redirects to:

https://nucleusfast.cloudflareaccess.com/cdn-cgi/access/login/mcp.nucleus.fast
?redirect_url=<original_authorize_url>

After Google SSO, CF Access redirects back with the JWT cookie set. The worker then:

  1. Validates the JWT
  2. Generates a one-time auth code (60s TTL in KV)
  3. Redirects to redirect_uri?code=<code>&state=<state>
POST /oauth/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=<code>
&code_verifier=<pkce_verifier>
&redirect_uri=<uri>
&client_id=<id>

Validates PKCE (SHA-256(code_verifier) == stored code_challenge), consumes the code, returns:

{
"access_token": "<token>",
"token_type": "Bearer",
"expires_in": 28800,
"refresh_token": "<token>"
}

Refresh:

POST /oauth/token
grant_type=refresh_token&refresh_token=<token>

Returns a new access_token.

TokenTTLKV key
Auth code60 secondsauth_code:<code>
Access token8 hourstoken:<token>
Refresh token30 daysrefresh:<token>

POST /mcp
Authorization: Bearer <access_token>
Content-Type: application/json
Accept: application/json, text/event-stream
  1. Validates the bearer token → KV lookup → CF Access JWT
  2. Creates McpServer with all 120+ tools registered
  3. Connects to WebStandardStreamableHTTPServerTransport (stateless)
  4. Returns MCP response (JSON or SSE stream)

Supports both streaming (Accept: text/event-stream) and non-streaming responses.


The query_nucleus MCP tool calls this endpoint on the main app:

POST /api/query
Authorization: Bearer <CF Access JWT> (or Cf-Access-Jwt-Assertion header)
Content-Type: application/json
{
"question": "which projects ran over budget last quarter?"
}

Response:

{
"sql": "SELECT p.name, b.total_budget, SUM(te.hours * st.rate) AS actual_cost ...",
"results": [...],
"count": 12
}

Errors:

StatusMeaning
400 { error: "Generated query is not a SELECT statement" }AI produced a non-SELECT (safety rejection)
400 { error: "Query execution failed" }SQL was valid but failed at runtime (schema mismatch, etc.)
502 { error: "AI query generation failed" }Workers AI / Kimi K2.5 unavailable

How it works:

  1. Fetches compact schema from sqlite_master at request time
  2. Builds a system prompt with schema + user context (user_id, person_id, access_level)
  3. Calls @cf/moonshotai/kimi-k2.5 via Workers AI binding
  4. Extracts SQL from response (strips markdown fences)
  5. Validates SELECT-only, injects LIMIT 200 if absent
  6. Executes against D1, returns { sql, results, count }

Binding: MCP_SESSIONS

Key patternValueTTL
auth_code:<code>AuthCode JSON60s
token:<token>TokenPayload JSON8h
refresh:<token>TokenPayload JSON30d
client:<id>RegisteredClient JSONnone

Terminal window
cd apps/mcp
npx wrangler kv namespace create MCP_SESSIONS

Update wrangler.toml with the returned namespace ID (replace placeholder-create-via-wrangler).

Terminal window
npx wrangler secret put CF_ACCESS_AUD

Get the AUD value from the Cloudflare Access application for mcp.nucleus.fast (Zero Trust dashboard → Access → Applications → mcp.nucleus.fast → Overview).

Terminal window
pnpm --filter @nucleus/mcp deploy
# or from apps/mcp/
pnpm deploy
Terminal window
pnpm --filter @nucleus/mcp dev
# Worker at http://localhost:8790

Note: CF Access JWT validation requires a live CF Access team domain. For local testing, temporarily bypass JWT validation with a DEV_MODE check or test against a staging deployment.


VariableValueDescription
NUCLEUS_API_URLhttps://app.nucleus.fastBase URL for all API proxy calls
CF_ACCESS_TEAM_DOMAINnucleusfastCF Access team domain
CF_ACCESS_AUD<secret>CF Access application AUD for mcp.nucleus.fast
MCP_SERVER_URLhttps://mcp.nucleus.fastPublic MCP server URL (used in OAuth metadata)

apps/mcp/
wrangler.toml
src/
index.ts # Hono entry: OAuth + MCP endpoint
types.ts # Env bindings, TokenPayload, AuthCode
auth/
cf-access.ts # CF Access JWT validation
session.ts # KV helpers (codes, tokens, clients)
authorize.ts # GET /oauth/authorize
token.ts # POST /oauth/token
mcp/
server.ts # createNucleusMcp() — registers all tools
proxy.ts # nucleusApi() / apiToolCall() helpers
tools/ # 46 tool modules (~120 tools total)
me.ts # get_me, get_dashboard, get_notifications, query_nucleus
search.ts
tasks.ts
… (43 more files)
resources/
config.ts # 7 MCP resources (statuses, priorities, etc.)