Skip to content

Using Servers in Real Hosts

Most servers are used from hosts someone else built. Knowing how those hosts are configured tells you what your server must look like from the outside: what command launches it, what URL it lives at, which environment variables it reads, and how a user authorizes it.

flowchart LR
  cc["Claude Code"]
  cd["Claude Desktop"]
  api["Claude API MCP connector"]
  insp["MCP Inspector"]
  s["Your MCP server"]
  cc --> s
  cd --> s
  api --> s
  insp --> s
Four hosts, one server

Claude Code adds servers with claude mcp add. The transport flag chooses how it connects.

Terminal window
# Remote server over Streamable HTTP
claude mcp add --transport http notion https://mcp.notion.com/mcp
# Remote server with a static bearer token
claude mcp add --transport http secure-api https://api.example.com/mcp \
--header "Authorization: Bearer your-token"
# Local server over stdio — everything after -- is the launch command
claude mcp add --transport stdio airtable --env AIRTABLE_API_KEY=YOUR_KEY \
-- npx -y airtable-mcp-server

The -- separator is not optional for stdio servers: it tells the CLI where its own options end and the server command begins.

ScopeWhere it is storedShared?Use for
local (default)~/.claude.json, per projectNoPersonal experiments
project.mcp.json at the repo rootYes, via gitTeam-wide servers
user~/.claude.jsonNoTools you want in every project
Terminal window
claude mcp add --transport http stripe --scope project https://mcp.stripe.com
claude mcp add --transport stdio db --scope user -- npx -y @bytebase/dbhub

A project-scoped server is just an entry in .mcp.json. Commit it so teammates get the same servers. Values support ${VAR} and ${VAR:-default} expansion in command, args, env, and headers, so secrets stay out of the repo.

{
"mcpServers": {
"shared-server": {
"type": "http",
"url": "https://example.com/mcp"
},
"local-db": {
"type": "stdio",
"command": "/path/to/server",
"args": ["--config", "${CLAUDE_PROJECT_DIR}/config.json"],
"env": {
"DB_URL": "${DB_URL:-postgresql://localhost}"
},
"timeout": 600000
}
}
}

Remote servers that require OAuth are authorized interactively: run /mcp inside a session and follow the browser flow, or claude mcp login <name> from the shell. Pre-registered client credentials can be supplied with --client-id and --client-secret (read from MCP_CLIENT_SECRET in CI).

Terminal window
claude mcp list # every configured server and its status
claude mcp get notion # details for one server
claude mcp remove notion # drop it

Useful knobs: MCP_TIMEOUT=10000 claude bounds server startup in milliseconds; a per-server timeout in .mcp.json bounds tool execution; MAX_MCP_OUTPUT_TOKENS caps how much tool output is fed to the model. Inside Claude Code your tools appear as mcp__<server>__<tool> — the same namespacing idea from the previous lesson, which is what you use in permission rules and hooks.

Claude Desktop reads claude_desktop_config.json (macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\). The shape is the same mcpServers map.

{
"mcpServers": {
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/Users/me/Desktop"]
}
}
}

Restart the app after editing. Because Desktop launches stdio servers itself, the command must be on the app’s PATH — a common failure is a server that works in your shell but not from the app because node lives in a version-manager directory.

If you are calling the Messages API directly, you can let Anthropic make the MCP connection server-side instead of running a client yourself. Two request parts are required together: mcp_servers declares the connection, and an mcp_toolset entry in tools exposes it to the model. Omitting either half is a validation error, and every server must be referenced by exactly one toolset.

import Anthropic from '@anthropic-ai/sdk';
const anthropic = new Anthropic();
const response = await anthropic.beta.messages.create({
model: 'claude-opus-5',
max_tokens: 16000,
betas: ['mcp-client-2025-11-20'],
mcp_servers: [
{ type: 'url', url: 'https://mcp.example.com/mcp', name: 'example-mcp', authorization_token: process.env.EXAMPLE_TOKEN },
],
tools: [
{
type: 'mcp_toolset',
mcp_server_name: 'example-mcp',
default_config: { enabled: false }, // allowlist mode
configs: { get_weather: { enabled: true } }, // only this tool is exposed
},
],
messages: [{ role: 'user', content: 'What is the weather in Bangkok?' }],
});

The connector only reaches servers that are publicly routable over HTTPS, and the loop runs on Anthropic’s side — you get the final message, not per-call hooks. Use it when you want the least code; run your own client when you need consent prompts, local stdio servers, or custom routing.

While developing, the MCP Inspector is the fastest host to point at your server. It runs your server, shows every tool, resource, and prompt, and logs the raw protocol traffic.

Terminal window
# Web UI against a local stdio server
npx @modelcontextprotocol/inspector node build/index.js
# Web UI against a remote server
npx @modelcontextprotocol/inspector --server-url https://api.example.com/mcp --transport http
# CLI mode for scripts and CI
npx @modelcontextprotocol/inspector --cli node build/index.js --method tools/list

It negotiates both protocol eras, so it is also how you check that a legacy-only client would still work against your server. The testing lesson goes deeper.

  • Publish the launch command. Hosts need a one-liner; npx -y your-package or uvx your-package is the convention.
  • Read secrets from the environment on stdio; never ask for them as tool arguments.
  • Expose a stable HTTPS URL for remote use and implement the OAuth resource-server side so /mcp in Claude Code can authorize it.
  • Keep tool names header-safe and unique, because hosts will prefix them and may mirror them into HTTP headers.
In "claude mcp add --transport stdio db -- npx -y @bytebase/dbhub", what does the -- do?
Which scope stores the server in .mcp.json so it can be committed and shared with a team?
What happens if you pass mcp_servers to the Claude API without a matching mcp_toolset in tools?
What is the default_config: { enabled: false } pattern in an mcp_toolset for?