Follow Discord
Sweep 22 Oct 2026 · 21:04Z Build v2.1.309 528 read Stable v2.1.308 Latest v2.1.309 Next v2.1.309 Feeds RSS JSON llms.txt llms-full.txt Unofficial
One capture · mcp

One read of Model Context Protocolmcp-20260826T033707Z

205 pages moved out of 343 read.

Pages moved 205 significant first
Pages read 343 in this capture
Captured 03:37 UTC
Corpus hash 9669ef239dbb corpus-hash

What this read moved

76-100 of 205, page 4 of 9

This capture is too large to show at once. Changes 76-100 of 205 are below, significant first; the rest are on the following screens.

docs/draft/learn/versioning New page · 62 lines, new page

# Versioning ## Revisions ## Feature States ## Negotiation

A whole new page. There's nothing to diff it against, so here is what it says.

# Versioning

The Model Context Protocol uses string-based version identifiers following the format
`YYYY-MM-DD`, to indicate the last date backwards incompatible changes were made.

<Info>
  The protocol version will *not* be incremented when the
  protocol is updated, as long as the changes maintain backwards compatibility. This allows
  for incremental improvements while preserving interoperability.
</Info>

## Revisions

Revisions may be marked as:

* **Draft**: in-progress specifications, not yet ready for consumption.
* **Current**: the current protocol version, which is ready for use and may continue to
  receive backwards compatible changes.
* **Final**: past, complete specifications that will not be changed.

The **current** protocol version is [**2026-07-28**](/specification/2026-07-28/).

## Feature States

Individual features of the specification may additionally be marked as
**Deprecated** under the
[feature lifecycle and deprecation policy](/community/feature-lifecycle):
the feature remains part of the specification, but is scheduled for removal.
Deprecated features document a migration path (or state that none is required)
and remain in the specification for at least twelve months, or at least
ninety days under the policy's
[expedited-removal exception](/community/feature-lifecycle#expedited-removal),
before they become eligible for removal, after which they may be **Removed**
in a future revision.

Features that are currently Deprecated are listed in the
[deprecated features registry](/specification/draft/deprecated).

## Negotiation

Every request declares the protocol version it is using via the
`io.modelcontextprotocol/protocolVersion` key in its
[`_meta`](/specification/draft/basic/index#meta) field, and the server accepts or
rejects each request independently. On Streamable HTTP, the same value is also carried
in the
[`MCP-Protocol-Version` header](/specification/draft/basic/transports/streamable-http#protocol-version-header).
Clients and servers **MAY** support multiple protocol versions simultaneously.

If the server does not support the requested version, it responds with an
[`UnsupportedProtocolVersionError`](/specification/draft/basic/versioning#protocol-version-negotiation)
listing the versions it does support. The client can then retry the request with a
mutually supported version, or surface an error to the user if none exists.

Clients that want to select a version up front can call
[`server/discover`](/specification/draft/server/discover), a mandatory RPC that
returns the server's supported protocol versions, capabilities, and identity in a
single request. Calling it is optional: a client is free to send any request directly
and handle a version error if one comes back.

For interoperability with servers and clients that implement the
handshake-based protocol revisions (`2025-11-25` and earlier), see
[Backward Compatibility](/specification/draft/basic/versioning#backward-compatibility-with-initialization-based-versions).

docs/draft/sdk New page · 47 lines, new page

# SDKs ## Available SDKs ## Getting Started ## Next Steps

A whole new page. There's nothing to diff it against, so here is what it says.

# SDKs

> Official SDKs for building with Model Context Protocol

Build MCP servers and clients using our official SDKs. SDKs are classified into tiers based on feature completeness, protocol support, and maintenance commitment. Learn more about [SDK tiers](/community/sdk-tiers).

## Available SDKs

| SDK                                                                                        | Repository                                                                                    |                                              Tier |
| :----------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------- | ------------------------------------------------: |
| <Icon icon="square-js" size={24} />   [TypeScript](https://ts.sdk.modelcontextprotocol.io) | [modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk) |   <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="python" size={24} />   [Python](https://py.sdk.modelcontextprotocol.io)        | [modelcontextprotocol/python-sdk](https://github.com/modelcontextprotocol/python-sdk)         |   <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="square-c" size={24} />   [C#](https://csharp.sdk.modelcontextprotocol.io)      | [modelcontextprotocol/csharp-sdk](https://github.com/modelcontextprotocol/csharp-sdk)         |   <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="golang" size={24} />   [Go](https://go.sdk.modelcontextprotocol.io)            | [modelcontextprotocol/go-sdk](https://github.com/modelcontextprotocol/go-sdk)                 |   <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="rust" size={24} />   [Rust](https://rust.sdk.modelcontextprotocol.io)          | [modelcontextprotocol/rust-sdk](https://github.com/modelcontextprotocol/rust-sdk)             |   <Badge color="blue" shape="pill">Tier 1</Badge> |
| <Icon icon="java" size={24} />   [Java](https://java.sdk.modelcontextprotocol.io)          | [modelcontextprotocol/java-sdk](https://github.com/modelcontextprotocol/java-sdk)             | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="gem" size={24} />   [Ruby](https://ruby.sdk.modelcontextprotocol.io)           | [modelcontextprotocol/ruby-sdk](https://github.com/modelcontextprotocol/ruby-sdk)             | <Badge color="purple" shape="pill">Tier 2</Badge> |
| <Icon icon="swift" size={24} />   Swift                                                    | [modelcontextprotocol/swift-sdk](https://github.com/modelcontextprotocol/swift-sdk)           | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="php" size={24} />   [PHP](https://php.sdk.modelcontextprotocol.io)             | [modelcontextprotocol/php-sdk](https://github.com/modelcontextprotocol/php-sdk)               | <Badge color="orange" shape="pill">Tier 3</Badge> |
| <Icon icon="square-k" size={24} />   [Kotlin](https://kotlin.sdk.modelcontextprotocol.io)  | [modelcontextprotocol/kotlin-sdk](https://github.com/modelcontextprotocol/kotlin-sdk)         | <Badge color="orange" shape="pill">Tier 3</Badge> |

See [SDK Tiering System](/community/sdk-tiers) for details on what each tier means.

## Getting Started

Each SDK provides the same functionality but follows the idioms and best practices of its language. All SDKs support:

* Creating MCP servers that expose tools, resources, and prompts
* Building MCP clients that can connect to any MCP server
* Local and remote transport protocols
* Protocol compliance with type safety

Visit the SDK page for your chosen language to find installation instructions, documentation, and examples.

## Next Steps

Ready to start building with MCP? Choose your path:

<CardGroup cols={2}>
  <Card title="Build a Server" icon="server" href="/docs/draft/develop/build-server">
    Learn how to create your first MCP server
  </Card>

  <Card title="Build a Client" icon="computer" href="/docs/draft/develop/build-client">
    Create applications that connect to MCP servers
  </Card>
</CardGroup>

docs/draft/tools/debugging New page · 368 lines, new page

# Debugging ## Debugging tools overview ## Implementing logging ### Server-side logging ## Common issues ### Working directory ### Environment variables ### Server startup ### Connection problems ## Debugging in Claude Desktop ### Checking server status ### Viewing logs ### Using Chrome DevTools ## Debugging workflow ### Development cycle ### Testing changes ## Best practices ### Logging strategy ### Security considerations ## Getting help ## Next steps

A whole new page. There's nothing to diff it against, so here is what it says.

# Debugging

> A comprehensive guide to debugging Model Context Protocol (MCP) integrations

Effective debugging is essential when developing MCP servers or integrating
them with applications. This guide covers the debugging tools and approaches
available in the MCP ecosystem.

## Debugging tools overview

MCP provides several tools for debugging at different levels:

1. **[MCP Inspector](/docs/draft/tools/inspector)**: interactive, transport-agnostic
   testing UI. Connect to stdio or Streamable HTTP servers, invoke
   [tools](/specification/latest/server/tools),
   [prompts](/specification/latest/server/prompts), and
   [resources](/specification/latest/server/resources), and watch the
   notification stream. This should be your first stop.
2. **Server logging**: structured logs to stderr (stdio transport) or via
   [OpenTelemetry](https://opentelemetry.io/) (all transports).
   [Logging](/specification/draft/server/utilities/logging) over the protocol
   (`notifications/message`) is deprecated as of protocol version `2026-07-28`.
3. **Client developer tools**: most MCP clients expose logs and connection
   state. See [Debugging in Claude Desktop](#debugging-in-claude-desktop)
   below for one example, or consult your client's documentation.

## Implementing logging

### Server-side logging

When building a server that uses the local
[stdio transport](/specification/draft/basic/transports/stdio), all messages
logged to stderr (standard error) will be captured by the host application
automatically.

<Warning>
  Local MCP servers should not log messages to stdout (standard out), as this
  will interfere with protocol operation.
</Warning>

For servers using the
[Streamable HTTP transport](/specification/draft/basic/transports/streamable-http),
stderr is not captured by the client. Use your own server-side log aggregation
or [OpenTelemetry](https://opentelemetry.io/) for logs, and standard HTTP
tooling (curl, browser DevTools Network panel) to inspect requests and SSE
streams.

<Warning>
  The `notifications/message` mechanism below is deprecated as of protocol
  version `2026-07-28`. It remains available during the deprecation window.
</Warning>

For all [transports](/specification/latest/basic/transports), record what the
server is doing as it runs:

<CodeGroup>
  ```python Python theme={null}
  import logging

  from mcp.server import MCPServer

  logger = logging.getLogger(__name__)

  mcp = MCPServer("reports")


  @mcp.tool()
  async def fetch_report(report_id: str) -> str:
      """Fetch a report by id."""
      logger.info("Fetching report %s", report_id)
      return f"Report {report_id} is ready."
  ```

  ```typescript TypeScript theme={null}
  await server.sendLoggingMessage({
    level: "info",
    data: "Server started successfully",
  });
  ```
</CodeGroup>

MCP defines eight
[RFC 5424 severity levels](/specification/latest/server/utilities/logging#log-levels)
(`debug` through `emergency`). Clients opt in to log messages per request by
setting the
[`io.modelcontextprotocol/logLevel`](/specification/draft/server/utilities/logging#per-request-log-level)
field in the request's `_meta`. Servers must not send `notifications/message`
for requests that omit this field.

Important events to log:

* Startup steps
* Resource access
* Tool execution
* Error conditions
* Performance metrics

## Common issues

The examples below use Claude Desktop's
[`claude_desktop_config.json`](/docs/draft/develop/connect-local-servers); the same
principles apply to any stdio-based MCP client.

### Working directory

When an MCP client launches a stdio server:

* The working directory for servers launched via the client's config may be
  undefined (like `/` on macOS) since the client could be started from
  anywhere
* Always use absolute paths in your configuration and `.env` files to ensure
  reliable operation
* For testing servers directly via command line, the working directory will be
  where you run the command

For example in `claude_desktop_config.json`, use:

```json theme={null}
{
  "mcpServers": {
    "filesystem": {
      "command": "npx",
      "args": [
        "-y",
        "@modelcontextprotocol/server-filesystem",
        "/Users/username/data"
      ]
    }
  }
}
```

Instead of relative paths like `./data`

### Environment variables

MCP servers launched over stdio inherit only a limited subset of environment
variables automatically (the exact set is platform-dependent).

To override the default variables or provide your own, you can specify an
`env` key in `claude_desktop_config.json`:

```json theme={null}
{
  "mcpServers": {
    "myserver": {
      "command": "mcp-server-myapp",
      "env": {
        "MYAPP_API_KEY": "some_key"
      }
    }
  }
}
```

### Server startup

Common startup problems:

1. **Path Issues**
   * Incorrect server executable path
   * Missing required files
   * Permission problems
   * Try using an absolute path for `command`

2. **Configuration Errors**
   * Invalid JSON syntax
   * Missing required fields
   * Type mismatches

3. **Environment Problems**
   * Missing environment variables
   * Incorrect variable values
   * Permission restrictions

### Connection problems

When servers fail to connect:

1. Check client logs
2. Verify server process is running
3. Test standalone with [Inspector](/docs/draft/tools/inspector)
4. Verify
   [protocol compatibility](/docs/draft/learn/versioning#negotiation): call
   [`server/discover`](/specification/draft/server/discover) to see which
   protocol versions the server supports. An
   `UnsupportedProtocolVersionError` (`-32022`) lists the server's supported
   versions in its `data` field
5. Check the
   [per-request `_meta` fields](/specification/draft/basic/index#meta):
   every request must carry `io.modelcontextprotocol/protocolVersion` and
   `io.modelcontextprotocol/clientCapabilities`, and clients should also
   include `io.modelcontextprotocol/clientInfo`. A request missing either
   required field is rejected with error `-32602` (Invalid params), the same
   code returned for many other malformed inputs. If the server needs a
   capability the request's `clientCapabilities` did not declare, such as
   [elicitation](/specification/draft/client/elicitation), it returns a
   `MissingRequiredClientCapabilityError` (`-32021`) naming the missing
   capabilities. Inspect the request's `_meta` and the
   [`server/discover`](/specification/draft/server/discover) response to
   verify both sides declared what you expect

## Debugging in Claude Desktop

Claude Desktop is one of many MCP clients. It is available on
macOS and Windows.

### Checking server status

Click the "Add files, connectors, and more" plus icon in the chat input, then
hover over the **Connectors** menu to see connected servers and available
tools.

<img src="https://mintcdn.com/mcp/zNouQwo2h8cbxlDS/images/available-mcp-tools.png?fit=max&auto=format&n=zNouQwo2h8cbxlDS&q=85&s=e2ace1ac88895a5fe30ebd8d01456bc3" alt="Available MCP tools" width="437" height="244" data-path="images/available-mcp-tools.png" />

### Viewing logs

Log files are written to:

* macOS: `~/Library/Logs/Claude`
* Windows: `%APPDATA%\Claude\logs`

<CodeGroup>
  ```bash macOS theme={null}
  tail -n 20 -F ~/Library/Logs/Claude/mcp*.log
  ```

  ```powershell Windows theme={null}
  type "$env:AppData\Claude\logs\mcp*.log"
  ```
</CodeGroup>

The logs capture:

* Server connection events
* Configuration issues
* Runtime errors
* Message exchanges

### Using Chrome DevTools

Access Chrome's developer tools inside Claude Desktop to investigate
client-side errors:

1. Create a `developer_settings.json` file with `allowDevTools` set to true:

<CodeGroup>
  ```bash macOS theme={null}
  echo '{"allowDevTools": true}' > ~/Library/Application\ Support/Claude/developer_settings.json
  ```

  ```powershell Windows theme={null}
  '{"allowDevTools": true}' | Set-Content "$env:AppData\Claude\developer_settings.json"
  ```
</CodeGroup>

2. Open DevTools: `Command-Option-I` (macOS) or `Ctrl+Alt+I` (Windows)

Note: You'll see two DevTools windows:

* Main content window
* App title bar window

Use the Console panel to inspect client-side errors.

Use the Network panel to inspect:

* Message payloads
* Connection timing

## Debugging workflow

### Development cycle

1. Initial Development
   * Use [Inspector](/docs/draft/tools/inspector) for basic testing
   * Implement core functionality
   * Add logging points

2. Integration Testing
   * Test in your target MCP client
   * Monitor logs
   * Check error handling

### Testing changes

To test changes efficiently:

* **Configuration changes**: Restart the MCP client
* **Server code changes**: Restart the client (for Claude Desktop, fully quit
  and reopen; closing the window is not enough)
* **Quick iteration**: Use [Inspector](/docs/draft/tools/inspector) during
  development

## Best practices

### Logging strategy

1. **Structured Logging**
   * Use consistent formats

Cut at 300 lines. The page has the rest.

docs/draft/tools/inspector New page · 144 lines, new page

# MCP Inspector ## Quickstart ### Inspecting published servers ## Launcher flags vs. client flags ## Where to go next

A whole new page. There's nothing to diff it against, so here is what it says.

# MCP Inspector

> Interactive developer tooling for testing and debugging MCP servers, in the browser, on the command line, and in the terminal

The [MCP Inspector](https://github.com/modelcontextprotocol/inspector) is the reference developer tool for testing and debugging [MCP servers](/docs/draft/learn/server-concepts). It ships as a single package, `@modelcontextprotocol/inspector`, providing **three clients behind one binary**:

| Client  | Invocation                                  | What it's for                                                                     |
| ------- | ------------------------------------------- | --------------------------------------------------------------------------------- |
| **Web** | `npx @modelcontextprotocol/inspector`       | A full graphical inspector in the browser. The default, and the richest surface.  |
| **CLI** | `npx @modelcontextprotocol/inspector --cli` | A scriptable, machine-readable client for CI, shell pipelines, and coding agents. |
| **TUI** | `npx @modelcontextprotocol/inspector --tui` | An interactive terminal UI, for when a browser isn't available or wanted.         |

All three are built on the same shared core, so a connection behaves identically across them: the same transports, the same configuration files, the same OAuth state on disk, and the same [protocol-era](/docs/draft/tools/inspector/protocol-eras) negotiation (legacy vs. modern 2026-07-28).

<Frame caption="The MCP Inspector web client, connected to a server, with the monitoring sidebar pinned so protocol traffic stays visible while you work.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-monitor-sidebar.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=eef6e546b9831b3d169e26bba8c54ce3" width="3840" height="2160" data-path="images/inspector/web-monitor-sidebar.png" />
</Frame>

## Quickstart

The Inspector requires **Node 22.19.0 or newer** and runs directly through `npx`. No installation is required:

<Tabs>
  <Tab title="Web">
    ```bash theme={null}
    # Launch the web UI and connect to a local stdio server
    npx @modelcontextprotocol/inspector node path/to/server/index.js

    # Or launch with no target and add servers from the UI
    npx @modelcontextprotocol/inspector
    ```

    The command prints a URL containing a one-time session token; open it in your browser. See [Web client](/docs/draft/tools/inspector/web).
  </Tab>

  <Tab title="CLI">
    ```bash theme={null}
    # List a server's tools and exit
    npx @modelcontextprotocol/inspector --cli node path/to/server/index.js --method tools/list

    # Call a tool and pipe the result into jq
    npx @modelcontextprotocol/inspector --cli https://api.example.com/mcp --transport http \
      --method tools/call --tool-name get_weather --tool-arg city=Boston --format json | jq .result
    ```

    See [CLI client](/docs/draft/tools/inspector/cli).
  </Tab>

  <Tab title="TUI">
    ```bash theme={null}
    npx @modelcontextprotocol/inspector --tui node path/to/server/index.js
    ```

    See [TUI client](/docs/draft/tools/inspector/tui).
  </Tab>
</Tabs>

### Inspecting published servers

Pass the command that launches the server as the Inspector's arguments, or point it at a remote server with `--server-url`:

<Tabs>
  <Tab title="npm package">
    ```bash theme={null}
    npx -y @modelcontextprotocol/inspector npx @modelcontextprotocol/server-filesystem ~/Desktop
    ```
  </Tab>

  <Tab title="PyPI package">
    ```bash theme={null}
    npx @modelcontextprotocol/inspector uvx mcp-server-git --repository ~/code/mcp/servers.git
    ```
  </Tab>

  <Tab title="Remote HTTP server">
    ```bash theme={null}
    npx @modelcontextprotocol/inspector --server-url https://api.example.com/mcp --transport http
    ```
  </Tab>
</Tabs>

Always read a server's own README first, since every server requires different commands and arguments.

## Launcher flags vs. client flags

`mcp-inspector`, the binary that `npx @modelcontextprotocol/inspector` runs, is a thin launcher. It owns only two things:

1. **The mode flag:** `--web` (default), `--cli`, or `--tui`. At most one; passing two errors with `Specify at most one of --web, --cli, or --tui.`
2. **`-h` / `--help`.**

Everything else (`--catalog`, `--config`, `--server-url`, `--transport`, `--method`, the OAuth flags) is defined by the *client*, not the launcher, and the clients do not all define the same set. The [Configuration and flags](/docs/draft/tools/inspector/configuration) page is organized that way, by owner.

<Note>
  Mode flags are recognized only at the front of the command line: the first token that isn't `--web` / `--cli` / `--tui` ends launcher parsing, and everything after it is forwarded to the client unchanged. That's what lets a literal `--cli` appear later as one of your server's own arguments:

  ```bash theme={null}
  mcp-inspector --cli node server.js --cli   # mode is CLI; the trailing --cli goes to server.js
  ```
</Note>

<Note>
  `--help` behaves differently with and without a mode flag. Bare `mcp-inspector   --help` prints the launcher's help and exits. With a mode flag it is
  forwarded, so `mcp-inspector --cli --help` prints the CLI's full flag
  reference instead.
</Note>

## Where to go next

<CardGroup cols={2}>
  <Card title="Web client" icon="browser" href="/docs/draft/tools/inspector/web">
    A tab-by-tab walkthrough of the graphical inspector.
  </Card>

  <Card title="CLI client" icon="terminal" href="/docs/draft/tools/inspector/cli">
    Method reference, output formats, exit codes, and CI recipes.
  </Card>

  <Card title="TUI client" icon="table-columns" href="/docs/draft/tools/inspector/tui">
    Terminal navigation and keyboard reference.
  </Card>

  <Card title="Configuration and flags" icon="sliders" href="/docs/draft/tools/inspector/configuration">
    Catalog vs. config files, the full per-client flag reference, and
    environment variables.
  </Card>

  <Card title="Authorization" icon="lock" href="/docs/draft/tools/inspector/authorization">
    The OAuth flow end to end, mid-session re-authorization, and loopback
    callbacks.
  </Card>

  <Card title="Protocol eras" icon="code-branch" href="/docs/draft/tools/inspector/protocol-eras">
    Legacy vs. modern (2026-07-28) operation, and how every tab changes between
    protocol eras.
  </Card>

  <Card title="Recipes" icon="book" href="/docs/draft/tools/inspector/recipes">
    Importing client configs, reviewing MCP Apps, Docker, and network hosting.
  </Card>

  <Card title="Debugging guide" icon="bug" href="/docs/draft/tools/debugging">
    Broader debugging strategies beyond the Inspector.
  </Card>
</CardGroup>

docs/draft/tools/inspector/authorization New page · 159 lines, new page

# Authorization ## The flow, end to end ## Callback URLs ## Where credentials live ## Mid-session re-authorization ## Non-interactive and CI runs ## Handing off from the web client to the CLI ## Inspecting auth state

A whole new page. There's nothing to diff it against, so here is what it says.

# Authorization

> How the MCP Inspector performs OAuth, re-authorizes mid-session, and shares tokens between its clients

Remote MCP servers usually require authorization. The Inspector implements the full [authorization](/specification/latest/basic/authorization) flow in all three clients, sharing the resulting tokens on disk so a login done once is usable everywhere.

## The flow, end to end

<Steps>
  <Step title="Connect, and get refused">
    The Inspector connects to the server URL. The server answers `401`. When the
    response carries a `WWW-Authenticate` header, it points at the
    protected-resource metadata URL (`resource_metadata`) and, optionally, the
    scopes the request requires.
  </Step>

  <Step title="Discover the authorization server">
    The Inspector fetches the server's [protected-resource and
    authorization-server
    metadata](/specification/latest/basic/authorization/authorization-server-discovery)
    to learn the endpoints and the supported grants.
  </Step>

  <Step title="Register or identify the client">
    The Inspector identifies itself to the authorization server through
    whichever mechanism is configured: [dynamic client
    registration](/specification/latest/basic/authorization/client-registration#dynamic-client-registration),
    a pre-registered static client (`--client-id` / `--client-secret`), a
    [Client ID Metadata
    Document](/specification/latest/basic/authorization/client-registration#client-id-metadata-documents)
    (`--client-metadata-url`), or an [enterprise-managed
    IdP](/extensions/auth/enterprise-managed-authorization).
  </Step>

  <Step title="Authorize in the browser">
    The Inspector opens the authorization URL. You sign in and consent.
  </Step>

  <Step title="Receive the callback">
    The authorization server redirects to the Inspector's callback URL, carrying
    the authorization code.
  </Step>

  <Step title="Exchange and retry">
    The code is exchanged for tokens, the tokens are persisted, and the original
    connect (or, for a [mid-session challenge](#mid-session-re-authorization),
    the request that was refused) is retried automatically.
  </Step>
</Steps>

<Frame caption="Connection Info after a completed OAuth flow: the authorization status, the dynamically registered client, and the granted scopes.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/auth-connection-info.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=01a8d11058c2d33e0069b8dec98591b5" width="3840" height="2160" data-path="images/inspector/auth-connection-info.png" />
</Frame>

## Callback URLs

The web app listens for the OAuth callback on its own URL, while the CLI and TUI deliberately share a second one:

| Surface | Default callback                       | Why                                                                                |
| ------- | -------------------------------------- | ---------------------------------------------------------------------------------- |
| **Web** | `http://localhost:6274/oauth/callback` | The main app server already has an HTTP listener.                                  |
| **CLI** | `http://127.0.0.1:6276/oauth/callback` | A dedicated loopback listener, so it doesn't collide with a running web Inspector. |
| **TUI** | `http://127.0.0.1:6276/oauth/callback` | The same listener as the CLI.                                                      |

**Register `http://127.0.0.1:6276/oauth/callback`** on any IdP that requires pre-registered redirect URIs before using the CLI or TUI. A predictable default is the point: you register once and reuse it.

Override with `--callback-url` or `MCP_OAUTH_CALLBACK_URL`.

<Warning>
  The callback URL **must bind a loopback host**: `localhost`, `127.0.0.0/8`, or
  `[::1]`. The listener receives the authorization code over plaintext `http`,
  so a non-loopback host is rejected with an error and there is no flag to
  override that. If your browser runs on a different machine, forward the
  callback port to it; `--print-handoff` (below) prints a ready-made
  `portForwardCmd`.
</Warning>

<Note>
  Redirect URIs must match your registration **exactly**. `http://localhost:6276/...` and `http://127.0.0.1:6276/...` are different URIs to an authorization server, even though they reach the same listener.

  Only one process can hold the default port at a time; a second concurrent flow fails with `EADDRINUSE`. Use a different fixed port per instance, or `http://127.0.0.1:0/oauth/callback` for an OS-assigned ephemeral port when your authorization server supports dynamic redirect-URI registration.
</Note>

## Where credentials live

| File                                                                                                            | Contents                                                                                                                               |
| --------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |
| `~/.mcp-inspector/storage/oauth.json`                                                                           | Tokens and client information, keyed by canonicalized server URL. Written owner-only.                                                  |
| `~/.mcp-inspector/storage/client.json`                                                                          | Install-level client settings (client metadata URL, enterprise IdP). The same file the web client's **Client Settings** dialog writes. |
| The server's `oauth` block in the [catalog file](/docs/draft/tools/inspector/configuration#catalog-file-format) | Per-server client id/secret, scopes, the enterprise-managed flag, and the [step-up](#mid-session-re-authorization) policy.             |

The path to `oauth.json` is resolved in order: `MCP_INSPECTOR_OAUTH_STATE_PATH`, then `<MCP_STORAGE_DIR>/oauth.json` (see [Environment variables](/docs/draft/tools/inspector/configuration#environment-variables)), then the default above. All three clients resolve it the same way. Command-line `--client-id` / `--client-secret` / `--client-metadata-url` override `client.json`.

## Mid-session re-authorization

A server can refuse a *single* request mid-session with a `401` or a `403 insufficient_scope`, and the Inspector handles both without dropping the connection:

* **Re-authorization**: the token expired or was revoked. The Inspector parses the `WWW-Authenticate` challenge and re-runs the flow, then retries the failed request.
* **Step-up**: the request needs scopes the current token doesn't carry. The Inspector re-authorizes for the union of the held and required scopes, so the new token covers everything the old one did plus the newly required scopes.

In the **web** client this surfaces as a re-authorization banner. In the **CLI** it prompts on stderr:

```
Proceed with step-up authorization? [y/N]
```

Answer **y** to continue. Piped input works (`echo y | ...`), as long as it's newline-terminated or stdin closes. **N**, or EOF with no answer, declines. A non-TTY stdin that sends nothing within 5 seconds fails with `auth_required`, which is distinct from an explicit decline. Enterprise-managed step-up re-mints silently, with no prompt.

## Non-interactive and CI runs

Interactive OAuth requires a TTY on **stdin or stderr**, or [`MCP_AUTO_OPEN_ENABLED=true`](/docs/draft/tools/inspector/configuration#environment-variables). Redirecting stderr into a pipe, as in `2>&1 | tee`, still works because stdin stays a TTY. When neither is true, which is the normal CI shape, the CLI fails fast with `auth_required` rather than waiting up to fifteen minutes for a callback nobody will complete.

For CI, be explicit:

```bash theme={null}
mcp-inspector --cli "$URL" --transport http --stored-auth-only --method tools/list
```

`--stored-auth-only` never starts interactive OAuth or step-up, never opens a browser, uses the shared store if a token is there, and fails immediately otherwise.

## Handing off from the web client to the CLI

The common case: a human completed OAuth in the web Inspector on this machine, and now a script wants to use that token.

| Flag                    | Behavior                                                                                                                                                                                                                                                          |
| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--use-stored-auth`     | Read the stored auth for `--server-url` and inject `Authorization: Bearer`. When a refresh token is stored, run the refresh grant first and inject the **fresh** token, persisting the rotation. Exits `3` (listing the stored server URLs) when nothing matches. |
| `--wait-for-auth <sec>` | Poll the state file until a token for `--server-url` appears, then inject it. Times out at `<sec>` with exit `3`. Use after handing a login off to a human.                                                                                                       |
| `--list-stored-auth`    | Print `{ oauthStatePath, storedServerUrls }` and exit without connecting.                                                                                                                                                                                         |
| `--print-handoff`       | Print a JSON block (`deepLink`, `portForwardCmd`, `oauthStatePath`, `apiToken`) for `--server-url` and exit; this is everything a remote script needs to drive the browser side.                                                                                  |
| `--relogin`             | Delete the stored OAuth for this server URL before connecting. HTTP/SSE only.                                                                                                                                                                                     |

A typical remote-VM sequence:

```bash theme={null}
# On the VM: print what the human needs in order to complete OAuth in their browser
mcp-inspector --cli --server-url https://api.example/mcp --print-handoff

# Then block until the token lands, and run the call with it
mcp-inspector --cli --transport http --server-url https://api.example/mcp \
  --wait-for-auth 120 --method tools/list
```

The `deepLink` in the handoff block navigates a browser straight to a *connected* Inspector; see [Deep links](/docs/draft/tools/inspector/web#deep-links).

<Note>
  Because the stored entry records no expiry, a stored refresh token is
  exercised on **every** `--use-stored-auth` run. With rotating (single-use)
  refresh tokens that opens two narrow failure windows: two concurrent
  invocations against the same state file can race for the token, and a crash
  between a successful refresh and the write-back leaves the rotated token
  unsaved. Both are unlikely; re-authorize in the web client to recover.
</Note>

## Inspecting auth state

* **Web**: the Connection Info panel shows discovery results, the registered client, granted scopes, and token state, and offers **Clear OAuth state** for the active server.
* **TUI**: the **Auth** tab (`a`) shows the same fields and clears state the same way.
* **CLI**: `--list-stored-auth` shows what's on disk, and `--relogin` discards it and starts over.

docs/draft/tools/inspector/cli New page · 196 lines, new page

# CLI client ## Choosing a server ## Methods ### Passing arguments ## Output ## Probing MCP Apps ## Exit codes and error envelopes ## Authorization in scripts ## Recipes ### Verify a server in CI ### Branch on the failure class ### Smoke-test every tool that has a UI ### Inspect a catalog without connecting ## Proxies

A whole new page. There's nothing to diff it against, so here is what it says.

# CLI client

> Scripting the MCP Inspector: methods, output formats, exit codes, and CI recipes

Each CLI run connects to a server, invokes the single request you name with `--method`, prints the result, and exits. That makes it a good fit for CI pipelines, shell one-liners, and coding agents that need to verify a server change immediately.

```bash theme={null}
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list
```

The examples below use the installed `mcp-inspector` binary. Without a global install, prefix each command with `npx @modelcontextprotocol/inspector` instead, as above.

## Choosing a server

The CLI accepts a positional command (stdio), a `--server-url` (HTTP/SSE), or a named server out of a catalog or config file:

```bash theme={null}
# stdio: everything positional is the command to spawn
mcp-inspector --cli node build/index.js --method tools/list

# HTTP
mcp-inspector --cli https://api.example.com/mcp --transport http --method tools/list

# From a file
mcp-inspector --cli --config ./mcp.json --server myserver --method tools/list
```

When the server comes from a file, its per-server settings (headers, timeouts, OAuth, [protocol era](/docs/draft/tools/inspector/protocol-eras), and roots) apply to the connection, resolved exactly as the TUI and web client resolve them. A `--header` flag overrides the file's headers for that run while leaving its timeouts and OAuth in place.

Later examples abbreviate whichever of these forms you use, along with its `--transport` or `--config`/`--server` flags, as `<server>`.

<Note>
  **The config file is the only durable way to give a run its
  [roots](/specification/draft/client/roots):** there is no roots flag, and
  `--method roots/set` applies only to that one short-lived connection. Roots
  configured for a server are advertised at connect, so a server that calls
  `roots/list` (as `@modelcontextprotocol/server-filesystem` does, to learn its
  allowed directories) gets them.
</Note>

See [Configuration and flags](/docs/draft/tools/inspector/configuration) for `--catalog` vs. `--config`, the `--` separator, and the shared server-selection flags.

## Methods

| `--method`                     | Required companions                                   | Notes                                                                            |
| ------------------------------ | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
| `initialize`                   | None                                                  | Connect-only probe: `{serverInfo, protocolVersion, capabilities, instructions}`. |
| `tools/list`                   | None                                                  |                                                                                  |
| `tools/call`                   | `--tool-name`, plus `--tool-arg` / `--tool-args-json` |                                                                                  |
| `resources/list`               | None                                                  |                                                                                  |
| `resources/read`               | `--uri`                                               |                                                                                  |
| `resources/templates/list`     | None                                                  |                                                                                  |
| `prompts/list`                 | None                                                  |                                                                                  |
| `prompts/get`                  | `--prompt-name`, `--prompt-args`                      |                                                                                  |
| `logging/setLevel`             | `--log-level`                                         | Legacy era only; modern servers opt in per request instead.                      |
| `servers/list`, `servers/show` | None                                                  | Read the catalog **without connecting** to anything.                             |

Stream- or session-only methods (`logging/tail`, for example) are rejected, since a process that exits can't hold a stream open.

### Passing arguments

`--tool-arg` takes `key=value` and **coerces** values by JSON-parsing them, so `count=1` becomes a number and `"012"` becomes `12`:

```bash theme={null}
mcp-inspector --cli <server> --method tools/call --tool-name mytool \
  --tool-arg key=value --tool-arg count=1 --tool-arg 'options={"format":"json"}'
```

`--tool-args-json` takes the whole argument object at once and passes it **verbatim**, with no coercion, so `"012"` stays the string `012`. The two are mutually exclusive:

```bash theme={null}
mcp-inspector --cli <server> --method tools/call --tool-name mytool \
  --tool-args-json '{"zip":"10001"}'
```

## Output

`--format text` (the default) pretty-prints for humans. `--format json` emits a single JSON object on stdout with no banners, so the whole output pipes cleanly:

```bash theme={null}
mcp-inspector --cli <server> --method tools/list --format json | jq '.result.tools[].name'
```

## Probing MCP Apps

`--app-info` reports whether a tool ships an [MCP App](/extensions/apps/overview) UI (its `ui://` resource, CSP, and permissions) **without calling the tool**, so a pipeline can decide whether it needs a browser before invoking anything:

```bash theme={null}
# One tool -> one JSON line
mcp-inspector --cli <server> --method tools/call --tool-name my_tool --app-info
# {"hasApp":true,"toolName":"my_tool","resourceUri":"ui://...","csp":{...},"permissions":{...}}

# Every tool -> NDJSON, one line each, over a single connection
mcp-inspector --cli <server> --method tools/list --app-info | jq -c 'select(.hasApp)'
```

Exit codes distinguish the outcomes: a tool with an app exits `0`, one with no app exits `2`, and a missing tool exits `5`, so a typo isn't mistaken for "no app". A probe failure (unreadable UI resource, malformed `resourceUri`) is reported in a `resourceError` field rather than aborting, so one bad tool never kills a whole listing.

<Note>
  `tools/list --app-info` always emits NDJSON (one line per tool) regardless of
  `--format`; `--format json` reshapes only the single-tool output of
  `tools/call --app-info`.
</Note>

## Exit codes and error envelopes

Every non-zero exit maps to a stable failure class, so a caller can branch on *why* without scraping prose:

| Code | Meaning                                                                      |
| ---- | ---------------------------------------------------------------------------- |
| `0`  | Success.                                                                     |
| `1`  | Usage or unexpected error (the catch-all).                                   |
| `2`  | No MCP App found on the tool (`--app-info` probe).                           |
| `3`  | Server requires authentication (401/403, `WWW-Authenticate`, OAuth).         |
| `4`  | Server unreachable (DNS, connection refused, timeout, `fetch failed`).       |
| `5`  | Tool error: `tools/call` returned `isError: true`, or the tool wasn't found. |

On any non-zero exit the CLI also writes a **single JSON line to stderr**:

```json theme={null}
{
  "error": {
    "code": "auth_required",
    "message": "Unauthorized",
    "status": 401,
    "url": "https://api.example/mcp"
  }
}
```

Because it's one line, a caller can parse it with `2>&1 | tail -1 | jq .error`.

A `tools/call` that returns `isError: true` still prints its payload, but exits `5`, so an `&&` chain doesn't proceed on a failed call.

## Authorization in scripts

By default the CLI runs the same loopback OAuth flow as the TUI: it opens a browser and waits on a localhost callback that a CI job can't complete. Two flags make non-interactive runs predictable:

* `--stored-auth-only`: never start interactive OAuth or step-up, and never auto-open a browser. Use tokens from the shared store if present, otherwise fail immediately with `auth_required`. This is the flag CI wants.
* `--use-stored-auth`: reuse a token that the web Inspector already obtained on this machine, refreshing it first when a refresh token is stored.

Without either, and with no TTY on stdin or stderr, the CLI fails fast with `auth_required` rather than hanging for fifteen minutes on a callback nobody will complete.

See [Authorization](/docs/draft/tools/inspector/authorization) for the full flow, the web-to-CLI handoff, and `--print-handoff`.

## Recipes

### Verify a server in CI

```bash theme={null}
set -euo pipefail

# Fail the build if the server can't be reached or doesn't expose the tool
mcp-inspector --cli --config ./ci-servers.json --server my-server \
  --stored-auth-only --method tools/list --format json \
  | jq -e '.result.tools | map(.name) | index("get_weather")' > /dev/null
```

### Branch on the failure class

```bash theme={null}
if out=$(mcp-inspector --cli "$URL" --transport http --method tools/list 2>err.json); then
  echo "$out"
else
  case $? in
    3) echo "needs auth: run the web inspector once to sign in" ;;
    4) echo "server unreachable" ;;
    *) jq .error < err.json ;;
  esac
fi
```

### Smoke-test every tool that has a UI

```bash theme={null}
mcp-inspector --cli "$URL" --transport http --method tools/list --app-info \
  | jq -r 'select(.hasApp) | .toolName'
```

### Inspect a catalog without connecting

```bash theme={null}
mcp-inspector --cli --catalog ~/.mcp-inspector/mcp.json --method servers/list
mcp-inspector --cli --catalog ~/.mcp-inspector/mcp.json --method servers/show --server my-server
```

<Warning>
  `servers/show` redacts secret-bearing fields (`env` values, sensitive headers,
  OAuth client secrets), but it does **not** scrub credentials embedded in a
  server `url` (userinfo or query tokens) or in stdio `args`. Treat raw URL and
  `detail` fields as sensitive before pasting them into an issue.
</Warning>

## Proxies

Connections to remote HTTP/SSE servers honor the conventional proxy variables: `HTTPS_PROXY` / `HTTP_PROXY` (and their lowercase forms) select the proxy and `NO_PROXY` exempts hosts. No Inspector-specific flag is needed, and the proxy agent is loaded lazily, so runs without a proxy pay nothing. The same applies to the web client's backend.

docs/draft/tools/inspector/configuration New page · 193 lines, new page

# Configuration and flags ## The launcher owns exactly two things ## Choosing servers ### `--catalog` vs. `--config` ### Ad-hoc targets ### Shared server-selection flags ### The `--` separator ## Web-only flags ## CLI and TUI: OAuth client flags ## CLI-only flags ## Environment variables ### Read by the launcher ### CLI and TUI ### Web backend environment variables ## Catalog file format

A whole new page. There's nothing to diff it against, so here is what it says.

# Configuration and flags

> Catalog vs. config files, which client owns which flag, and every environment variable

The `mcp-inspector` binary is a launcher: it reads two flags of its own and forwards every other argument to one of three clients (web, CLI, or TUI). Each client defines its own flags, so a flag that works in one can be unknown to another (`--method`, for example, is CLI-only). This page groups flags and environment variables by the client that owns them.

## The launcher owns exactly two things

| Flag                        | Behavior                                                                                                                                                                                                                                                                                         |
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `--web` / `--cli` / `--tui` | Selects the client, `--web` by default. Passing more than one fails with `Specify at most one of --web, --cli, or --tui.` Launcher flags must come first: parsing stops at the first argument the launcher does not own, and everything from that point on is forwarded to the client unchanged. |
| `-h` / `--help`             | With no mode flag, prints the launcher's own help and exits. With a mode flag it is forwarded, so `mcp-inspector --cli --help` prints the CLI's help.                                                                                                                                            |

Everything below belongs to a client.

## Choosing servers

### `--catalog` vs. `--config`

All three clients resolve `--catalog` and `--config` through the same shared code, so each flag behaves the same in the web app, the CLI, and the TUI. Where the two differ from each other is the table below.

|                             | `--catalog <path>`                                                           | `--config <path>`                                       |
| --------------------------- | ---------------------------------------------------------------------------- | ------------------------------------------------------- |
| **Writable?**               | Yes, the Inspector's own server list.                                        | No. Served as-is, never written, seeded, or migrated.   |
| **Missing file?**           | Created and seeded (see below).                                              | **Errors.**                                             |
| **Default**                 | `~/.mcp-inspector/mcp.json`, or the `MCP_CATALOG_PATH` environment variable. | None; you must pass it.                                 |
| **Editable in the web UI?** | Yes.                                                                         | No.                                                     |
| **Use it for**              | Your own working set of servers.                                             | A read-only session against someone else's config file. |

The two are **mutually exclusive**, and neither combines with an ad-hoc target. Passing both is rejected identically by all three clients.

<Note>
  **What a freshly seeded catalog contains depends on the client.** The web backend seeds two sample servers, so a first launch has something to connect to immediately:

  ```json theme={null}
  {
    "mcpServers": {
      "filesystem-server-default": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
      },
      "everything-server-default": {
        "type": "stdio",
        "command": "npx",
        "args": ["-y", "@modelcontextprotocol/server-everything"]
      }
    }
  }
  ```

  The CLI and TUI seed an empty `{ "mcpServers": {} }` instead: they are non-interactive or list-driven, so sample entries would be noise rather than a starting point.

  Either way, seeding happens only when the file does not exist yet, and a read-only `--config` is never seeded at all.
</Note>

<Note>
  `--config` is what you want when pointing the Inspector at a config file you
  didn't write: a coworker's, a client application's, or one checked into a
  repo. It guarantees the Inspector will not touch the file.
</Note>

### Ad-hoc targets

Instead of a file you can name one server directly, either as a positional command (stdio) or a URL:

```bash theme={null}
mcp-inspector node build/index.js                              # stdio, positional
mcp-inspector --server-url https://api.example.com/mcp --transport http
```

### Shared server-selection flags

Defined **separately by each of web, CLI, and TUI**, so they're available in all three, with the divergences noted:

| Flag                     | Meaning                                               | Divergence                                                                                      |
| ------------------------ | ----------------------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `--catalog <path>`       | Writable catalog file.                                | None                                                                                            |
| `--config <path>`        | Read-only session file.                               | None                                                                                            |
| `--server <name>`        | Pick one named server out of the file.                | **Web and CLI only.** The TUI loads every server in the file and lets you choose interactively. |
| `--transport <type>`     | `stdio`, `sse`, or `http`.                            | Ad-hoc targets only.                                                                            |
| `--server-url <url>`     | Server URL for SSE/HTTP.                              | Ad-hoc targets only.                                                                            |
| `--cwd <path>`           | Working directory for a stdio server process.         | None                                                                                            |
| `-e <KEY=VALUE>`         | Environment variables for a stdio server. Repeatable. | None                                                                                            |
| `--header "Name: Value"` | HTTP headers for an HTTP/SSE server. Repeatable.      | Requires an ad-hoc HTTP/SSE server on the web client.                                           |
| `[target...]`            | Positional command/URL for one ad-hoc server.         | None                                                                                            |

### The `--` separator

The **web and CLI** clients split their arguments at a bare `--` and pass everything after it to the target command as its own arguments. This is how you pass a flag that the Inspector would otherwise eat:

```bash theme={null}
mcp-inspector node build/index.js -- --config /etc/myserver.conf --verbose
```

Without the separator, `--config` would be read as the Inspector's own read-only-session flag.

## Web-only flags

| Flag    | Meaning                                                                                               |
| ------- | ----------------------------------------------------------------------------------------------------- |
| `--dev` | Run the Vite dev server instead of the pre-built bundle. Useful when working on the Inspector itself. |

## CLI and TUI: OAuth client flags

These five are defined by the **CLI and TUI** only. The web client obtains the same settings through its Client Settings dialog.

| Flag                          | Environment variable     | Meaning                                                                                                                                                                                                                                                                                                          |
| ----------------------------- | ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `--client-config <path>`      | `MCP_CLIENT_CONFIG_PATH` | Install-level client config. Default `~/.mcp-inspector/storage/client.json`.                                                                                                                                                                                                                                     |
| `--client-id <id>`            | None                     | OAuth client ID for a static client. Overrides `client.json`.                                                                                                                                                                                                                                                    |
| `--client-secret <secret>`    | None                     | OAuth client secret for confidential clients. Overrides `client.json`.                                                                                                                                                                                                                                           |
| `--client-metadata-url <url>` | None                     | CIMD metadata URL. Overrides `client.json`.                                                                                                                                                                                                                                                                      |
| `--callback-url <url>`        | `MCP_OAUTH_CALLBACK_URL` | The redirect URI sent to the authorization server. Default `http://127.0.0.1:6276/oauth/callback`. Must be a loopback host (`127.0.0.1` or `localhost`): the local callback listener receives the authorization code over plaintext `http`, so any other host is rejected and there is no flag to override this. |

## CLI-only flags

The whole scripting surface belongs to the CLI. See [CLI client](/docs/draft/tools/inspector/cli) for usage.

| Group              | Flags                                                                                                                                                  |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **What to invoke** | `--method`, `--tool-name`, `--tool-arg`, `--tool-args-json`, `--uri`, `--prompt-name`, `--prompt-args`, `--log-level`, `--metadata`, `--tool-metadata` |
| **How to run it**  | `--connect-timeout`, `--format`, `--app-info`                                                                                                          |
| **Auth**           | `--use-stored-auth`, `--stored-auth-only`, `--relogin`, `--wait-for-auth`, `--list-stored-auth`, `--print-handoff`                                     |

## Environment variables

Environment variables split the same way as flags: two are read by the launcher itself, and the rest belong to the CLI and TUI or to the web backend.

### Read by the launcher

| Variable    | Effect                                                                                                                                                              |
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MCP_DEBUG` | Append the error stack to a top-level failure. Only when set to a meaningful value: `0`, `false`, and empty read as off.                                            |
| `DEBUG`     | Same, with the same meaningful-value rule, so a stray `DEBUG=0` doesn't turn stack traces on and `DEBUG` still works as the npm `debug` package's namespace filter. |

### CLI and TUI

| Variable                         | Effect                                                                                                                                                                                              |
| -------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `MCP_CATALOG_PATH`               | Fallback for `--catalog`. Honored only when no ad-hoc target is given, so a shell that exports it can still run one-off ad-hoc invocations.                                                         |
| `MCP_CLIENT_CONFIG_PATH`         | Fallback for `--client-config`.                                                                                                                                                                     |
| `MCP_OAUTH_CALLBACK_URL`         | Fallback for `--callback-url`.                                                                                                                                                                      |
| `MCP_STORAGE_DIR`                | Directory for the OAuth state file (`<dir>/oauth.json`).                                                                                                                                            |
| `MCP_INSPECTOR_OAUTH_STATE_PATH` | Per-file override of the OAuth state path. Takes precedence over `MCP_STORAGE_DIR`.                                                                                                                 |
| `MCP_AUTO_OPEN_ENABLED`          | Controls browser auto-open and whether interactive OAuth may run without a TTY. `true` forces auto-open and allows OAuth prompts without a TTY, `false` never opens, and unset opens only on a TTY. |

### Web backend environment variables

| Variable                                  | Effect                                                                                                                    |
| ----------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
| `MCP_INSPECTOR_API_TOKEN`                 | Pin the [session token](/docs/draft/tools/inspector/web#the-session-token) instead of generating a random one per launch. |
| `DANGEROUSLY_OMIT_AUTH`                   | Disable the `/api/*` token check entirely.                                                                                |
| `HOST`                                    | Bind host. Defaults to `localhost`.                                                                                       |
| `CLIENT_PORT`                             | Web UI port. Defaults to `6274`.                                                                                          |
| `DANGEROUSLY_BIND_ALL_INTERFACES`         | Required opt-in to bind a wildcard host (`0.0.0.0`, `::`, or any equivalent spelling).                                    |
| `ALLOWED_ORIGINS`                         | Comma-separated origin allow-list. **Replaces** the default list rather than merging.                                     |
| `MCP_SANDBOX_PORT`                        | Pin the MCP Apps sandbox port, which is dynamic by default.                                                               |
| `HTTPS_PROXY` / `HTTP_PROXY` / `NO_PROXY` | Standard proxy routing for outbound MCP connections.                                                                      |

<Warning>
  Never combine `DANGEROUSLY_OMIT_AUTH` and `DANGEROUSLY_BIND_ALL_INTERFACES`.
  The web backend spawns processes and holds OAuth tokens, so anyone who can
  reach it can drive it.
</Warning>

## Catalog file format

A catalog or config file is the familiar MCP client config shape (a `mcpServers` object) with per-server Inspector settings alongside:

```json theme={null}
{
  "mcpServers": {
    "my-stdio-server": {
      "command": "node",
      "args": ["build/index.js"],
      "env": { "API_KEY": "..." }
    },
    "my-modern-server": {
      "type": "http",
      "url": "https://api.example.com/mcp",
      "protocolEra": "modern",
      "modernLogLevel": "info",
      "headers": { "X-Tenant": "acme" },
      "roots": [{ "uri": "file:///Users/me/project", "name": "project" }]
    }
  }
}
```

Fields that equal their default are omitted when the Inspector writes the file back, keeping diffs minimal. `protocolEra` (see [Protocol eras](/docs/draft/tools/inspector/protocol-eras)) defaults to `legacy` and `modernLogLevel` to `debug`.

You do not have to hand-write these; the web client can [import an existing client config](/docs/draft/tools/inspector/recipes#importing-an-existing-client-config) from Claude Desktop, Cursor, Cline, or VS Code, or a registry `server.json`.

docs/draft/tools/inspector/protocol-eras New page · 232 lines, new page

# Protocol eras ## The `Protocol Era` setting ## Reproducing each era locally ## Logging ## Resource subscriptions ## Tasks ## Multi-round tool results (MRTR) ## Tools: mirrored headers and excluded tools ### `-32602` error panels ## Network and Protocol: headers and the error taxonomy ## Sessions

A whole new page. There's nothing to diff it against, so here is what it says.

# Protocol eras

> How the Inspector negotiates legacy vs. modern MCP, and how every feature is handled between protocol eras

The 2026-07-28 revision of MCP made substantial changes to the protocol. The Inspector therefore treats **protocol era** (legacy or modern, meaning before or as of that revision) as a first-class, per-server setting, orthogonal to the transport: the same HTTP URL can be inspected as a legacy server or as a modern one. Several tabs render meaningfully different UI and traffic depending on which era is in effect.

## The `Protocol Era` setting

Each server carries a `protocolEra` of `legacy`, `auto`, or `modern`. In the web client it lives in **Server Settings**; in a catalog or config file it is the `protocolEra` field; in the CLI and TUI it comes from that same file.

| Era      | What the Inspector does at connect                                                      |
| -------- | --------------------------------------------------------------------------------------- |
| `legacy` | **The default.** Plain `initialize`, no probing at all.                                 |
| `auto`   | Probe `server/discover` first, and fall back to `initialize` on any non-modern outcome. |
| `modern` | Pin exactly `2026-07-28`. No fallback, so a non-modern server fails loudly.             |

<Note>
  **Why `legacy` is the default, and not `auto`.** A debugging tool must not
  auto-probe. A `server/discover` probe stalls against silent legacy stdio
  servers, and it pollutes the recorded transcript you came here to read. Opting
  into `auto` or `modern` is a deliberate act, so what you see in the Protocol
  tab is what your server would have seen from a client behaving the way you
  configured.
</Note>

Era selection works the same way in all three clients.

Once connected, the negotiated era is reported in the connection header and in **Connection Info**. On a modern connection, `server/discover` also supplies `capabilities` (including `extensions`), `instructions`, and the list of `supportedVersions`. The server's name and version arrive in the result `_meta` under `io.modelcontextprotocol/serverInfo`.

<Frame caption="Server Settings: the Protocol Era selector, with all three choices.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/settings-protocol-era.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=34566c45f97c8af0e2c0d9ee0493b572" width="3840" height="2160" data-path="images/inspector/settings-protocol-era.png" />
</Frame>

## Reproducing each era locally

Every section below ends with a **Reproduce with ...** pointer to a JSON config for one of the **composable test servers** shipped in the Inspector repository. Clone the repo, build the test servers, then point the Inspector at the config the section names.

```bash theme={null}
git clone https://github.com/modelcontextprotocol/inspector
cd inspector && npm install && npm run build
cd clients/web && npm run test-servers:build
```

***

## Logging

<Tabs>
  <Tab title="Legacy">
    Logging is **session-scoped**. The client sends `logging/setLevel` once, and the server emits `notifications/message` at or above that level for the rest of the session.

    The **Logs** tab shows a **Set Active Level** selector plus a **Set** button. Choose a level, click Set, and subsequent server logs stream into the panel.

    Reproduce with `test-servers/configs/logging-legacy-http.json`.
  </Tab>

  <Tab title="Modern">
    `logging/setLevel` is **gone**. Instead the client opts in **per request**, by stamping `_meta["io.modelcontextprotocol/logLevel"]` on each outgoing request. A server MUST NOT emit `notifications/message` for a request that did not opt in.

    The **Logs** tab therefore shows a **Log Level per Request** control instead. Pick a level and every subsequent request carries the stamp, visible in the Network tab's request body. Logs emitted while handling a request ride that request's SSE response stream.

    Set the control to **Off** and the `logLevel` key is omitted entirely, so the same tool call produces no logs at all. That silence is correct behavior, not a bug.

    The per-server default is `debug` (opted in at the most verbose level, since the Inspector is a debugging tool); set `modernLogLevel: "off"` on a server to opt back out by default.

    Reproduce with `test-servers/configs/logging-modern-http.json`.
  </Tab>
</Tabs>

<Frame caption="Legacy: the Logs tab offers a session-scoped Set Active Level control, and a log arrives after calling send_notification.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/logs-legacy.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=ec5fddd86d1fe7ecc1ef83549798f33e" width="3840" height="2160" data-path="images/inspector/logs-legacy.png" />
</Frame>

<Frame caption="Modern: the same tab instead offers Log Level per Request. The level is stamped on every outgoing request, and the log rides that request's stream.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/logs-modern.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=85a1d7118a1e693930aefab8ca6b21a3" width="3840" height="2160" data-path="images/inspector/logs-modern.png" />
</Frame>

***

## Resource subscriptions

<Tabs>
  <Tab title="Legacy">
    Clicking **Subscribe** on a resource sends `resources/subscribe`. The Subscriptions section lists the URI with no stream chrome. When the resource changes, the server emits `notifications/resources/updated` and the subscribed tile's last-updated time is stamped.

    Reproduce with `test-servers/configs/subscriptions-legacy-http.json`, which also serves an `update_resource` tool so you can drive the notification round-trip yourself.
  </Tab>

  <Tab title="Modern">
    The same **Subscribe** button instead sends **`subscriptions/listen`**, with a filter carrying `resourceSubscriptions` plus the `resourcesListChanged` opt-in. The subscription is confirmed when the server sends `notifications/subscriptions/acknowledged`.

    Because the subscription is now a long-lived stream rather than a session flag, the Subscriptions section grows a **stream-status badge** in its header that moves from `Connecting...` to `Listening`. If the stream drops, the Inspector reconnects by re-sending `subscriptions/listen`.

    Reproduce with `test-servers/configs/subscriptions-modern-http.json`.
  </Tab>
</Tabs>

<Frame caption="A modern subscription: the Subscriptions section carries a LISTENING stream-status badge that a legacy subscription has no need for.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/resources-subscriptions-modern.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=5ca4c64c6783eb210fe54248f03de144" width="3840" height="2160" data-path="images/inspector/resources-subscriptions-modern.png" />
</Frame>

***

## Tasks

Tasks change the most between protocol eras, including *how the Inspector UI tab is gated*.

<Tabs>
  <Tab title="Legacy">
    The **Tasks** tab appears when the server advertises `capabilities.tasks`. Run a tool with **Run as task** enabled and the tab lists it, populated by `tasks/list` and polled with `tasks/get`. The completed payload is fetched with a **blocking `tasks/result`**, and **Cancel** sends `tasks/cancel`.

    Reproduce with `test-servers/configs/tasks-legacy-http.json`.
  </Tab>

  <Tab title="Modern">
    Tasks are an **extension** (`io.modelcontextprotocol/tasks`, [SEP-2663](/seps/2663-tasks-extension)), so the tab is gated on the *negotiated extension* rather than on `capabilities.tasks`.

    Run a tool as a task and `tools/call` returns a `CreateTaskResult` (`resultType: "task"`, visible in the Protocol and Network tabs). The Inspector polls **`tasks/get`** only; there is no `tasks/list`, so **Refresh** re-polls the handles the client already knows about. A completed task **inlines its result**, with no blocking `tasks/result` call.

    A task that needs more information moves to `input_required` and surfaces an embedded [elicitation](/specification/draft/client/elicitation) in the pending-request modal (the dialog the web client opens whenever a request is waiting on you). Answering it sends **`tasks/update`** carrying the `inputResponses`, and the next poll completes.

    Reproduce with `test-servers/configs/tasks-modern-http.json` (tools `modern_task` and `modern_input_task`).
  </Tab>
</Tabs>

<Frame caption="Legacy: the Tasks tab is populated from tasks/list, and the payload is fetched with a blocking tasks/result.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/tasks-legacy.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=777729bfc1b58b359808c8120d9597d3" width="3840" height="2160" data-path="images/inspector/tasks-legacy.png" />
</Frame>

<Frame caption="Modern: the client polls tasks/get on handles it already holds, and the completed task inlines its result; note resultType: complete in the full task object.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/tasks-modern.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=8e90d128528463fd672537c79db2e1fe" width="3840" height="2160" data-path="images/inspector/tasks-modern.png" />
</Frame>

***

## Multi-round tool results (MRTR)

On the modern era a tool can return `input_required` instead of a final result, embedding an [elicitation](/specification/draft/client/elicitation), a [sampling](/specification/draft/client/sampling) request, or a [`roots/list`](/specification/draft/client/roots) request. The client answers that embedded request and retries the `tools/call` under a fresh JSON-RPC id until the call reaches `complete`.

The Inspector drives MRTR **manually**, so each round pauses at the **pending-request modal**, tagged `input_required`, for you to answer. The Protocol view groups the whole exchange as one MRTR conversation rather than as unrelated calls.

`test-servers/configs/mrtr-showcase-http.json` bundles every shape in one modern server:

| Tool            | What it exercises                                                             |
| --------------- | ----------------------------------------------------------------------------- |
| `mrtr_confirm`  | A single elicitation round.                                                   |
| `mrtr_two_step` | Two elicitation rounds, threaded through `requestState`.                      |
| `mrtr_sample`   | An embedded sampling request, routed to the Sampling panel.                   |
| `mrtr_roots`    | An embedded `roots/list`, answered silently from configured roots (no modal). |
| `mrtr_edge`     | An `inputRequests`-only round, then a `requestState`-only round.              |
| `mrtr_loop`     | Never completes, so the client stops at its `MRTR_MAX_ROUNDS` limit.          |

<Note>
  The legacy `collect_elicitation` pattern (a server calling
  `server.elicitInput`) **errors** on a 2026-07-28 connection, because
  server-to-client requests aren't allowed there. MRTR is its modern
  replacement.
</Note>

<Frame caption="An MRTR round paused at the pending-request modal, tagged input_required. Answering it retries the original request.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/mrtr-pending-request.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=99f8acb7f845a12aed42bcea4d310fee" width="3840" height="2160" data-path="images/inspector/mrtr-pending-request.png" />
</Frame>

***

## Tools: mirrored headers and excluded tools

[SEP-2243](/seps/2243-http-standardization) lets a tool annotate an argument with `x-mcp-header`, asking a Streamable HTTP client to mirror that argument's value into an `Mcp-Param-*` request header.

The Inspector surfaces both halves of that contract in the **Tools** tab:

* A tool with a **valid** annotation shows a **"Mirrored request headers (SEP-2243)"** section in its detail panel, for example `city -> Mcp-Param-City`.
* A tool with an **invalid** annotation (say, a header name of `"Bad Header"`, where the space makes it an invalid RFC 9110 token) appears struck through in the sidebar under an **"Excluded (SEP-2243)"** divider, with the reason on hover. A conforming client MUST drop such a tool from `tools/list`; the Inspector shows you *why* it was dropped instead of silently hiding it.

Reproduce with `test-servers/configs/xmcpheader-modern-http.json`.

<Warning>
  **`Mcp-Param-*` mirroring is skipped by the SDK in the browser.** Calling a
  mirrored tool from the *web* client omits the header, so a strict server
  answers `-32020` (`HeaderMismatch`, see the [error
  taxonomy](#network-and-protocol-headers-and-the-error-taxonomy) below). The
  same tool called from the **CLI** or **TUI**, which both run on Node, mirrors
  correctly. The header is dropped by an environment check inside the SDK,
  outside the Inspector's control.
</Warning>

<Frame caption="get_weather shows its mirrored city -> Mcp-Param-City header, while invalid_header_tool is struck through under the Excluded (SEP-2243) divider.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/tools-sep2243.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=98b020f6612b3a76b78b1a8d6159c2c0" width="3840" height="2160" data-path="images/inspector/tools-sep2243.png" />
</Frame>

### `-32602` error panels

Under the modern era a `tools/call` that rejects with `-32602` renders as a distinct **error panel**:

* **Unknown Tool**: when the message names a tool the server does not list. Reproduce by calling any name absent from the server's `tools/list`.
* **Invalid Parameters**: any other `-32602`. Reproduce with the `trigger_invalid_params` tool in the config above.

Both eras reject with `-32602`; only the Inspector's presentation changes. On a legacy connection you get one generic JSON-RPC failure and have to read the message to tell which case you hit.

***

## Network and Protocol: headers and the error taxonomy

The modern era standardizes a set of `Mcp-*` HTTP headers and introduces a richer JSON-RPC error taxonomy ([SEP-2243](/seps/2243-http-standardization) / [SEP-2575](/seps/2575-stateless-mcp)). The two monitoring tabs divide the work:

* The **Network** tab is the HTTP view: mirrored `Mcp-*` headers are highlighted and sentinel values decoded.
* The **Protocol** tab is the JSON-RPC view: each spec error renders distinctly rather than as a generic failure.

`test-servers/configs/modern-network-http.json` serves four tools that produce a real HTTP status plus a JSON-RPC error body, one per class:

| Tool                          | HTTP  | JSON-RPC code | Meaning                                                      |
| ----------------------------- | ----- | ------------- | ------------------------------------------------------------ |
| `trigger_header_mismatch`     | `400` | `-32020`      | A required mirrored header was missing or wrong.             |
| `trigger_missing_capability`  | `400` | `-32021`      | The request omitted a client capability the server requires. |
| `trigger_unsupported_version` | `400` | `-32022`      | Unsupported version; supported versions in `data.supported`. |
| `trigger_method_not_found`    | `404` | `-32601`      | Method not found.                                            |

<Frame caption="The Network tab shows the HTTP layer; here, the 400 Bad Request the strict server answered with.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/network-modern-headers.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=7df4c01f5ab68aa7632ac3b5a5866b42" width="3840" height="2160" data-path="images/inspector/network-modern-headers.png" />
</Frame>

<Frame caption="The Protocol tab renders the same failure as a typed spec error: -32022 UnsupportedProtocolVersion, with the versions the server does support.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/protocol-modern-error.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=d578ea8262ff327e4d61d3697937900b" width="3840" height="2160" data-path="images/inspector/protocol-modern-error.png" />
</Frame>

***

## Sessions

A legacy Streamable HTTP connection may carry a server-assigned session id (`Mcp-Session-Id`), which the client tears down with an HTTP `DELETE`. A modern connection is **sessionless and per-request**: with no session id the client SDK sends no `DELETE` to the server, so disconnect is purely local.

This has a practical consequence for your own test servers. A stateless modern handler constructed per request cannot hold state between calls, which is why `test-servers/configs/subscriptions-modern-http.json`, unlike its legacy counterpart, omits an `update_resource` tool: the mutation would run against a throwaway server instance and be invisible to the next read.

docs/draft/tools/inspector/recipes New page · 207 lines, new page

# Recipes ## Connecting stdio vs. HTTP servers ### stdio ### HTTP and SSE ## Importing an existing client config ## Reviewing an MCP App ## Docker ## Hosting on a network ## Development workflow

A whole new page. There's nothing to diff it against, so here is what it says.

# Recipes

> Practical guides for transports, importing configs, reviewing MCP Apps, Docker, and network hosting

## Connecting stdio vs. HTTP servers

### stdio

A stdio server is a process the Inspector spawns. Everything positional is the command line:

```bash theme={null}
mcp-inspector node build/index.js -- --verbose --config /etc/myserver.conf
```

Put `--` before any arguments meant for your server. Without the separator, `--verbose` would be
parsed by the Inspector and never reach the server.

Give the process environment variables with `-e` and a working directory with `--cwd`:

```bash theme={null}
mcp-inspector -e API_KEY=abc123 -e REGION=us-east-1 --cwd ~/projects/my-server \
  node build/index.js
```

The server's `stderr` lands in the **Console** tab (web) or the Console tab (`o`, TUI), which is where most stdio servers put their diagnostics, so check there first when a connection fails for no visible reason.

### HTTP and SSE

```bash theme={null}
mcp-inspector --server-url https://api.example.com/mcp --transport http \
  --header "X-Tenant: acme"
```

`--transport` accepts `http` (Streamable HTTP) and `sse`. If the server is protected, see [Authorization](/docs/draft/tools/inspector/authorization): no setup is needed in advance, because when the server answers `401` the Inspector runs the OAuth flow described there and retries the connection.

For an HTTP server, also decide its [protocol era](/docs/draft/tools/inspector/protocol-eras). The default is `legacy`; set `modern` or `auto` in Server Settings (or `protocolEra` in the catalog file) to exercise the 2026-07-28 behavior.

## Importing an existing client config

On the Servers screen, **Add Servers** can import MCP servers you have already configured
elsewhere instead of retyping them. It parses Claude Desktop, Cursor, Cline, and VS Code client
configs directly, and it also reads a server's own [MCP Registry](/registry/about) `server.json`.

Import merges into the active [catalog](/docs/draft/tools/inspector/configuration#choosing-servers)
(the Inspector's writable server list), so existing entries aren't clobbered. If you'd rather
not touch your catalog at all, launch against the foreign file read-only instead:

```bash theme={null}
mcp-inspector --config ~/Library/Application\ Support/Claude/claude_desktop_config.json
```

`--config` guarantees the file is served as-is and never written, seeded, or migrated.

<Frame caption="Add Servers offers import from an existing client config or from a registry server.json.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/import-config.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=c9f5229c2d827f4bcab37938879f21f2" width="3840" height="2160" data-path="images/inspector/import-config.png" />
</Frame>

## Reviewing an MCP App

[MCP Apps](/extensions/apps/overview) are tools that carry a UI widget. For an automated reviewer (CI or an agent), use the CLI for every check that returns JSON, and open a browser only to inspect the rendered widget.

<Steps>
  <Step title="Probe the security posture without calling the tool">
    ```bash theme={null}
    mcp-inspector --cli --transport http --server-url https://example.com/mcp \
      --method tools/call --tool-name <tool> --app-info
    ```

    One JSON line on stdout; exit `0` if the tool has an app, `2` if not, so an `&&` chain short-circuits:

    ```json theme={null}
    {
      "hasApp": true,
      "toolName": "get_pros",
      "resourceUri": "ui://pros/view.html",
      "csp": { "connectDomains": ["https://api.example.com"] },
      "permissions": { "clipboard": false },
      "prefersBorder": true,
      "resourceMimeType": "text/html"
    }
    ```

    `csp` and `permissions` (and `domain`, when the resource declares one) live on the UI **resource** rather than the tool, so `--app-info` reads that resource. The tool is never called.
  </Step>

  <Step title="Get the full result payload, still with no browser">
    ```bash theme={null}
    mcp-inspector --cli --transport http --server-url https://example.com/mcp \
      --method tools/call --tool-name <tool> --tool-args-json '{"zip":"10001"}' --format json
    ```
  </Step>

  <Step title="Launch the web Inspector once, loopback-only">
    ```bash theme={null}
    TOKEN="$(openssl rand -hex 24)"
    HOST=127.0.0.1 CLIENT_PORT=6274 MCP_SANDBOX_PORT=6275 \
    MCP_AUTO_OPEN_ENABLED=false MCP_INSPECTOR_API_TOKEN="$TOKEN" \
    mcp-inspector --web &
    ```

    Pinning `MCP_SANDBOX_PORT` matters here: the app's UI is served from a separate sandbox port that is dynamic by default, and your automation needs a fixed address to reach it.
  </Step>

  <Step title="Navigate one deep link to a rendered widget">
    ```
    http://127.0.0.1:6274/?serverUrl=<encoded url>&transport=http&autoConnect=<TOKEN>&openApp=<tool>&appArgs=<base64url(JSON)>&autoOpen=<TOKEN>
    ```

    `appArgs` is the tool's arguments as base64url-encoded JSON, and every deep-link parameter is described under [Deep links](/docs/draft/tools/inspector/web#deep-links). `autoConnect` and `autoOpen` must both equal the session token, since `autoOpen` fires a tool call straight from the URL and needs the same gate as `autoConnect`.
  </Step>

  <Step title="Wait on a deterministic signal instead of sleeping">
    The Apps screen exposes a stable automation contract. Poll these attributes instead of sleeping:

    | Selector                            | Attribute         | Values                                                                                                  |
    | ----------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------- |
    | `[data-testid="apps-form"]`         | `data-app-status` | `ready` (on failure, `data-app-error` carries the reason)                                               |
    | `[data-testid="connection-status"]` | `data-status`     | `connecting`, then `connected` or `error` (`data-error-message` has the detail)                         |
    | `[data-testid="connection-status"]` | `data-deeplink`   | `parsed`, `rejected`, or `none` (`none` means no deep link was given, `rejected` means one was refused) |
  </Step>
</Steps>

## Docker

A container image is published to GitHub Container Registry for `linux/amd64` and `linux/arm64`:

```bash theme={null}
docker run --rm -p 6274:6274 ghcr.io/modelcontextprotocol/inspector
```

Read the [session token](/docs/draft/tools/inspector/web#the-session-token) from the container logs, or pin it with `-e MCP_INSPECTOR_API_TOKEN=<value>`.

The image defaults to `--web`, bound to `0.0.0.0:6274` with browser auto-open off, and runs as a non-root user. It sets `DANGEROUSLY_BIND_ALL_INTERFACES=true` because a container must bind the wildcard address to be reachable through `-p`.

Its `HEALTHCHECK` probes the web UI, so add `--no-healthcheck` when running `--cli` or `--tui` (neither has a web server). `<target>` below is an [ad-hoc target](/docs/draft/tools/inspector/configuration#ad-hoc-targets): a positional stdio command, or `--server-url <url> --transport http`.

```bash theme={null}
docker run --rm --no-healthcheck ghcr.io/modelcontextprotocol/inspector --cli <target> --method tools/list
```

<Warning>
  **If you remap the published port, set `ALLOWED_ORIGINS`.** With `-p
      8080:6274` the browser's origin becomes `http://localhost:8080`, which no
  longer matches the in-container port, and connects will `403`. Either run `-e
      CLIENT_PORT=8080 -p 8080:8080`, or set `-e
      ALLOWED_ORIGINS=http://localhost:8080,http://127.0.0.1:8080`.
</Warning>

## Hosting on a network

The Inspector binds `localhost` by default and its backend spawns processes, so treat exposing it to a network as a deliberate decision.

The Inspector refuses to bind the **wildcard** all-interfaces addresses (`0.0.0.0`, `::`, and every equivalent spelling) unless you set `DANGEROUSLY_BIND_ALL_INTERFACES=true`. Binding a **specific** address is allowed with no opt-in, because that's one deliberate exposure rather than every interface at once, which is the shape DNS-rebinding attacks target.

| Goal                                         | What to do                                                                                                                                             |
| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Reach it from another machine on the LAN** | `HOST=192.168.1.50`. The default origin allow-list follows the bind host, so `http://192.168.1.50:6274` is accepted with no further config.            |
| **Behind TLS or a reverse proxy**            | The browser's `Origin` becomes the public origin, which won't match the bind host. Set `ALLOWED_ORIGINS=https://inspector.example.com`.                |
| **Wildcard bind (containers)**               | Set `DANGEROUSLY_BIND_ALL_INTERFACES=true`. Loopback access still works out of the box; reaching it at a non-loopback address needs `ALLOWED_ORIGINS`. |

<Warning>
  `ALLOWED_ORIGINS` **replaces** the default list rather than merging with it. List every origin you'll browse from, including the loopback forms you want to keep:

  ```
  ALLOWED_ORIGINS=http://localhost:6274,http://127.0.0.1:6274,http://192.168.1.50:6274
  ```

  Each entry must include the scheme; a scheme-less value is dropped with a warning. A blank value does **not** disable the check; it falls back to the default. There is no knob to turn origin validation off.
</Warning>

Two further caveats when going off loopback:

* **MCP Apps need their sandbox port reachable too.** It's a separate, dynamic-by-default port; pin it with `MCP_SANDBOX_PORT` and expose or forward it. The Docker image publishes only `6274`.
* **MCP Apps can't render over TLS or at a bare IPv6 literal.** The sandbox URL is always plain `http`, so an `https://` page blocks the iframe as mixed content; and a bracketed IPv6 literal isn't a valid CSP host-source, so browse at a name or an IPv4 address.

Whatever the shape: keep authentication on. Do not set `DANGEROUSLY_OMIT_AUTH` on anything reachable by anyone but you.

## Development workflow

A loop that works well in practice:

<Steps>
  <Step title="Start with the CLI">
    `--method initialize` confirms the server starts, handshakes, and reports
    the capabilities you expect, in one second, with a machine-readable answer.
    Most "it doesn't work" turns out to be here.
  </Step>

  <Step title="Move to the web client for exploration">
    Schema-driven forms, rendered results, and the Protocol tab beside them make
    it fast to find the case where a tool misbehaves.
  </Step>

  <Step title="Test the edges">
    Invalid inputs, missing required prompt arguments, concurrent calls, and,
    for HTTP servers, both protocol eras. Verify the *errors* are as intentional
    as the successes.
  </Step>

  <Step title="Lock it in with the CLI">
    Turn what you found into a CI assertion: pipe the CLI's `--format json`
    output to `jq -e` with `--stored-auth-only`, so a missing token fails fast
    instead of starting interactive OAuth. See [Verify a server in
    CI](/docs/draft/tools/inspector/cli#verify-a-server-in-ci) for the full
    command.
  </Step>
</Steps>

docs/draft/tools/inspector/tui New page · 78 lines, new page

# TUI client ## Choosing servers ## Tabs ## Navigation ## Authorizing an HTTP server ## Requirements

A whole new page. There's nothing to diff it against, so here is what it says.

# TUI client

> The terminal MCP Inspector: navigation, tabs, and keyboard reference

The TUI is the Inspector's terminal interface, with the same interactive exploration of tools, resources, and prompts as the web client. Use it on a remote host over SSH, in a locked-down environment, or when you prefer to stay in the terminal.

```bash theme={null}
npx @modelcontextprotocol/inspector --tui node build/index.js   # with an ad-hoc stdio server
```

<Frame caption="The TUI connected to a server, on the Tools tab, showing a tool's input schema.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/tui-tools.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=5d11d64f4b98df8c26e7ac048f38576f" width="2986" height="1832" data-path="images/inspector/tui-tools.png" />
</Frame>

## Choosing servers

Unlike the CLI, the TUI has no `--server <name>` flag for picking one entry: it reads its servers from a catalog or config file, loads every server in it, and lets you pick from an on-screen list:

```bash theme={null}
mcp-inspector --tui --catalog mcp.json   # writable catalog, seeded empty if missing (unlike the web client)
mcp-inspector --tui --config mcp.json    # read-only session, errors if absent
```

With neither `--catalog` nor `--config`, and no [ad-hoc target](/docs/draft/tools/inspector/configuration#ad-hoc-targets), it uses the default writable catalog `~/.mcp-inspector/mcp.json`. See [Configuration and flags](/docs/draft/tools/inspector/configuration).

## Tabs

| Tab           | Key | What it shows                                                                               |
| ------------- | --- | ------------------------------------------------------------------------------------------- |
| **Info**      | `i` | Server info, capabilities, and negotiated protocol details.                                 |
| **Auth**      | `a` | OAuth state for the selected server, plus a **Clear OAuth state** action.                   |
| **Resources** | `r` | Browse and read resources.                                                                  |
| **Prompts**   | `m` | List prompts and render them with arguments.                                                |
| **Tools**     | `t` | View tools and execute them with form-like inputs.                                          |
| **Protocol**  | `p` | JSON-RPC request/response/notification history.                                             |
| **Network**   | `n` | HTTP traffic for SSE and [Streamable HTTP](/specification/latest/basic/transports) servers. |
| **Console**   | `o` | `stderr` from a connected stdio server process.                                             |

The accelerators avoid collisions rather than always taking the first letter: **P**rotocol takes `p` so Pro**m**pts takes `m`, and **C**onsole takes `o` because `c` is the global Connect action.

## Navigation

| Key                              | Action                                              |
| -------------------------------- | --------------------------------------------------- |
| `Left` / `Right` arrows or `Tab` | Switch tabs                                         |
| `Up` / `Down` arrows             | Move through the current list                       |
| `Enter`                          | Select an item, execute a tool, or fetch a resource |
| `c`                              | Connect to the selected server                      |
| `d`                              | Disconnect                                          |
| `Esc` or `Ctrl+C`                | Exit                                                |

## Authorizing an HTTP server

1. Select an HTTP or SSE server and press **`c`** to connect.
2. If the server requires authorization, the TUI starts OAuth automatically and opens the authorization URL in a browser.
3. When the browser redirect lands on the TUI's loopback listener, the connection finishes on its own, with no second **`c`**.
4. Use the **Auth** tab to inspect the resulting OAuth state, or to clear it.

The TUI's callback listener defaults to `http://127.0.0.1:6276/oauth/callback`. The port is fixed on purpose: a pre-registered (static) OAuth client, a [Client ID Metadata Document (CIMD)](/specification/latest/basic/authorization/client-registration#client-id-metadata-documents), or an enterprise-managed IdP all need a redirect URI known in advance. Register that URI once and it works across sessions. On a remote host where your browser is on another machine, forward the callback port so the redirect reaches this listener; see [Callback URLs](/docs/draft/tools/inspector/authorization#callback-urls).

The trade-off is that only one TUI OAuth flow can hold the port at a time; a second concurrent flow fails with `EADDRINUSE`. To override it, pass `--callback-url` or set `MCP_OAUTH_CALLBACK_URL`: use a different fixed port per instance, or `http://127.0.0.1:0/oauth/callback` for an OS-assigned ephemeral port when your authorization server registers redirect URIs dynamically.

<Warning>
  Redirect URIs must match **exactly** what you registered. `localhost` and
  `127.0.0.1` are different URIs as far as an authorization server is concerned.
</Warning>

Per-server OAuth fields in the catalog (static client id/secret, scopes, the enterprise-managed flag) are applied automatically. Install-wide settings (CIMD, enterprise IdP) come from `~/.mcp-inspector/storage/client.json`, the same file the web client's **Client Settings** dialog writes. Point at a different one with `--client-config` or `MCP_CLIENT_CONFIG_PATH`.

See [Authorization](/docs/draft/tools/inspector/authorization) for the full picture.

<Frame caption="The Auth tab. It shows the same OAuth fields as the web client's Connection Info, or reports that the server needs no authorization.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/tui-auth.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=2e5ef574e80e81c4b49fa2ef0eac0528" width="2986" height="1832" data-path="images/inspector/tui-auth.png" />
</Frame>

## Requirements

The TUI needs a real TTY with raw-mode support. It will not run usefully in a headless CI job; use the [CLI](/docs/draft/tools/inspector/cli) there.

docs/draft/tools/inspector/web New page · 168 lines, new page

# Web client ## The session token ## Dev mode ## The tab bar ### The monitoring sidebar ## Servers ### Server Settings ## Tools ## Resources ## Prompts ## Apps ## Protocol, Network, and Console ## Deep links ## Host binding and origins

A whole new page. There's nothing to diff it against, so here is what it says.

# Web client

> A tab-by-tab walkthrough of the graphical MCP Inspector

The web client is the Inspector's richest surface: a single-page app backed by a small Node server that owns the actual MCP connections. It is the default mode, so `npx @modelcontextprotocol/inspector` with no mode flag lands here.

```bash theme={null}
npx @modelcontextprotocol/inspector                       # empty, add servers in the UI
npx @modelcontextprotocol/inspector node build/index.js   # with an ad-hoc stdio server
npx @modelcontextprotocol/inspector --catalog ./mcp.json  # with a catalog file
```

## The session token

The Node server behind the web client guards every `/api/*` route with a per-launch token, because it can spawn processes on your machine. The launcher prints a URL containing that token: **open that URL**, and don't type `localhost:6274` from memory.

The browser recovers the token from three places, in priority order:

1. `window.__INSPECTOR_API_TOKEN__`, injected into `index.html` on every page load. This is what makes a bare-URL reload or a bookmark keep working.
2. A `?MCP_INSPECTOR_API_TOKEN=...` query string, the form used in that printed URL.
3. `sessionStorage`, as a backstop.

Set the `MCP_INSPECTOR_API_TOKEN` environment variable to pin a known token (useful for scripted launches), or set `DANGEROUSLY_OMIT_AUTH=true` to disable the check entirely, but only on a machine where nothing else can reach the port. Both are described under [Web backend environment variables](/docs/draft/tools/inspector/configuration#web-backend-environment-variables).

## Dev mode

`--dev` is a **web-only** flag. It runs the Vite dev server instead of serving the pre-built bundle, which matters if you're working on the Inspector itself:

```bash theme={null}
mcp-inspector --web --dev
```

Production `--web` serves a built bundle. In the published package that bundle always ships; in a fresh source checkout it doesn't, so the runner builds it on demand the first time you launch.

## The tab bar

| Tab           | Shown when                                                            | What it does                                                                   |
| ------------- | --------------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| **Servers**   | Always                                                                | The server list: add, edit, import, connect, and open per-server settings.     |
| **Apps**      | The server exposes MCP App tools                                      | Renders a tool's UI in a sandboxed frame.                                      |
| **Tools**     | `tools` capability                                                    | Browse schemas, fill arguments, call, inspect results.                         |
| **Prompts**   | `prompts` capability                                                  | List prompts, supply arguments, preview generated messages.                    |
| **Resources** | `resources` capability                                                | Browse, read, and subscribe to resources.                                      |
| **Tasks**     | `capabilities.tasks` (legacy era) or the tasks extension (modern era) | Track long-running tool calls.                                                 |
| **Logs**      | `logging` capability                                                  | Server `notifications/message` output, plus the era-appropriate level control. |
| **Protocol**  | Always                                                                | The JSON-RPC transcript: requests, responses, notifications.                   |
| **Network**   | HTTP / SSE servers                                                    | The raw HTTP view: status, headers, bodies.                                    |
| **Console**   | stdio servers                                                         | The server process's `stderr`.                                                 |

**Network** and **Console** never appear together. Legacy and modern eras are described in [Protocol eras](/docs/draft/tools/inspector/protocol-eras).

<Frame caption="The tab bar on a connected server. Which tabs appear depends on the capabilities the server reported.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-tab-bar.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=04bb61c4a45ff12e8337c701e195c386" width="3840" height="2400" data-path="images/inspector/web-tab-bar.png" />
</Frame>

### The monitoring sidebar

**Tasks**, **Logs**, **Protocol**, **Network**, and **Console** form a *monitor group*. Pin the group and they leave the tab bar and move into a resizable right-hand column, so you can watch traffic while working in Tools or Resources. The column width and the selected monitor tab persist across reloads.

<Frame caption="The monitoring sidebar pinned beside the Tools screen. The Protocol stream stays visible while you work.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-monitor-sidebar.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=eef6e546b9831b3d169e26bba8c54ce3" width="3840" height="2160" data-path="images/inspector/web-monitor-sidebar.png" />
</Frame>

## Servers

The Servers screen is the entry point. A server row carries its transport, its connection state, and a control that opens its per-server settings.

Where that list comes from, and whether it's editable, depends on how you launched:

| Launch                                       | Server list                                                             | Editable? |
| -------------------------------------------- | ----------------------------------------------------------------------- | --------- |
| `mcp-inspector --web`                        | The default catalog `~/.mcp-inspector/mcp.json`, seeded on first launch | Yes       |
| `--catalog <path>`                           | That file, seeded with the sample servers if missing                    | Yes       |
| `--config <path>`                            | That file, read-only (never written or seeded)                          | No        |
| `--server-url <url>` or a positional command | One ad-hoc server, held in memory                                       | No        |

On a first launch the web client seeds the catalog with two sample servers: a filesystem server scoped to `/tmp` and the canonical "everything" reference server. See [Configuration and flags](/docs/draft/tools/inspector/configuration) for the full rules, including why the CLI and TUI seed an empty catalog instead.

### Server Settings

* **Protocol Era**: `legacy` / `auto` / `modern`. See [Protocol eras](/docs/draft/tools/inspector/protocol-eras).
* **Log level per request**: the level a modern-era connection stamps on each outgoing request by default, or `off` to opt out (see [Logging](/docs/draft/tools/inspector/protocol-eras#logging)).
* **Advertised Extensions**: which extensions the Inspector declares in `capabilities.extensions`. A debugging knob: a server may legitimately change what it registers based on what you advertise. Uncheck the Tasks extension and reconnect against the `test-servers/configs/advertised-extensions-http.json` fixture (setup in [Reproducing each era locally](/docs/draft/tools/inspector/protocol-eras#reproducing-each-era-locally)) to watch a tool disappear.
* **Roots**: the roots advertised via the `roots` client capability. `@modelcontextprotocol/server-filesystem`, for instance, calls `roots/list` to learn its allowed directories.
* **Headers**, **timeouts**, and **OAuth** fields.
* **Fetch lists one page at a time**: when off, list results are auto-aggregated across pages on connect; when on, each list loads page 1 only with a **Load next page** control and an *N pages loaded* status. Reproduce with `test-servers/configs/pagination-http.json`, which paginates 12 tools, resources, and prompts into three pages each.

<Frame caption="Server Settings with Advertised Extensions expanded. Unchecking one changes what the Inspector declares at connect.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-server-settings.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=d42be09ee8de7e45e58a8ff1a444ba52" width="3840" height="2160" data-path="images/inspector/web-server-settings.png" />
</Frame>

## Tools

Select a tool to see its description, its input schema rendered as a form, and its annotations. Fill the form and call it; the result renders below with structured content, embedded resources, and images handled natively.

On modern-era servers this screen also shows mirrored `Mcp-Param-*` headers, excluded tools, and distinct `-32602` error panels, all covered in [Protocol eras](/docs/draft/tools/inspector/protocol-eras#tools-mirrored-headers-and-excluded-tools).

<Frame caption="A tool call and its rendered result. The argument form collapses into the result panel once the call returns.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-tools.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=7ef469a969f398ac0ec70cf019c133da" width="3840" height="2160" data-path="images/inspector/web-tools.png" />
</Frame>

## Resources

Lists resources and resource templates with their MIME types and descriptions, reads content on selection, and offers **Subscribe** on servers that support subscriptions. The subscription mechanics differ by era; see [Resource subscriptions](/docs/draft/tools/inspector/protocol-eras#resource-subscriptions).

<Frame caption="A resource read, with an active subscription listed below the resource list.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-resources.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=1a94ea452e1ef8aaf2c9f486ed810b28" width="3840" height="2160" data-path="images/inspector/web-resources.png" />
</Frame>

## Prompts

Lists prompt templates with their arguments, and renders the generated messages for the arguments you supply, which is the fastest way to confirm a prompt produces what you intended.

<Frame caption="A prompt rendered with the arguments supplied.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-prompts.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=81b16312b1adff5601622a72444b0f92" width="3840" height="2160" data-path="images/inspector/web-prompts.png" />
</Frame>

## Apps

[MCP Apps](/extensions/apps/overview) are tools that carry UI. The Apps tab renders one in a sandboxed iframe served from a **separate port**, exercises the `ui/*` bridge, and shows the view's `ui/message` submissions and its `notifications/message` logs in side panels.

* The sandbox port is dynamic by default; pin it with `MCP_SANDBOX_PORT` if you need to expose or forward it.
* The sandbox is gated by a `frame-ancestors` CSP, and a bracketed IPv6 literal is not a valid CSP host-source, so browse the Inspector at `localhost`, `127.0.0.1`, a hostname, or a LAN IPv4, **not** at a bare `http://[::1]:...`.
* The sandbox URL is always plain `http`, so an `https://` Inspector page blocks the frame as mixed content. MCP Apps need a plain-`http` origin today.

See [Recipes](/docs/draft/tools/inspector/recipes#reviewing-an-mcp-app) for the CLI-first automated review flow.

<Frame caption="An MCP App rendered in its sandboxed frame, with the app's own logs below it.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-apps.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=bd311848514f7d251640012986abfc4d" width="3840" height="2160" data-path="images/inspector/web-apps.png" />
</Frame>

## Protocol, Network, and Console

The three tabs show the same traffic at different levels of detail:

* **Protocol**: the JSON-RPC transcript. Requests paired with responses, notifications inline, [MRTR](/docs/draft/tools/inspector/protocol-eras#multi-round-tool-results-mrtr) rounds grouped as one conversation, and spec errors rendered by class.
* **Network**: the HTTP layer, for SSE and Streamable HTTP servers. Status codes, request and response headers, and bodies. On modern connections the standardized `Mcp-*` headers are highlighted and sentinel values decoded.
* **Console**: the connected stdio server process's `stderr`, which is where most stdio servers put their own diagnostics.

Secrets are masked in these views, and entries can be cleared or exported.

<Frame caption="The Protocol tab with an entry expanded, showing the full JSON-RPC exchange.">
  <img src="https://mintcdn.com/mcp/gk28X8wi_tbRYzej/images/inspector/web-protocol.png?fit=max&auto=format&n=gk28X8wi_tbRYzej&q=85&s=f31338c83a389c5588f11c0d5b2b97ed" width="3840" height="2160" data-path="images/inspector/web-protocol.png" />
</Frame>

## Deep links

A driver (a script, a CI harness, or the CLI's [`--print-handoff`](/docs/draft/tools/inspector/authorization#handing-off-from-the-web-client-to-the-cli)) can reach a *connected* Inspector with a single navigation:

```
http://127.0.0.1:6274/?serverUrl=<url>&transport=http|sse&autoConnect=<token>
```

| Parameter     | Meaning                                                                                                        |
| ------------- | -------------------------------------------------------------------------------------------------------------- |
| `serverUrl`   | The MCP server URL. Restricted to `http:` / `https:`; a crafted `javascript:` or `file:` value is rejected.    |
| `transport`   | `http` (default) or `sse`.                                                                                     |
| `autoConnect` | **Required CSRF gate.** Must equal the per-launch session token, which only whatever started the server knows. |

Three further parameters land you on a *rendered app*: `openApp=<toolName>` names the tool, `appArgs=<base64url(JSON)>` supplies its arguments (merged over the tool's schema defaults), and `autoOpen=<token>` fires the tool call automatically. Because `autoOpen` fires a call, it carries the same mandatory token gate as `autoConnect`.

## Host binding and origins

By default the Inspector binds `localhost` and accepts requests only from the loopback origins for its port. Treat both defaults as security boundaries, since the backend spawns processes on your machine.

Binding all interfaces (`HOST=0.0.0.0`) is **refused** unless you set `DANGEROUSLY_BIND_ALL_INTERFACES=true`. Binding a *specific* non-loopback address is allowed with no opt-in, since that's a single deliberate exposure rather than every interface at once.

See the [Hosting on a network](/docs/draft/tools/inspector/recipes#hosting-on-a-network) recipe for the full matrix, and [Configuration](/docs/draft/tools/inspector/configuration#web-backend-environment-variables) for the variables.

docs/draft/tutorials/security/authorization New page · 1098 lines, new page

# Understanding Authorization in MCP ## When Should You Use Authorization? ## The Authorization Flow: Step by Step ## Implementation Example ### Keycloak Setup ### MCP Server Setup ## Testing the MCP Server ## Common Pitfalls and How to Avoid Them ## Related Standards and Documentation

A whole new page. There's nothing to diff it against, so here is what it says.

# Understanding Authorization in MCP

> Learn how to implement secure authorization for MCP servers using OAuth 2.1 to protect sensitive resources and operations

Authorization in the Model Context Protocol (MCP) secures access to sensitive resources and operations exposed by MCP servers. If your MCP server handles user data or administrative actions, authorization ensures only permitted users can access its endpoints.

MCP uses standardized authorization flows to build trust between MCP clients and MCP servers. Its design doesn't focus on one specific authorization or identity system, but rather follows the conventions outlined for [OAuth 2.1](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-13). For detailed information, see the [Authorization specification](/specification/latest/basic/authorization).

## When Should You Use Authorization?

While authorization for MCP servers is **optional**, it is strongly recommended when:

* Your server accesses user-specific data (emails, documents, databases)
* You need to audit who performed which actions
* Your server grants access to its APIs that require user consent
* You're building for enterprise environments with strict access controls
* You want to implement rate limiting or usage tracking per user

<Tip>
  **Authorization for Local MCP Servers**

  For MCP servers using the [STDIO transport](/specification/latest/basic/transports#stdio), you can use environment-based credentials or credentials provided by third-party libraries embedded directly in the MCP server instead. Because a STDIO-built MCP server runs locally, it has access to a range of flexible options when it comes to acquiring user credentials that may or may not rely on in-browser authentication and authorization flows.

  OAuth flows, in turn, are designed for HTTP-based transports where the MCP server is remotely-hosted and the client uses OAuth to establish that a user is authorized to access said remote server.
</Tip>

## The Authorization Flow: Step by Step

Let's walk through what happens when a client wants to connect to your protected MCP server:

<Steps>
  <Step title="Initial Handshake">
    When your MCP client first tries to connect, your server responds with a `401 Unauthorized` and tells the client where to find authorization information, captured in a [Protected Resource Metadata (PRM) document](https://datatracker.ietf.org/doc/html/rfc9728). The document is hosted by the MCP server, follows a predictable path pattern, and is provided to the client in the `resource_metadata` parameter within the `WWW-Authenticate` header.

    ```http theme={null}
    HTTP/1.1 401 Unauthorized
    WWW-Authenticate: Bearer realm="mcp",
      resource_metadata="https://your-server.com/.well-known/oauth-protected-resource"
    ```

    This tells the client that authorization is required for the MCP server and where to get the necessary information to kickstart the authorization flow.
  </Step>

  <Step title="Protected Resource Metadata Discovery">
    With the URI pointer to the PRM document, the client will fetch the metadata to learn about the authorization server, supported scopes, and other resource information. The data is typically encapsulated in a JSON blob, similar to the one below.

    ```json theme={null}
    {
      "resource": "https://your-server.com/mcp",
      "authorization_servers": ["https://auth.your-server.com"],
      "scopes_supported": ["mcp:tools", "mcp:resources"]
    }
    ```

    You can see a more comprehensive example in [RFC 9728 Section 3.2](https://datatracker.ietf.org/doc/html/rfc9728#name-protected-resource-metadata-r).
  </Step>

  <Step title="Authorization Server Discovery">
    Next, the client discovers what the authorization server can do by fetching its metadata. If the PRM document lists more than one authorization server, the client can decide which one to use.

    With an authorization server selected, the client will then construct a standard metadata URI and issue a request to the [OpenID Connect (OIDC) Discovery](https://openid.net/specs/openid-connect-discovery-1_0.html) or [OAuth 2.0 Auth Server Metadata](https://datatracker.ietf.org/doc/html/rfc8414) endpoints (depending on authorization server support)
    and retrieve another set of metadata properties that will allow it to know the endpoints it needs to complete the authorization flow.

    ```json theme={null}
    {
      "issuer": "https://auth.your-server.com",
      "authorization_endpoint": "https://auth.your-server.com/authorize",
      "token_endpoint": "https://auth.your-server.com/token",
      "registration_endpoint": "https://auth.your-server.com/register"
    }
    ```
  </Step>

  <Step title="Client Registration">
    With all the metadata out of the way, the client now needs to make sure that it's registered with the authorization server. This can be done in two ways.

    First, the client can be **pre-registered** with a given authorization server, in which case it can have embedded client registration information that it uses to complete the authorization flow.

    Alternatively, the client can use **Dynamic Client Registration** (DCR) to dynamically register itself with the authorization server. The latter scenario requires the authorization server to support DCR. If the authorization server does support DCR, the client will send a request to the `registration_endpoint` with its information:

    ```json theme={null}
    {
      "client_name": "My MCP Client",
      "redirect_uris": ["http://localhost:3000/callback"],
      "grant_types": ["authorization_code", "refresh_token"],
      "response_types": ["code"]
    }
    ```

    If the registration succeeds, the authorization server will return a JSON blob with client registration information.

    <Tip>
      **No DCR or Pre-Registration**

      In case an MCP client connects to an MCP server that doesn't use an authorization server that supports DCR and the client is not pre-registered with said authorization server, it's the responsibility of the client developer to provide an affordance for the end-user to enter client information manually.
    </Tip>
  </Step>

  <Step title="User Authorization">
    The client will now need to open a browser to the `/authorize` endpoint, where the user can log in and grant the required permissions. The authorization server will then redirect back to the client with an authorization code that the client exchanges for tokens:

    ```json theme={null}
    {
      "access_token": "eyJhbGciOiJSUzI1NiIs...",
      "refresh_token": "def502...",
      "token_type": "Bearer",
      "expires_in": 3600
    }
    ```

    The access token is what the client will use to authenticate requests to the MCP server. This step follows standard [OAuth 2.1 authorization code with PKCE](https://oauth.net/2/grant-types/authorization-code/) conventions.
  </Step>

  <Step title="Making Authenticated Requests">
    Finally, the client can make requests to your MCP server using the access token embedded in the `Authorization` header:

    ```http theme={null}
    GET /mcp HTTP/1.1
    Host: your-server.com
    Authorization: Bearer eyJhbGciOiJSUzI1NiIs...
    ```

    The MCP server will need to validate the token and process the request if the token is valid and has the required permissions.
  </Step>
</Steps>

## Implementation Example

To get started with a practical implementation, we will use a [Keycloak](https://www.keycloak.org/) authorization server hosted in a Docker container. Keycloak is an open-source authorization server that can be easily deployed locally for testing and experimentation.

Make sure that you download and install [Docker Desktop](https://www.docker.com/products/docker-desktop/). We will need it to deploy Keycloak on our development machine.

### Keycloak Setup

From your terminal application, run the following command to start the Keycloak container:

```bash theme={null}
docker run -p 127.0.0.1:8080:8080 -e KC_BOOTSTRAP_ADMIN_USERNAME=admin -e KC_BOOTSTRAP_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak start-dev
```

This command will pull the Keycloak container image locally and bootstrap the basic configuration. It will run on port `8080` and have an `admin` user with `admin` password.

<Warning>
  **Not for Production**

  The configuration above may be suitable for testing and experimentation; however, you should never use it in production. Refer to the [Configuring Keycloak for production](https://www.keycloak.org/server/configuration-production) guide for additional details on how to deploy the authorization server for scenarios that require reliability, security, and high availability.
</Warning>

You will be able to access the Keycloak authorization server from your browser at `http://localhost:8080`.

<Frame>
  <img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-browser.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=cba689d986e113cbe937d732ac0558b6" alt="Keycloak admin dashboard authentication dialog." width="1834" height="1450" data-path="images/tutorial-authorization/keycloak-browser.png" />
</Frame>

When running with the default configuration, Keycloak will already support many of the capabilities that we need for MCP servers, including Dynamic Client Registration. You can check this by looking at the OIDC configuration, available at:

```http theme={null}
http://localhost:8080/realms/master/.well-known/openid-configuration
```

We will also need to set up Keycloak to support our scopes and allow our host (local machine) to dynamically register clients, as the default policies restrict anonymous dynamic client registration.

Go to **Client scopes** in the Keycloak dashboard and create a new `mcp:tools` scope. We will use this to access all of the tools on our MCP server.

<Frame>
  <img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-scopes.png?fit=max&auto=format&n=sAd4SGUO-cEUqgzn&q=85&s=3cd49dc2e070027609ae495751e0db58" alt="Configuring Keycloak scopes." width="1999" height="1710" data-path="images/tutorial-authorization/keycloak-scopes.png" />
</Frame>

After creating the scope, make sure that you assign its type to **Default** and have flipped the **Include in token scope** switch, as this will be needed for token validation.

Let's now also set up an **audience** for our Keycloak-issued tokens. An audience is important to configure because it embeds the intended destination directly into the issued access token. This helps your MCP server to verify that the token it got was actually meant for it rather than some other API. This is key to help avoid token passthrough scenarios.

To do this, open your `mcp:tools` client scope and click on **Mappers**, followed by **Configure a new mapper**. Select **Audience**.

<Frame>
  <img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/scope-add-audience.gif?s=6ea9cf20c397f4c79c491c2e39019272" alt="Configuring an audience for a token in Keycloak." width="1080" height="921" data-path="images/tutorial-authorization/scope-add-audience.gif" />
</Frame>

For **Name**, use `audience-config`. Add a value for **Included Custom Audience**, set to `http://localhost:3000`. This will be the URI of our test server.

<Warning>
  **Not for Production**

  The audience configuration above is meant for testing. For production scenarios, additional set-up and configuration will be required to ensure that audiences are properly constrained for issued tokens. Specifically, the audience needs to be based on the resource parameter passed from the client, not a fixed value.
</Warning>

Now, navigate to **Clients**, then **Client registration**, and then **Trusted Hosts**. Disable the **Client URIs Must Match** setting and add the hosts from which you're testing. You can get your current host IP by running the `ifconfig` command on Linux or macOS, or `ipconfig` on Windows. You can see the IP address you need to add by looking at the keycloak logs for a line that looks like `Failed to verify remote host : 192.168.215.1`. Check that the IP address is associated with your host. This may be for a bridge network depending on your docker setup.

<Frame>
  <img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client.gif?s=b5d40b36a5f1ea1e818821bb8ea77f6b" alt="Setting up client registration details in Keycloak." width="1199" height="1027" data-path="images/tutorial-authorization/keycloak-client.gif" />
</Frame>

<Warning>
  **Getting the Host**

  If you are running Keycloak from a container, you will also be able to see the host IP from the Terminal in the container logs.
</Warning>

Lastly, we need to register a new client that we can use with the **MCP server itself** to talk to Keycloak for things like [token introspection](https://oauth.net/2/token-introspection/). To do that:

1. Go to **Clients**.
2. Click **Create client**.
3. Give your client a unique **Client ID** and click **Next**.
4. Enable **Client authentication** and click **Next**.
5. Click **Save**.

Worth noting that token introspection is just *one of* the available approaches to validate tokens. This can also be done with the help of standalone libraries, specific to each language and platform.

When you open the client details, go to **Credentials** and take note of the **Client Secret**.

<Frame>
  <img src="https://mintcdn.com/mcp/sAd4SGUO-cEUqgzn/images/tutorial-authorization/keycloak-client-auth.gif?s=7152c41a5746994fd399024bc4659e40" alt="Creating a new client in Keycloak." width="1200" height="1023" data-path="images/tutorial-authorization/keycloak-client-auth.gif" />
</Frame>

<Warning>
  **Handling Secrets**

  Never embed client credentials directly in your code. We recommend using environment variables or specialized solutions for secret storage.
</Warning>

With Keycloak configured, every time the authorization flow is triggered, your MCP server will receive a token like this:

```text theme={null}
eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI1TjcxMGw1WW5MWk13WGZ1VlJKWGtCS3ZZMzZzb3JnRG5scmlyZ2tlTHlzIn0.eyJleHAiOjE3NTU1NDA4MTcsImlhdCI6MTc1NTU0MDc1NywiYXV0aF90aW1lIjoxNzU1NTM4ODg4LCJqdGkiOiJvbnJ0YWM6YjM0MDgwZmYtODQwNC02ODY3LTgxYmUtMTIzMWI1MDU5M2E4IiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjMwMDAiLCJzdWIiOiIzM2VkNmM2Yi1jNmUwLTQ5MjgtYTE2MS1mMmY2OWM3YTAzYjkiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiI3OTc1YTViNi04YjU5LTRhODUtOWNiYS04ZmFlYmRhYjg5NzQiLCJzaWQiOiI4ZjdlYzI3Ni0zNThmLTRjY2MtYjMxMy1kYjA4MjkwZjM3NmYiLCJzY29wZSI6Im1jcDp0b29scyJ9.P5xCRtXORly0R0EXjyqRCUx-z3J4uAOWNAvYtLPXroykZuVCCJ-K1haiQSwbURqfsVOMbL7jiV-sD6miuPzI1tmKOkN_Yct0Vp-azvj7U5rEj7U6tvPfMkg2Uj_jrIX0KOskyU2pVvGZ-5BgqaSvwTEdsGu_V3_E0xDuSBq2uj_wmhqiyTFm5lJ1WkM3Hnxxx1_AAnTj7iOKMFZ4VCwMmk8hhSC7clnDauORc0sutxiJuYUZzxNiNPkmNeQtMCGqWdP1igcbWbrfnNXhJ6NswBOuRbh97_QraET3hl-CNmyS6C72Xc0aOwR_uJ7xVSBTD02OaQ1JA6kjCATz30kGYg
```

Decoded, it will look like this:

```json theme={null}
{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "5N710l5YnLZMwXfuVRJXkBKvY36sorgDnlrirgkeLys"
}.{
  "exp": 1755540817,
  "iat": 1755540757,
  "auth_time": 1755538888,
  "jti": "onrtac:b34080ff-8404-6867-81be-1231b50593a8",
  "iss": "http://localhost:8080/realms/master",
  "aud": "http://localhost:3000",
  "sub": "33ed6c6b-c6e0-4928-a161-f2f69c7a03b9",
  "typ": "Bearer",
  "azp": "7975a5b6-8b59-4a85-9cba-8faebdab8974",
  "sid": "8f7ec276-358f-4ccc-b313-db08290f376f",
  "scope": "mcp:tools"
}.[Signature]
```

<Warning>
  **Embedded Audience**

  Notice the `aud` claim embedded in the token - it's currently set to be the URI of the test MCP server and it's inferred from the scope that we've previously configured. This will be important in our implementation to validate.
</Warning>

### MCP Server Setup

We will now set up our MCP server to use the locally-running Keycloak authorization server. Depending on your programming language preference, you can use one of the supported [MCP SDKs](/docs/draft/sdk).

For our testing purposes, we will create an extremely simple MCP server that exposes two tools - one for addition and another for multiplication. The server will require authorization to access these.

<Tabs>
  <Tab title="TypeScript">
    You can see the complete TypeScript project in the [sample repository](https://github.com/localden/min-ts-mcp-auth).

    Prior to running the code below, ensure that you have a `.env` file with the following content:

    ```env theme={null}
    # Server host/port
    HOST=localhost
    PORT=3000

    # Auth server location
    AUTH_HOST=localhost
    AUTH_PORT=8080
    AUTH_REALM=master

    # Keycloak OAuth client credentials
    OAUTH_CLIENT_ID=<YOUR_SERVER_CLIENT_ID>
    OAUTH_CLIENT_SECRET=<YOUR_SERVER_CLIENT_SECRET>
    ```

    `OAUTH_CLIENT_ID` and `OAUTH_CLIENT_SECRET` are associated with the MCP server client we created earlier.

    In addition to implementing the MCP authorization specification, the server below also does token introspection via Keycloak to make sure that the token it receives from the client is valid. It also implements basic logging to allow you to easily diagnose any issues.

    ```typescript theme={null}
    import "dotenv/config";
    import express from "express";
    import { randomUUID } from "node:crypto";
    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
    import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
    import { z } from "zod";
    import cors from "cors";
    import {
      mcpAuthMetadataRouter,
      getOAuthProtectedResourceMetadataUrl,
    } from "@modelcontextprotocol/sdk/server/auth/router.js";
    import { requireBearerAuth } from "@modelcontextprotocol/sdk/server/auth/middleware/bearerAuth.js";
    import { OAuthMetadata } from "@modelcontextprotocol/sdk/shared/auth.js";

Cut at 300 lines. The page has the rest.

docs/draft/tutorials/security/security_best_practices New page · 983 lines, new page

# Security Best Practices ## Introduction ### Purpose and Scope ## Attacks and Mitigations ### Confused Deputy Problem #### Terminology #### Vulnerable Conditions #### Architecture and Attack Flows ##### Normal OAuth proxy usage (preserves user consent) ##### Malicious OAuth proxy usage (skips user consent) #### Attack Description #### Mitigation ##### Consent Flow Implementation ##### Required Protections ### Token Passthrough #### Risks #### Mitigation ### Server-Side Request Forgery (SSRF) #### Attack Description #### Risks #### Mitigation #### SSRF Against Authorization Servers #### Resources and Tools ### State Handle Hijacking #### Attack Description #### Mitigation ### Local MCP Server Compromise #### Attack Description #### Risks #### Mitigation ### OAuth Authorization URL Validation #### Attack Description #### Risks #### Mitigation ### stdio Transport Security in Proxy Scenarios #### Attack Description #### Risks #### Mitigation ### Mix-Up Attacks #### Attack Description #### Mitigation ### Localhost Redirect URI Impersonation #### Attack Description #### Mitigation ### CIMD Trust Policies ### Scope Minimization #### Attack Description #### Risks #### Mitigation #### Common Mistakes

A whole new page. There's nothing to diff it against, so here is what it says.

# Security Best Practices

> Security considerations, attack vectors, and best practices for MCP implementations

## Introduction

### Purpose and Scope

This document provides security considerations for the Model Context
Protocol (MCP), complementing the
[MCP Authorization](/specification/latest/basic/authorization)
specification. This document identifies security risks, attack vectors,
and best practices specific to MCP implementations.

The primary audience for this document includes developers implementing
MCP authorization flows, MCP server operators, and security
professionals evaluating MCP-based systems. This document should be read
alongside the MCP Authorization specification and
[OAuth 2.0 security best practices](https://datatracker.ietf.org/doc/html/rfc9700).

## Attacks and Mitigations

This section gives a detailed description of attacks on MCP
implementations, along with potential countermeasures.

### Confused Deputy Problem

Attackers can exploit MCP proxy servers that connect to third-party
APIs, creating
"[confused deputy](https://en.wikipedia.org/wiki/Confused_deputy_problem)"
vulnerabilities. This attack allows malicious clients to obtain
authorization codes without proper user consent by exploiting the
combination of static client IDs, dynamic client registration, and
consent cookies.

#### Terminology

**MCP Proxy Server**
: An MCP server that connects MCP clients to third-party APIs, offering
MCP features while delegating operations and acting as a single OAuth
client to the third-party API server.

**Third-Party Authorization Server**
: Authorization server that protects the third-party API. It may lack
dynamic client registration support, requiring the MCP proxy to use a
static client ID for all requests.

**Third-Party API**
: The protected resource server that provides the actual API
functionality. Access to this API requires tokens issued by the
third-party authorization server.

**Static Client ID**
: A fixed OAuth 2.0 client identifier used by the MCP proxy server when
communicating with the third-party authorization server. This Client ID
refers to the MCP server acting as a client to the Third-Party API. It
is the same value for all MCP server to Third-Party API interactions
regardless of which MCP client initiated the request.

#### Vulnerable Conditions

This attack becomes possible when all of the following conditions are
present:

* MCP proxy server uses a **static client ID** with a third-party
  authorization server
* MCP proxy server allows MCP clients to **dynamically register** (each
  getting their own client\_id)
* The third-party authorization server sets a **consent cookie** after
  the first authorization
* MCP proxy server does not implement proper per-client consent before
  forwarding to third-party authorization

#### Architecture and Attack Flows

##### Normal OAuth proxy usage (preserves user consent)

```mermaid theme={null}
sequenceDiagram
    participant UA as User-Agent (Browser)
    participant MC as MCP Client
    participant M as MCP Proxy Server
    participant TAS as Third-Party Authorization Server

    Note over UA,M: Initial Auth flow completed

    Note over UA,TAS: Step 1: Legitimate user consent for Third Party Server

    M->>UA: Redirect to third party authorization server
    UA->>TAS: Authorization request (client_id: mcp-proxy)
    TAS->>UA: Authorization consent screen
    Note over UA: Review consent screen
    UA->>TAS: Approve
    TAS->>UA: Set consent cookie for client ID: mcp-proxy
    TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
    UA->>M: 3P Authorization code
    Note over M,TAS: Exchange 3P code for 3P token
    Note over M: Generate MCP authorization code
    M->>UA: Redirect to MCP Client with MCP authorization code

    Note over M,UA: Exchange code for token, etc.
```

##### Malicious OAuth proxy usage (skips user consent)

```mermaid theme={null}
sequenceDiagram
    participant UA as User-Agent (Browser)
    participant M as MCP Proxy Server
    participant TAS as Third-Party Authorization Server
    participant A as Attacker


    Note over UA,A: Step 2: Attack (leveraging existing cookie, skipping consent)
    A->>M: Dynamically register malicious client, redirect_uri: attacker.com
    A->>UA: Sends malicious link
    UA->>TAS: Authorization request (client_id: mcp-proxy) + consent cookie
    rect rgba(255, 17, 0, 0.67)
    TAS->>TAS: Cookie present, consent skipped
    end

   TAS->>UA: 3P Authorization code + redirect to mcp-proxy-server.com
   UA->>M: 3P Authorization code
   Note over M,TAS: Exchange 3P code for 3P token
   Note over M: Generate MCP authorization code
   M->>UA: Redirect to attacker.com with MCP Authorization code
   UA->>A: MCP Authorization code delivered to attacker.com
   Note over M,A: Attacker exchanges MCP code for MCP token
   A->>M: Attacker impersonates user to MCP server
```

#### Attack Description

When an MCP proxy server uses a static client ID to authenticate with
a third-party authorization server, the following attack becomes
possible:

1. A user authenticates normally through the MCP proxy server to access
   the third-party API
2. During this flow, the third-party authorization server sets a cookie
   on the user agent indicating consent for the static client ID
3. An attacker later sends the user a malicious link containing a
   crafted authorization request which contains a malicious redirect URI
   along with a new dynamically registered client ID
4. When the user clicks the link, their browser still has the consent
   cookie from the previous legitimate request
5. The third-party authorization server detects the cookie and skips the
   consent screen
6. The MCP authorization code is redirected to the attacker's server
   (specified in the malicious `redirect_uri` parameter during
   [dynamic client registration](/specification/latest/basic/authorization#dynamic-client-registration))
7. The attacker exchanges the stolen authorization code for access
   tokens for the MCP server without the user's explicit approval
8. The attacker now has access to the third-party API as the compromised
   user

#### Mitigation

To prevent confused deputy attacks, MCP proxy servers **MUST** implement
per-client consent and proper security controls as detailed below.

##### Consent Flow Implementation

The following diagram shows how to properly implement per-client consent
that runs **before** the third-party authorization flow:

```mermaid theme={null}
sequenceDiagram
    participant Client as MCP Client
    participant Browser as User's Browser
    participant MCP as MCP Server
    participant ThirdParty as Third-Party AuthZ Server

    Note over Client,ThirdParty: 1. Client Registration (Dynamic)
    Client->>MCP: Register with redirect_uri
    MCP-->>Client: client_id

    Note over Client,ThirdParty: 2. Authorization Request
    Client->>Browser: Open MCP server authorization URL
    Browser->>MCP: GET /authorize?client_id=...&redirect_uri=...

    alt Check MCP Server Consent
        MCP->>MCP: Check consent for this client_id
        Note over MCP: Not previously approved
    end

    MCP->>Browser: Show MCP server-owned consent page
    Note over Browser: "Allow [Client Name] to access [Third-Party API]?"
    Browser->>MCP: POST /consent (approve)
    MCP->>MCP: Store consent decision for client_id

    Note over Client,ThirdParty: 3. Forward to Third-Party
    MCP->>Browser: Redirect to third-party /authorize
    Note over MCP: Use static client_id for third-party

    Browser->>ThirdParty: Authorization request (static client_id)
    ThirdParty->>Browser: User authenticates & consents
    ThirdParty->>Browser: Redirect with auth code

    Browser->>MCP: Callback with third-party code
    MCP->>ThirdParty: Exchange code for token (using static client_id)
    MCP->>Browser: Redirect to client's registered redirect_uri
```

##### Required Protections

**Per-Client Consent Storage**

MCP proxy servers **MUST**:

* Maintain a registry of approved `client_id` values per user
* Check this registry **before** initiating the third-party
  authorization flow
* Store consent decisions securely (server-side database, or server
  specific cookies)

**Consent UI Requirements**

The MCP-level consent page **MUST**:

* Clearly identify the requesting MCP client by name
* Display the specific third-party API scopes being requested
* Show the registered `redirect_uri` where tokens will be sent
* Implement CSRF protection (e.g., state parameter, CSRF tokens)
* Prevent iframing via `frame-ancestors` CSP directive or
  `X-Frame-Options: DENY` to prevent clickjacking

**Consent Cookie Security**

If using cookies to track consent decisions, they **MUST**:

* Use `__Host-` prefix for cookie names
* Set `Secure`, `HttpOnly`, and `SameSite=Lax` attributes
* Be cryptographically signed or use server-side sessions
* Bind to the specific `client_id` (not just "user has consented")

**Redirect URI Validation**

The MCP proxy server **MUST**:

* Validate that the `redirect_uri` in authorization requests exactly
  matches the registered URI
* Reject requests if the `redirect_uri` has changed without
  re-registration
* Use exact string matching (not pattern matching or wildcards)

**OAuth State Parameter Validation**

The OAuth `state` parameter is critical to prevent authorization code
interception and CSRF attacks. Proper state validation ensures that
consent approval at the authorization endpoint is enforced at the
callback endpoint.

MCP proxy servers implementing OAuth flows **MUST**:

* Generate a cryptographically secure random `state` value for each
  authorization request
* Store the `state` value server-side (in a secure session store or
  encrypted cookie) **only after** consent has been explicitly approved
* Set the `state` tracking cookie/session **immediately before**
  redirecting to the third-party identity provider (not before consent
  approval)
* Validate at the callback endpoint that the `state` query parameter
  exactly matches the stored value in the callback request's cookies or
  in the request's cookie-based session
* Reject any callback requests where the `state` parameter is missing
  or does not match
* Ensure `state` values are single-use (delete after validation) and
  have a short expiration time (e.g., 10 minutes)

The consent cookie or session containing the `state` value **MUST NOT**
be set until **after** the user has approved the consent screen at the
MCP server's authorization endpoint. Setting this cookie before consent
approval renders the consent screen ineffective, as an attacker could
bypass it by crafting a malicious authorization request.

### Token Passthrough

"Token passthrough" is an anti-pattern where an MCP server accepts
tokens from an MCP client without validating that the tokens were
properly issued *to the MCP server* and passes them through to the
downstream API.

An attacker can gain unauthorized access or otherwise compromise an
MCP server if the server accepts tokens issued for other resources.
This vulnerability has two critical dimensions:

1. **Audience validation failures.** When an MCP server doesn't verify
   that tokens were specifically intended for it (for example, via the
   audience claim, as mentioned in
   [RFC9068](https://www.rfc-editor.org/rfc/rfc9068.html)), it may
   accept tokens originally issued for other services. This breaks a
   fundamental OAuth security boundary, allowing attackers to reuse
   legitimate tokens across different services than intended.
2. **Token passthrough.** If the MCP server not only accepts tokens
   with incorrect audiences but also forwards these unmodified tokens
   to downstream services, it can potentially cause the
   ["confused deputy" problem](#confused-deputy-problem), where the
   downstream API may incorrectly trust the token as if it came from
   the MCP server or assume the token was validated by the upstream

Cut at 300 lines. The page has the rest.

specification/2024-11-05/architecture/index New page · 182 lines, new page

# Architecture ## Core Components ### Host ### Clients ### Servers ## Design Principles ## Message Types ## Capability Negotiation

A whole new page. There's nothing to diff it against, so here is what it says.

# Architecture

The Model Context Protocol (MCP) follows a client-host-server architecture where each
host can run multiple client instances. This architecture enables users to integrate AI
capabilities across applications while maintaining clear security boundaries and
isolating concerns. Built on JSON-RPC, MCP provides a stateful session protocol focused
on context exchange and sampling coordination between clients and servers.

## Core Components

```mermaid theme={null}
graph LR
    subgraph "Application Host Process"
        H[Host]
        C1[Client 1]
        C2[Client 2]
        C3[Client 3]
        H --> C1
        H --> C2
        H --> C3
    end

    subgraph "Local machine"
        S1[Server 1<br>Files & Git]
        S2[Server 2<br>Database]
        R1[("Local<br>Resource A")]
        R2[("Local<br>Resource B")]

        C1 --> S1
        C2 --> S2
        S1 <--> R1
        S2 <--> R2
    end

    subgraph "Internet"
        S3[Server 3<br>External APIs]
        R3[("Remote<br>Resource C")]

        C3 --> S3
        S3 <--> R3
    end
```

### Host

The host process acts as the container and coordinator:

* Creates and manages multiple client instances
* Controls client connection permissions and lifecycle
* Enforces security policies and consent requirements
* Handles user authorization decisions
* Coordinates AI/LLM integration and sampling
* Manages context aggregation across clients

### Clients

Each client is created by the host and maintains an isolated server connection:

* Establishes one stateful session per server
* Handles protocol negotiation and capability exchange
* Routes protocol messages bidirectionally
* Manages subscriptions and notifications
* Maintains security boundaries between servers

A host application creates and manages multiple clients, with each client having a 1:1
relationship with a particular server.

### Servers

Servers provide specialized context and capabilities:

* Expose resources, tools and prompts via MCP primitives
* Operate independently with focused responsibilities
* Request sampling through client interfaces
* Must respect security constraints
* Can be local processes or remote services

## Design Principles

MCP is built on several key design principles that inform its architecture and
implementation:

1. **Servers should be extremely easy to build**
   * Host applications handle complex orchestration responsibilities
   * Servers focus on specific, well-defined capabilities
   * Simple interfaces minimize implementation overhead
   * Clear separation enables maintainable code

2. **Servers should be highly composable**
   * Each server provides focused functionality in isolation
   * Multiple servers can be combined seamlessly
   * Shared protocol enables interoperability
   * Modular design supports extensibility

3. **Servers should not be able to read the whole conversation, nor "see into" other
   servers**
   * Servers receive only necessary contextual information
   * Full conversation history stays with the host
   * Each server connection maintains isolation
   * Cross-server interactions are controlled by the host
   * Host process enforces security boundaries

4. **Features can be added to servers and clients progressively**
   * Core protocol provides minimal required functionality
   * Additional capabilities can be negotiated as needed
   * Servers and clients evolve independently
   * Protocol designed for future extensibility
   * Backwards compatibility is maintained

## Message Types

MCP defines three core message types based on
[JSON-RPC 2.0](https://www.jsonrpc.org/specification):

* **Requests**: Bidirectional messages with method and parameters expecting a response
* **Responses**: Successful results or errors matching specific request IDs
* **Notifications**: One-way messages requiring no response

Each message type follows the JSON-RPC 2.0 specification for structure and delivery
semantics.

## Capability Negotiation

The Model Context Protocol uses a capability-based negotiation system where clients and
servers explicitly declare their supported features during initialization. Capabilities
determine which protocol features and primitives are available during a session.

* Servers declare capabilities like resource subscriptions, tool support, and prompt
  templates
* Clients declare capabilities like sampling support and notification handling
* Both parties must respect declared capabilities throughout the session
* Additional capabilities can be negotiated through extensions to the protocol

```mermaid theme={null}
sequenceDiagram
    participant Host
    participant Client
    participant Server

    Host->>+Client: Initialize client
    Client->>+Server: Initialize session with capabilities
    Server-->>Client: Respond with supported capabilities

    Note over Host,Server: Active Session with Negotiated Features

    loop Client Requests
        Host->>Client: User- or model-initiated action
        Client->>Server: Request (tools/resources)
        Server-->>Client: Response
        Client-->>Host: Update UI or respond to model
    end

    loop Server Requests
        Server->>Client: Request (sampling)
        Client->>Host: Forward to AI
        Host-->>Client: AI response
        Client-->>Server: Response
    end

    loop Notifications
        Server--)Client: Resource updates
        Client--)Server: Status changes
    end

    Host->>Client: Terminate
    Client->>-Server: End session
    deactivate Server
```

Each capability unlocks specific protocol features for use during the session. For
example:

* Implemented [server features](/specification/2024-11-05/server) must be
  advertised in the server's capabilities
* Emitting resource subscription notifications requires the server to declare
  subscription support
* Tool invocation requires the server to declare tool capabilities
* [Sampling](/specification/2024-11-05/client/sampling) requires the client to
  declare support in its capabilities

This capability negotiation ensures clients and servers have a clear understanding of
supported functionality while maintaining protocol extensibility.

specification/2024-11-05/basic/index New page · 71 lines, new page

# Overview ## Protocol Layers ## Auth ## Schema

A whole new page. There's nothing to diff it against, so here is what it says.

# Overview

All messages between MCP clients and servers **MUST** follow the
[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines
three fundamental types of messages:

| Type            | Description                            | Requirements                           |
| --------------- | -------------------------------------- | -------------------------------------- |
| `Requests`      | Messages sent to initiate an operation | Must include unique ID and method name |
| `Responses`     | Messages sent in reply to requests     | Must include same ID as request        |
| `Notifications` | One-way messages with no reply         | Must not include an ID                 |

**Responses** are further sub-categorized as either **successful results** or **errors**.
Results can follow any JSON object structure, while errors must include an error code and
message at minimum.

## Protocol Layers

The Model Context Protocol consists of several key components that work together:

* **Base Protocol**: Core JSON-RPC message types
* **Lifecycle Management**: Connection initialization, capability negotiation, and
  session control
* **Server Features**: Resources, prompts, and tools exposed by servers
* **Client Features**: Sampling and root directory lists provided by clients
* **Utilities**: Cross-cutting concerns like logging and argument completion

All implementations **MUST** support the base protocol and lifecycle management
components. Other components **MAY** be implemented based on the specific needs of the
application.

These protocol layers establish clear separation of concerns while enabling rich
interactions between clients and servers. The modular design allows implementations to
support exactly the features they need.

See the following pages for more details on the different components:

<CardGroup cols={3}>
  <Card title="Lifecycle" icon="arrows-rotate" href="/specification/2024-11-05/basic/lifecycle" />

  <Card title="Resources" icon="file-lines" href="/specification/2024-11-05/server/resources" />

  <Card title="Prompts" icon="message" href="/specification/2024-11-05/server/prompts" />

  <Card title="Tools" icon="wrench" href="/specification/2024-11-05/server/tools" />

  <Card title="Logging" icon="rectangle-list" href="/specification/2024-11-05/server/utilities/logging" />

  <Card title="Sampling" icon="code" href="/specification/2024-11-05/client/sampling" />
</CardGroup>

## Auth

Authentication and authorization are not currently part of the core MCP specification,
but we are considering ways to introduce them in future. Join us in
[GitHub Discussions](https://github.com/modelcontextprotocol/specification/discussions)
to help shape the future of the protocol!

Clients and servers **MAY** negotiate their own custom authentication and authorization
strategies.

## Schema

The full specification of the protocol is defined as a
[TypeScript schema](http://github.com/modelcontextprotocol/specification/tree/main/schema/2024-11-05/schema.ts).
This is the source of truth for all protocol messages and structures.

There is also a
[JSON Schema](http://github.com/modelcontextprotocol/specification/tree/main/schema/2024-11-05/schema.json),
which is automatically generated from the TypeScript source of truth, for use with
various automated tooling.

specification/2024-11-05/basic/lifecycle New page · 214 lines, new page

# Lifecycle ## Lifecycle Phases ### Initialization #### Version Negotiation #### Capability Negotiation ### Operation ### Shutdown #### stdio #### HTTP ## Error Handling

A whole new page. There's nothing to diff it against, so here is what it says.

# Lifecycle

The Model Context Protocol (MCP) defines a rigorous lifecycle for client-server
connections that ensures proper capability negotiation and state management.

1. **Initialization**: Capability negotiation and protocol version agreement
2. **Operation**: Normal protocol communication
3. **Shutdown**: Graceful termination of the connection

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    Note over Client,Server: Initialization Phase
    activate Client
    Client->>+Server: initialize request
    Server-->>Client: initialize response
    Client--)Server: initialized notification

    Note over Client,Server: Operation Phase
    rect rgb(200, 220, 250)
        note over Client,Server: Normal protocol operations
    end

    Note over Client,Server: Shutdown
    Client--)-Server: Disconnect
    deactivate Server
    Note over Client,Server: Connection closed
```

## Lifecycle Phases

### Initialization

The initialization phase **MUST** be the first interaction between client and server.
During this phase, the client and server:

* Establish protocol version compatibility
* Exchange and negotiate capabilities
* Share implementation details

The client **MUST** initiate this phase by sending an `initialize` request containing:

* Protocol version supported
* Client capabilities
* Client implementation information

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "roots": {
        "listChanged": true
      },
      "sampling": {}
    },
    "clientInfo": {
      "name": "ExampleClient",
      "version": "1.0.0"
    }
  }
}
```

The server **MUST** respond with its own capabilities and information:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "protocolVersion": "2024-11-05",
    "capabilities": {
      "logging": {},
      "prompts": {
        "listChanged": true
      },
      "resources": {
        "subscribe": true,
        "listChanged": true
      },
      "tools": {
        "listChanged": true
      }
    },
    "serverInfo": {
      "name": "ExampleServer",
      "version": "1.0.0"
    }
  }
}
```

After successful initialization, the client **MUST** send an `initialized` notification
to indicate it is ready to begin normal operations:

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "notifications/initialized"
}
```

* The client **SHOULD NOT** send requests other than
  [pings](/specification/2024-11-05/basic/utilities/ping) before the server
  has responded to the `initialize` request.
* The server **SHOULD NOT** send requests other than
  [pings](/specification/2024-11-05/basic/utilities/ping) and
  [logging](/specification/2024-11-05/server/utilities/logging) before
  receiving the `initialized` notification.

#### Version Negotiation

In the `initialize` request, the client **MUST** send a protocol version it supports.
This **SHOULD** be the *latest* version supported by the client.

If the server supports the requested protocol version, it **MUST** respond with the same
version. Otherwise, the server **MUST** respond with another protocol version it
supports. This **SHOULD** be the *latest* version supported by the server.

If the client does not support the version in the server's response, it **SHOULD**
disconnect.

#### Capability Negotiation

Client and server capabilities establish which optional protocol features will be
available during the session.

Key capabilities include:

| Category | Capability     | Description                                                                         |
| -------- | -------------- | ----------------------------------------------------------------------------------- |
| Client   | `roots`        | Ability to provide filesystem [roots](/specification/2024-11-05/client/roots)       |
| Client   | `sampling`     | Support for LLM [sampling](/specification/2024-11-05/client/sampling) requests      |
| Client   | `experimental` | Describes support for non-standard experimental features                            |
| Server   | `prompts`      | Offers [prompt templates](/specification/2024-11-05/server/prompts)                 |
| Server   | `resources`    | Provides readable [resources](/specification/2024-11-05/server/resources)           |
| Server   | `tools`        | Exposes callable [tools](/specification/2024-11-05/server/tools)                    |
| Server   | `logging`      | Emits structured [log messages](/specification/2024-11-05/server/utilities/logging) |
| Server   | `experimental` | Describes support for non-standard experimental features                            |

Capability objects can describe sub-capabilities like:

* `listChanged`: Support for list change notifications (for prompts, resources, and
  tools)
* `subscribe`: Support for subscribing to individual items' changes (resources only)

### Operation

During the operation phase, the client and server exchange messages according to the
negotiated capabilities.

Both parties **SHOULD**:

* Respect the negotiated protocol version
* Only use capabilities that were successfully negotiated

### Shutdown

During the shutdown phase, one side (usually the client) cleanly terminates the protocol
connection. No specific shutdown messages are defined—instead, the underlying transport
mechanism should be used to signal connection termination:

#### stdio

For the stdio [transport](/specification/2024-11-05/basic/transports), the
client **SHOULD** initiate shutdown by:

1. First, closing the input stream to the child process (the server)
2. Waiting for the server to exit, or sending `SIGTERM` if the server does not exit
   within a reasonable time
3. Sending `SIGKILL` if the server does not exit within a reasonable time after `SIGTERM`

The server **MAY** initiate shutdown by closing its output stream to the client and
exiting.

#### HTTP

For HTTP [transports](/specification/2024-11-05/basic/transports), shutdown
is indicated by closing the associated HTTP connection(s).

## Error Handling

Implementations **SHOULD** be prepared to handle these error cases:

* Protocol version mismatch
* Failure to negotiate required capabilities
* Initialize request timeout
* Shutdown timeout

Implementations **SHOULD** implement appropriate timeouts for all requests, to prevent
hung connections and resource exhaustion.

Example initialization error:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32602,
    "message": "Unsupported protocol version",
    "data": {
      "supported": ["2024-11-05"],
      "requested": "1.0.0"
    }
  }
}
```

specification/2024-11-05/basic/messages New page · 65 lines, new page

# Messages ## Requests ## Responses ## Notifications

A whole new page. There's nothing to diff it against, so here is what it says.

# Messages

All messages in MCP **MUST** follow the
[JSON-RPC 2.0](https://www.jsonrpc.org/specification) specification. The protocol defines
three types of messages:

## Requests

Requests are sent from the client to the server or vice versa.

```typescript theme={null}
{
  jsonrpc: "2.0";
  id: string | number;
  method: string;
  params?: {
    [key: string]: unknown;
  };
}
```

* Requests **MUST** include a string or integer ID.
* Unlike base JSON-RPC, the ID **MUST NOT** be `null`.
* The request ID **MUST NOT** have been previously used by the requestor within the same
  session.

## Responses

Responses are sent in reply to requests.

```typescript theme={null}
{
  jsonrpc: "2.0";
  id: string | number;
  result?: {
    [key: string]: unknown;
  }
  error?: {
    code: number;
    message: string;
    data?: unknown;
  }
}
```

* Responses **MUST** include the same ID as the request they correspond to.
* Either a `result` or an `error` **MUST** be set. A response **MUST NOT** set both.
* Error codes **MUST** be integers.

## Notifications

Notifications are sent from the client to the server or vice versa. They do not expect a
response.

```typescript theme={null}
{
  jsonrpc: "2.0";
  method: string;
  params?: {
    [key: string]: unknown;
  };
}
```

* Notifications **MUST NOT** include an ID.

specification/2024-11-05/basic/transports New page · 93 lines, new page

# Transports ## stdio ## HTTP with SSE #### Security Warning ## Custom Transports

A whole new page. There's nothing to diff it against, so here is what it says.

# Transports

MCP currently defines two standard transport mechanisms for client-server communication:

1. [stdio](#stdio), communication over standard in and standard out
2. [HTTP with Server-Sent Events](#http-with-sse) (SSE)

Clients **SHOULD** support stdio whenever possible.

It is also possible for clients and servers to implement
[custom transports](#custom-transports) in a pluggable fashion.

## stdio

In the **stdio** transport:

* The client launches the MCP server as a subprocess.
* The server receives JSON-RPC messages on its standard input (`stdin`) and writes
  responses to its standard output (`stdout`).
* Messages are delimited by newlines, and **MUST NOT** contain embedded newlines.
* The server **MAY** write UTF-8 strings to its standard error (`stderr`) for logging
  purposes. Clients **MAY** capture, forward, or ignore this logging.
* The server **MUST NOT** write anything to its `stdout` that is not a valid MCP message.
* The client **MUST NOT** write anything to the server's `stdin` that is not a valid MCP
  message.

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server Process

    Client->>+Server Process: Launch subprocess
    loop Message Exchange
        Client->>Server Process: Write to stdin
        Server Process->>Client: Write to stdout
        Server Process--)Client: Optional logs on stderr
    end
    Client->>Server Process: Close stdin, terminate subprocess
    deactivate Server Process
```

## HTTP with SSE

In the **SSE** transport, the server operates as an independent process that can handle
multiple client connections.

#### Security Warning

When implementing HTTP with SSE transport:

1. Servers **MUST** validate the `Origin` header on all incoming connections to prevent DNS rebinding attacks
2. When running locally, servers **SHOULD** bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0)
3. Servers **SHOULD** implement proper authentication for all connections

Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites.

The server **MUST** provide two endpoints:

1. An SSE endpoint, for clients to establish a connection and receive messages from the
   server
2. A regular HTTP POST endpoint for clients to send messages to the server

When a client connects, the server **MUST** send an `endpoint` event containing a URI for
the client to use for sending messages. All subsequent client messages **MUST** be sent
as HTTP POST requests to this endpoint.

Server messages are sent as SSE `message` events, with the message content encoded as
JSON in the event data.

```mermaid theme={null}
sequenceDiagram
    participant Client
    participant Server

    Client->>Server: Open SSE connection
    Server->>Client: endpoint event
    loop Message Exchange
        Client->>Server: HTTP POST messages
        Server->>Client: SSE message events
    end
    Client->>Server: Close SSE connection
```

## Custom Transports

Clients and servers **MAY** implement additional custom transport mechanisms to suit
their specific needs. The protocol is transport-agnostic and can be implemented over any
communication channel that supports bidirectional message exchange.

Implementers who choose to support custom transports **MUST** ensure they preserve the
JSON-RPC message format and lifecycle requirements defined by MCP. Custom transports
**SHOULD** document their specific connection establishment and message exchange patterns
to aid interoperability.

specification/2024-11-05/basic/utilities/cancellation New page · 79 lines, new page

# Cancellation ## Cancellation Flow ## Behavior Requirements ## Timing Considerations ## Implementation Notes ## Error Handling

A whole new page. There's nothing to diff it against, so here is what it says.

# Cancellation

The Model Context Protocol (MCP) supports optional cancellation of in-progress requests
through notification messages. Either side can send a cancellation notification to
indicate that a previously-issued request should be terminated.

## Cancellation Flow

When a party wants to cancel an in-progress request, it sends a `notifications/cancelled`
notification containing:

* The ID of the request to cancel
* An optional reason string that can be logged or displayed

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "notifications/cancelled",
  "params": {
    "requestId": "123",
    "reason": "User requested cancellation"
  }
}
```

## Behavior Requirements

1. Cancellation notifications **MUST** only reference requests that:
   * Were previously issued in the same direction
   * Are believed to still be in-progress
2. The `initialize` request **MUST NOT** be cancelled by clients
3. Receivers of cancellation notifications **SHOULD**:
   * Stop processing the cancelled request
   * Free associated resources
   * Not send a response for the cancelled request
4. Receivers **MAY** ignore cancellation notifications if:
   * The referenced request is unknown
   * Processing has already completed
   * The request cannot be cancelled
5. The sender of the cancellation notification **SHOULD** ignore any response to the
   request that arrives afterward

## Timing Considerations

Due to network latency, cancellation notifications may arrive after request processing
has completed, and potentially after a response has already been sent.

Both parties **MUST** handle these race conditions gracefully:

```mermaid theme={null}
sequenceDiagram
   participant Client
   participant Server

   Client->>Server: Request (ID: 123)
   Note over Server: Processing starts
   Client--)Server: notifications/cancelled (ID: 123)
   alt
      Note over Server: Processing may have<br/>completed before<br/>cancellation arrives
   else If not completed
      Note over Server: Stop processing
   end
```

## Implementation Notes

* Both parties **SHOULD** log cancellation reasons for debugging
* Application UIs **SHOULD** indicate when cancellation is requested

## Error Handling

Invalid cancellation notifications **SHOULD** be ignored:

* Unknown request IDs
* Already completed requests
* Malformed notifications

This maintains the "fire and forget" nature of notifications while allowing for race
conditions in asynchronous communication.

specification/2024-11-05/basic/utilities/ping New page · 62 lines, new page

# Ping ## Overview ## Message Format ## Behavior Requirements ## Usage Patterns ## Implementation Considerations ## Error Handling

A whole new page. There's nothing to diff it against, so here is what it says.

# Ping

The Model Context Protocol includes an optional ping mechanism that allows either party
to verify that their counterpart is still responsive and the connection is alive.

## Overview

The ping functionality is implemented through a simple request/response pattern. Either
the client or server can initiate a ping by sending a `ping` request.

## Message Format

A ping request is a standard JSON-RPC request with no parameters:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": "123",
  "method": "ping"
}
```

## Behavior Requirements

1. The receiver **MUST** respond promptly with an empty response:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": "123",
  "result": {}
}
```

2. If no response is received within a reasonable timeout period, the sender **MAY**:
   * Consider the connection stale
   * Terminate the connection
   * Attempt reconnection procedures

## Usage Patterns

```mermaid theme={null}
sequenceDiagram
    participant Sender
    participant Receiver

    Sender->>Receiver: ping request
    Receiver->>Sender: empty response
```

## Implementation Considerations

* Implementations **SHOULD** periodically issue pings to detect connection health
* The frequency of pings **SHOULD** be configurable
* Timeouts **SHOULD** be appropriate for the network environment
* Excessive pinging **SHOULD** be avoided to reduce network overhead

## Error Handling

* Timeouts **SHOULD** be treated as connection failures
* Multiple failed pings **MAY** trigger connection reset
* Implementations **SHOULD** log ping failures for diagnostics

specification/2024-11-05/basic/utilities/progress New page · 85 lines, new page

# Progress ## Progress Flow ## Behavior Requirements ## Implementation Notes

A whole new page. There's nothing to diff it against, so here is what it says.

# Progress

The Model Context Protocol (MCP) supports optional progress tracking for long-running
operations through notification messages. Either side can send progress notifications to
provide updates about operation status.

## Progress Flow

When a party wants to *receive* progress updates for a request, it includes a
`progressToken` in the request metadata.

* Progress tokens **MUST** be a string or integer value
* Progress tokens can be chosen by the sender using any means, but **MUST** be unique
  across all active requests.

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "some_method",
  "params": {
    "_meta": {
      "progressToken": "abc123"
    }
  }
}
```

The receiver **MAY** then send progress notifications containing:

* The original progress token
* The current progress value so far
* An optional "total" value

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "notifications/progress",
  "params": {
    "progressToken": "abc123",
    "progress": 50,
    "total": 100
  }
}
```

* The `progress` value **MUST** increase with each notification, even if the total is
  unknown.
* The `progress` and the `total` values **MAY** be floating point.

## Behavior Requirements

1. Progress notifications **MUST** only reference tokens that:
   * Were provided in an active request
   * Are associated with an in-progress operation

2. Receivers of progress requests **MAY**:
   * Choose not to send any progress notifications
   * Send notifications at whatever frequency they deem appropriate
   * Omit the total value if unknown

```mermaid theme={null}
sequenceDiagram
    participant Sender
    participant Receiver

    Note over Sender,Receiver: Request with progress token
    Sender->>Receiver: Method request with progressToken

    Note over Sender,Receiver: Progress updates
    loop Progress Updates
        Receiver-->>Sender: Progress notification (0.2/1.0)
        Receiver-->>Sender: Progress notification (0.6/1.0)
        Receiver-->>Sender: Progress notification (1.0/1.0)
    end

    Note over Sender,Receiver: Operation complete
    Receiver->>Sender: Method response
```

## Implementation Notes

* Senders and receivers **SHOULD** track active progress tokens
* Both parties **SHOULD** implement rate limiting to prevent flooding
* Progress notifications **MUST** stop after completion

specification/2024-11-05/client/roots New page · 184 lines, new page

# Roots ## User Interaction Model ## Capabilities ## Protocol Messages ### Listing Roots ### Root List Changes ## Message Flow ## Data Types ### Root #### Project Directory #### Multiple Repositories ## Error Handling ## Security Considerations ## Implementation Guidelines

A whole new page. There's nothing to diff it against, so here is what it says.

# Roots

The Model Context Protocol (MCP) provides a standardized way for clients to expose
filesystem "roots" to servers. Roots define the boundaries of where servers can operate
within the filesystem, allowing them to understand which directories and files they have
access to. Servers can request the list of roots from supporting clients and receive
notifications when that list changes.

## User Interaction Model

Roots in MCP are typically exposed through workspace or project configuration interfaces.

For example, implementations could offer a workspace/project picker that allows users to
select directories and files the server should have access to. This can be combined with
automatic workspace detection from version control systems or project files.

However, implementations are free to expose roots through any interface pattern that
suits their needs—the protocol itself does not mandate any specific user
interaction model.

## Capabilities

Clients that support roots **MUST** declare the `roots` capability during
[initialization](/specification/2024-11-05/basic/lifecycle#initialization):

```json theme={null}
{
  "capabilities": {
    "roots": {
      "listChanged": true
    }
  }
}
```

`listChanged` indicates whether the client will emit notifications when the list of roots
changes.

## Protocol Messages

### Listing Roots

To retrieve roots, servers send a `roots/list` request:

**Request:**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "roots/list"
}
```

**Response:**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "roots": [
      {
        "uri": "file:///home/user/projects/myproject",
        "name": "My Project"
      }
    ]
  }
}
```

### Root List Changes

When roots change, clients that support `listChanged` **MUST** send a notification:

```json theme={null}
{
  "jsonrpc": "2.0",
  "method": "notifications/roots/list_changed"
}
```

## Message Flow

```mermaid theme={null}
sequenceDiagram
    participant Server
    participant Client

    Note over Server,Client: Discovery
    Server->>Client: roots/list
    Client-->>Server: Available roots

    Note over Server,Client: Changes
    Client--)Server: notifications/roots/list_changed
    Server->>Client: roots/list
    Client-->>Server: Updated roots
```

## Data Types

### Root

A root definition includes:

* `uri`: Unique identifier for the root. This **MUST** be a `file://` URI in the current
  specification.
* `name`: Optional human-readable name for display purposes.

Example roots for different use cases:

#### Project Directory

```json theme={null}
{
  "uri": "file:///home/user/projects/myproject",
  "name": "My Project"
}
```

#### Multiple Repositories

```json theme={null}
[
  {
    "uri": "file:///home/user/repos/frontend",
    "name": "Frontend Repository"
  },
  {
    "uri": "file:///home/user/repos/backend",
    "name": "Backend Repository"
  }
]
```

## Error Handling

Clients **SHOULD** return standard JSON-RPC errors for common failure cases:

* Client does not support roots: `-32601` (Method not found)
* Internal errors: `-32603`

Example error:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Roots not supported",
    "data": {
      "reason": "Client does not have roots capability"
    }
  }
}
```

## Security Considerations

1. Clients **MUST**:
   * Only expose roots with appropriate permissions
   * Validate all root URIs to prevent path traversal
   * Implement proper access controls
   * Monitor root accessibility

2. Servers **SHOULD**:
   * Handle cases where roots become unavailable
   * Respect root boundaries during operations
   * Validate all paths against provided roots

## Implementation Guidelines

1. Clients **SHOULD**:
   * Prompt users for consent before exposing roots to servers
   * Provide clear user interfaces for root management
   * Validate root accessibility before exposing
   * Monitor for root changes

2. Servers **SHOULD**:
   * Check for roots capability before usage
   * Handle root list changes gracefully
   * Respect root boundaries in operations
   * Cache root information appropriately

specification/2024-11-05/client/sampling New page · 222 lines, new page

# Sampling ## User Interaction Model ## Capabilities ## Protocol Messages ### Creating Messages ## Message Flow ## Data Types ### Messages #### Text Content #### Image Content ### Model Preferences #### Capability Priorities #### Model Hints ## Error Handling ## Security Considerations

A whole new page. There's nothing to diff it against, so here is what it says.

# Sampling

The Model Context Protocol (MCP) provides a standardized way for servers to request LLM
sampling ("completions" or "generations") from language models via clients. This flow
allows clients to maintain control over model access, selection, and permissions while
enabling servers to leverage AI capabilities—with no server API keys necessary.
Servers can request text or image-based interactions and optionally include context from
MCP servers in their prompts.

## User Interaction Model

Sampling in MCP allows servers to implement agentic behaviors, by enabling LLM calls to
occur *nested* inside other MCP server features.

Implementations are free to expose sampling through any interface pattern that suits
their needs—the protocol itself does not mandate any specific user interaction
model.

<Warning>
  For trust & safety and security, there **SHOULD** always
  be a human in the loop with the ability to deny sampling requests.

  Applications **SHOULD**:

  * Provide UI that makes it easy and intuitive to review sampling requests
  * Allow users to view and edit prompts before sending
  * Present generated responses for review before delivery
</Warning>

## Capabilities

Clients that support sampling **MUST** declare the `sampling` capability during
[initialization](/specification/2024-11-05/basic/lifecycle#initialization):

```json theme={null}
{
  "capabilities": {
    "sampling": {}
  }
}
```

## Protocol Messages

### Creating Messages

To request a language model generation, servers send a `sampling/createMessage` request:

**Request:**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "sampling/createMessage",
  "params": {
    "messages": [
      {
        "role": "user",
        "content": {
          "type": "text",
          "text": "What is the capital of France?"
        }
      }
    ],
    "modelPreferences": {
      "hints": [
        {
          "name": "claude-3-sonnet"
        }
      ],
      "intelligencePriority": 0.8,
      "speedPriority": 0.5
    },
    "systemPrompt": "You are a helpful assistant.",
    "maxTokens": 100
  }
}
```

**Response:**

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "result": {
    "role": "assistant",
    "content": {
      "type": "text",
      "text": "The capital of France is Paris."
    },
    "model": "claude-3-sonnet-20240307",
    "stopReason": "endTurn"
  }
}
```

## Message Flow

```mermaid theme={null}
sequenceDiagram
    participant Server
    participant Client
    participant User
    participant LLM

    Note over Server,Client: Server initiates sampling
    Server->>Client: sampling/createMessage

    Note over Client,User: Human-in-the-loop review
    Client->>User: Present request for approval
    User-->>Client: Review and approve/modify

    Note over Client,LLM: Model interaction
    Client->>LLM: Forward approved request
    LLM-->>Client: Return generation

    Note over Client,User: Response review
    Client->>User: Present response for approval
    User-->>Client: Review and approve/modify

    Note over Server,Client: Complete request
    Client-->>Server: Return approved response
```

## Data Types

### Messages

Sampling messages can contain:

#### Text Content

```json theme={null}
{
  "type": "text",
  "text": "The message content"
}
```

#### Image Content

```json theme={null}
{
  "type": "image",
  "data": "base64-encoded-image-data",
  "mimeType": "image/jpeg"
}
```

### Model Preferences

Model selection in MCP requires careful abstraction since servers and clients may use
different AI providers with distinct model offerings. A server cannot simply request a
specific model by name since the client may not have access to that exact model or may
prefer to use a different provider's equivalent model.

To solve this, MCP implements a preference system that combines abstract capability
priorities with optional model hints:

#### Capability Priorities

Servers express their needs through three normalized priority values (0-1):

* `costPriority`: How important is minimizing costs? Higher values prefer cheaper models.
* `speedPriority`: How important is low latency? Higher values prefer faster models.
* `intelligencePriority`: How important are advanced capabilities? Higher values prefer
  more capable models.

#### Model Hints

While priorities help select models based on characteristics, `hints` allow servers to
suggest specific models or model families:

* Hints are treated as substrings that can match model names flexibly
* Multiple hints are evaluated in order of preference
* Clients **MAY** map hints to equivalent models from different providers
* Hints are advisory—clients make final model selection

For example:

```json theme={null}
{
  "hints": [
    { "name": "claude-3-sonnet" }, // Prefer Sonnet-class models
    { "name": "claude" } // Fall back to any Claude model
  ],
  "costPriority": 0.3, // Cost is less important
  "speedPriority": 0.8, // Speed is very important
  "intelligencePriority": 0.5 // Moderate capability needs
}
```

The client processes these preferences to select an appropriate model from its available
options. For instance, if the client doesn't have access to Claude models but has Gemini,
it might map the sonnet hint to `gemini-1.5-pro` based on similar capabilities.

## Error Handling

Clients **SHOULD** return errors for common failure cases:

Example error:

```json theme={null}
{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -1,
    "message": "User rejected sampling request"
  }
}
```

## Security Considerations

1. Clients **SHOULD** implement user approval controls
2. Both parties **SHOULD** validate message content
3. Clients **SHOULD** respect model preference hints
4. Clients **SHOULD** implement rate limiting
5. Both parties **MUST** handle sensitive data appropriately

specification/2024-11-05/index New page · 129 lines, new page

# Specification ## Overview ## Key Details ### Base Protocol ### Features ### Additional Utilities ## Security and Trust & Safety ### Key Principles ### Implementation Guidelines ## Learn More

A whole new page. There's nothing to diff it against, so here is what it says.

# Specification

[Model Context Protocol](https://modelcontextprotocol.io) (MCP) is an open protocol that
enables seamless integration between LLM applications and external data sources and
tools. Whether you're building an AI-powered IDE, enhancing a chat interface, or creating
custom AI workflows, MCP provides a standardized way to connect LLMs with the context
they need.

This specification defines the authoritative protocol requirements, based on the
TypeScript schema in
[schema.ts](https://github.com/modelcontextprotocol/specification/blob/main/schema/2024-11-05/schema.ts).

For implementation guides and examples, visit
[modelcontextprotocol.io](https://modelcontextprotocol.io).

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD
NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be
interpreted as described in [BCP 14](https://datatracker.ietf.org/doc/html/bcp14)
\[[RFC2119](https://datatracker.ietf.org/doc/html/rfc2119)]
\[[RFC8174](https://datatracker.ietf.org/doc/html/rfc8174)] when, and only when, they
appear in all capitals, as shown here.

## Overview

MCP provides a standardized way for applications to:

* Share contextual information with language models
* Expose tools and capabilities to AI systems
* Build composable integrations and workflows

The protocol uses [JSON-RPC](https://www.jsonrpc.org/) 2.0 messages to establish
communication between:

* **Hosts**: LLM applications that initiate connections
* **Clients**: Connectors within the host application
* **Servers**: Services that provide context and capabilities

MCP takes some inspiration from the
[Language Server Protocol](https://microsoft.github.io/language-server-protocol/), which
standardizes how to add support for programming languages across a whole ecosystem of
development tools. In a similar way, MCP standardizes how to integrate additional context
and tools into the ecosystem of AI applications.

## Key Details

### Base Protocol

* [JSON-RPC](https://www.jsonrpc.org/) message format
* Stateful connections
* Server and client capability negotiation

### Features

Servers offer any of the following features to clients:

* **Resources**: Context and data, for the user or the AI model to use
* **Prompts**: Templated messages and workflows for users
* **Tools**: Functions for the AI model to execute

Clients may offer the following feature to servers:

* **Sampling**: Server-initiated agentic behaviors and recursive LLM interactions

### Additional Utilities

* Configuration
* Progress tracking
* Cancellation
* Error reporting
* Logging

## Security and Trust & Safety

The Model Context Protocol enables powerful capabilities through arbitrary data access
and code execution paths. With this power comes important security and trust
considerations that all implementors must carefully address.

### Key Principles

1. **User Consent and Control**
   * Users must explicitly consent to and understand all data access and operations
   * Users must retain control over what data is shared and what actions are taken
   * Implementors should provide clear UIs for reviewing and authorizing activities

2. **Data Privacy**
   * Hosts must obtain explicit user consent before exposing user data to servers
   * Hosts must not transmit resource data elsewhere without user consent
   * User data should be protected with appropriate access controls

3. **Tool Safety**
   * Tools represent arbitrary code execution and must be treated with appropriate
     caution
   * Hosts must obtain explicit user consent before invoking any tool
   * Users should understand what each tool does before authorizing its use

4. **LLM Sampling Controls**
   * Users must explicitly approve any LLM sampling requests
   * Users should control:
     * Whether sampling occurs at all
     * The actual prompt that will be sent
     * What results the server can see
   * The protocol intentionally limits server visibility into prompts

### Implementation Guidelines

While MCP itself cannot enforce these security principles at the protocol level,
implementors **SHOULD**:

1. Build robust consent and authorization flows into their applications
2. Provide clear documentation of security implications
3. Implement appropriate access controls and data protections
4. Follow security best practices in their integrations
5. Consider privacy implications in their feature designs

## Learn More

Explore the detailed specification for each protocol component:

<CardGroup cols={5}>
  <Card title="Architecture" icon="sitemap" href="/specification/2024-11-05/architecture" />

  <Card title="Base Protocol" icon="code" href="/specification/2024-11-05/basic" />

  <Card title="Server Features" icon="server" href="/specification/2024-11-05/server" />

  <Card title="Client Features" icon="user" href="/specification/2024-11-05/client" />

  <Card title="Contributing" icon="pencil" href="/community/contributing" />
</CardGroup>

specification/2024-11-05/server/index New page · 29 lines, new page

# Overview

A whole new page. There's nothing to diff it against, so here is what it says.

# Overview

Servers provide the fundamental building blocks for adding context to language models via
MCP. These primitives enable rich interactions between clients, servers, and language
models:

* **Prompts**: Pre-defined templates or instructions that guide language model
  interactions
* **Resources**: Structured data or content that provides additional context to the model
* **Tools**: Executable functions that allow models to perform actions or retrieve
  information

Each primitive can be summarized in the following control hierarchy:

| Primitive | Control                | Description                                        | Example                         |
| --------- | ---------------------- | -------------------------------------------------- | ------------------------------- |
| Prompts   | User-controlled        | Interactive templates invoked by user choice       | Slash commands, menu options    |
| Resources | Application-controlled | Contextual data attached and managed by the client | File contents, git history      |
| Tools     | Model-controlled       | Functions exposed to the LLM to take actions       | API POST requests, file writing |

Explore these key primitives in more detail below:

<CardGroup cols={3}>
  <Card title="Prompts" icon="message" href="/specification/2024-11-05/server/prompts" />

  <Card title="Resources" icon="file-lines" href="/specification/2024-11-05/server/resources" />

  <Card title="Tools" icon="wrench" href="/specification/2024-11-05/server/tools" />
</CardGroup>
Feedback