Skip to content

MCP API

The MCP API has two route groups: a project-scoped management route set and the streamable-HTTP tool gateway an MCP client speaks. All management routes live under /v1/projects/:projectId/mcp/...; there is deliberately no global (non-project-scoped) route.

View as Markdown Open the plain-text version of this page.
Route group Base path Runtime Auth
Management <api-origin>/v1/projects/:projectId/mcp/... Node + API Worker Bearer key with mcp:manage, or session with project read/project:mcp:manage
Tool gateway <mcp-origin>/v1?mcp=<target> Node + MCP Worker Project API key with mcp:invoke only (sessions rejected)

?mcp=<target> is required on every tool-gateway request (otherwise 400 MCP_ID_REQUIRED); see Tool gateway for the provon / duckduckgo / <serverId> values. On Node the shared-origin path is /mcp/v1?mcp=<target>.

Local Node: http://127.0.0.1:3000 (both groups on one origin; the gateway at /mcp/v1). Hosted: management under https://api.provon.dev/v1, the tool gateway under https://mcp.provon.dev/v1. When the management service is not wired on a runtime, management routes return 501 MCP_MANAGEMENT_UNAVAILABLE. On the edge, the API Worker hosts the management routes and the MCP Worker hosts the HTTP tool gateway.

Servers#

List servers#

http
GET /v1/projects/:projectId/mcp/servers

Management read. Returns { "servers": McpServer[] }.

Create a server#

http
POST /v1/projects/:projectId/mcp/servers

Management write. 201. Returns { "server": McpServer }.

json
{
  "displayName": "Acme internal tools",
  "sourceKind": "mcp",
  "baseUrl": "https://mcp.acme.example.com",
  "openapiSpec": null,
  "enabled": true
}
Field Required Notes
displayName yes non-empty string
sourceKind yes "mcp" or "openapi"
baseUrl yes for MCP HTTP upstream endpoint
openapiSpec when sourceKind: "openapi" the raw OpenAPI object
enabled no defaults true

The server record carries connectionStatus (unknown | reachable | unreachable | disabled) separately from enabled; connection health and authentication posture are distinct states and are never collapsed into one status.

Update a server#

http
PATCH /v1/projects/:projectId/mcp/servers/:sid

Management write. Body may include displayName, baseUrl, openapiSpec, and/or enabled. Returns { "server": McpServer }. Unknown server → 404 MCP_SERVER_NOT_FOUND.

Delete a server#

http
DELETE /v1/projects/:projectId/mcp/servers/:sid

Management write. 204 on success.

Test connection#

http
POST /v1/projects/:projectId/mcp/servers/:sid/test

Management write. Opens an outbound HTTP connection and records status. Returns { "ok": bool, "connectionStatus": "unknown"|"reachable"|"unreachable"|"disabled", "message": string|null, "toolCount": number|null }.

Discover (read-only)#

http
POST /v1/projects/:projectId/mcp/servers/:sid/discover

Management write. Lists the upstream's tools without writing the catalog, publishing, or changing the stored connection status. Returns { "reachable": bool, "classification": string, "message": string|null, "tools": Tool[] }; the provisional tools become catalog entries only after an explicit sync.

Sync catalog#

http
POST /v1/projects/:projectId/mcp/servers/:sid/sync

Management write. Works on Node and Worker for OpenAPI servers (pure normalization) and native HTTP MCP servers (edge outbound fetch). Returns { "catalogVersion": number, "tools": Tool[], "archivedTools": Tool[] }.

Credentials#

Sealed upstream secrets. Credentials are typed by kind (static_secret today; oauth2 reserved) and by the whitelisted outbound authMethod.

List credentials#

http
GET /v1/projects/:projectId/mcp/servers/:sid/credentials

Management read. Returns { "credentials": Credential[] } where each credential exposes id, serverId, label, kind (static_secret | oauth2), authMethod (authorization_bearer | x_api_key), role (primary | backup), enabled, lastVerifiedAt, and createdAt — never the secret. Exactly one credential is the active primary; every other credential is a backup held for cutover. There is no numeric selection priority.

Create a credential#

http
POST /v1/projects/:projectId/mcp/servers/:sid/credentials

Management write. 201. Returns { "credential": Credential }.

json
{
  "label": "acme-robot",
  "secret": "<upstream token>",
  "authMethod": "authorization_bearer",
  "enabled": true
}

The secret is sealed at rest. authMethod selects the outbound header whitelist (Authorization: Bearer or X-Api-Key). New credentials are backup until an isolation test promotes them.

Test a credential#

http
POST /v1/projects/:projectId/mcp/servers/:sid/credentials/:cid/test

Management write. Isolation-tests the sealed secret against the upstream WITHOUT making it the primary. Returns { "ok": bool, "classification": string, "message": string|null }.

Promote a credential#

http
POST /v1/projects/:projectId/mcp/servers/:sid/credentials/:cid/promote

Management write. Atomically cuts over an isolation-verified credential as the server's primary — the store demotes the current primary and promotes this one in one batch. Returns { "credential": Credential }.

Pause / resume a credential#

http
POST /v1/projects/:projectId/mcp/servers/:sid/credentials/:cid/pause
POST /v1/projects/:projectId/mcp/servers/:sid/credentials/:cid/resume

Management write. Holds a credential (never used on the outbound path) or re-enables it without revoking.

Revoke a credential#

http
DELETE /v1/projects/:projectId/mcp/servers/:sid/credentials/:cid

Management write. 204; unknown → 404 MCP_CREDENTIAL_NOT_FOUND.

Tool catalog#

Project-wide catalog (Workbench)#

http
GET /v1/projects/:projectId/mcp/tools

Management read. Merges code-defined hosted tools with persisted federation tools into one read-only view. Returns { "tools": CatalogToolView[], "catalogVersion": number }. Each view carries origin (hosted | federation), serverId (null for hosted), normalizedToolName, title, description, inputSchema, publishState, catalogVersion, lastSyncedAt, httpMethod/httpPath (OpenAPI only), connection display facts (never the upstream URL), defaultAllowed (only hosted tools are pre-approved), and availability ({ state: "available" } or { state: "unavailable", reason: "draft"|"archived"|"server_disabled"|"server_unreachable" }). A federation row stays on the page for management even when its upstream is unreachable; callers cannot invoke it while the auth posture is missing, invalid, or revoked.

List tools for a server#

http
GET /v1/projects/:projectId/mcp/servers/:sid/tools

Management read. Returns { "tools": Tool[] } — every cataloged tool for the server, scoped to the project and server regardless of publish state.

Set publish state#

http
PATCH /v1/projects/:projectId/mcp/servers/:sid/tools/:tid

Management write. Body { "publishState": "draft"|"published"|"deprecated"|"archived" }. Only published and deprecated are listable and invocable. Returns { "tool": Tool }. Unknown → 404 MCP_TOOL_NOT_FOUND.

OpenAPI import#

http
POST /v1/projects/:projectId/mcp/openapi/import

Management write. 201. The body supplies the OpenAPI document by reference or inline, but exactly one of:

json
{ "displayName": "Optional name", "specUrl": "https://example.com/openapi.json" }
json
{ "displayName": "Optional name", "specText": "<uploaded OpenAPI document contents>" }

Omitting both or sending both returns 400 INVALID_FIELD. There is no inline spec object field. Returns { "server": Server, "tools": Tool[] }. Each imported operation becomes a draft tool; it is not discoverable or callable until explicitly published with the PATCH .../tools/:tid route. See OpenAPI import for operation-to-tool mapping.

Access policies#

Explicit principal allow/deny. Default-deny, deny-overrides, narrowing-only. The built-in hosted tools (the four provon tools plus duckduckgo web_search) are pre-approved and need no policy; federation tools stay closed until allowed.

List policies#

http
GET /v1/projects/:projectId/mcp/policies

Management read. { "policies": Policy[] }.

Create a policy#

http
POST /v1/projects/:projectId/mcp/policies

Management write. 201. Returns { "policy": Policy }.

json
{
  "effect": "allow",
  "priority": 100,
  "principalKind": "api_key",
  "principalRef": "<invoking api key id>",
  "serverIds": null,
  "toolNames": null,
  "actions": ["read", "invoke"]
}
Field Notes
effect "allow" or "deny"
priority integer; higher evaluated first under deny-overrides (default 100)
principalKind user | api_key
principalRef an opaque id (user id or API key id)
actions one or more of read, invoke (there is no manage tool action)
serverIds null = all, or an explicit array
toolNames null = all, or an explicit array

Delete a policy#

http
DELETE /v1/projects/:projectId/mcp/policies/:pid

Management write. 204; unknown → 404 MCP_POLICY_NOT_FOUND.

Gateway enablement#

The project MCP tool gateway can be paused or resumed as a whole:

http
GET  /v1/projects/:projectId/mcp/settings
POST /v1/projects/:projectId/mcp/pause
POST /v1/projects/:projectId/mcp/resume

GET requires a management read; pause and resume require mcp:manage. Each returns the current { "settings": ... }.

Tool gateway (streamable HTTP MCP)#

http
POST /v1?mcp=<target>      # Hosted MCP Worker
POST /mcp/v1?mcp=<target>  # Node, shared origin

The ?mcp= query parameter is required on every request; omitting it returns 400 MCP_ID_REQUIRED. Its value selects which tools are exposed:

  • ?mcp=provon — the hosted Provon tools (search_traces, get_trace, list_findings, get_finding);
  • ?mcp=duckduckgo — the hosted web_search tool;
  • ?mcp=<serverId> — a federated MCP server's published tools.

On Node (shared origin) the endpoint is POST /mcp/v1; the Worker also answers GET/DELETE session probes on the same path.

Runs on Node and the MCP Worker. Speak JSON-RPC 2.0 (stateless) with an mcp:invoke-bearing key. This ingress accepts project API keys only: a Workspace session (or any non-API-key caller) receives 401 UNAUTHORIZED; a key without mcp:invoke is rejected. A paused project gateway returns 503 MCP_PAUSED. Supported methods:

Method Purpose
initialize handshake; advertises the server name/version (provon-mcp)
notifications/initialized post-init notification
ping liveness
tools/list discover hosted + published tools inside the principal's scope
tools/call invoke a tool by name

Errors use the reserved JSON-RPC range: -32601 method not found, -32000 session expired, -32001 permission denied, -32002 rate limited, -32003 upstream failure, -32004 gateway paused, -32603 internal. (On the HTTP ingress a paused gateway returns 503 MCP_PAUSED before the JSON-RPC body; -32004 is the JSON-RPC code surfaced on bypass paths such as the Workbench playground.)

Upstream HTTP-status → JSON-RPC mapping differs by transport:

  • Native MCP (both runtimes): 401/403 → -32001, 404 → -32601, 429 → -32002, other HTTP → -32003, transport/timeout → -32003.
  • OpenAPI direct-fetch on Node: every non-2xx (including 401/403/404/500) → -32001; fetch abort / network error → -32000.
  • OpenAPI direct-fetch on the Worker: every non-2xx and every transport / blocked-host / over-size error → -32003.

See Security for guardrail and audit behavior.