Where to see a connected server's tools in Claude Desktop
To verify a local server connected, click the "Add files, connectors, and more" indicator at the bottom-left of the conversation input box, hover "Connectors", click "Manage connectors", and select the server (e.g. "filesystem") to list its available tools.
Claude Desktop must be fully quit and restarted after config changes
After saving claude_desktop_config.json, you must completely quit Claude Desktop and relaunch it; the application only loads new server configuration and spawns the MCP server processes at startup.
claude_desktop_config.json file location on macOS and Windows
Claude Desktop's MCP server configuration file lives at `~/Library/Application Support/Claude/claude_desktop_config.json` on macOS and `%APPDATA%\Claude\claude_desktop_config.json` on Windows. It is created or opened via Settings > Developer tab > "Edit Config" button, where Settings is reached from the Claude menu in the OS menu bar (not the in-window settings).
Claude exposes remote server resources and prompts through the attachment menu, not automatically
In Claude, after a remote server connects, its resources and prompts are reached via the 'Add files, connectors, and more' control at the bottom-left of the message input, then hovering 'Connectors' and the specific server entry to open the attachment menu. Resources and prompts are user-selected attachments rather than being injected automatically, so a server that only exposes resources/prompts will appear to 'do nothing' until the user picks them from that menu.
Clients can disable individual tools, so a tool may be invisible even though the server advertises it
Claude lets users enable or disable specific tools per connector, set usage limits, and configure other security parameters from the Connectors settings by clicking the connected server. If a tool never gets called, check that it has not been disabled in the client's per-connector tool permission settings before debugging the server.
Clients that support MCP (ecosystem)
MCP is an open protocol supported by a wide range of clients: AI assistants such as Claude and ChatGPT, and development tools such as Visual Studio Code (Copilot chat MCP servers), Cursor, and MCPJam. The stated goal is 'build once and integrate everywhere', though each client's feature support differs.
Tools in sampling require the sampling.tools client capability
Servers can request tool use during sampling by including a tools array and an optional toolChoice field in the sampling request. Those tool definitions are scoped to that sampling request and need not correspond to tools the server exposes. Clients declare support through the sampling.tools capability, and servers must not send tool-enabled sampling requests to clients that have not declared it.
Protocol does not mandate UI patterns for resources
Resources are application-driven: the client application decides how to retrieve, process and present context, using tree/list browsing views, search and filter interfaces, automatic context inclusion or smart suggestions, and manual or bulk selection. The protocol does not mandate specific UI patterns, so a server should not assume any particular resource-picker behaviour in a host application.
Per-request protocol version via _meta key io.modelcontextprotocol/protocolVersion
In MCP 2026-07-28, every request declares the protocol version it uses via the `io.modelcontextprotocol/protocolVersion` key inside the request's `_meta` field, and the server accepts or rejects each request independently. There is no single up-front handshake that locks the version.
MCP protocol version format YYYY-MM-DD
MCP protocol versions are string identifiers in the format `YYYY-MM-DD`, where the date indicates the last date backwards-incompatible changes were made. The version is NOT incremented for backwards-compatible updates, so a single version string can cover incremental improvements.
Revision states: Draft, Current, Final
MCP spec revisions are marked as Draft (in progress, not ready for consumption), Current (ready for use and may still receive backwards compatible changes), or Final (past, complete specifications that will not change). The current protocol version is 2026-07-28.
Deprecated and Removed feature states
Individual MCP features may be marked Deprecated under the feature lifecycle policy: they remain in the spec but are scheduled for removal, must document a migration path (or state none is needed), and remain for at least twelve months (or at least ninety days under the expedited-removal exception) before becoming eligible for removal, after which they may be Removed in a future revision. Currently deprecated features are listed in the deprecated features registry at /specification/2026-07-28/deprecated.
Clients and servers MAY support multiple protocol versions
MCP clients and servers MAY support multiple protocol versions simultaneously; version support is negotiated per request rather than fixed for a connection.
Backward compatibility with handshake-based revisions 2025-11-25 and earlier
Protocol revisions 2025-11-25 and earlier used an initialization/handshake-based version negotiation. For interoperability between the per-request negotiation of 2026-07-28 and those older handshake-based clients and servers, follow the Backward Compatibility section at /specification/2026-07-28/basic/versioning#backward-compatibility-with-initialization-based-versions.
Pick an SDK tier based on protocol-feature risk
When choosing an SDK for a server or client, prefer Tier 1 SDKs (TypeScript, Python, C#, Go) if you need the newest protocol features or the broadest feature completeness; Tier 2 (Java, Rust, Ruby) and Tier 3 (Swift, PHP, Kotlin) SDKs carry weaker guarantees on feature completeness, protocol support, and maintenance, which can cause a server to lag behind the current spec.
UnsupportedProtocolVersionError (-32022) and server/discover
To verify protocol compatibility, call `server/discover` to see which protocol versions a server supports. An UnsupportedProtocolVersionError with code -32022 lists the server's supported versions in its `data` field.
Absolute paths in claude_desktop_config.json server args
In `claude_desktop_config.json`, pass absolute paths rather than relative ones, e.g. {"mcpServers": {"filesystem": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/username/data"]}}} instead of a relative path like `./data`.
Checking connected servers in Claude Desktop UI
In Claude Desktop, click the "Add files, connectors, and more" plus icon in the chat input, then hover over the Connectors menu to see connected MCP servers and their available tools.
Inspector negotiates legacy vs modern protocol eras
The Inspector supports protocol-era negotiation, distinguishing legacy operation from modern (protocol version 2026-07-28) operation; every tab of the Inspector changes behaviour between protocol eras. This matters when a server works in one client and not another because of protocol version differences.
Roots can only be given durably via the config file
The Inspector CLI has no roots flag; `--method roots/set` applies only to that one short-lived connection. The config file is the only durable way to give a run its roots. Roots configured for a server are advertised at connect time, so a server that calls `roots/list` (as `@modelcontextprotocol/server-filesystem` does, to learn its allowed directories) receives them.
Inspector catalog/config file format is the mcpServers config shape
A catalog or config file uses the familiar MCP client config shape, an `mcpServers` object keyed by server name, with per-server Inspector settings alongside. A stdio entry uses `command`, `args`, and `env`; a remote entry uses `"type": "http"`, `url`, plus optional `protocolEra`, `modernLogLevel`, `headers` (object), and `roots` (array of `{ "uri": "file:///...", "name": "..." }`). Fields equal to their default are omitted when the Inspector writes the file back, keeping diffs minimal; `protocolEra` defaults to `legacy` and `modernLogLevel` defaults to `debug`.
Import an existing client config into the Inspector
You do not have to hand-write Inspector catalog files: the web client can import an existing client config from Claude Desktop, Cursor, Cline, or VS Code, or a registry `server.json`.
Invalid x-mcp-header names must cause the tool to be dropped from tools/list
If a tool's `x-mcp-header` annotation names an invalid header (for example `"Bad Header"`, where the space makes it an invalid RFC 9110 token), a conforming client MUST drop that tool from `tools/list`. This is a common cause of a tool being invisible in one client; the Inspector shows such tools struck through under an 'Excluded (SEP-2243)' divider with the reason on hover instead of silently hiding them.
MCP protocol eras: legacy vs modern (2026-07-28)
The 2026-07-28 MCP revision changed the protocol substantially, splitting behavior into two 'protocol eras': legacy (before that revision) and modern (as of 2026-07-28). Protocol era is orthogonal to transport, so the same HTTP URL can be spoken to as a legacy server or a modern one.
protocolEra setting values: legacy, auto, modern
MCP Inspector servers carry a `protocolEra` field with values `legacy`, `auto`, or `modern`. `legacy` is the default and sends a plain `initialize` with no probing. `auto` probes `server/discover` first and falls back to `initialize` on any non-modern outcome. `modern` pins exactly protocol version `2026-07-28` with no fallback, so a non-modern server fails loudly. The setting lives in Server Settings in the web client, and as the `protocolEra` field in a catalog or config file used by the CLI and TUI.
Why not auto-probe server/discover by default
Auto-probing is not the default for a debugging client because a `server/discover` probe stalls against silent legacy stdio servers and pollutes the recorded transcript. Opting into `auto` or `modern` should be a deliberate act.
Server-to-client requests (server.elicitInput) fail on 2026-07-28
The legacy pattern of a server calling `server.elicitInput` to elicit input errors on a 2026-07-28 connection, because server-to-client requests are not allowed there. Multi-round tool results (MRTR) are the modern replacement.
Browser SDK skips Mcp-Param-* mirroring, causing -32020 HeaderMismatch
The MCP SDK skips `Mcp-Param-*` header mirroring in browser environments due to an internal environment check, so calling a mirrored tool from a web client omits the header and a strict server answers `-32020` (HeaderMismatch). The same tool called from a Node-based CLI or TUI mirrors correctly. This is a classic 'works in one client, not another' failure.
Import existing client configs into the Inspector
On the Inspector's Servers screen, **Add Servers** can import MCP servers already configured elsewhere: it parses Claude Desktop, Cursor, Cline, and VS Code client configs directly, and also reads a server's own MCP Registry `server.json`. Import merges into the active catalog so existing entries aren't clobbered.
--config serves a foreign client config read-only
To test against another client's config without modifying it, launch e.g. `mcp-inspector --config ~/Library/Application Support/Claude/claude_desktop_config.json`. `--config` guarantees the file is served as-is and is never written, seeded, or migrated.
Protocol era setting for HTTP servers (legacy/modern/auto)
For an HTTP MCP server the Inspector has a protocol era setting whose default is `legacy`; set `modern` or `auto` in Server Settings (or `protocolEra` in the catalog file) to exercise the 2026-07-28 protocol behavior. Testing both eras is recommended for HTTP servers.
CORS must expose Mcp-Session-Id header
Browser-based MCP clients cannot read the session id unless the HTTP server's CORS configuration exposes it. In Express this is `app.use(cors({ origin: '*', exposedHeaders: ['Mcp-Session-Id'] }))`. Forgetting `exposedHeaders` is a classic reason a server works in one client (e.g. a local CLI) but fails in a browser-hosted client.
Adding an HTTP MCP server in VS Code
In Visual Studio Code, press Cmd+Shift+P, choose 'MCP: Add server...', select HTTP, and enter the URL such as `http://localhost:3000`. The resulting `mcp.json` entry looks like `"my-mcp-server-18676652": { "url": "http://localhost:3000", "type": "http" }`. On connect the browser opens a consent prompt for the `mcp:tools` scope, after which tools appear above the server entry in mcp.json and can be invoked in chat using the `#` sign.
Testing an MCP App: Claude custom connector or basic-host
To see an MCP App render you need a host that supports MCP Apps. Claude web and Claude Desktop support them: tunnel the local server with `npx cloudflared tunnel --url http://localhost:3001` and add the generated URL under Settings > Connectors > Add custom connector (custom connectors require a paid Pro, Max or Team plan). Alternatively use the test host in the ext-apps repo at `examples/basic-host`, started with `SERVERS='["http://localhost:3001/mcp"]' npm start` and opened at `http://localhost:8080`.
MCP Apps is an extension; host support varies
MCP Apps is an extension to the core MCP specification rather than part of it, so host support varies by client. As documented, it is supported by Claude, Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, and Archestra.AI. A client matrix page lists extension support across clients. A server relying on MCP Apps will render interactively in these hosts and fall back to plain output elsewhere.
Extensions are opt-in and vary by client
MCP extensions such as Enterprise-Managed Authorization are opt-in and never active by default; support varies by client, and the client matrix at /extensions/client-matrix tracks implementation status. Enterprise-Managed Authorization typically also requires client-level configuration by the organization's IT team in addition to the MCP client application supporting it.
MCP extensions are opt-in and vary by client
Support for MCP extensions such as OAuth Client Credentials varies by client and extensions are never active by default — they are always opt-in. Consult the MCP client matrix (/extensions/client-matrix) for current implementation status before relying on an extension working across clients.
Auth extensions are never active by default and need client support
Both MCP authorization extensions require explicit support from the MCP client and are never active by default; support varies by client, so consult the client matrix before relying on them. This is a common cause of a server working with one client and failing with another.
Official MCP extension identifiers (MCP Apps, OAuth Client Credentials, Enterprise Auth)
Three official MCP extensions and their identifier strings: MCP Apps = `io.modelcontextprotocol/ui` (interactive HTML interfaces rendered inline in the conversation); OAuth Client Credentials = `io.modelcontextprotocol/oauth-client-credentials` (machine-to-machine auth without interactive user login); Enterprise-Managed Authorization = `io.modelcontextprotocol/enterprise-managed-authorization` (centralized access control via an enterprise IdP).
Clients supporting the MCP Apps (io.modelcontextprotocol/ui) extension
Clients listed as supporting the MCP Apps extension (`io.modelcontextprotocol/ui`) are: Claude (web), Claude Desktop, VS Code GitHub Copilot, Microsoft 365 Copilot, Goose, Postman, MCPJam, ChatGPT, Cursor, Archestra.AI, and PostHog Code. Only Archestra.AI is additionally listed as supporting Enterprise-Managed Authorization; no listed client supports the OAuth Client Credentials extension.
MCP extensions are opt-in and negotiated via `extensions` capability field
Official MCP extensions are always opt-in: a client only uses an extension if BOTH the client and the server declare support for it in the `extensions` field of their capabilities. If either side omits the identifier, the extension must not be used.
Auth extension support is tracked separately from core MCP authorization (DCR, CIMD)
Support for the auth extensions (OAuth Client Credentials and Enterprise-Managed Authorization) is tracked separately from core MCP authorization features such as Dynamic Client Registration (DCR) and CIMD. A client supporting core MCP auth does not imply support for these extensions; check each extension specification and the modelcontextprotocol/ext-auth repository for current implementation status.
Tasks capability negotiation: client _meta vs server/discover
Tasks use standard MCP extension negotiation. The client declares support per request by putting `io.modelcontextprotocol/tasks: {}` inside `_meta.io.modelcontextprotocol/clientCapabilities.extensions` of each request's params. The server advertises the same extension key under `capabilities.extensions` in the result of `server/discover`.
Never return a task to a client that did not declare support
Before returning a `CreateTaskResult`, a server must verify the client included `io.modelcontextprotocol/tasks` in its per-request capabilities. Returning a task to a client that did not declare support is a compatibility error and will break that client.
Task support varies by host client
Because MCP Tasks is an extension to the core specification, host support varies by client and requires explicit opt-in from both client and server; the documentation points to an extensions client matrix page for which clients support which extensions.
MCP clients validate the MCPB fileSha256 hash before install
The MCP Registry does not validate the `fileSha256` value declared for an MCPB package, but MCP clients DO validate the hash before installation to ensure file integrity, and downstream registries may add their own validation. A wrong or stale hash therefore passes registry publishing but breaks installation in clients.
Protocol version negotiation and UnsupportedProtocolVersionError
The client declares the version it speaks per request in `io.modelcontextprotocol/protocolVersion`, and the server's `server/discover` response lists accepted versions in `supportedVersions`. If a server does not support the requested version it rejects the request with an `UnsupportedProtocolVersionError` listing the versions it does support, and the client retries with a mutually supported version.
Sampling and logging deprecated in 2026-07-28
As of protocol version 2026-07-28 the client primitives Sampling (`sampling/createMessage`, delivered via the Multi Round-Trip Requests pattern) and Logging are deprecated. New implementations should integrate directly with LLM provider APIs instead of sampling, and should log to `stderr` (stdio transport) or use OpenTelemetry instead of protocol logging.
listChanged capability gates tools list_changed notifications
`notifications/tools/list_changed` is only sent to clients that requested "toolsListChanged": true in their `subscriptions/listen` filter, and it is only available from servers that declared "listChanged": true inside their `tools` capability in the `server/discover` response. A server that omits listChanged cannot deliver tool-list change notifications.
Capabilities exist so unsupported operations are never attempted
The client declares its capabilities in `io.modelcontextprotocol/clientCapabilities` on every request and the server returns its own `capabilities` object from `server/discover`. This tells each party which primitives the other can handle (tools, resources, prompts) and whether change notifications are available, so unsupported operations are never attempted. Client identity (`clientInfo`) and server identity (`serverInfo`) exist for debugging and compatibility.
Use Streamable HTTP; SSE is deprecated but can be published for legacy clients
Remote MCP servers should use the Streamable HTTP transport. The SSE transport is deprecated, so a `"sse"` remote should only be published to support existing (older) clients. A server can support both transports simultaneously at different URLs, for example streamable-http at https://analytics.example.com/mcp and sse at https://analytics.example.com/sse, listed as two entries in `remotes`.
remotes and packages can coexist in server.json
The `remotes` property can coexist with the `packages` property in one `server.json`, letting MCP host applications choose the preferred installation method. A packages entry uses `registryType` (e.g. "npm"), `identifier` (e.g. "@example/email-integration-mcp"), `version`, and a `transport` object such as {"type": "stdio"}, while the remotes entry offers a hosted streamable-http URL.
Registry aggregator version comparison rules
MCP Registry aggregators SHOULD interpret versions as semantic versions when possible and compare using these rules: if one version is marked "latest", treat it as later; if both are valid semver, use semver comparison; if neither is valid semver, compare published timestamps; if one is valid semver and the other is not, treat the semantic version as later.
Extension breaking changes: use capability flags or a new identifier
Extensions evolve independently of the core protocol and updates do not require core maintainer review. To change an extension, prefer capability flags or versioning inside the extension settings object rather than minting a new extension identifier; only if a breaking change is unavoidable should a new identifier be used (e.g. `io.modelcontextprotocol/my-extension-v2`). A breaking change is any modification that makes existing implementations fail or behave incorrectly, including removing or renaming fields, changing field types, altering semantics of existing behavior, and adding new required fields.
MCP extension identifier format vendor-prefix/extension-name
MCP extensions are optional additions to the spec, identified by a unique extension identifier of the form `{vendor-prefix}/{extension-name}`, for example `io.modelcontextprotocol/oauth-client-credentials`. Identifiers follow the same rules as `_meta` keys but the vendor prefix is mandatory. Official extensions use the `io.modelcontextprotocol` vendor prefix; third-party extensions should use a reversed domain name the author owns (a company owning example.com uses `com.example/my-extension`) to avoid collisions.
Extensions are disabled by default and require explicit opt-in
MCP extensions are always disabled by default and require explicit opt-in from the developer. SDKs may choose to implement extensions but it is not required for protocol conformance; SDK maintainers have full autonomy over which extensions they support, and SDK documentation should list the supported extensions.
Graceful degradation when the other side lacks an extension
If one side supports an extension and the other does not, the supporting side must either fall back to core protocol behavior or reject the request with an appropriate error if the extension is mandatory. Practical guidance: a server offering UI-enhanced tools should still return meaningful text content for clients without the UI extension, while a server requiring a specific authentication extension may reject connections from clients that do not support it. Documenting the expected fallback behavior in the extension spec is recommended.
Official MCP extensions: auth, apps, tasks
Official MCP extensions live in the modelcontextprotocol GitHub organization in repositories prefixed with `ext-`. Current ones include: `ext-auth` (OAuth Client Credentials for machine-to-machine authentication, and Enterprise-Managed Authorization for centralized access control), `ext-apps` / MCP Apps (lets servers display interactive UI elements such as charts, forms and video players inline within conversations), and MCP Tasks (asynchronous task execution for long-running operations with polling, mid-flight input and durable handles). Experimental extensions live in repositories prefixed with `experimental-ext-`.
Absolute paths and executable resolution in claude_desktop_config.json
Always pass absolute paths to your server in claude_desktop_config.json (get them with `pwd` on macOS/Linux or `cd` on Windows Command Prompt). On Windows use double backslashes (`\\`) or forward slashes in JSON paths. You may need the full path to the launcher executable in the `command` field, obtainable via `which uv` on macOS/Linux or `where uv` on Windows.
Example Claude Desktop entries for Python, Node, Java, .NET, Ruby servers
Python/uv: `"command": "uv", "args": ["--directory", "/ABSOLUTE/PATH/.../weather", "run", "weather.py"]`. TypeScript: `"command": "node", "args": ["/ABSOLUTE/PATH/.../weather/build/index.js"]`. Spring AI Java: `"command": "java", "args": ["-Dspring.ai.mcp.server.stdio=true", "-jar", "/ABSOLUTE/PATH/.../mcp-weather-stdio-server-0.0.1-SNAPSHOT.jar"]`. Kotlin shadow jar: `"command": "java", "args": ["-jar", ".../build/libs/weather-0.1.0-all.jar"]`. C#: `"command": "dotnet", "args": ["run", "--project", "/ABSOLUTE/PATH/TO/PROJECT", "--no-build"]`. Ruby: `"command": "bundle", "args": ["exec", "ruby", "weather.rb"], "cwd": "/ABSOLUTE/PATH/.../weather"`.