Connecting Hosts

This content was generated with the assistance of AI and should be verified against the official documentation of each tool and framework before being relied on in production.

This section’s bibliography lists the reference material consulted while preparing these pages.

This page was written against the 2026-07-28 revision of the specification and the Claude Code documentation for version 2.1.283 (the commands below were executed with the locally installed Claude Code 2.1.267), VS Code 1.139.0, Claude Desktop and the GitHub Copilot cloud agent as documented on 2026-09-29 (see the Model Context Protocol baseline for the SDK versions). Host products change their menus and flags quickly, so the official pages are linked after every example; where a detail could not be confirmed in the official documentation, this page describes the behaviour without naming a flag.

A server is only useful once a host launches or contacts it, discovers its tools and shows them to a model. The servers on Python MCP Servers, Spring Boot MCP Servers and Node.js MCP Servers are the same docs-assistant on three stacks. This page connects them to the hosts a developer actually uses. The generic mechanics of MCP configuration in Claude Code and Copilot — the .mcp.json scopes, the flags of claude mcp add, VS Code’s mcp.json and the Copilot cloud agent JSON — are already documented on Configuring MCP Servers and are not repeated here; this page adds what is specific to docs-assistant: the three stacks over both transports, the protocol-era skew, authentication against the setup from Authorization, output limits, tool naming and troubleshooting.

The hosts and the endpoints

A host is the application the user talks to; it embeds one MCP client per configured server (see MCP Architecture). The client either starts the server as a child process and speaks over its standard streams (stdio) or POSTs to a URL (Streamable HTTP), as described on Transports.

flowchart LR subgraph Hosts CC[Claude Code] CD[Claude Desktop] VS[VS Code and Copilot Chat] CA[Copilot cloud agent] end subgraph Servers PY[Python docs-assistant] TS[TypeScript docs-assistant] JV[Java docs-assistant] end CC -- stdio --> PY CC -- Streamable HTTP --> JV CD -- stdio via config file --> TS VS -- stdio or HTTP --> PY VS -- HTTP --> JV CA -- local command or HTTP --> TS

The scratch builds used to write this page expose these endpoints. Replace the paths with your own; stdio entries must use absolute paths because the host does not start the process in your project directory.

Table 1. The docs-assistant endpoints used on this page
Stack stdio command Streamable HTTP URL Protocol revision spoken

Python (mcp 2.2.0 MCPServer)

python server.py

http://127.0.0.1:8000/mcp after python server.py streamable-http

2026-07-28, plus legacy clients

TypeScript (@modelcontextprotocol/server 2.2.0)

npx tsx src/stdio.ts

http://127.0.0.1:3000/mcp after npx tsx src/http.ts

2026-07-28, plus legacy clients

Java (Spring AI 2.0.1, Spring Boot 4.1.1)

java -jar docs-assistant.jar, built with the stdio starter and the stdio properties

http://127.0.0.1:8080/mcp after java -jar docs-assistant.jar

2025-11-25 only

The Java server is the outlier because the MCP Java SDK 2.0.x speaks only protocol 2025-11-25 (see Spring Boot MCP Servers). That is not a problem for a host: a host negotiates. Claude Code’s documentation says it asks HTTP servers whether they support the newer revision and falls back to the older one otherwise; when the scratch Python and Spring Boot servers were registered in a throw-away home directory, claude mcp list reported both as connected, so the same host talks 2026-07-28 to one and 2025-11-25 to the other with no configuration difference. The other hosts below document their MCP support on a per-feature basis rather than per revision; this page does not claim which revision each of them sends, so test the server you deploy against each host you support.

Claude Code

Claude Code is the host with the most complete MCP support: tools, resources (as @ mentions), prompts (as slash commands), OAuth and dynamic tool updates. This section shows the commands for docs-assistant; the flag reference and the scope precedence are on Configuring MCP Servers.

Adding the server with claude mcp add

The syntax is claude mcp add [options] <name> <commandOrUrl> [args…​]. Options such as --transport, --scope and --env come before the server name, and for stdio everything after the -- separator is the server command, passed untouched. Both transports for the three stacks:

Registering docs-assistant in Claude Code, per stack and transport
# Python, stdio (the host starts the process)
$ claude mcp add --transport stdio docs-assistant-py -- \
    /abs/path/.venv/bin/python /abs/path/server.py

# TypeScript, stdio
$ claude mcp add --transport stdio docs-assistant-ts -- \
    npx tsx /abs/path/src/stdio.ts

# Java, stdio (jar built with the stdio starter)
$ claude mcp add --transport stdio docs-assistant-java -- \
    java -jar /abs/path/docs-assistant.jar

# Python or TypeScript, Streamable HTTP (already running)
$ claude mcp add --transport http docs-assistant http://127.0.0.1:8000/mcp

# Java, Streamable HTTP, shared with the team through .mcp.json
$ claude mcp add --transport http --scope project docs-assistant-java http://127.0.0.1:8080/mcp

The --transport flag accepts stdio, http and sse (stdio is the default when it is omitted). sse is the deprecated HTTP+SSE transport; use http. Each of the commands above was run against a throw-away HOME and printed Added stdio MCP server …​ or Added HTTP MCP server …​. The stdio servers were started by claude mcp list itself, the HTTP ones needed the server running first. See Claude Code — Connect Claude Code to tools via MCP.

To pass configuration to a stdio server, repeat --env KEY=value before the name; keep credentials out of the command line and out of .mcp.json and let the server read them from the environment (the OAuth section below shows the alternative for HTTP).

Scopes and the .mcp.json it writes

The default scope is local (private to you in the current project), --scope project writes a .mcp.json at the repository root that is meant to be committed, and --scope user makes the server available in every project. With --scope project, the registration above produced exactly this file (paths shortened):

.mcp.json written by claude mcp add --scope project
{
  "mcpServers": {
    "docs-assistant-py": {
      "type": "stdio",
      "command": "/abs/path/.venv/bin/python",
      "args": ["/abs/path/server.py"],
      "env": {}
    },
    "docs-assistant-http": {
      "type": "http",
      "url": "http://127.0.0.1:8000/mcp"
    }
  }
}

Project-scoped servers are not started or contacted until you approve them: in the throw-away directory, claude mcp list showed both as Pending approval (run claude to approve) and did not launch the Python process. In non-interactive claude -p runs and in cloud sessions there is no prompt, so those runs load a project-scoped server without asking, which is one more reason never to commit a server that can change things without a review of the tools it exposes. Start interactive Claude Code once in the project to approve, or reset the decisions with claude mcp reset-project-choices. The official page gives the same scope table and the approval rule; see Claude Code — Connect Claude Code to tools via MCP.

Checking what is connected

claude mcp list health-checks every approved server and claude mcp get <name> shows one server. Output from the same session, with the scratch paths shortened:

claude mcp list and claude mcp get
$ claude mcp list
Checking MCP server health...

docs-assistant-ts: npx --prefix .../ts-server tsx .../src/stdio.ts - ✔ Connected
docs-http: http://127.0.0.1:8000/mcp (HTTP) - ✔ Connected
docs-java: http://127.0.0.1:8080/mcp (HTTP) - ✔ Connected
broken: /nonexistent/cmd  - ✘ Failed to connect - ENOENT: no such file or directory, posix_spawn '/nonexistent/cmd'

$ claude mcp get docs-http
docs-http:
  Scope: User config (available in all your projects)
  Status: ✔ Connected
  Type: http
  URL: http://127.0.0.1:8000/mcp

The three statuses the official page documents are connected, needs authentication and failed to connect; for a failure, claude mcp get adds an Issue: line with the HTTP status or error code and any text the server returned. Inside a session, /mcp lists the same servers, lets you authenticate and shows each server’s tools.

Signing in to a protected server

A docs-assistant protected as on Authorization answers an unauthenticated request with a 401 and a WWW-Authenticate challenge that points at its Protected Resource Metadata. Claude Code follows that discovery chain itself, so there is nothing to configure for an OAuth-capable server: the registration is the plain http command, and the server shows up as needing authentication. Two ways to run the browser login:

OAuth login in Claude Code
# inside a session
> /mcp
# select docs-assistant, choose to authenticate, finish the sign-in in the browser

# or from the shell
$ claude mcp login docs-assistant
$ claude mcp login docs-assistant --no-browser   # SSH sessions: print the URL instead
$ claude mcp logout docs-assistant               # clear the stored credentials

The client identity step of the authorization flow is automatic when the authorization server supports Dynamic Client Registration; the documentation states that Claude Code also supports servers that use a Client ID Metadata Document instead and discovers them automatically. When the authorization server offers neither, register a client yourself and give Claude Code the client id, a masked prompt for the secret and a fixed callback port for the redirect URI you registered:

Pre-registered OAuth client
$ claude mcp add --transport http \
    --client-id <client-id> --client-secret --callback-port 8080 \
    docs-assistant https://docs-mcp.example.com/mcp

--client-secret prompts for the value with masked input, or reads the MCP_CLIENT_SECRET environment variable for CI. These flags exist in claude mcp add --help of the installed 2.1.267 and are described on Claude Code — Connect Claude Code to tools via MCP. Tokens are then stored and sent by Claude Code on every request to that server only, consistent with the audience-binding and no-passthrough rules of the authorization page.

Output limits: MAX_MCP_OUTPUT_TOKENS

A tool result goes straight into the model’s context, so Claude Code caps it. According to the documentation, Claude Code prints a warning when a result exceeds 10,000 tokens and enforces a default limit of 25,000 tokens, which the environment variable MAX_MCP_OUTPUT_TOKENS changes for tools that do not declare their own limit; the string exists in the installed binary too. A tool can instead ask for a larger text limit itself through the anthropic/maxResultSizeChars key in its _meta on the tools/list result; that value then replaces the variable for that tool’s text content, up to a documented ceiling of 500,000 characters, and image data stays subject to MAX_MCP_OUTPUT_TOKENS.

Raising the limit for a whole session or for one tool
$ MAX_MCP_OUTPUT_TOKENS=50000 claude

How this relates to the structuredContent that docs-assistant returns: the official page describes the limits in terms of text content and images and says nothing specific about structuredContent. The SDKs, however, return the same result twice, once as a JSON text block in content (for hosts and models that only read text) and once as structuredContent (see the tools/call response on Transports), so a large result costs its size in the context whichever field the host feeds to the model. The practical rules are therefore the ones that make tool output small in the first place: cap top_k, return snippets and identifiers rather than whole pages, and let the model read a page through the docs://{component}/{page} resource on demand (see Tools, Resources, Prompts & Elicitation). Raise the limit for a tool only when large output is its purpose, for example a schema dump. The declaration is one line in each stack (each snippet was type-checked or run against the real package):

Declaring anthropic/maxResultSizeChars on a tool, three stacks
@mcp.tool(meta={"anthropic/maxResultSizeChars": 200000})
def get_schema(component: str) -> str:
    ...
server.registerTool(
    'get_schema',
    {
        description: 'Return the schema of one component',
        inputSchema: z.object({ component: z.string() }),
        _meta: { 'anthropic/maxResultSizeChars': 200000 }
    },
    async ({ component }) => ({ content: [{ type: 'text', text: component }] })
);
public class LargeResult implements MetaProvider {
    @Override
    public Map<String, Object> getMeta() {
        return Map.of("anthropic/maxResultSizeChars", 200000);
    }
}

@McpTool(name = "get_schema", description = "Return the schema of one component",
         metaProvider = LargeResult.class)
public String getSchema(@McpToolParam(description = "component") String component) { ... }

The Python signature MCPServer.tool(…​, meta=…​) and the Python output [\{'anthropic/maxResultSizeChars': 200000}] of list_tools() were checked by running it; the TypeScript _meta option compiled with tsc --noEmit against @modelcontextprotocol/server 2.2.0; the Java metaProvider attribute and the MetaProvider.getMeta() interface were read from the spring-ai-mcp-annotations 2.0.1 jar and the class compiled with javac. Whether Claude Code honours the key is documented on the Claude Code MCP page; the key is a Claude Code convention, not part of the MCP specification, and other hosts ignore it.

How the tools are named

Every tool of a connected server appears to Claude Code as mcp<server-name><tool-name>, with characters outside letters, digits, underscore and hyphen replaced by underscores. Registered as docs-assistant, the search tool is mcpdocs-assistantsearch_docs. Permission rules, hook matchers and allowed-tools lists use that name; the rule syntax is on Configuring MCP Servers and Permissions & Settings. The name of the server you chose in claude mcp add is therefore part of every permission rule: renaming docs-assistant-http to docs silently detaches the rules written for the old name. Because search_docs is declared read-only (see Tools, Resources, Prompts & Elicitation), it is a reasonable candidate for an allow rule, but remember that the annotation is a hint from the server that the host does not verify; the decision to trust a server is yours. Resources of the server become @ mentions and its prompts slash commands, so answer_with_citations is available as a command once the server is connected.

Claude Desktop

Claude Desktop has two separate ways to add a server, and they are not interchangeable. This section was checked against the MCP project’s connect guides and Anthropic’s support article on custom connectors on 2026-09-29.

Local servers are listed in claude_desktop_config.json. Open the Claude menu, choose Settings, then Developer, and click Edit Config; that creates the file if it does not exist. It lives at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS and %APPDATA%\Claude\claude_desktop_config.json on Windows. The file uses the same mcpServers object as .mcp.json, and the app must be quit and restarted to load a change. A stdio docs-assistant looks like this:

claude_desktop_config.json
{
  "mcpServers": {
    "docs-assistant": {
      "command": "/abs/path/.venv/bin/python",
      "args": ["/abs/path/server.py"]
    }
  }
}

Use absolute paths, as the troubleshooting list of the official guide stresses. The host writes each server’s standard error to mcp-server-<name>.log and connection problems to mcp.log in ~/Library/Logs/Claude on macOS and %APPDATA%\Claude\logs on Windows, so a print() to stdout that corrupts the protocol shows up there. See MCP — Connect to local MCP servers.

Remote servers are not added to that file. Anthropic’s support article states that remote MCP servers must be added through Settings and Connectors, using Add and then Add custom connector, where you enter the server URL and optionally an OAuth client id and secret under advanced settings; the same article says custom connectors are available on the Free, Pro, Max, Team and Enterprise plans and that servers configured in claude_desktop_config.json are separate and are not available on claude.ai or in Cowork. The MCP guide walks through the same dialogs and adds that, once connected, the server’s resources and prompts are attached from the "Add files, connectors, and more" menu and its tools can be enabled or disabled per tool. The pages read do not say whether a http://127.0.0.1 URL is accepted for a custom connector, so develop against the stdio entry and use the connector dialog for a deployed HTTPS endpoint (see Deploying Remote Servers). See MCP — Connect to remote MCP servers and Get started with custom connectors using remote MCP.

Claude Code can also import Desktop’s local servers with claude mcp add-from-claude-desktop, which the CLI and the official page both restrict to macOS and Windows Subsystem for Linux.

VS Code and GitHub Copilot

VS Code (1.139.0 here) reads MCP servers from .vscode/mcp.json in the workspace, from the user profile (command MCP: Open User Configuration) and, in the portable format, from a .mcp.json at the workspace root. The important difference from Claude Code is the top-level key: VS Code’s own file uses servers (Claude Code’s .mcp.json and Claude Desktop use mcpServers), and it adds a top-level inputs array for values VS Code should ask the user for instead of storing them. An input of type promptString with "password": true masks what is typed, and a server references it as $\{input:<id>}.

.vscode/mcp.json for the three docs-assistant builds
{
  "inputs": [
    {
      "type": "promptString",
      "id": "docs-gateway-key",
      "description": "API key of the docs-assistant gateway",
      "password": true
    }
  ],
  "servers": {
    "docs-assistant-py": {
      "type": "stdio",
      "command": "${workspaceFolder}/.venv/bin/python",
      "args": ["${workspaceFolder}/server.py"]
    },
    "docs-assistant-ts": {
      "type": "stdio",
      "command": "npx",
      "args": ["tsx", "${workspaceFolder}/src/stdio.ts"]
    },
    "docs-assistant-java": {
      "type": "http",
      "url": "http://127.0.0.1:8080/mcp"
    },
    "docs-assistant-gateway": {
      "type": "http",
      "url": "https://docs-mcp.example.com/mcp",
      "headers": {
        "X-Api-Key": "${input:docs-gateway-key}"
      }
    }
  }
}

The keys used above are the documented ones: type is stdio (with command, and optionally args, cwd, env, envFile and a dev object with file-watch patterns) or http/sse (with url, optional headers and an optional oauth object whose clientId is required when present). The http entry for OAuth-protected servers needs no header at all: for a server configured with OAuth, the documentation says VS Code runs the flow and opens a browser on the first connection. ${workspaceFolder} is substituted in paths. VS Code supports tools, resources, prompts (invoked as /<server>.<prompt>) and MCP Apps, and it offers MCP: Add Server and MCP: List Servers commands, which start, stop and show the log of each server. Verify the JSON with a linter before committing it: a stray comma makes VS Code ignore the whole file. The example above was parsed with jq.

Whether VS Code sends the 2026-07-28 revision or the previous one is not stated on the pages read; as with every host above, test it against the server you deploy. See VS Code — Add and manage MCP servers in VS Code and VS Code — MCP configuration reference. The same file format is described from the Copilot side on Configuring MCP Servers.

The Copilot cloud agent

The Copilot cloud agent runs unattended in a GitHub-hosted environment, so its MCP configuration is stricter than the interactive hosts. A repository administrator sets it in the repository’s Settings, under Copilot and then MCP servers; the page has an MCP configuration box and a Save MCP configuration button that validates the JSON. (The menu label in the current GitHub documentation is MCP servers, not "Cloud agent".) The format is the mcpServers object with three rules that differ from the other hosts:

  • every server needs a tools array, either the names of the tools the agent may call or "*"; allowlist only the read-only ones, because the agent calls them without asking

  • type is local, stdio, http or sse, and local servers take command and args, remote ones url

  • values that come from secrets must be named with the COPILOT_MCP_ prefix, stored as Agents secrets or variables, and referenced as $COPILOT_MCP_<NAME> or $\{COPILOT_MCP_<NAME>}; the documentation adds that they live in the copilot environment

Copilot cloud agent — MCP configuration for docs-assistant
{
  "mcpServers": {
    "docs-assistant": {
      "type": "local",
      "command": "python",
      "args": ["server.py"],
      "env": {
        "DOCS_DB_URL": "$COPILOT_MCP_DOCS_DB_URL"
      },
      "tools": ["search_docs"]
    }
  }
}

This example starts the Python build from the repository checkout, which only works if the environment the agent runs in has the dependencies installed; the GitHub page describes how to customise that environment and it is not repeated here. DOCS_DB_URL is a hypothetical setting of a real vector store behind search_docs, supplied by the secret rather than by the file.

Two limits matter for docs-assistant. First, GitHub states that the cloud agent and Copilot code review support only MCP tools: the resource template docs://{component}/{page} and the prompt answer_with_citations are invisible to it, so anything the agent must be able to do has to be reachable through a tool, which is one reason docs-assistant keeps search_docs self-contained. Second, GitHub states that remote MCP servers that use OAuth for authorization are not supported, so the OAuth-protected deployment of Authorization cannot be reached directly from the cloud agent; run a local server as above or front the endpoint with something that authenticates without an interactive OAuth flow, and keep that credential read-only. The other rules of the platform, such as the default GitHub and Playwright servers, are on the official page; see GitHub Copilot — Configure MCP servers for your repository.

Pointers to other hosts

Two more families of hosts speak MCP, and their setup belongs to other AI sub-sections rather than here. Microsoft Copilot Studio can connect an existing MCP server to an agent from a tool’s Model Context Protocol wizard (or through a custom connector) and supports the Streamable transport, with authentication of none, an API key, or OAuth 2.0; Microsoft 365 Copilot declarative agents use MCP servers through plugins built with the Microsoft 365 Agents Toolkit. Both are covered in the Conversational Channels sub-section. ElevenLabs agents accept custom MCP servers by URL, with an optional secret and headers, over SSE or Streamable HTTP transport, and offer approval modes for tool use; that is covered in ElevenLabs Agents. For any of these, deploy docs-assistant as an HTTPS endpoint as described on Deploying Remote Servers and give it the least privilege the agent needs. See Copilot Studio — Connect your agent to an existing MCP server, Plugins for Microsoft 365 Copilot and ElevenLabs — Model Context Protocol.

Choosing a host

Table 2. The hosts side by side
Claude Code Claude Desktop VS Code and Copilot Chat Copilot cloud agent

Configuration

claude mcp add, .mcp.json, ~/.claude.json

claude_desktop_config.json for local, Settings > Connectors for remote

.vscode/mcp.json or user profile

Repository Settings > Copilot > MCP servers

Top-level key

mcpServers

mcpServers

servers (plus inputs)

mcpServers, with a required tools per server

Transports

stdio, http, sse

stdio (file), remote URL (connector)

stdio, http, sse

local/stdio, http, sse

Secrets

environment variables, OAuth login

connector OAuth fields; environment in the file

$\{input:id} prompts, envFile

COPILOT_MCP_ secrets in the copilot environment

OAuth to the server

yes, /mcp or claude mcp login

yes, in the connector dialog

yes, automatic browser flow

no for remote servers

Tools, resources, prompts

all three

all three

all three, plus MCP Apps

tools only

Approval

per tool through permission rules

per tool in the app

per tool in the chat

none: the tools allowlist is the control

Troubleshooting a server that does not appear

Work from the outside in; each step below names the command that would show the failure.

  • Nothing listed at all. In Claude Code a project-scoped server stays Pending approval until you run claude in the project and accept it (claude mcp list says so); in Claude Desktop the file is read only at start-up, so quit the app completely and reopen it; VS Code shows servers only from a file it parsed, so check the JSON and the key (servers, not mcpServers, in .vscode/mcp.json).

  • Listed, but failed to connect. Run claude mcp get <name>: an ENOENT …​ posix_spawn issue means the command or path is wrong (use absolute paths), ConnectionRefused means the HTTP server is not running or the port differs. Run the stdio command by hand in a terminal and pipe it a request; a server that prints anything to standard output other than protocol messages, such as a startup banner or a print(), corrupts the stream (see Transports).

  • Failed on start-up only. Claude Code allows a server up to the greatest of 60 seconds, the applicable tool timeout and MCP_TIMEOUT, so a slow npx or uvx first run can be given longer with MCP_TIMEOUT=<milliseconds>; a Spring Boot jar or a Python import of a large model is the usual reason.

  • Needs authentication. The server answered 401. Run /mcp or claude mcp login <name>. If the login loops or the authorization server rejects the client, revisit client identity and iss handling on Authorization; if the registration needs a fixed redirect URI, pass --callback-port.

  • Connected, but the tools are missing or renamed. Look at the exact mcp<server><tool> name in /mcp and compare it with your permission rules. For the Copilot cloud agent, the tools array is the allowlist, so a tool missing from it is not available to the agent.

  • Connected to the wrong protocol behaviour. Claude Code lets you force the behaviour: the documentation names MCP_SDK_GENERATION to choose between its v1 and v2 runtimes (v2 from Claude Code 2.1.232) and MCP_PROTOCOL_NEGOTIATION set to auto or legacy to control whether it asks servers for the newer revision. The names exist in the installed binary; use them only to reproduce a problem, not as a permanent setting.

  • Reproduce without the host. The MCP Inspector CLI and the contract script on Testing & Debugging speak to the server without any host in between; if they pass and the host fails, the fault is in the host configuration.

Where to go next