Skip to content

The /mcp Endpoint

The /mcp endpoint turns NimbleBrain into a Streamable HTTP MCP server. It is NimbleBrain’s public programmatic surface: external MCP clients (Claude Desktop, Claude Code, Cursor, another NimbleBrain instance, or any MCP-compatible host) connect here and access the tools installed across your workspaces.

The runtime also serves a /v1/* REST API, but that is a trusted same-origin surface for the bundled web client — not a supported external integration point. For programmatic access from outside the platform, use /mcp.

This makes NimbleBrain both an MCP client (connecting to installed bundles) and an MCP server (exposing composed tools to external hosts). The composition layer in between, which handles tool aggregation, feature gating, role filtering, and workspace routing, is what external clients benefit from without needing to know about individual bundles.

For end-user client setup walkthroughs, see Connecting External Clients.

Path/mcp
MethodsPOST, DELETE (GET returns 405)
ProtocolStreamable HTTP (MCP transport)
AuthOAuth 2.0 via WorkOS AuthKit (JWT bearer), discovered via RFC 9728
ScopeSessions are bound to your identity. Tool calls route to a workspace by the namespaced tool name.

The methods correspond to the Streamable HTTP session lifecycle:

  • POST — initialize a session or send subsequent JSON-RPC messages.
  • DELETE — terminate a session (with a valid Mcp-Session-Id).
  • GET — returns 405 Method Not Allowed with Allow: POST, DELETE. NimbleBrain does not offer the optional GET-style server-to-client SSE channel; progress and task updates flow over the POST that started them. Spec-compliant clients fall back to POST-only automatically.

Authentication is OAuth-based. There is no static API key.

The first time a client hits /mcp without a valid JWT, it receives a 401 with a WWW-Authenticate header:

Bearer error="unauthorized",
error_description="Authorization required",
resource_metadata="https://your-instance/.well-known/oauth-protected-resource"

Clients that implement RFC 9728 (Protected Resource Metadata) and RFC 8414 (Authorization Server Metadata) — Claude Code, Claude Desktop, and Cursor all do — follow the discovery chain automatically, open a browser for user login against AuthKit, and attach the returned JWT on subsequent requests.

Adapter configuration (dev, oidc, or workos) lives in instance.json. See auth adapters for the full setup, including the workos adapter and authkitDomain required to enable MCP OAuth.

NimbleBrain serves two unauthenticated discovery endpoints that MCP clients fetch during the OAuth flow. Both return 404 MCP OAuth not configured unless the workos adapter has an authkitDomain set.

RFC 9728 Protected Resource Metadata. Clients fetch this after the 401’s WWW-Authenticate header points them here. It tells them which authorization server to use.

{
"resource": "https://your-instance",
"authorization_servers": ["https://your-subdomain.authkit.app"],
"bearer_methods_supported": ["header"]
}

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

Section titled “GET /.well-known/oauth-authorization-server”

RFC 8414 Authorization Server Metadata, proxied from AuthKit for older MCP clients that look for this endpoint instead of Protected Resource Metadata.

  1. Client POSTs to /mcp with no token and gets 401 + WWW-Authenticate carrying the resource_metadata URL.
  2. Client fetches /.well-known/oauth-protected-resource to learn the authorization server (AuthKit).
  3. Client runs the OAuth authorization-code flow against AuthKit, opening a browser for user login.
  4. AuthKit issues a JWT; the client attaches it as Authorization: Bearer <jwt> on every /mcp request thereafter.

NimbleBrain verifies each access token against AuthKit’s JWKS endpoint (RS256, issuer validation). When organizationId is set on the adapter, only members of that WorkOS organization can authenticate.

Each initialize creates a new MCP session tracked server-side and addressed by the Mcp-Session-Id response header. Include that header on every subsequent request in the session.

Sessions are closed on DELETE /mcp (with a valid Mcp-Session-Id), transport close, idle-TTL eviction, or server shutdown.

When the server can’t serve a request because the session isn’t available, the 404 response includes error.data.reason:

{
"jsonrpc": "2.0",
"error": {
"code": -32000,
"message": "Session not found",
"data": { "reason": "not_found" }
},
"id": null
}
ReasonMeaning
not_foundThe session registry has no record. Most often idle-TTL eviction, or the session never existed.
unavailableThe session exists in the registry, but the live transport isn’t on this process. Possible causes: process restart since initialize, or a sticky-routing miss.

Spec-compliant clients re-initialize on either reason.

The OAuth resource URL advertised on /.well-known/oauth-protected-resource (and the one in the WWW-Authenticate header) must match the scheme the client connected on. If you terminate TLS at an upstream proxy, the pod sees an internal http:// connection while the client used https://, which makes OAuth resource validation fail.

NimbleBrain honors X-Forwarded-Proto to derive the advertised scheme. Configure your proxy (ALB, nginx, Caddy) to set it based on the actual client connection. The Host header (url.host) is used verbatim; X-Forwarded-Host is deliberately not honored.

The endpoint aggregates:

  • Tools from installed and running bundles across your workspaces, plus identity-owned tools.
  • Tool discoverytools/list returns every tool visible to the caller.
  • Tool executiontools/call routes to the correct workspace and bundle based on the namespaced tool name.

Two filters apply to both tools/list and tools/call:

  • Feature flags — tools whose controlling flag is false are excluded (for example, the skills__* authoring tools when skillManagement is false). See Feature Flags.
  • Role — admin-only tools (such as nb__manage_users, nb__manage_workspaces) are excluded when the caller’s role is not admin or owner.

In dev mode (no auth), you can hit the endpoint with curl:

Terminal window
# Initialize a session
curl -i -X POST http://localhost:27247/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": { "name": "curl", "version": "1.0" }
}
}'

Extract the Mcp-Session-Id response header and use it on subsequent calls:

Terminal window
curl -X POST http://localhost:27247/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Mcp-Session-Id: <session-id>" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":2}'

With auth enabled, include Authorization: Bearer <jwt> on every request.