> ## Documentation Index
> Fetch the complete documentation index at: https://mcpjam-mintlify-docs-update-pr-2772-1782277917413.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# MCPJam API

> Programmatic access to the MCP servers in your MCPJam projects — diagnostics, tool calls, prompt rendering, and asynchronous eval runs.

<Warning>
  **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](#versioning--stability), and expect to revisit it. Feedback is very
  welcome on [Discord](https://discord.gg/JEnDtz8X6z) or
  [GitHub](https://github.com/MCPJam/inspector/issues).
</Warning>

<Card title="Create an API key" icon="key" href="https://app.mcpjam.com/settings/api-keys" horizontal>
  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.
</Card>

The MCPJam API lets you operate the MCP servers saved in your
[hosted](/hosted/overview) 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](#not-in-the-api-yet).

## Base URL

```text theme={null}
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:

```bash theme={null}
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**](https://app.mcpjam.com/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.

<Note>
  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.
</Note>

### 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**](https://app.mcpjam.com/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](https://app.mcpjam.com/settings/api-keys).

## 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:

| Kind            | Shape                                                                               |
| --------------- | ----------------------------------------------------------------------------------- |
| Single resource | The 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.

| Code                    | HTTP | Meaning                                                                  |
| ----------------------- | ---- | ------------------------------------------------------------------------ |
| `UNAUTHORIZED`          | 401  | Missing, invalid, revoked, or orphaned key                               |
| `OAUTH_REQUIRED`        | 401  | The **target MCP server** needs an OAuth grant — distinct from a bad key |
| `FORBIDDEN`             | 403  | Key is valid but not allowed to do this                                  |
| `VALIDATION_ERROR`      | 400  | Malformed body or parameters                                             |
| `NOT_FOUND`             | 404  | Unknown project, server, or resource                                     |
| `FEATURE_NOT_SUPPORTED` | 422  | The target server doesn't support this MCP capability                    |
| `RATE_LIMITED`          | 429  | Too many requests — see [Rate limits](#rate-limits)                      |
| `SERVER_UNREACHABLE`    | 502  | Could not connect to the target MCP server                               |
| `TIMEOUT`               | 504  | The target MCP server connected but didn't respond in time               |
| `INTERNAL_ERROR`        | 500  | Something 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 theme={null}
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:

| Endpoint                                                      | What it does                                                |
| ------------------------------------------------------------- | ----------------------------------------------------------- |
| `GET /me`                                                     | The account behind the key                                  |
| `GET /projects`                                               | Projects the key can access (optionally `?organizationId=`) |
| `GET /projects/{projectId}/servers`                           | The project's saved MCP servers                             |
| `GET /projects/{projectId}/eval-suites`                       | The project's eval suites, with latest-run summaries        |
| `GET`, `POST /projects/{projectId}/hosts`                     | List 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-sessions`                                          | Chat sessions (`?projectId=&status=&limit=&before=`)        |

**Server diagnostics & primitives** (`/projects/{projectId}/servers/{serverId}/...`):

| Endpoint                       | What it does                                                                |
| ------------------------------ | --------------------------------------------------------------------------- |
| `POST .../validate`            | Connect, initialize, and capture a capability snapshot                      |
| `POST .../doctor`              | Full health workflow: probe → connect → initialize → capabilities           |
| `POST .../check-oauth`         | Does this server require an OAuth grant?                                    |
| `POST .../tools`               | List the server's tools                                                     |
| `POST .../tools/call`          | Execute a tool; returns the MCP `CallToolResult` verbatim                   |
| `POST .../prompts`             | List the server's prompts                                                   |
| `POST .../prompts/get`         | Render a prompt; returns the MCP `GetPromptResult` verbatim                 |
| `POST .../resources`           | List the server's resources                                                 |
| `POST .../resources/read`      | Read one resource by URI                                                    |
| `POST .../export`              | One JSON snapshot of tools, resources, and prompts                          |
| `POST .../oauth/import-tokens` | Store OAuth tokens you obtained yourself; later calls inject + refresh them |

**Eval runs** (`/projects/{projectId}/...`):

| Endpoint                                                   | What it does                                                                                                                                                                                                                                              |
| ---------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `POST .../eval-suites`                                     | Author-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-runs`                                       | Create 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}/iterations`                     | Per-iteration results: tool calls, token usage, latency (paginated)                                                                                                                                                                                       |
| `GET .../eval-runs/{runId}/iterations/{iterationId}/trace` | Full trace: messages + expected-vs-actual analysis                                                                                                                                                                                                        |
| `GET .../eval-suites/{suiteId}/runs`                       | Recent 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](/sdk/concepts/saving-results) does:

| Endpoint                                    | What it does                                                          |
| ------------------------------------------- | --------------------------------------------------------------------- |
| `POST .../eval-ingest/report`               | One-shot: create a run from finished results and finalize it          |
| `POST .../eval-ingest/runs/start`           | Chunked flow: create (or idempotently reuse) a run by `externalRunId` |
| `POST .../eval-ingest/runs/iterations`      | Append an iteration batch to a started run                            |
| `POST .../eval-ingest/runs/finalize`        | Finalize a chunked run (idempotent)                                   |
| `POST .../eval-ingest/artifacts/upload-url` | Mint an upload URL for eval artifacts (JUnit XML, Jest/Vitest JSON)   |

**Chatboxes** (`/projects/{projectId}/chatboxes...`) — read-only:

| Endpoint                        | What it does                                                                            |
| ------------------------------- | --------------------------------------------------------------------------------------- |
| `GET .../chatboxes`             | List 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](https://raw.githubusercontent.com/MCPJam/inspector/main/docs/reference/openapi.json)
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:

```bash theme={null}
# 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](#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](https://discord.gg/JEnDtz8X6z) — 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](/changelog/overview).
* **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.
