# MCP Server

> Connect Claude Code, Cursor, or any MCP client to inference shell and use all platform capabilities as tools.

**URL:** https://inference.sh/docs/connectors/mcp-server
**Last updated:** 2026-11-07

---

Connect Claude Code, Cursor, or any MCP client to inference shell and use all platform capabilities as tools.

---

## What You Get

When you connect an MCP client to inference shell, it gets access to:

| Tool Group | Tools |
|------------|-------|
| **Apps** | `app_list`, `app_get`, `app_run`, `task_get`, `task_list`, `task_logs`, `task_cancel` |
| **Workspace** | `open_workspace`, `open_thread_panel` |
| **Knowledge** | `knowledge_list`, `knowledge_search`, `knowledge_get`, `knowledge_create`, `knowledge_delete` |
| **Skills** | `skill_use`, `skill_files`, `skill_view`, `skill_upload`, `skill_store`, `skill_search` |
| **Artifacts** | `artifact_guidance`, `artifact_publish`, `artifact_get`, `artifact_list`, `artifact_comments`, `artifact_reply`, `artifact_resolve`, `artifact_asset_upload`, `artifact_asset_list`, `artifact_asset_delete` |
| **MCP Proxy** | `mcp_list`, `mcp_search`, `mcp_get`, `mcp_tools`, `mcp_run`, `mcp_connect`, `mcp_disconnect` |

The **MCP proxy** tools are especially powerful — they let your coding agent discover, connect to, and call tools on any MCP server through inference shell, without running those servers locally.

### Task tools

| Tool | Purpose | Key arguments |
|------|---------|---------------|
| `app_run` | Run an app; creates a task | `app` (required; `namespace/name` or `namespace/name@version`), `input` (required), `function` (multi-function apps), `infrastructure` (`cloud`, `private`, `private_first`; default `cloud`), `wait` (see [Waiting for `app_run`](#waiting-for-app_run)) |
| `task_get` | Get task status and output | `task_id` (required), `wait` (optional boolean; answers once the task finishes), `include_logs` (default false; returns the tail of each of the task's logs as `structuredContent.logs`), `log_lines` (lines per log with `include_logs`; default 200, max 2000) |
| `task_list` | List recent tasks | `limit` (default 20, max 100), `cursor`, `status` (`queued`, `running`, `completed`, `failed`, `cancelled`, `rejected`) |
| `task_logs` | Fetch logs for one phase | `task_id` (required), `log_type` (`build`, `run`, `serve`, `setup`, `task`; default `run`) |
| `task_cancel` | Cancel a running task | `task_id` (required), `force` (optional boolean) |

REST equivalents: [Tasks API](/docs/api/rest/tasks).

### Workspace entrypoints

Hosts that support the [MCP Apps](https://modelcontextprotocol.io) UI extension can open the full inference.sh web app inside the conversation. These tools take no arguments and are read-only.

| Tool | Entrypoint | Purpose |
|------|------------|---------|
| `open_workspace` | `global` | Open the workspace fullscreen: run apps, talk to agents, edit flows, publish artifacts, and watch runs. |
| `open_thread_panel` | `thread` | Open the same app beside the conversation so the flow, artifact, or run under discussion stays visible while you chat. |

When the host renders either tool, it loads the built-in **workspace viewer**, which fills the host panel and frames the deployment's web app (for example `https://app.inference.sh`). The viewer:

1. Calls the app-only `workspace_session` tool to mint a **short-lived bound session** (about 55 minutes) for the signed-in user on the MCP connection: the same identity as the host's OAuth session, not a separate API key pasted into the frame. The framed app uses that bearer token only (no cookies), stays locked to the connection's team, and disables workspace switching.
2. On navigation or selection, calls `workspace_focus` so the MCP server remembers what the user is looking at on this MCP session.
3. On panel close, calls `workspace_session_end` to revoke the frame session, and mints a fresh one when the app reports the previous one expired.

`workspace_session`, `workspace_session_end`, and `workspace_focus` are **not** exposed to the model (`visibility: app` only). Hosts and integrators should not surface them in tool lists meant for the agent.

**Frame session limits.** The credential minted for the iframe is narrower than a normal browser login:

| Property | Behavior |
|----------|----------|
| **Team** | Acts only as the MCP connection’s team (`team_id` in the mint response). The token ignores `X-Team-ID` and default-team fallback. If the user can no longer access that team, the frame session stops working. |
| **Sign-in** | A bound frame session is **not** the person’s own browser or CLI sign-in. Routes that require a personal session refuse it, for example approving an OAuth grant, approving a device login, or picking another team to mint long-lived credentials. Complete those flows in the host app or in a normal inference.sh login, not inside the embedded workspace. |
| **Scopes** | Standard developer API scopes **except** creating API keys, further limited to what the MCP connection itself is allowed to do (OAuth scope grants and scoped API keys). A grant with no overlap cannot open the workspace. |
| **Lifetime** | About 55 minutes, fixed TTL (not extended on use). At most **one** active frame session per MCP session: calling `workspace_session` again revokes the previous mint. |
| **Revocation** | `workspace_session_end` when the panel closes; terminating the MCP session (`DELETE` on the Streamable HTTP session) revokes the frame session too. If the MCP session only goes idle and lapses, the frame token may still work until its own expiry so an open panel is not cut off while the host reconnects. |

**Session focus.** `workspace_focus` stores a snapshot on the MCP session:

| Field | Meaning |
|-------|---------|
| `path` | App-relative path the frame is showing (required), e.g. `/flows/abc` |
| `kind` | Object type when known: `flow`, `artifact`, `chat`, `agent`, `app`, `task`, or empty for lists/home |
| `id` | Id of the object named by the path, when applicable |
| `selection` | Selected sub-part (node id, version, etc.) |
| `title` | Display name for the model |

The next `open_workspace` or `open_thread_panel` call includes this focus in its text result and `structuredContent.focus`, so the model can refer to “this flow” without the user pasting an id. `workspace_focus` requires a **stateful** MCP session (Streamable HTTP with session id); stateless API-key-only clients get an error because there is nowhere to store focus.

`workspace_session` requires a **signed-in user**, a **team** on the MCP connection, and a **stateful MCP session** (Streamable HTTP with session id). Anonymous access, connections with no team context, or stateless API-key-only clients cannot mint a frame session.

---

## Setup

### Claude Code

Add to your MCP settings (`~/.claude/settings.json` or project `.mcp.json`):

```json
{
  "mcpServers": {
    "inference": {
      "type": "streamable-http",
      "url": "https://api.inference.sh/mcp",
      "headers": {
        "Authorization": "Bearer inf_your_api_key"
      }
    }
  }
}
```

### Cursor

Add to your Cursor MCP settings:

```json
{
  "mcpServers": {
    "inference": {
      "url": "https://api.inference.sh/mcp",
      "headers": {
        "Authorization": "Bearer inf_your_api_key"
      }
    }
  }
}
```

### Any MCP Client

The server card is at:

```
GET https://api.inference.sh/.well-known/mcp-server-card
```

```json
{
  "name": "sh.inference/mcp-server",
  "description": "inference shell MCP server",
  "remotes": [
    {
      "type": "streamable-http",
      "url": "https://api.inference.sh/mcp",
      "supportedProtocolVersions": ["2026-07-28", "2025-11-25", "2025-06-18", "2025-03-26", "2024-11-05"]
    }
  ]
}
```

---

## Server discovery proxy

The web app and CLI resolve custom MCP server URLs without browser CORS issues via a public proxy:

```
GET https://api.inference.sh/mcps/discover?url=https://example.com
```

| Step | What it tries |
|------|----------------|
| 1 | `GET {origin}/.well-known/mcp-server-card` (MCP server card) |
| 2 | OAuth authorization-server metadata at `{origin}` |

Returns the discovered JSON on success (`200`, cached five minutes). Use this when building UI that needs to preview a server before connect.

When you connect a custom MCP server through the API with a bare origin (`https://host` only), the platform also tries [UCP](https://ucp.dev) at `GET /.well-known/ucp` and uses the first transport with `"transport": "mcp"` and a non-empty `endpoint`.

---

## Authentication

### API key (simplest)

All requests to the MCP server require a Bearer token:

```
Authorization: Bearer inf_your_api_key
```

Get your API key from [settings → workspace → api keys](https://app.inference.sh/settings/team/keys) or via CLI:

```bash
belt login
```

### OAuth (MCP-native clients)

MCP clients that support OAuth can authorize against inference shell instead of pasting an API key. Discovery metadata is at [`/.well-known/oauth-authorization-server`](https://api.inference.sh/.well-known/oauth-authorization-server) on the API host. After the user approves access in the browser, the client calls `POST https://api.inference.sh/mcp` with the issued access token.

Partner and first-party clients that use [Client ID Metadata Documents](https://github.com/modelcontextprotocol/modelcontextprotocol/pull/991) show as **verified** on the consent screen; first-party documents hosted at `https://inference.sh/oauth/clients/*.json` keep the `client_name` from the document. See [Authorization code flow](/docs/api/rest/device-auth#authorization-code-flow-mcp-and-third-party-clients) and [Verified clients on the consent screen](/docs/api/rest/device-auth#verified-clients-on-the-consent-screen).

---

## Example: Running Apps from Claude Code

Once connected, Claude Code can run inference shell apps directly:

```
You: Generate an image of a sunset over mountains

Claude: I'll use inference shell to generate that image.
        [Calling app_run with app="infsh/flux-schnell", input={prompt: "sunset over mountains"}]

        Here's your image: https://files.inference.sh/...
```

## Example: Using Connectors from Claude Code

Your connected MCP servers are available through the proxy tools:

```
You: Check my Linear backlog for high-priority items

Claude: [Calling mcp_run with slug="linear", tool="list_issues", input={priority: 1}]

        You have 3 high-priority issues:
        - INF-201: Fix auth token refresh
        - INF-198: Billing webhook timeout
        - INF-195: Rate limit on /agents/run
```

---

## Protocol

The server implements the [Model Context Protocol](https://modelcontextprotocol.io) specification:

- **Transport**: Streamable HTTP (JSON-RPC 2.0)
- **Endpoint**: `POST https://api.inference.sh/mcp`
- **Supported versions** (newest first): `2026-07-28`, `2025-11-25`, `2025-06-18`, `2025-03-26`, `2024-11-05`
- **Handshake**: `initialize` / `initialized` (through `2025-11-25`), or stateless `server/discover` (`2026-07-28` and later)
- **Tools**: `tools/list`, `tools/call`
- **Resources**: `resources/list`, `resources/read` (MCP Apps pages; see below)
- **Tasks extension** (`io.modelcontextprotocol/tasks`): advertised in server capabilities; see below
- **MCP Apps extension** (`io.modelcontextprotocol/ui`): advertised with MIME type `text/html;profile=mcp-app`; see below
- **Progress**: `notifications/progress` when the client sends `_meta.progressToken` on `tools/call`
- **Cancellation**: `notifications/cancelled` cancels the in-flight JSON-RPC request (and the platform task for `app_run`)

Send the negotiated protocol version on every request via the `MCP-Protocol-Version` header and in `params._meta` as `io.modelcontextprotocol/protocolVersion`. Clients on `2026-07-28` also mirror routing fields in `Mcp-Method` and `Mcp-Name` (for example `taskId` on `tasks/get`).

Capabilities advertise **tools**, **resources**, and the **tasks** and **MCP Apps** extensions. Logging and prompts are omitted rather than advertised as empty.

### Stateless vs handshake connections

Under protocol version **2026-07-28**, clients that support it start with **`server/discover`**. The server returns supported versions and capabilities; each HTTP request can stand alone without a persistent Streamable HTTP session id.

Older clients call **`initialize`** and keep a **session id** on later requests. Stateful features on this server, including `workspace_focus`, `workspace_session`, and the frame session described under [Workspace entrypoints](#workspace-entrypoints), require that stateful session.

### Claude handshake fallback

Anthropic's chat products render MCP App tool cards only on connections they opened with **`initialize`**. On a purely stateless 2026-07-28 connection, `tools/call` and `resources/read` can succeed while every card shows **"Unable to reach &lt;connector&gt;"** ([anthropics/claude-ai-mcp#636](https://github.com/anthropics/claude-ai-mcp/issues/636)).

The server recognises those clients by the `clientInfo.name` they send in MCP request params (not by `User-Agent`). When the name is **`Anthropic`**, **`Anthropic/Toolbox`**, **`Anthropic/ClaudeAI`**, or **`claude-ai`**, a **`server/discover`** call receives **method not found** so the client falls back to **`initialize`** and keeps a session.

**Claude Code**, **Cursor**, **Codex**, and other clients still get a normal **`server/discover`** response and may stay stateless.

### Tool list metadata

Each entry from `tools/list` includes optional `annotations` (MCP tool hints). On this server, every tool sets:

| Field | Meaning |
|-------|---------|
| `annotations.title` | Human-readable name; always matches the top-level `title` (some clients, including the Claude connector directory, read only this field) |
| `readOnlyHint` | Tool does not mutate remote state |
| `destructiveHint` | Tool may delete or irreversibly change data (`knowledge_delete`, `task_cancel`, `mcp_disconnect`, and similar) |
| `idempotentHint` | Repeated calls with the same input have no extra effect |
| `openWorldHint` | Tool may reach arbitrary external systems |

Connector tools you reach through the MCP proxy (`mcp_tools`, `mcp_run`, or [REST tool proxy](/docs/api/rest/credentials#mcp-tool-proxy)) pass through annotations from the upstream server; inference.sh does not rewrite them.

### Tasks extension and `app_run`

Long-running app runs use the [MCP tasks extension](https://modelcontextprotocol.io) when your client declares it:

1. On each request, include client capabilities in `params._meta.io.modelcontextprotocol/clientCapabilities` with:

```json
{
  "extensions": {
    "io.modelcontextprotocol/tasks": {}
  }
}
```

2. Call `tools/call` with `name: "app_run"`. The server returns `resultType: "task"` and a `taskId` (the platform task ID).
3. Poll `tasks/get` with that `taskId` until `status` is `completed` or `cancelled`. Suggested poll interval is in `pollIntervalMs` (about 2 seconds).
4. When `status` is `completed`, the task `result` field carries the same tool output shape as a synchronous `app_run` (including `isError` for failed runs). A `cancelled` task carries `result` too when its app returned something before stopping.
5. Call `tasks/cancel` to stop a run in progress.

### Waiting for `app_run`

Without the tasks extension, `app_run` takes an optional **`wait`** boolean:

- **`wait: true`**: the HTTP connection streams progress notifications and ends with the final tool result.
- **`wait: false`**: the result comes back as soon as the task exists, with `task_id` and a status such as `received`. Call `task_get` with `wait: true` to get the output; the result text says so.

When `wait` is omitted, it defaults to **false for hosts that declared the [MCP Apps extension](#mcp-apps-interactive-uis)** and **true for every other client**. A host that renders the task card gets the task ID at once, so the card shows the run live (queued, running, then the output) instead of waiting for the run to end. The model then waits on `task_get`.

`task_get` with `wait: true` holds the answer until the task is completed, failed, or cancelled, for up to 45 seconds. A task still running then comes back with its current status and a line asking to call again. Without `wait`, `task_get` answers at once with the current status.

`tasks/update` is accepted for protocol compliance; `app_run` does not use multi-step input, so updates are no-ops.

Task IDs are platform task IDs — use `task_get`, `task_logs`, or `task_cancel` tools (or the [Tasks REST API](/docs/api/rest/tasks)) interchangeably.

### MCP Apps (interactive UIs)

[MCP Apps](https://modelcontextprotocol.io) (SEP-1865) let a host render an HTML page beside a tool result. The inference shell MCP server acts as both a **host** (for connector tools your agents call) and a **server** (for platform tools such as `app_run`).

**Declare the extension** in client capabilities on every request (same shape as the tasks extension), or once in `initialize` params under `capabilities` on a session connection; the server keeps what `initialize` declared for the rest of the session:

```json
"io.modelcontextprotocol/clientCapabilities": {
  "extensions": {
    "io.modelcontextprotocol/ui": {}
  }
}
```

**Resources** are HTML documents with MIME type `text/html;profile=mcp-app`. Built-in viewers cover tasks, artifacts, and the [workspace](#workspace-entrypoints); app and artifact pages are addressed as:

```text
ui://inference.sh/artifacts/{artifact_id}@{version_number}
```

That resource is a small viewer that carries the page and nests it, so the host gets an MCP App and the page keeps speaking only to its viewer. The bare page, which the viewers read to nest it, is `ui://inference.sh/artifact-pages/{artifact_id}@{version_number}` with MIME type `text/html`. Viewers normally load that document with `resources/read`. Some hosts (for example ChatGPT) let an MCP App card read only the resource it was opened with, so the artifact viewer loads the nested page through the page-only `artifact_page_get` tool instead, which returns the same HTML and URI.

Use `resources/list` to enumerate built-in pages and `resources/read` to fetch one. Each resource carries `_meta.ui.csp` with `connectDomains`, `resourceDomains`, and `frameDomains` so the host can load scripts, styles, and nested frames safely. The same policy is also sent as `_meta.openai/widgetCSP` (`connect_domains`, `resource_domains`, `frame_domains`) on every client: Apps SDK hosts apply their default CSP when that key is missing, which blocks the app origin that serves viewer scripts. Only `_meta.ui.domain` (the Claude connector content domain) still depends on which client connected. The workspace viewer's resource also lists the deployment app origin in `frameDomains`, because it nests the full app in an iframe; no other resource does.

Built-in **viewer** URIs in `tools/list` and `resources/list` include a platform revision in the URI itself (derived from the viewer shell and its resource `_meta`). Hosts that cache a viewer by URI therefore pick up CSP or domain changes on deploy instead of reusing an older shell under stale framing rules.

**Tool results** name a page when one applies:

| Field | Where |
|-------|--------|
| `structuredContent.ui.resource_uri` | JSON body on `app_run`, `task_get`, `artifact_get`, `artifact_publish`, and similar tools |
| `_meta.ui.resourceUri` | MCP result metadata (hosts that read `_meta` use this URI with `resources/read`) |

Tools that render a card name their viewer in `tools/list` under `_meta.ui.resourceUri`: `app_run` (task viewer), `artifact_get` and `artifact_publish` (artifact viewer), and the [workspace entrypoints](#workspace-entrypoints). `task_get` has no card of its own: it usually follows an `app_run` whose card already shows the task.

When an app version declares an [app UI artifact](/docs/api/rest/apps#custom-ui-page-ui) in `metadata.ui`, finished runs return that page URI so the host can replace the generic task output view.

**Page-only tools** — `artifact_data_get`, `artifact_data_list`, `artifact_data_set`, `artifact_data_update`, `artifact_data_delete`, `artifact_viewer_get`, and `artifact_page_get` are not for the model. They are marked with `_meta.ui.visibility: ["app"]` and back the [artifact page runtime](/docs/artifacts/capabilities) (`window.inferencesh`) when a page is nested in one of the server's viewers. `artifact_page_get` takes `artifact_id` and optional `version_number` and returns `structuredContent` with `uri`, `version_number`, and `html`, the rendered page at `ui://inference.sh/artifact-pages/{id}@{n}`. If the caller cannot read the requested version, the live version is served (same fallback as `resources/read` on that URI).

**Outbound connectors** — when agents call tools on connected MCP servers, the platform preserves tool `_meta`, reads the declared UI resource at call time (capped at 2 MiB), stores the document once by hash, and attaches `ui.page_hash` on the tool-call record (older rows may still inline `ui.html`). Chat and audit UIs fetch stored HTML with [`GET /mcp-ui-pages/{hash}`](/docs/api/rest/mcp-servers#get-stored-page). Follow-up `tools/call` and `resources/read` from the page use the connection id when the server is not in the directory. See [MCP Servers API — tool calls](/docs/api/rest/mcp-servers#list-and-call-tools).

### Connecting to other MCP servers

When you use MCP proxy tools (`mcp_run`, etc.), the platform’s outbound MCP client also speaks the tasks extension and follows `input_required` flows (for example elicitation) on your behalf, polling remote `tasks/get` until a tool finishes.

### MCP Apps (interactive UI)

Remote tools may return MCP Apps metadata (`_meta.ui`) with an HTML resource. In agent chat, that UI is rendered as an A2UI **`McpApp`** embed: sandboxed HTML, tool input/result over `postMessage`, and proxied `tools/call` through your workspace credential. See [Connector tools on agents — MCP Apps UI](/docs/connectors/agent-tools#mcp-apps-ui-interactive-tool-pages).

---

## Next

- [Connectors Overview](/docs/connectors/overview) — what connectors are and how they work
- [Browsing & Connecting](/docs/connectors/browsing) — set up connections to MCP servers
