Skip to main content
The MCPJam API is in preview. It works today and we use it ourselves, but it has not been generally announced and the surface may change — including in breaking ways — while we finish the design. Pin your integration to the behaviors documented on this page, build a tolerant reader, and expect to revisit it. Feedback is very welcome on Discord or GitHub.

Create an API key

Go straight to key management in the hosted app. If you’re signed out, you’ll be asked to sign in and then land right back on the API keys page.
The MCPJam API lets you operate the MCP servers saved in your hosted projects — from CI, scripts, or your own agents — without opening the UI. What you can do with it today:
  • Discover your resources — list your projects, their servers, eval suites, and chat sessions, so every ID the other routes need is self-serve
  • Validate a server — connect, initialize, and capture a capability snapshot
  • Run the doctor — the probe → connect → initialize → capabilities workflow
  • Check OAuth requirements — does this server need an OAuth grant?
  • List tools, prompts, and resources — the server’s MCP primitives
  • Call a tool / render a prompt — execute primitives and get the MCP result back verbatim
  • Read a resource by URI
  • Export a full snapshot — tools, resources, and prompts as one JSON document
  • Manage a project’s hosts — list, read, create (from a built-in template like claude/chatgpt/cursor, or from a full host config), rename, and delete the named model + capability profiles you run chats and eval suites against
  • List a project’s chatboxes — name, access mode, attached servers, and share link — and read one chatbox’s settings: model, system prompt, tool-approval policy, and resolved servers
  • Run eval suites asynchronously — create a run, get a 202 + runId immediately, then poll status, per-iteration results (tool calls, token usage, latency), and full traces. Runs appear live in the hosted UI, tagged source: "api"
  • Import OAuth tokens — complete OAuth yourself (e.g. the SDK’s runOAuthLogin) and push the tokens; subsequent calls inject and refresh them server-side
All endpoints operate on servers you have already added to a project in the hosted inspector. Managing API keys remains UI-only — see Not in the API yet.

Base URL

https://app.mcpjam.com/api/v1
The API is path-versioned. All routes on this page are relative to the base URL above.

Authentication

Every request needs an MCPJam API key in the Authorization header:
curl -X POST \
  "https://app.mcpjam.com/api/v1/projects/$PROJECT_ID/servers/$SERVER_ID/tools" \
  -H "Authorization: Bearer $MCPJAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{}'

Creating a key

  1. Open Settings → API keys in the hosted app (the card at the top of this page takes you there directly).
  2. Click Create API key, name it, and pick the organization it will act in.
  3. Copy the key (sk_…) immediately — it is shown exactly once and never stored by MCPJam in retrievable form.
Keys can be revoked from the same page at any time. Revocation takes effect immediately.

Scope

A key is bound to one MCPJam organization at creation and acts as you, inside that organization. Per-request authorization still applies: a call only succeeds if the key’s owner can access that project and server. A key can never reach projects outside its organization.
Two deliberate restrictions while in preview:
  • API keys cannot manage API keys. Requests to the key-management surface with an sk_… bearer fail with 403 FORBIDDEN. Create and revoke keys in the UI.
  • Guest sessions cannot use the API. Sign in to create keys.

Keep keys secret

Treat an API key like a password:
  • Load it from an environment variable or secret manager — never commit it to source control or ship it in client-side code.
  • Scope keys narrowly: one key per integration, named so you can tell them apart.
  • Rotate by creating a replacement key, switching traffic, then revoking the old one.
  • If a key leaks, revoke it immediately in Settings → API keys.
If you see 401 UNAUTHORIZED with details.reason: "ORPHANED_KEY", the key is no longer bound to an organization and cannot be used — create a new key from Settings.

Conventions

Requests. Operations on servers are POST with a JSON body (most accept an empty {}). Reads — the catalog listings and eval-run polling — are GET and take their options (cursor, limit, filters) as query parameters. Responses. Three envelope shapes, used consistently:
KindShape
Single resourceThe resource object directly
Collection{ "items": [...], "nextCursor": "..." }nextCursor omitted on the last page
Error{ "code": "...", "message": "...", "details": {...} } with a matching HTTP status
Pagination. Collections are cursor-based. Pass the previous response’s nextCursor as cursor in the next request (body field on POST lists, query parameter on GET lists). Cursors are opaque — don’t parse them. Authoring vs running suites. POST /eval-suites creates a runnable suite (the suite plus its test cases) synchronously and responds 201 with the suiteId, WITHOUT running anything — author once, run later. POST /eval-runs is the async counterpart: it validates and creates a run synchronously, then detaches execution and responds 202 with a runId. Poll GET /eval-runs/{runId} until status is completed, failed, or cancelled. IDs. Every identifier the API takes is discoverable through the API itself: GET /projects lists your projects, GET /projects/{projectId}/servers lists each project’s servers, and GET /projects/{projectId}/eval-suites lists its eval suites. Start from GET /me to confirm which account a key acts as.

Errors

Errors always use the canonical body { code, message, details? }. The code is stable and machine-readable; message is human-readable and may change; details is an optional, unstructured bag.
CodeHTTPMeaning
UNAUTHORIZED401Missing, invalid, revoked, or orphaned key
OAUTH_REQUIRED401The target MCP server needs an OAuth grant — distinct from a bad key
FORBIDDEN403Key is valid but not allowed to do this
VALIDATION_ERROR400Malformed body or parameters
NOT_FOUND404Unknown project, server, or resource
FEATURE_NOT_SUPPORTED422The target server doesn’t support this MCP capability
RATE_LIMITED429Too many requests — see Rate limits
SERVER_UNREACHABLE502Could not connect to the target MCP server
TIMEOUT504The target MCP server connected but didn’t respond in time
INTERNAL_ERROR500Something failed on our side
New error codes may be added over time; treat unknown codes as non-retryable failures unless the HTTP status says otherwise.

Rate limits

Each key gets 60 requests per minute sustained, with bursts up to 10. Exceeding it returns 429 RATE_LIMITED with a Retry-After header (in seconds):
HTTP/1.1 429 Too Many Requests
Retry-After: 7

{ "code": "RATE_LIMITED", "message": "API key rate limit exceeded. Slow down and retry." }
Honor Retry-After, add jittered exponential backoff, and expect these limits to be tuned during the preview.

Endpoints

Each endpoint is fully documented — request and response schemas, examples, and an interactive playground — under Endpoints in the sidebar. Catalog — discover the IDs everything else takes:
EndpointWhat it does
GET /meThe account behind the key
GET /projectsProjects the key can access (optionally ?organizationId=)
GET /projects/{projectId}/serversThe project’s saved MCP servers
GET /projects/{projectId}/eval-suitesThe project’s eval suites, with latest-run summaries
GET, POST /projects/{projectId}/hostsList hosts, or create one from a template or full config
GET, PATCH, DELETE /projects/{projectId}/hosts/{hostId}Read, rename/edit, or delete a host
GET /chat-sessionsChat sessions (?projectId=&status=&limit=&before=)
Server diagnostics & primitives (/projects/{projectId}/servers/{serverId}/...):
EndpointWhat it does
POST .../validateConnect, initialize, and capture a capability snapshot
POST .../doctorFull health workflow: probe → connect → initialize → capabilities
POST .../check-oauthDoes this server require an OAuth grant?
POST .../toolsList the server’s tools
POST .../tools/callExecute a tool; returns the MCP CallToolResult verbatim
POST .../promptsList the server’s prompts
POST .../prompts/getRender a prompt; returns the MCP GetPromptResult verbatim
POST .../resourcesList the server’s resources
POST .../resources/readRead one resource by URI
POST .../exportOne JSON snapshot of tools, resources, and prompts
POST .../oauth/import-tokensStore OAuth tokens you obtained yourself; later calls inject + refresh them
Eval runs (/projects/{projectId}/...):
EndpointWhat it does
POST .../eval-suitesAuthor-only: create a suite + its test cases from name + serverIds + a default model + tests, WITHOUT running it; responds 201 + suiteId. Per-test model/provider/runs/expectedToolCalls are optional and fall back to suite defaults
POST .../eval-runsCreate a run from a suiteId (rerun; serverIds optional — defaults to the suite’s saved servers) or suiteName + inline tests + serverIds (new suite); responds 202 + runId
GET .../eval-runs/{runId}Run status, result, and summary — poll until terminal
GET .../eval-runs/{runId}/iterationsPer-iteration results: tool calls, token usage, latency (paginated)
GET .../eval-runs/{runId}/iterations/{iterationId}/traceFull trace: messages + expected-vs-actual analysis
GET .../eval-suites/{suiteId}/runsRecent runs for a suite, newest first
Eval result ingestion (/projects/{projectId}/eval-ingest/...) — how @mcpjam/sdk saves results from eval runs executed outside the platform (local dev, CI). The {projectId} segment accepts the literal default for the key org’s Default project. Most users never call these directly — set MCPJAM_API_KEY and the SDK reporter does:
EndpointWhat it does
POST .../eval-ingest/reportOne-shot: create a run from finished results and finalize it
POST .../eval-ingest/runs/startChunked flow: create (or idempotently reuse) a run by externalRunId
POST .../eval-ingest/runs/iterationsAppend an iteration batch to a started run
POST .../eval-ingest/runs/finalizeFinalize a chunked run (idempotent)
POST .../eval-ingest/artifacts/upload-urlMint an upload URL for eval artifacts (JUnit XML, Jest/Vitest JSON)
Chatboxes (/projects/{projectId}/chatboxes...) — read-only:
EndpointWhat it does
GET .../chatboxesList the project’s published chatboxes: name, access mode, attached servers, share link
GET .../chatboxes/{chatboxId}One chatbox’s settings: model, system prompt, tool-approval policy, resolved servers
The same surface is available as an OpenAPI specification for Postman, Swagger UI, or client generation.

Run evals from the API

The shortest useful loop — create a run with one inline test, then poll:
# 1. Create (responds 202 immediately; -f + jq -e fail fast on errors)
RUN_ID=$(curl -fsS -X POST \
  "https://app.mcpjam.com/api/v1/projects/$PROJECT_ID/eval-runs" \
  -H "Authorization: Bearer $MCPJAM_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "suiteName": "smoke",
    "serverIds": ["'$SERVER_ID'"],
    "tests": [{
      "title": "echo works",
      "query": "Use the echo tool to say hi",
      "runs": 1,
      "model": "anthropic/claude-haiku-4.5",
      "provider": "anthropic",
      "expectedToolCalls": [{ "toolName": "echo", "arguments": { "text": "hi" } }]
    }]
  }' | jq -er .runId)

# 2. Poll until status is completed | failed | cancelled
curl -s "https://app.mcpjam.com/api/v1/projects/$PROJECT_ID/eval-runs/$RUN_ID" \
  -H "Authorization: Bearer $MCPJAM_API_KEY"

# 3. Inspect per-iteration tool calls, token usage, latency
curl -s "https://app.mcpjam.com/api/v1/projects/$PROJECT_ID/eval-runs/$RUN_ID/iterations" \
  -H "Authorization: Bearer $MCPJAM_API_KEY"
model takes an id from the hosted catalog — provider/name form, e.g. anthropic/claude-haiku-4.5 — and runs on your organization’s credits. Provider-native ids (e.g. claude-sonnet-4-5) are bring-your-own-key: pass the key in modelApiKeys. A model the API can’t execute is rejected at create time with VALIDATION_ERROR; details.hostedModels lists the valid hosted ids for that provider. Reruns are even shorter: { "suiteId": "..." } reruns the suite exactly as configured, connecting the suite’s saved server selection (the 202 response lists the resolved servers). Pass serverIds to override the selection; a suite with no saved selection requires it (VALIDATION_ERROR with details.reason: "NO_SAVED_SERVER_SELECTION" otherwise). Per-organization concurrency is capped (default 2 concurrent runs); exceeding it returns 429 with details.reason: "CONCURRENT_RUN_LIMIT" — wait for an active run to finish. If a server answers 401 OAUTH_REQUIRED, complete the OAuth flow yourself (the SDK’s runOAuthLogin handles interactive, headless, and client-credentials flows) and push the result to POST .../oauth/import-tokens once. Subsequent calls inject the stored token and refresh it server-side.

Not in the API yet

To set expectations while in preview, these are not available over the API today (most exist in the hosted inspector UI):
  • Creating or revoking API keys (UI-only by design — see Authentication)
  • Chat and conformance suites
  • Browser-based OAuth flows initiated by the API (use oauth/import-tokens after completing OAuth yourself)
If one of these blocks you, tell us on Discord — it directly shapes what we stabilize first.

Versioning & stability

  • The API is path-versioned (/api/v1). When it reaches general availability, breaking changes will require a new version path.
  • During the preview, breaking changes to v1 may still happen; we’ll note them in the changelog.
  • Additive changes — new endpoints, new optional request fields, new response fields, new error codes — are considered non-breaking and can ship at any time. Write clients that ignore unknown fields.
  • Error codes are stable identifiers; error messages are not.