> ## Documentation Index
> Fetch the complete documentation index at: https://mcpjam-docs-uer-50-remove-em-dashes.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
* **Author eval suites**: create a runnable suite (name, default model, servers, test cases) synchronously and get a `suiteId` back; no run is started and no credits are spent
* **Run eval suites asynchronously**: create a run from a saved suite, 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`. Each test's case body is an ordered `steps` array (prompt / toolCall / interact / assert); per-test `model`/`provider`/`runs` 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",
      "runs": 1,
      "model": "anthropic/claude-haiku-4.5",
      "provider": "anthropic",
      "steps": [
        { "id": "s1", "kind": "prompt", "prompt": "Use the echo tool to say hi" },
        {
          "id": "s2",
          "kind": "assert",
          "assertion": {
            "type": "toolCalledWith",
            "toolName": "echo",
            "args": { "args": { "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.
