{ "openapi": "3.1.0", "info": { "title": "MCPJam API", "version": "1.0.0-preview", "description": "Programmatic access to MCP servers saved in your MCPJam projects — live diagnostics (validate, inspect, export) and operations: call tools, render prompts, run eval suites asynchronously and poll their results, and import OAuth tokens.\n\n**The API is in preview**: the surface may change while we finish the design. Error `code` values are stable; error `message` strings are not. Write clients that ignore unknown response fields.", "contact": { "name": "MCPJam", "url": "https://github.com/MCPJam/inspector/issues" } }, "servers": [ { "url": "https://app.mcpjam.com/api/v1", "description": "Hosted MCPJam" } ], "security": [{ "bearerAuth": [] }], "tags": [ { "name": "Hosts", "description": "Project hosts: named model + capability profiles you run chats and eval suites against." }, { "name": "Computer environments", "description": "Custom Computer images: digest-pinned Dockerfile environments your project's computers boot from." }, { "name": "Server diagnostics", "description": "Connect-level health checks against a saved MCP server." }, { "name": "Primitives", "description": "The server's MCP primitives: tools, prompts, and resources." }, { "name": "Export", "description": "Full-server snapshots for diffing and CI." }, { "name": "Execution", "description": "Run the server's primitives: call tools, render prompts." }, { "name": "Eval runs", "description": "Asynchronous eval suite runs: create with 202, poll status, iterations, and traces." }, { "name": "OAuth", "description": "Bring-your-own OAuth: import externally obtained tokens for a server." }, { "name": "Chatboxes", "description": "Read-only access to the chatboxes published from a project: listing, settings, attached servers, and share links." }, { "name": "Catalog", "description": "Discover the resources the other routes operate on: your account, projects, servers, eval suites, and chat sessions." }, { "name": "Tunnels", "description": "Relay tunnels that expose local MCP servers through a public URL, registered as first-class project servers (the `mcpjam tunnel` CLI flow)." } ], "paths": { "/harness/{harnessId}/builtin-tools": { "get": { "operationId": "listHarnessBuiltinTools", "tags": ["Harness"], "summary": "List a harness's native built-in tools", "description": "The native tools an agent harness (e.g. `claude-code`) runs INSIDE its sandbox — Bash, Read, Edit, Glob, Grep, WebSearch, and the like. Display-only: these execute via the harness's own agent loop and are NOT callable through MCPJam. Static published-package metadata; no project scope.", "parameters": [{ "name": "harnessId", "in": "path", "required": true, "schema": { "type": "string", "enum": ["claude-code"] }, "description": "The harness id." }], "responses": { "200": { "description": "The harness's built-in tools.", "content": { "application/json": { "schema": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "type": "object", "required": ["key", "name"], "properties": { "key": { "type": "string" }, "name": { "type": "string" }, "commonName": { "type": "string" }, "toolUseKind": { "type": "string" }, "description": { "type": "string" }, "inputSchema": { "type": "object", "additionalProperties": true } } } } } } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/computer-environments": { "get": { "operationId": "listComputerEnvironments", "tags": ["Computer environments"], "summary": "List a project's computer environments", "description": "The custom Computer images (digest-pinned Dockerfile environments) saved in the project — your own personal drafts plus the project-shared ones.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "responses": { "200": { "description": "The project's environments.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "post": { "operationId": "createComputerEnvironment", "tags": ["Computer environments"], "summary": "Create a computer environment", "description": "Create a personal-draft environment from a Dockerfile and respond `201` with its detail. Build it (`POST .../build`) before a computer can boot from it. Guest callers are denied (a write).", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentCreateRequest" }, "example": { "name": "scraper", "dockerfile": "FROM debian:bookworm-slim@sha256:...\nRUN apt-get update" } } } }, "responses": { "201": { "description": "The environment was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironment" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer-environments/{environmentId}": { "get": { "operationId": "getComputerEnvironment", "tags": ["Computer environments"], "summary": "Get a computer environment", "description": "One environment's Dockerfile, sharing, and latest build. An environment that exists but belongs to a different project reads as `404 NOT_FOUND`.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "responses": { "200": { "description": "The environment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironment" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "patch": { "operationId": "updateComputerEnvironment", "tags": ["Computer environments"], "summary": "Update a computer environment", "description": "Edit an environment's name and/or Dockerfile. Re-build it for the changes to take effect on a computer. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentUpdateRequest" }, "example": { "name": "scraper (v2)" } } } }, "responses": { "200": { "description": "The updated environment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironment" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "delete": { "operationId": "deleteComputerEnvironment", "tags": ["Computer environments"], "summary": "Delete a computer environment", "description": "Permanently delete an environment. Computers booted from it fall back to the base image. Deleting a project-shared environment requires project admin. Bodyless — any field is rejected. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "responses": { "200": { "description": "The environment was deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentDeleted" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer-environments/{environmentId}/builds": { "get": { "operationId": "listComputerEnvironmentBuilds", "tags": ["Computer environments"], "summary": "List an environment's builds", "description": "An environment's builds, newest first, each with its status and capped log preview.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "responses": { "200": { "description": "The environment's builds.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentBuildPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer-environments/{environmentId}/build": { "post": { "operationId": "buildComputerEnvironment", "tags": ["Computer environments"], "summary": "Build a computer environment", "description": "Trigger a build of the environment's image and respond `202`. The build runs asynchronously — poll the builds list for status. Bodyless. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "202": { "description": "The build was accepted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironmentBuildStarted" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer-environments/{environmentId}/promote": { "post": { "operationId": "promoteComputerEnvironment", "tags": ["Computer environments"], "summary": "Share an environment with the project", "description": "Promote a personal-draft environment to a project-shared one. Requires project admin. Bodyless. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "The now-shared environment.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerEnvironment" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer-environments/{environmentId}/use": { "post": { "operationId": "useComputerEnvironment", "tags": ["Computer environments"], "summary": "Boot your computer from an environment", "description": "Attach the environment to the caller's computer, which re-provisions it from the pinned image (installed files are wiped). The environment must have a `ready` build, and the computer provider must match the build's. Bodyless. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/environmentId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "The environment was attached; the computer is re-provisioning.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerAttached" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/computer/reset": { "post": { "operationId": "resetComputer", "tags": ["Computer environments"], "summary": "Reset your computer to its image", "description": "Reset the caller's computer back to its current image, wiping mutable state. Bodyless. Guest callers are denied (a write).", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "The reset was requested.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ComputerReset" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/hosts": { "get": { "operationId": "listHosts", "tags": ["Hosts"], "summary": "List a project's hosts", "description": "The hosts saved in the project — the named model + capability profiles you attach to chats and eval suites. Returns the `id`s the host detail/update/delete routes take.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "responses": { "200": { "description": "The project's hosts.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "post": { "operationId": "createHost", "tags": ["Hosts"], "summary": "Create a host (from a template or a full config)", "description": "Creates a host in the project and responds `201` with the new host's detail. Seed the host config one of two ways: pass `template` (a built-in template id, optional `theme`) to seed server-side from the same presets the inspector UI uses, OR pass `config` (a full host config v2). Exactly one of `template` or `config` is required.\n\nGuest callers are denied (host creation is a write).", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostCreateRequest" }, "example": { "name": "Claude", "template": "claude", "theme": "dark" } } } }, "responses": { "201": { "description": "The host was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostDetail" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/hosts/{hostId}": { "get": { "operationId": "getHost", "tags": ["Hosts"], "summary": "Get a host", "description": "One host's full settings, including its resolved host config. A host that exists but belongs to a different project reads as `404 NOT_FOUND`.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/hostId" } ], "responses": { "200": { "description": "The host settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostDetail" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "patch": { "operationId": "updateHost", "tags": ["Hosts"], "summary": "Update a host", "description": "Edit a host's display name and/or its host config. Only the fields you pass change. Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/hostId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostUpdateRequest" }, "example": { "name": "Claude (renamed)" } } } }, "responses": { "200": { "description": "The updated host settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostDetail" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "delete": { "operationId": "deleteHost", "tags": ["Hosts"], "summary": "Delete a host", "description": "Permanently delete a host from the project. Pass `{ \"force\": true }` to delete a host that is still referenced (e.g. by an eval suite). Guest callers are denied (a write).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/hostId" } ], "requestBody": { "required": false, "content": { "application/json": { "schema": { "type": "object", "properties": { "force": { "type": "boolean", "description": "Delete even if the host is still referenced." } } } } } }, "responses": { "200": { "description": "The host was deleted.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/HostDeleted" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/servers/{serverId}/validate": { "post": { "operationId": "validateServer", "tags": ["Server diagnostics"], "summary": "Validate a server", "description": "Connects to the server, initializes the MCP session, and returns a connection snapshot (server info, negotiated capabilities). The same check the hosted inspector runs when you connect a server.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "Server connected and initialized.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ValidateResult" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/doctor": { "post": { "operationId": "runDoctor", "tags": ["Server diagnostics"], "summary": "Run the doctor", "description": "Runs the full doctor workflow — probe → connect → initialize → capabilities → primitives — and returns a step-by-step report. The richest signal for \"is this server healthy, and why not.\"", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "Doctor report. `status` summarizes the outcome; per-step detail is in `checks`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/DoctorReport" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/check-oauth": { "post": { "operationId": "checkOAuth", "tags": ["Server diagnostics"], "summary": "Check OAuth requirement", "description": "Lightweight, no MCP connection: reports whether the server is configured to require an OAuth grant.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "OAuth requirement for the server.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CheckOAuthResult" }, "example": { "useOAuth": true, "serverUrl": "https://mcp.example.com/mcp" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/servers/{serverId}/tools": { "post": { "operationId": "listTools", "tags": ["Primitives"], "summary": "List tools", "description": "Returns the server's tools as a collection page. Each item is the MCP tool definition (`name`, `description`, `inputSchema`, ...).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/cursorBody" }, "responses": { "200": { "description": "One page of tools.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ToolPage" }, "example": { "items": [ { "name": "create_issue", "description": "Create a new issue in the tracker", "inputSchema": { "type": "object", "properties": { "title": { "type": "string" } } } } ] } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/prompts": { "post": { "operationId": "listPrompts", "tags": ["Primitives"], "summary": "List prompts", "description": "Returns the server's prompts as a collection page.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/cursorBody" }, "responses": { "200": { "description": "One page of prompts.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/PromptPage" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/resources": { "post": { "operationId": "listResources", "tags": ["Primitives"], "summary": "List resources", "description": "Returns the server's resources as a collection page.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/cursorBody" }, "responses": { "200": { "description": "One page of resources.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResourcePage" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/resources/read": { "post": { "operationId": "readResource", "tags": ["Primitives"], "summary": "Read a resource", "description": "Reads a single resource by URI and returns its contents.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["uri"], "properties": { "uri": { "type": "string", "description": "The resource URI, exactly as returned by the resources list.", "example": "file:///readme.md" } } } } } }, "responses": { "200": { "description": "The resource contents.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ResourceContents" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/export": { "post": { "operationId": "exportServer", "tags": ["Export"], "summary": "Export a server snapshot", "description": "Lists tools, resources, and prompts in one call and returns a single JSON snapshot — handy for diffing a server's surface over time in CI.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "$ref": "#/components/requestBodies/emptyBody" }, "responses": { "200": { "description": "Full server snapshot.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ExportSnapshot" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/tools/call": { "post": { "operationId": "callTool", "tags": ["Execution"], "summary": "Call a tool", "description": "Executes a tool on the server and returns the MCP `CallToolResult` directly. Tool-level failures (`isError: true` in the result) are **successful calls** — the server answered; only transport/auth errors use the error envelope.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["toolName"], "properties": { "toolName": { "type": "string", "description": "Tool name, exactly as returned by the tools list." }, "parameters": { "type": "object", "description": "Tool arguments matching the tool's `inputSchema`. Defaults to `{}`.", "additionalProperties": true } } }, "example": { "toolName": "create_issue", "parameters": { "title": "Bug: login fails" } } } } }, "responses": { "200": { "description": "The MCP `CallToolResult`. Check `isError` for tool-level failures.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/CallToolResult" }, "example": { "content": [{ "type": "text", "text": "Issue created: #42" }] } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/servers/{serverId}/prompts/get": { "post": { "operationId": "getPrompt", "tags": ["Execution"], "summary": "Render a prompt", "description": "Renders a prompt with arguments and returns the MCP `GetPromptResult` directly (`description?`, `messages`).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "type": "object", "required": ["promptName"], "properties": { "promptName": { "type": "string", "description": "Prompt name, exactly as returned by the prompts list." }, "arguments": { "type": "object", "description": "Prompt arguments. String, number, and boolean values.", "additionalProperties": { "type": ["string", "number", "boolean"] } } } }, "example": { "promptName": "summarize", "arguments": { "style": "bullet" } } } } }, "responses": { "200": { "description": "The MCP `GetPromptResult`.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/GetPromptResult" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "422": { "$ref": "#/components/responses/FeatureNotSupported" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/eval-runs": { "post": { "operationId": "createEvalRun", "tags": ["Eval runs"], "summary": "Create an eval run (async)", "description": "Creates a suite run from an existing `suiteId` (rerun) and/or inline `tests`, then **detaches execution and responds `202` immediately** with the `runId`. Validation and quota errors surface on this request; poll `GET /eval-runs/{runId}` for progress. The run appears live in the hosted UI Runs tab, tagged `source: \"api\"`.\n\nA bare `suiteId` with no inline tests reruns the suite as configured. Per-organization concurrency is capped (default 2 concurrent runs); exceeding it returns `429` with `details.reason: \"CONCURRENT_RUN_LIMIT\"`.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalRunCreateRequest" }, "example": { "suiteName": "smoke", "serverIds": ["srv_abc123"], "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" } } } } ] } ] } } } }, "responses": { "202": { "description": "Run created; execution continues in the background.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalRunCreated" }, "example": { "runId": "run_abc123", "suiteId": "suite_def456", "status": "running", "caseUpsert": { "committed": [{ "name": "echo works" }], "failed": [] } } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/projects/{projectId}/eval-runs/{runId}": { "get": { "operationId": "getEvalRun", "tags": ["Eval runs"], "summary": "Get run status", "description": "Run status, result, and summary. Poll until `status` is terminal (`completed`, `failed`, or `cancelled`).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/runId" } ], "responses": { "200": { "description": "The run.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalRun" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/eval-runs/{runId}/iterations": { "get": { "operationId": "listEvalRunIterations", "tags": ["Eval runs"], "summary": "List run iterations", "description": "Per-iteration results: actual tool calls, structured token usage, and latency. Cursor-paginated.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/runId" }, { "name": "limit", "in": "query", "required": false, "description": "Page size, 1–200. Defaults to 50.", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } }, { "name": "cursor", "in": "query", "required": false, "description": "Opaque cursor from a previous response's `nextCursor`.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "One page of iterations.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalIterationPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/eval-runs/{runId}/iterations/{iterationId}/trace": { "get": { "operationId": "getEvalIterationTrace", "tags": ["Eval runs"], "summary": "Get an iteration trace", "description": "Full trace envelope for one iteration: conversation messages, expected-vs-actual tool call analysis, and spans. The shape is rich and may evolve — treat it as an open document. Returns `404` with `details.reason: \"TRACE_NOT_AVAILABLE\"` when the iteration finished without a stored trace.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/runId" }, { "$ref": "#/components/parameters/iterationId" } ], "responses": { "200": { "description": "The trace envelope.", "content": { "application/json": { "schema": { "type": "object", "description": "Trace document: `messages`, `prompts` (per-prompt expected/actual tool-call analysis), `spans`, and more.", "additionalProperties": true } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/eval-runs/{runId}/iterations/{iterationId}/steps": { "get": { "operationId": "getEvalRunSteps", "tags": ["Eval runs"], "summary": "Get an iteration's step results", "description": "One row per authored test step, in order, with `status` (`ok`/`fail`/`skipped`/`pending`), a `reason`, and any `evidence` (screenshots, replay-video offset, widget tool calls). The fastest way to see which step failed and why. Unlike `/trace`, a missing trace is not a `404` — step verdicts still return, just without evidence.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/runId" }, { "$ref": "#/components/parameters/iterationId" } ], "responses": { "200": { "description": "The ordered step results as a page envelope.", "content": { "application/json": { "schema": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "description": "Authored steps in order with their verdict.", "items": { "type": "object", "required": ["stepId", "stepIndex", "kind", "status"], "properties": { "stepId": { "type": "string" }, "stepIndex": { "type": "integer" }, "kind": { "type": "string", "enum": ["prompt", "toolCall", "interact", "assert"] }, "status": { "type": "string", "enum": ["ok", "fail", "skipped", "pending"] }, "reason": { "type": ["string", "null"] }, "evidence": { "type": "object", "description": "Optional, omitted when the step produced none.", "additionalProperties": true } }, "additionalProperties": true } }, "nextCursor": { "type": "string" } }, "additionalProperties": true } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/eval-runs/{runId}/cancel": { "post": { "operationId": "cancelEvalRun", "tags": ["Eval runs"], "summary": "Cancel a run", "description": "Request cancellation of an in-flight run; marks the run and its pending/running iterations `cancelled`. A no-op success when the run is already cancelled; returns `409` when the run already reached a terminal status (`completed`/`failed`/`timed_out`).", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/runId" } ], "responses": { "200": { "description": "The cancelled run.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalRun" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "409": { "description": "The run already finished and cannot be cancelled." }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/eval-suites/{suiteId}/runs": { "get": { "operationId": "listEvalSuiteRuns", "tags": ["Eval runs"], "summary": "List a suite's runs", "description": "Recent runs for a suite, newest first.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/suiteId" }, { "name": "limit", "in": "query", "required": false, "description": "Maximum runs to return, 1–100. Defaults to 25.", "schema": { "type": "integer", "minimum": 1, "maximum": 100, "default": 25 } } ], "responses": { "200": { "description": "Recent runs, newest first.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalRunPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" } } } }, "/projects/{projectId}/servers/{serverId}/oauth/import-tokens": { "post": { "operationId": "importOAuthTokens", "tags": ["OAuth"], "summary": "Import OAuth tokens", "description": "Stores OAuth tokens you obtained yourself (e.g. via the SDK's `runOAuthLogin` — interactive loopback, headless, or client-credentials) for this server, scoped to your user, project, and server. Subsequent API calls against the server inject the stored access token automatically, and `401`s from the server trigger a server-side refresh — no further caller involvement.\n\nThis closes the loop after an `OAUTH_REQUIRED` error.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportTokensRequest" }, "example": { "serverUrl": "https://mcp.example.com/mcp", "clientInformation": { "clientId": "client_123" }, "tokens": { "access_token": "at_…", "refresh_token": "rt_…", "expires_in": 3600 } } } } }, "responses": { "200": { "description": "Tokens stored.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ImportTokensResult" }, "example": { "imported": true, "expiresAt": 1781142000000 } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" }, "504": { "$ref": "#/components/responses/Timeout" } } } }, "/me": { "get": { "operationId": "getMe", "tags": ["Catalog"], "summary": "Get the authenticated account", "description": "The user behind the API key (keys act as their creator, scoped to one organization).", "responses": { "200": { "description": "The account.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Me" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects": { "get": { "operationId": "listProjects", "tags": ["Catalog"], "summary": "List projects", "description": "Projects the caller can access. API keys only ever see projects inside the key's organization.", "parameters": [ { "name": "organizationId", "in": "query", "required": false, "description": "Filter to one organization.", "schema": { "type": "string" } } ], "responses": { "200": { "description": "The caller's projects.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/servers": { "get": { "operationId": "listProjectServers", "tags": ["Catalog"], "summary": "List a project's servers", "description": "The MCP servers saved in the project — the `serverId`s every other route takes. STDIO command/args/env and raw headers are never exposed.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "responses": { "200": { "description": "The project's servers.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ProjectServerPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/eval-suites": { "get": { "operationId": "listEvalSuites", "tags": ["Catalog"], "summary": "List a project's eval suites", "description": "Eval suites in the project with latest-run summaries and pass-rate trends — the `suiteId`s the eval-run routes take.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "responses": { "200": { "description": "The project's eval suites.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalSuitePage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } }, "post": { "operationId": "createEvalSuite", "tags": ["Eval runs"], "summary": "Create an eval suite (author-only, does not run)", "description": "Creates a runnable eval suite — the suite record plus its test cases — and responds `201` **synchronously**, WITHOUT executing anything. Use this to author a suite, then run it later with `POST /eval-runs` (passing the returned `suiteId`).\n\nThis is distinct from `POST /eval-runs`, which creates a run and **detaches execution**, responding `202` with a `runId`. There is no concurrency cap here (no run is started).\n\nThe body uses an ergonomic authoring shape: a suite-level default `model` (and optional `provider`) applies to every test unless the test overrides it; `provider` is derived from a `provider/model` id when neither is supplied. Each test's case body is an ordered `steps` array (prompt / toolCall / interact / assert).\n\nGuest callers are denied (suite creation is a write).", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalSuiteCreateRequest" }, "example": { "name": "smoke", "serverIds": ["srv_abc123"], "model": "anthropic/claude-haiku-4.5", "tests": [ { "title": "lists files", "steps": [ { "id": "s1", "kind": "prompt", "prompt": "List the files in the project root." }, { "id": "s2", "kind": "assert", "assertion": { "type": "toolCalledWith", "toolName": "list_files", "args": { "args": {} } } } ] } ] } } } }, "responses": { "201": { "description": "The suite was created.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/EvalSuiteCreated" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/chat-sessions": { "get": { "operationId": "listChatSessions", "tags": ["Catalog"], "summary": "List chat sessions", "description": "Chat sessions visible to the caller (personal + project-shared merged). Top-level rather than project-nested because `projectId` is an optional filter, not an owning scope.", "parameters": [ { "name": "projectId", "in": "query", "required": false, "description": "Filter to one project.", "schema": { "type": "string" } }, { "name": "status", "in": "query", "required": false, "description": "`active` (default) or `archived`.", "schema": { "type": "string", "enum": ["active", "archived"], "default": "active" } }, { "name": "limit", "in": "query", "required": false, "description": "Page size, 1–200. Defaults to 50.", "schema": { "type": "integer", "minimum": 1, "maximum": 200, "default": 50 } }, { "name": "before", "in": "query", "required": false, "description": "Cursor: the previous page's `nextCursor` (a `lastActivityAt` timestamp).", "schema": { "type": "string" } } ], "responses": { "200": { "description": "One page of chat sessions, newest activity first.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatSessionPage" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/chatboxes": { "get": { "operationId": "listChatboxes", "tags": ["Chatboxes"], "summary": "List a project's chatboxes", "description": "The chatboxes published from this project — name, access mode, attached servers, and share link. Read-only.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "responses": { "200": { "description": "The project's chatboxes.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatboxPage" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/chatboxes/{chatboxId}": { "get": { "operationId": "getChatbox", "tags": ["Chatboxes"], "summary": "Get a chatbox", "description": "One chatbox's full read-only settings: model, system prompt, tool-approval policy, attached servers, and share link. A chatbox that exists but belongs to a different project reads as `404 NOT_FOUND`.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/chatboxId" } ], "responses": { "200": { "description": "The chatbox settings.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/ChatboxDetail" } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/tunnels": { "post": { "operationId": "createTunnel", "tags": ["Tunnels"], "summary": "Create (or revive) a relay tunnel for a named project server", "description": "Registers a server record named `name` if missing, mints a relay tunnel grant for it, and **persists the tunnel bearer URL (including the plaintext `?k=` secret) onto the server record's `url`** so evals and chatboxes can target the tunnel like any remote server. The plaintext persistence is a deliberate trade-off of the current tunnel MVP — the backend otherwise stores only a hash of the secret — mitigated by rotation: **every call rotates the secret, revokes the previous grant at the edge (disconnecting any live tunnel session for the server), and updates the stored URL**, so re-calling this route is also the rotation/recovery path.\n\nThe caller hosts the tunnel itself: connect a WebSocket to `relayWsUrl` (subprotocol `mcpjam-tunnel.v1`, `Authorization: Bearer `) and serve the relayed requests — this is what `mcpjam tunnel` does. When the host disconnects, the server record stays and calls to the public URL fail fast at the edge.", "parameters": [{ "$ref": "#/components/parameters/projectId" }], "requestBody": { "required": true, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TunnelCreateRequest" }, "example": { "name": "everything" } } } }, "responses": { "201": { "description": "Tunnel grant minted and the server record updated.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TunnelGrant" }, "example": { "serverId": "srv_abc123", "name": "everything", "existed": false, "slug": "calm-otter", "url": "https://calm-otter.tunnels.mcpjam.com/api/mcp/adapter-http/srv_abc123?k=…", "connectToken": "ct_…", "connectTokenExpiresAt": 1767225600000, "relayWsUrl": "wss://tunnels.mcpjam.com/agent", "secretVersion": 3 } } } }, "400": { "$ref": "#/components/responses/ValidationError" }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } }, "/projects/{projectId}/tunnels/{serverId}/close": { "post": { "operationId": "closeTunnel", "tags": ["Tunnels"], "summary": "Revoke a tunnel's live grant", "description": "Revokes the grant at the control plane and edge: the public URL stops working immediately and any live tunnel session is disconnected. The server record — including its now-dead `url` — is intentionally left untouched, so the next create revives the tunnel with the same slug.", "parameters": [ { "$ref": "#/components/parameters/projectId" }, { "$ref": "#/components/parameters/serverId" } ], "responses": { "200": { "description": "Grant revoked; the server record is unchanged.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/TunnelClosed" }, "example": { "serverId": "srv_abc123", "status": "closed" } } } }, "401": { "$ref": "#/components/responses/Unauthorized" }, "403": { "$ref": "#/components/responses/Forbidden" }, "404": { "$ref": "#/components/responses/NotFound" }, "429": { "$ref": "#/components/responses/RateLimited" }, "500": { "$ref": "#/components/responses/InternalError" }, "502": { "$ref": "#/components/responses/ServerUnreachable" } } } } }, "components": { "securitySchemes": { "bearerAuth": { "type": "http", "scheme": "bearer", "description": "MCPJam API key (`sk_…`). Create one at [Settings → API keys](https://app.mcpjam.com/settings/api-keys). Guest sessions cannot use the API, and API keys cannot manage other API keys." } }, "parameters": { "projectId": { "name": "projectId", "in": "path", "required": true, "description": "ID of the hosted project that contains the server.", "schema": { "type": "string" } }, "serverId": { "name": "serverId", "in": "path", "required": true, "description": "ID of the server inside the project.", "schema": { "type": "string" } }, "runId": { "name": "runId", "in": "path", "required": true, "description": "Eval run ID, as returned by `POST /eval-runs`.", "schema": { "type": "string" } }, "iterationId": { "name": "iterationId", "in": "path", "required": true, "description": "Iteration ID, as returned by the run's iterations list.", "schema": { "type": "string" } }, "suiteId": { "name": "suiteId", "in": "path", "required": true, "description": "Eval suite ID, as returned by `POST /eval-runs`.", "schema": { "type": "string" } }, "chatboxId": { "name": "chatboxId", "in": "path", "required": true, "description": "Chatbox ID, as returned by the project's chatbox list.", "schema": { "type": "string" } }, "hostId": { "name": "hostId", "in": "path", "required": true, "description": "Host ID, as returned by the project's host list.", "schema": { "type": "string" } }, "environmentId": { "name": "environmentId", "in": "path", "required": true, "description": "Computer environment ID, as returned by the project's environment list.", "schema": { "type": "string" } } }, "requestBodies": { "emptyBody": { "required": false, "description": "Accepts an empty JSON object.", "content": { "application/json": { "schema": { "type": "object", "properties": {} }, "example": {} } } }, "cursorBody": { "required": false, "description": "Optional pagination cursor.", "content": { "application/json": { "schema": { "type": "object", "properties": { "cursor": { "type": "string", "description": "Opaque pagination cursor from a previous response's `nextCursor`. Don't parse it." } } }, "example": {} } } } }, "schemas": { "Error": { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string", "description": "Stable, machine-readable error code. New codes may be added over time; treat unknown codes as non-retryable failures unless the HTTP status says otherwise.", "enum": [ "UNAUTHORIZED", "FORBIDDEN", "NOT_FOUND", "VALIDATION_ERROR", "RATE_LIMITED", "FEATURE_NOT_SUPPORTED", "SERVER_UNREACHABLE", "TIMEOUT", "OAUTH_REQUIRED", "INTERNAL_ERROR" ] }, "message": { "type": "string", "description": "Human-readable description. May change between releases — don't match on it." }, "details": { "type": "object", "description": "Optional, unstructured context bag.", "additionalProperties": true } } }, "ValidateResult": { "type": "object", "required": ["success", "status"], "properties": { "success": { "type": "boolean", "const": true }, "status": { "type": "string", "const": "connected" }, "initInfo": { "type": ["object", "null"], "description": "MCP initialization snapshot: server info, negotiated protocol version, and capabilities. `null` if the server connected but initialization data was unavailable.", "additionalProperties": true } } }, "DoctorReport": { "type": "object", "description": "Step-by-step health report from the probe → connect → initialize → capabilities workflow.", "required": ["generatedAt", "status", "connection", "checks"], "properties": { "generatedAt": { "type": "string", "format": "date-time", "description": "When the report was generated." }, "status": { "type": "string", "enum": ["ready", "oauth_required", "partial", "error"], "description": "Overall outcome. `partial` means the server connected but some primitive listings failed." }, "probe": { "type": ["object", "null"], "description": "Transport-level probe result (HTTP reachability, protocol hints), or `null` when skipped.", "additionalProperties": true }, "connection": { "type": "object", "required": ["status", "detail"], "properties": { "status": { "type": "string", "enum": ["connected", "error", "skipped"] }, "detail": { "type": "string" } } }, "initInfo": { "type": ["object", "null"], "additionalProperties": true }, "capabilities": { "type": ["object", "null"], "description": "The server's negotiated MCP capabilities.", "additionalProperties": true }, "tools": { "type": "array", "items": { "$ref": "#/components/schemas/Tool" } }, "resources": { "type": "array", "items": { "$ref": "#/components/schemas/Resource" } }, "resourceTemplates": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "prompts": { "type": "array", "items": { "$ref": "#/components/schemas/Prompt" } }, "checks": { "type": "object", "description": "Per-step status. Each check is `{ status: ok | error | skipped, detail }`.", "properties": { "probe": { "$ref": "#/components/schemas/DoctorCheck" }, "connection": { "$ref": "#/components/schemas/DoctorCheck" }, "initialization": { "$ref": "#/components/schemas/DoctorCheck" }, "capabilities": { "$ref": "#/components/schemas/DoctorCheck" }, "tools": { "$ref": "#/components/schemas/DoctorCheck" }, "resources": { "$ref": "#/components/schemas/DoctorCheck" }, "resourceTemplates": { "$ref": "#/components/schemas/DoctorCheck" }, "prompts": { "$ref": "#/components/schemas/DoctorCheck" } } }, "error": { "oneOf": [ { "type": "null" }, { "type": "object", "required": ["code", "message"], "properties": { "code": { "type": "string" }, "message": { "type": "string" }, "details": {} } } ], "description": "Terminal error, or `null` when the workflow completed." } }, "additionalProperties": true }, "DoctorCheck": { "type": "object", "required": ["status", "detail"], "properties": { "status": { "type": "string", "enum": ["ok", "error", "skipped"] }, "detail": { "type": "string" } } }, "CheckOAuthResult": { "type": "object", "required": ["useOAuth", "serverUrl"], "properties": { "useOAuth": { "type": "boolean", "description": "Whether the server is configured to require an OAuth grant." }, "serverUrl": { "type": ["string", "null"], "description": "The server's URL, or `null` for non-HTTP transports." } } }, "Tool": { "type": "object", "description": "MCP tool definition, as returned by the server.", "required": ["name"], "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "inputSchema": { "type": "object", "description": "JSON Schema for the tool's arguments.", "additionalProperties": true }, "outputSchema": { "type": "object", "description": "JSON Schema for the tool's structured output, when declared.", "additionalProperties": true } }, "additionalProperties": true }, "Prompt": { "type": "object", "description": "MCP prompt definition, as returned by the server.", "required": ["name"], "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "arguments": { "type": "array", "items": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string" }, "description": { "type": "string" }, "required": { "type": "boolean" } }, "additionalProperties": true } } }, "additionalProperties": true }, "Resource": { "type": "object", "description": "MCP resource descriptor, as returned by the server.", "required": ["uri"], "properties": { "uri": { "type": "string" }, "name": { "type": "string" }, "description": { "type": "string" }, "mimeType": { "type": "string" } }, "additionalProperties": true }, "ResourceContents": { "type": "object", "description": "MCP read-resource result.", "required": ["contents"], "properties": { "contents": { "type": "array", "items": { "type": "object", "required": ["uri"], "properties": { "uri": { "type": "string" }, "mimeType": { "type": "string" }, "text": { "type": "string", "description": "Present for text resources." }, "blob": { "type": "string", "description": "Base64-encoded payload, present for binary resources." } }, "additionalProperties": true } } }, "additionalProperties": true }, "ToolPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Tool" } }, "nextCursor": { "type": "string", "description": "Opaque cursor for the next page. Omitted on the last page." } } }, "PromptPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Prompt" } }, "nextCursor": { "type": "string", "description": "Opaque cursor for the next page. Omitted on the last page." } } }, "ResourcePage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Resource" } }, "nextCursor": { "type": "string", "description": "Opaque cursor for the next page. Omitted on the last page." } } }, "CallToolResult": { "type": "object", "description": "MCP tool execution result, returned verbatim from the server.", "required": ["content"], "properties": { "content": { "type": "array", "description": "Content blocks (`text`, `image`, `resource`, ...).", "items": { "type": "object", "additionalProperties": true } }, "structuredContent": { "type": "object", "description": "Structured output matching the tool's `outputSchema`, when declared.", "additionalProperties": true }, "isError": { "type": "boolean", "description": "`true` when the **tool** reported a failure. The HTTP call still succeeds — the server answered." } }, "additionalProperties": true }, "GetPromptResult": { "type": "object", "description": "MCP prompt render result, returned verbatim from the server.", "required": ["messages"], "properties": { "description": { "type": "string" }, "messages": { "type": "array", "items": { "type": "object", "required": ["role", "content"], "properties": { "role": { "type": "string", "enum": ["user", "assistant"] }, "content": { "description": "MCP prompt message content block.", "additionalProperties": true } }, "additionalProperties": true } } }, "additionalProperties": true }, "EvalTestStep": { "type": "object", "description": "One authored test step (the unified test model). `kind` discriminates: `prompt` is a user message (model turn); `toolCall` is a deterministic, model-free tool call; `interact` is one pure widget action; `assert` is an assertion (a `Predicate` like `toolCalledWith` / `widgetRendered`, or a DOM `WidgetAssertion`).", "required": ["id", "kind"], "properties": { "id": { "type": "string", "minLength": 1 }, "kind": { "type": "string", "enum": ["prompt", "toolCall", "interact", "assert"] }, "prompt": { "type": "string", "description": "User message (`kind: prompt`)." }, "serverName": { "type": "string", "description": "Server that owns the tool (`kind: toolCall`)." }, "toolName": { "type": "string", "description": "Tool name (`kind: toolCall` / `interact`)." }, "arguments": { "type": "object", "description": "Tool-call arguments (`kind: toolCall`).", "additionalProperties": true }, "action": { "type": "object", "description": "Widget action (`kind: interact`).", "additionalProperties": true }, "assertion": { "type": "object", "description": "Predicate or widget assertion (`kind: assert`).", "additionalProperties": true } }, "additionalProperties": true }, "EvalTestCase": { "type": "object", "description": "Inline eval test case. The case body is an ordered `steps` array (the unified test model).", "required": ["title", "runs", "model", "provider", "steps"], "properties": { "title": { "type": "string" }, "steps": { "type": "array", "minItems": 1, "description": "Ordered test steps. The first `prompt` step is the case query; `toolCalledWith` asserts are the expected tool calls; a single model-free `toolCall` step is a render-check.", "items": { "$ref": "#/components/schemas/EvalTestStep" } }, "runs": { "type": "integer", "minimum": 1, "maximum": 10, "description": "Iterations to execute for this case." }, "model": { "type": "string", "description": "Model ID. Hosted-catalog ids use `provider/name` form (e.g. `anthropic/claude-haiku-4.5`) and run on org credits; provider-native ids require a matching `modelApiKeys` entry (BYOK). Unknown models are rejected with VALIDATION_ERROR at create time." }, "provider": { "type": "string", "description": "Model provider, e.g. `anthropic`, `openai`." }, "isNegativeTest": { "type": "boolean", "description": "When `true`, the case passes if NO tools are called." }, "expectedOutput": { "type": "string" }, "advancedConfig": { "type": "object", "description": "Optional `system`, `temperature`, `toolChoice` overrides.", "additionalProperties": true } }, "additionalProperties": true }, "EvalSuiteCreateRequest": { "type": "object", "description": "Author-only suite-create body. A suite-level default `model` (and optional `provider`) applies to every test unless the test overrides it.", "required": ["name", "serverIds", "model", "tests"], "properties": { "name": { "type": "string", "minLength": 1, "description": "Suite name." }, "description": { "type": "string" }, "serverIds": { "type": "array", "minItems": 1, "description": "Servers (by canonical project ID) the suite's cases run against.", "items": { "type": "string" } }, "serverNames": { "type": "array", "description": "Optional display names, parallel to `serverIds`.", "items": { "type": "string" } }, "model": { "type": "string", "minLength": 1, "description": "Suite-level default model id (e.g. `anthropic/claude-haiku-4.5`). Used for any test that omits `model`." }, "provider": { "type": "string", "description": "Optional suite-level default provider. When omitted, the provider is derived from a `provider/model` id." }, "passCriteria": { "type": "object", "properties": { "minimumPassRate": { "type": "number" } } }, "tags": { "type": "array", "description": "Accepted for forward-compat; not persisted today (no-op).", "items": { "type": "string" } }, "tests": { "type": "array", "minItems": 1, "maxItems": 100, "description": "Test cases to create in the suite.", "items": { "type": "object", "required": ["title", "steps"], "properties": { "title": { "type": "string", "minLength": 1 }, "steps": { "type": "array", "minItems": 1, "description": "Ordered test steps (the unified test model). The first `prompt` step is the case query; `toolCalledWith` asserts are the expected tool calls; a single model-free `toolCall` step is a render-check.", "items": { "$ref": "#/components/schemas/EvalTestStep" } }, "runs": { "type": "integer", "minimum": 1, "maximum": 10, "description": "Iterations to execute for this case. Defaults to 1." }, "model": { "type": "string", "description": "Per-case model override. Defaults to the suite-level `model`." }, "provider": { "type": "string", "description": "Per-case provider override." }, "expectedOutput": { "type": "string" }, "isNegativeTest": { "type": "boolean", "description": "When `true`, the case passes if NO tools are called." }, "scenario": { "type": "string" }, "advancedConfig": { "type": "object", "description": "Optional `system`, `temperature`, `toolChoice` overrides.", "additionalProperties": true } }, "additionalProperties": true } } }, "additionalProperties": false }, "EvalSuiteCreated": { "type": "object", "required": ["suiteId", "name", "caseUpsert"], "properties": { "suiteId": { "type": "string" }, "name": { "type": "string" }, "servers": { "type": "array", "description": "The servers attached to the suite. `name` is present when supplied.", "items": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } }, "caseUpsert": { "type": "object", "description": "Per-case create outcomes. Partial failures don't abort the suite; an all-failed new suite is rejected with VALIDATION_ERROR.", "properties": { "committed": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } }, "failed": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "error": { "type": "string" } } } } } } } }, "EvalRunCreateRequest": { "type": "object", "description": "Two valid shapes: `suiteId` (rerun an existing suite, optionally upserting inline `tests` into it), or `suiteName` + `tests` + `serverIds` (create a new suite and run it). Inline `tests` alone — without a `suiteId` or a `suiteName` — are rejected with `VALIDATION_ERROR`.", "anyOf": [ { "required": ["suiteId"] }, { "required": ["suiteName", "tests", "serverIds"], "properties": { "tests": { "minItems": 1 } } } ], "properties": { "suiteId": { "type": "string", "description": "Existing suite to rerun. A bare `suiteId` with no `tests` reruns the suite exactly as configured." }, "suiteName": { "type": "string", "description": "Name for a new suite. Required (non-empty) when no `suiteId` is given." }, "suiteDescription": { "type": "string" }, "tests": { "type": "array", "maxItems": 100, "description": "Inline test cases to upsert into the suite before running.", "items": { "$ref": "#/components/schemas/EvalTestCase" } }, "serverIds": { "type": "array", "minItems": 1, "description": "Servers (by ID) the run connects to. Required when creating a new suite; optional on reruns — when omitted, the run connects the suite's saved server selection (the set its snapshot references). A rerun of a suite with no saved selection is rejected with `VALIDATION_ERROR` (`details.reason: \"NO_SAVED_SERVER_SELECTION\"`).", "items": { "type": "string" } }, "modelApiKeys": { "type": "object", "description": "Optional per-provider model API keys (e.g. `{ \"anthropic\": \"sk-ant-…\" }`). Falls back to your organization's configured providers when omitted.", "additionalProperties": { "type": "string" } }, "notes": { "type": "string" }, "passCriteria": { "type": "object", "properties": { "minimumPassRate": { "type": "number" } } }, "iterationOverride": { "type": "integer", "minimum": 1, "maximum": 10, "description": "Override the per-case `runs` count for this run only." } }, "additionalProperties": true }, "EvalRunCreated": { "type": "object", "required": ["runId", "suiteId", "status", "caseUpsert"], "properties": { "runId": { "type": "string" }, "suiteId": { "type": "string" }, "status": { "type": "string", "const": "running" }, "servers": { "type": "array", "description": "The servers the run connects to — explicit or derived from the suite's saved selection. `name` is present when known (always, on the derived path).", "items": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } }, "caseUpsert": { "type": "object", "description": "Per-case upsert outcomes for inline `tests`. Partial failures don't abort the run.", "properties": { "committed": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" } } } }, "failed": { "type": "array", "items": { "type": "object", "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "error": { "type": "string" } } } } } } } }, "EvalRun": { "type": "object", "required": ["id", "suiteId", "status", "source", "createdAt"], "properties": { "id": { "type": "string" }, "suiteId": { "type": "string" }, "runNumber": { "type": ["integer", "null"] }, "status": { "type": "string", "description": "Poll until terminal: `completed`, `failed`, or `cancelled`.", "enum": ["pending", "running", "completed", "failed", "cancelled"] }, "result": { "type": ["string", "null"], "description": "Pass/fail verdict once terminal.", "enum": ["passed", "failed", null] }, "summary": { "type": ["object", "null"], "properties": { "total": { "type": "integer" }, "passed": { "type": "integer" }, "failed": { "type": "integer" }, "passRate": { "type": "number" } } }, "source": { "type": "string", "description": "Run origin. API-created runs are `api`.", "enum": ["ui", "api", "sdk"] }, "notes": { "type": ["string", "null"] }, "createdAt": { "type": "number", "description": "Epoch milliseconds." }, "completedAt": { "type": ["number", "null"], "description": "Epoch milliseconds, `null` until terminal." } } }, "EvalRunPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EvalRun" } } } }, "EvalIteration": { "type": "object", "required": ["id", "iterationNumber", "status"], "properties": { "id": { "type": "string" }, "testCaseId": { "type": ["string", "null"] }, "title": { "type": ["string", "null"] }, "iterationNumber": { "type": "integer" }, "status": { "type": "string", "enum": ["pending", "running", "completed", "failed", "cancelled"] }, "result": { "type": ["string", "null"], "enum": ["passed", "failed", null] }, "model": { "type": ["string", "null"] }, "provider": { "type": ["string", "null"] }, "startedAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "durationMs": { "type": ["number", "null"], "description": "Wall-clock duration; `null` until terminal." }, "tokensUsed": { "type": ["number", "null"] }, "usage": { "type": ["object", "null"], "description": "Structured token usage (input/output/cached/reasoning) when available.", "additionalProperties": true }, "actualToolCalls": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "expectedToolCalls": { "type": "array", "items": { "type": "object", "additionalProperties": true } }, "error": { "type": ["string", "null"] } } }, "EvalIterationPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EvalIteration" } }, "nextCursor": { "type": "string", "description": "Opaque cursor for the next page. Omitted on the last page." } } }, "ImportTokensRequest": { "type": "object", "required": ["serverUrl", "tokens"], "properties": { "serverUrl": { "type": "string", "description": "The MCP server URL the tokens were obtained for." }, "oauthResourceUrl": { "type": "string", "description": "OAuth protected-resource URL, when it differs from `serverUrl`." }, "clientInformation": { "type": "object", "description": "OAuth client used to obtain the tokens. Optional, but without it server-side refresh cannot run — always include it when you provide a `refresh_token`.", "required": ["clientId"], "properties": { "clientId": { "type": "string" }, "clientSecret": { "type": "string", "format": "password" } } }, "tokens": { "type": "object", "required": ["access_token"], "properties": { "access_token": { "type": "string", "format": "password" }, "refresh_token": { "type": "string", "format": "password" }, "expires_in": { "type": "number", "description": "Seconds until the access token expires." }, "token_type": { "type": "string" }, "scope": { "type": "string" }, "id_token": { "type": "string", "format": "password" } } } } }, "Me": { "type": "object", "required": ["id", "email", "name"], "properties": { "id": { "type": "string" }, "email": { "type": "string" }, "name": { "type": "string" }, "imageUrl": { "type": ["string", "null"] }, "profilePictureUrl": { "type": ["string", "null"] }, "plan": { "type": ["string", "null"] }, "createdAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "updatedAt": { "type": ["number", "null"] } } }, "Project": { "type": "object", "required": ["id", "name"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "description": { "type": ["string", "null"] }, "icon": { "type": ["string", "null"] }, "organizationId": { "type": ["string", "null"] }, "visibility": { "type": ["string", "null"] }, "role": { "type": "string", "description": "Caller's role on the project, when resolved." }, "createdAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "updatedAt": { "type": ["number", "null"] } } }, "ProjectPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Project" } } } }, "ProjectServer": { "type": "object", "description": "A saved MCP server, projected toward the hosted (HTTP) shape. STDIO command/args/env and raw headers are never exposed.", "required": [ "id", "name", "enabled", "transportType", "useOAuth", "hasClientSecret" ], "properties": { "id": { "type": "string" }, "projectId": { "type": ["string", "null"] }, "name": { "type": "string" }, "enabled": { "type": "boolean" }, "transportType": { "type": "string" }, "url": { "type": ["string", "null"], "description": "Endpoint for HTTP-transport servers; `null` for stdio." }, "useOAuth": { "type": "boolean" }, "hasClientSecret": { "type": "boolean" }, "oauthScopes": { "type": "array", "items": { "type": "string" } }, "createdAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "updatedAt": { "type": ["number", "null"] } } }, "ProjectServerPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ProjectServer" } } } }, "EvalSuite": { "type": "object", "required": ["id", "totals", "passRateTrend"], "properties": { "id": { "type": "string" }, "name": { "type": ["string", "null"] }, "projectId": { "type": ["string", "null"] }, "createdAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "updatedAt": { "type": ["number", "null"] }, "latestRun": { "type": ["object", "null"], "properties": { "id": { "type": ["string", "null"] }, "status": { "type": ["string", "null"] }, "passRate": { "type": ["number", "null"] }, "passed": { "type": ["integer", "null"] }, "failed": { "type": ["integer", "null"] }, "createdAt": { "type": ["number", "null"] } } }, "totals": { "type": "object", "properties": { "passed": { "type": "integer" }, "failed": { "type": "integer" }, "runs": { "type": "integer" } } }, "passRateTrend": { "type": "array", "description": "Recent runs' pass rates, oldest first.", "items": { "type": "number" } } } }, "EvalSuitePage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/EvalSuite" } } } }, "ChatSession": { "type": "object", "required": ["id"], "properties": { "id": { "type": "string" }, "title": { "type": ["string", "null"] }, "status": { "type": ["string", "null"] }, "projectId": { "type": ["string", "null"] }, "visibility": { "type": ["string", "null"], "description": "`private` or `project`." }, "lastActivityAt": { "type": ["number", "null"], "description": "Epoch milliseconds." }, "createdAt": { "type": ["number", "null"] }, "isPinned": { "type": "boolean" }, "isUnread": { "type": "boolean" } } }, "ChatSessionPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ChatSession" } }, "nextCursor": { "type": "string", "description": "Pass as `before` to fetch the next page. Omitted on the last page." } } }, "ImportTokensResult": { "type": "object", "required": ["imported"], "properties": { "imported": { "type": "boolean", "const": true }, "expiresAt": { "type": ["number", "null"], "description": "Epoch milliseconds when the stored access token expires, or `null` if unknown." } } }, "ExportSnapshot": { "type": "object", "required": ["serverId", "exportedAt", "tools", "resources", "prompts"], "properties": { "serverId": { "type": "string" }, "exportedAt": { "type": "string", "format": "date-time" }, "tools": { "type": "array", "items": { "$ref": "#/components/schemas/Tool" } }, "resources": { "type": "array", "items": { "$ref": "#/components/schemas/Resource" } }, "prompts": { "type": "array", "items": { "$ref": "#/components/schemas/Prompt" } } } }, "ChatboxLink": { "type": "object", "description": "Share link for the chatbox. The URL embeds the access token; visible to any caller who can read the chatbox (the same audience that sees it in the hosted UI).", "required": ["path", "url"], "properties": { "path": { "type": "string", "description": "App-relative share path." }, "url": { "type": "string", "description": "Absolute share URL." } } }, "ChatboxServer": { "type": "object", "description": "A server attached to the chatbox. Chatboxes attach HTTP servers only, so `url` is the server's endpoint.", "required": ["id", "name", "useOAuth"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "url": { "type": ["string", "null"], "description": "HTTP endpoint of the server." }, "useOAuth": { "type": "boolean" } } }, "Chatbox": { "type": "object", "description": "Summary of a published chatbox, as returned by the list endpoint.", "required": ["id", "name", "serverCount", "serverNames"], "properties": { "id": { "type": "string" }, "projectId": { "type": ["string", "null"] }, "name": { "type": "string" }, "description": { "type": ["string", "null"] }, "mode": { "type": ["string", "null"], "description": "Who can use the chatbox.", "enum": [ "project_members", "invited_only", "anyone_with_link", null ] }, "hostStyle": { "type": ["string", "null"], "description": "Chat surface style the chatbox renders (e.g. `claude`, `chatgpt`)." }, "hostId": { "type": ["string", "null"], "description": "The named host the chatbox publishes (hosts and chatboxes are 1:1)." }, "hostName": { "type": ["string", "null"] }, "serverCount": { "type": "integer" }, "serverNames": { "type": "array", "items": { "type": "string" } }, "link": { "oneOf": [ { "$ref": "#/components/schemas/ChatboxLink" }, { "type": "null" } ] }, "createdAt": { "type": ["number", "null"], "description": "Unix epoch milliseconds." }, "updatedAt": { "type": ["number", "null"], "description": "Unix epoch milliseconds." } } }, "ChatboxDetail": { "type": "object", "description": "A chatbox's full read-only settings: the summary fields plus the host execution config and resolved servers.", "required": [ "id", "name", "serverCount", "serverNames", "requireToolApproval", "servers" ], "properties": { "id": { "type": "string" }, "projectId": { "type": ["string", "null"] }, "name": { "type": "string" }, "description": { "type": ["string", "null"] }, "mode": { "type": ["string", "null"], "description": "Who can use the chatbox.", "enum": [ "project_members", "invited_only", "anyone_with_link", null ] }, "hostStyle": { "type": ["string", "null"], "description": "Chat surface style the chatbox renders (e.g. `claude`, `chatgpt`)." }, "hostId": { "type": ["string", "null"], "description": "The named host the chatbox publishes (hosts and chatboxes are 1:1)." }, "hostName": { "type": ["string", "null"] }, "serverCount": { "type": "integer" }, "serverNames": { "type": "array", "items": { "type": "string" } }, "link": { "oneOf": [ { "$ref": "#/components/schemas/ChatboxLink" }, { "type": "null" } ] }, "createdAt": { "type": ["number", "null"], "description": "Unix epoch milliseconds." }, "updatedAt": { "type": ["number", "null"], "description": "Unix epoch milliseconds." }, "modelId": { "type": ["string", "null"], "description": "Model the chatbox chats with." }, "systemPrompt": { "type": ["string", "null"] }, "temperature": { "type": ["number", "null"] }, "requireToolApproval": { "type": "boolean" }, "servers": { "type": "array", "items": { "$ref": "#/components/schemas/ChatboxServer" } } } }, "ChatboxPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Chatbox" } } } }, "TunnelCreateRequest": { "type": "object", "required": ["name"], "properties": { "name": { "type": "string", "minLength": 1, "maxLength": 128, "description": "Server name to register the tunnel under. Reusing an existing server's name points that record at the tunnel: its `url` is overwritten and stdio records are converted to `transportType: \"http\"`." } } }, "TunnelGrant": { "type": "object", "description": "Everything the caller needs to host the tunnel connection. **Treat the whole object as a credential**: `url` embeds the plaintext `?k=` bearer secret and `connectToken` authenticates the relay WebSocket.", "required": [ "serverId", "name", "existed", "slug", "url", "connectToken", "relayWsUrl" ], "properties": { "serverId": { "type": "string", "description": "The registered project server's ID." }, "name": { "type": "string" }, "existed": { "type": "boolean", "description": "True when a server record with this name already existed (its config now points at the tunnel)." }, "previousUrl": { "type": "string", "description": "The record's previous URL, present when it existed with a different one (it has been overwritten)." }, "previousTransportType": { "type": "string", "description": "The record's previous transport, present when it existed (a `stdio` record has been converted to `http`)." }, "slug": { "type": "string", "description": "Stable public subdomain slug; survives re-creates for the same server." }, "url": { "type": "string", "description": "Public tunnel URL including the `?k=` bearer secret. Also persisted on the server record's `url`." }, "connectToken": { "type": "string", "description": "Bearer for the relay edge WebSocket handshake." }, "connectTokenExpiresAt": { "type": "number", "description": "Epoch-ms expiry of `connectToken`." }, "relayWsUrl": { "type": "string", "description": "Relay edge WebSocket endpoint to connect to." }, "secretVersion": { "type": "number", "description": "Monotonic secret version; each create bumps it." } } }, "TunnelClosed": { "type": "object", "required": ["serverId", "status"], "properties": { "serverId": { "type": "string" }, "status": { "type": "string", "enum": ["closed"] } } }, "Host": { "type": "object", "description": "A host in a project, as returned by the host list.", "required": ["id", "name", "hostConfigId", "modelId", "serverCount", "createdAt", "updatedAt"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "hostConfigId": { "type": "string", "description": "ID of the content-addressed host config this host points at." }, "modelId": { "type": "string", "description": "Resolved model id, e.g. `anthropic/claude-haiku-4.5`." }, "serverCount": { "type": "integer", "description": "Number of MCP servers attached to the host config." }, "createdAt": { "type": "number", "description": "Unix epoch milliseconds." }, "updatedAt": { "type": "number", "description": "Unix epoch milliseconds." } } }, "HostPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/Host" } } } }, "HostDetail": { "type": "object", "description": "A host plus its resolved host config (model, capabilities, host context, MCP profile).", "required": ["id", "name", "config"], "properties": { "id": { "type": "string" }, "name": { "type": "string" }, "config": { "type": "object", "additionalProperties": true, "description": "Host config v2 DTO. Opaque object; shape mirrors the inspector's host editor." } } }, "HostCreateRequest": { "type": "object", "description": "Create a host from a built-in template OR a full host config. Exactly one of `template` or `config` is required.", "required": ["name"], "properties": { "name": { "type": "string", "description": "Display name for the new host." }, "template": { "type": "string", "description": "Built-in template id to seed the host config from.", "enum": ["mcpjam", "claude", "claude-code", "chatgpt", "mistral", "cursor", "codex", "copilot", "vscode", "agentcore", "n8n", "perplexity"] }, "theme": { "type": "string", "enum": ["light", "dark"], "description": "Theme stamped into the seeded config (template only)." }, "config": { "type": "object", "additionalProperties": true, "minProperties": 1, "description": "Full host config v2, used verbatim (alternative to `template`)." } }, "oneOf": [{ "required": ["template"] }, { "required": ["config"] }] }, "HostUpdateRequest": { "type": "object", "description": "Edit a host. Provide at least one of `name` or `config`; only the fields you pass change.", "properties": { "name": { "type": "string", "description": "New display name." }, "config": { "type": "object", "additionalProperties": true, "minProperties": 1, "description": "Replacement host config v2." } }, "anyOf": [{ "required": ["name"] }, { "required": ["config"] }] }, "HostDeleted": { "type": "object", "required": ["id", "deleted"], "properties": { "id": { "type": "string" }, "deleted": { "type": "boolean", "enum": [true] } } }, "ComputerEnvironmentBuild": { "type": "object", "description": "One build of an environment's image.", "required": ["id", "status", "provider", "baseImageDigests", "createdAt"], "properties": { "id": { "type": "string" }, "status": { "type": "string", "enum": ["queued", "building", "ready", "failed"] }, "provider": { "type": "string", "enum": ["e2b", "stub"] }, "e2bBuildId": { "type": "string" }, "baseImageDigests": { "type": "array", "items": { "type": "string" }, "description": "Resolved sha256 digest of every FROM base (the reproducibility pin)." }, "logPreview": { "type": "string", "description": "Capped, ANSI-stripped tail of the build log." }, "error": { "type": "string" }, "createdAt": { "type": "number" }, "startedAt": { "type": "number" }, "finishedAt": { "type": "number" } } }, "ComputerEnvironment": { "type": "object", "description": "A project's custom Computer image: a digest-pinned Dockerfile plus its latest build.", "required": ["id", "projectId", "name", "dockerfile", "contentHash", "sharing", "isOwner", "currentBuild", "createdAt", "updatedAt"], "properties": { "id": { "type": "string" }, "projectId": { "type": "string" }, "name": { "type": "string" }, "dockerfile": { "type": "string" }, "contentHash": { "type": "string" }, "sharing": { "type": "string", "enum": ["user", "project"], "description": "`user` = a personal draft; `project` = shared with the whole project." }, "isOwner": { "type": "boolean" }, "currentBuild": { "oneOf": [{ "$ref": "#/components/schemas/ComputerEnvironmentBuild" }, { "type": "null" }] }, "createdAt": { "type": "number" }, "updatedAt": { "type": "number" } } }, "ComputerEnvironmentPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ComputerEnvironment" } }, "nextCursor": { "type": "string" } } }, "ComputerEnvironmentBuildPage": { "type": "object", "required": ["items"], "properties": { "items": { "type": "array", "items": { "$ref": "#/components/schemas/ComputerEnvironmentBuild" } }, "nextCursor": { "type": "string" } } }, "ComputerEnvironmentCreateRequest": { "type": "object", "description": "Create an environment from a Dockerfile. The Dockerfile must start FROM an allowlisted official base pinned by @sha256 digest; only FROM + RUN are supported.", "required": ["name", "dockerfile"], "properties": { "name": { "type": "string", "minLength": 1, "description": "Display name for the new environment; must be non-empty after trimming." }, "dockerfile": { "type": "string", "minLength": 1, "description": "Dockerfile text." } } }, "ComputerEnvironmentUpdateRequest": { "type": "object", "description": "Edit an environment. Provide at least one of `name` or `dockerfile`; only the fields you pass change.", "properties": { "name": { "type": "string", "minLength": 1, "description": "New display name; must be non-empty after trimming." }, "dockerfile": { "type": "string", "minLength": 1, "description": "Replacement Dockerfile text." } }, "anyOf": [{ "required": ["name"] }, { "required": ["dockerfile"] }] }, "ComputerEnvironmentDeleted": { "type": "object", "required": ["id", "deleted"], "properties": { "id": { "type": "string" }, "deleted": { "type": "boolean", "enum": [true] } } }, "ComputerEnvironmentBuildStarted": { "type": "object", "description": "A build was accepted (202). It runs in the background — poll the builds list for status.", "required": ["id", "buildId", "reused"], "properties": { "id": { "type": "string", "description": "The environment id." }, "buildId": { "type": "string" }, "reused": { "type": "boolean", "description": "True when an identical prior build was reused instead of rebuilding." } } }, "ComputerAttached": { "type": "object", "required": ["environmentId", "computerId", "status"], "properties": { "environmentId": { "type": "string" }, "computerId": { "type": "string" }, "status": { "type": "string", "description": "The computer's lifecycle status after attaching (it re-provisions)." } } }, "ComputerReset": { "type": "object", "required": ["projectId", "reset"], "properties": { "projectId": { "type": "string" }, "reset": { "type": "boolean" } } } }, "responses": { "InternalError": { "description": "Something failed on MCPJam's side.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "INTERNAL_ERROR", "message": "Unexpected internal error" } } } }, "ValidationError": { "description": "Malformed body or parameters.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "VALIDATION_ERROR", "message": "Invalid JSON body" } } } }, "Unauthorized": { "description": "Missing, invalid, revoked, or orphaned key (`UNAUTHORIZED`) — or the **target MCP server** needs an OAuth grant (`OAUTH_REQUIRED`), which is a property of the server, not your key.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "examples": { "badKey": { "summary": "Invalid or revoked key", "value": { "code": "UNAUTHORIZED", "message": "Invalid API key" } }, "oauthRequired": { "summary": "Target server needs an OAuth grant", "value": { "code": "OAUTH_REQUIRED", "message": "Server requires OAuth authorization" } } } } } }, "Forbidden": { "description": "Key is valid but not allowed to do this.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "FORBIDDEN", "message": "You do not have access to this project" } } } }, "NotFound": { "description": "Unknown project, server, or resource.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "NOT_FOUND", "message": "Server not found" } } } }, "FeatureNotSupported": { "description": "The target server doesn't support this MCP capability.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "FEATURE_NOT_SUPPORTED", "message": "Server does not support resources" } } } }, "RateLimited": { "description": "Per-key rate limit exceeded (60 requests/minute sustained, bursts up to 10). Honor `Retry-After` and back off with jitter.", "headers": { "Retry-After": { "description": "Seconds to wait before retrying.", "schema": { "type": "integer" } } }, "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "RATE_LIMITED", "message": "API key rate limit exceeded. Slow down and retry." } } } }, "ServerUnreachable": { "description": "Could not connect to the target MCP server.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "SERVER_UNREACHABLE", "message": "Failed to connect to server" } } } }, "Timeout": { "description": "The target MCP server connected but didn't respond in time.", "content": { "application/json": { "schema": { "$ref": "#/components/schemas/Error" }, "example": { "code": "TIMEOUT", "message": "Request to server timed out" } } } } } } }