Guide · IDE Integration
MCP server in VS Code
Visual Studio Code added built-in MCP support in version 1.99 (April 2025). MCP servers configured in .vscode/mcp.json or user settings are automatically discovered, their tools are surfaced in GitHub Copilot Chat's agent mode, and the Output panel shows every JSON-RPC message exchanged. This guide covers the complete setup path: configuration file format, HTTP vs stdio transport, tool invocation in Copilot Chat, API key injection with input variables, debugging protocol failures, and why your MCP server needs external monitoring when VS Code users depend on it.
TL;DR
Create .vscode/mcp.json in your workspace root with a servers object. Each entry is a named server with type ("http" or "stdio") and a URL or command. Restart VS Code (or use MCP: Restart Server from the Command Palette) — Copilot Chat will discover the server's tools automatically via tools/list and make them available in agent mode. View the raw protocol exchange in the Output panel: View → Output → MCP: <server-name>. Add AliveMCP monitoring so you know when the server is unreachable before developers start filing Copilot issues.
How VS Code discovers MCP servers
VS Code checks two locations for MCP server configurations, in priority order:
| Location | Scope | Committed to git? | Typical use |
|---|---|---|---|
.vscode/mcp.json | Workspace | Yes — shared with team | Project-specific MCP servers all contributors use |
settings.json (mcp.servers key) | User | No — in ~/.config/Code/User/ | Personal MCP servers used across all workspaces |
Workspace-scoped servers in .vscode/mcp.json only activate when the workspace is trusted (the VS Code Workspace Trust prompt). User-scoped servers in settings.json are available in all trusted workspaces. If the same server name appears in both locations, the workspace configuration wins.
VS Code sends an initialize JSON-RPC request to each configured server at startup and when you run MCP: Restart Server. It then calls tools/list to build the tool registry. The registry is refreshed automatically when the server sends a notifications/tools/list_changed notification — you do not need to restart VS Code when your tool list changes dynamically.
.vscode/mcp.json configuration format
The configuration file uses a servers object where each key is the server's display name in VS Code:
{
"servers": {
"my-api-tools": {
"type": "http",
"url": "https://api.example.com/mcp"
},
"local-dev-server": {
"type": "http",
"url": "http://localhost:3000/mcp"
},
"local-stdio-server": {
"type": "stdio",
"command": "node",
"args": ["/path/to/my-mcp-server/dist/index.js"],
"env": {
"NODE_ENV": "development"
}
}
}
}
The configuration supports three server types:
| Type | Required fields | When to use |
|---|---|---|
"http" | url | Remote or locally hosted MCP servers with HTTP/SSE transport. The server must implement the MCP Streamable HTTP transport (POST /mcp, GET /mcp for SSE). VS Code sends a single Content-Type: application/json POST for each request and handles SSE event streams on the GET channel. |
"stdio" | command, optionally args and env | Local MCP servers launched as subprocesses. VS Code starts the process, communicates over stdin/stdout with JSON-RPC newline-delimited messages, and kills the process when the workspace closes. |
"sse" | url | Legacy SSE-only transport (MCP spec before 2024-11-05). Use "http" for servers implementing the current Streamable HTTP transport instead. |
For HTTP servers, VS Code includes Mcp-Session-Id headers on subsequent requests after the initialize handshake completes. Your server must echo the session ID in its response headers and route subsequent requests to the correct session context.
Injecting secrets with input variables
Do not put API keys in .vscode/mcp.json — the file is committed to git. VS Code supports input variable substitution that prompts the developer once and stores the value in the VS Code secret storage:
{
"inputs": [
{
"id": "my-api-key",
"type": "promptString",
"description": "API key for my-api-tools server",
"password": true
}
],
"servers": {
"my-api-tools": {
"type": "http",
"url": "https://api.example.com/mcp",
"headers": {
"Authorization": "Bearer ${input:my-api-key}"
}
},
"local-stdio-server": {
"type": "stdio",
"command": "node",
"args": ["dist/index.js"],
"env": {
"DATABASE_URL": "${input:database-url}",
"API_SECRET": "${input:my-api-key}"
}
}
}
}
When VS Code first connects to a server that uses ${input:…} variables, it prompts the developer for each value. The values are stored in VS Code's encrypted secret storage (via OS keychain on macOS/Windows, libsecret on Linux). Subsequent workspace opens do not re-prompt unless the developer clears the secrets via MCP: Clear Stored Inputs from the Command Palette.
For stdio servers, the environment variables set in env are merged with VS Code's process environment. This means PATH, HOME, and other shell environment variables are available to the subprocess without explicit configuration — useful if your stdio server spawns child processes that need the developer's shell environment.
Tool invocation in Copilot Chat agent mode
MCP tools appear in GitHub Copilot Chat when the chat is in agent mode. You enter agent mode by selecting the @workspace agent or choosing a custom MCP server agent from the agent picker. The tool selection dropdown (the tools button, with a tools-wrench icon) shows all tools from all connected MCP servers.
Copilot Chat invokes MCP tools by generating a tools/call JSON-RPC request with the tool name and arguments derived from the LLM's response. The tool call goes through an approval step:
| Approval mode | Setting | Behavior |
|---|---|---|
| Always ask | Default | VS Code shows the tool name + arguments inline in the chat; developer clicks "Allow" or "Block" before execution. |
| Allow for this session | Click "Always Allow" on first prompt | Subsequent calls to the same tool in the same VS Code session run without prompting. Resets on VS Code restart. |
| Always allow | Click "Always Allow" then "Remember" | Permanently stored in user settings — the tool runs without prompt across all future sessions. Use only for read-only tools you fully trust. |
Tools that modify files, call external APIs, or run shell commands should never be permanently allowed — the approval step is the last line of defense before an LLM-generated tool call executes. Your MCP server should implement tool annotations (readOnlyHint, destructiveHint) to signal the risk level to VS Code, which uses them to color-code the approval prompt.
The tool result is returned inline in the chat thread. If your tool returns content with type: "text", VS Code renders the text in the chat message. If the tool returns type: "image", VS Code renders the image inline. If the tool sets isError: true, Copilot Chat shows the error and may ask the user to try a different approach.
Debugging MCP connections in VS Code
The Output panel shows every JSON-RPC message exchanged with each MCP server. Open it with View → Output, then select MCP: <server-name> from the dropdown:
[2026-06-26 14:23:01] → initialize
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"clientInfo": { "name": "Visual Studio Code", "version": "1.99.3" },
"capabilities": { "sampling": {} }
}
}
[2026-06-26 14:23:01] ← initialize
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"protocolVersion": "2024-11-05",
"serverInfo": { "name": "my-api-tools", "version": "1.2.0" },
"capabilities": { "tools": {} }
}
}
[2026-06-26 14:23:01] → notifications/initialized
{}
[2026-06-26 14:23:01] → tools/list
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list"
}
[2026-06-26 14:23:01] ← tools/list
{
"jsonrpc": "2.0",
"id": 2,
"result": {
"tools": [
{ "name": "search_docs", "description": "Search documentation", "inputSchema": { ... } },
{ "name": "create_issue", "description": "Create a new issue", "inputSchema": { ... } }
]
}
}
Common failure patterns visible in the Output panel:
| Symptom in Output panel | Root cause | Fix |
|---|---|---|
Connection timeout / no response after initialize | Server unreachable (wrong URL, port not open, CORS blocking preflight) | Test the URL directly with curl; check CORS headers on OPTIONS response |
tools/list returns empty array | Tool registration code throws during startup; tools registered after initialize completes | Add startup logging; register tools synchronously before calling server.connect() |
initialize returns wrong protocolVersion | Server running old MCP SDK version not updated to 2024-11-05 | Update @modelcontextprotocol/sdk to latest; check version field in initialize response |
Tool call returns isError: true with empty message | Tool handler threw without returning an MCP error — SDK converted unhandled exception to isError | Add try/catch in tool handler; return { content: [{ type: "text", text: error.message }], isError: true } |
| SSE stream closes immediately after connection | Server not implementing the GET /mcp SSE endpoint; returning 404 or 405 | Implement the SSE stream endpoint; VS Code uses GET for server-to-client notifications |
For stdio servers, the Output panel also shows stderr from the subprocess. Print diagnostic messages to stderr from your MCP server — they appear in the VS Code Output panel without interfering with the JSON-RPC protocol on stdout.
Per-workspace vs user-level server configuration
Choosing between workspace and user scope affects security, portability, and sharing:
| Consideration | Workspace (.vscode/mcp.json) | User (settings.json) |
|---|---|---|
| Shared with team via git | Yes — all contributors get the same servers | No — personal to the developer's machine |
| Workspace trust required | Yes — untrusted workspaces skip MCP | No — user servers always available in trusted workspaces |
| Secrets in config | Use input variables only — file is in git | Can use literal values — file is not shared, but still plaintext on disk |
| Server tied to repo | Yes — obvious which server belongs to which project | No — must be general-purpose enough for all workspaces |
The recommended pattern for team-shared MCP servers: put the server URL and non-secret configuration in .vscode/mcp.json (committed), and use ${input:…} variables for any secrets. Each developer is prompted once for their API key; after that, VS Code handles credential storage securely.
Add .vscode/mcp.json.example to your repository showing the structure with placeholder values, and add .vscode/mcp.json to .gitignore only if the configuration itself contains workspace-specific values that differ per developer. If all developers use the same server URL, commit the file.
Monitoring MCP servers that VS Code users depend on
When a team commits an MCP server to .vscode/mcp.json, every developer who opens the workspace connects to that server. A server outage silently breaks Copilot Chat tool calls for the entire team — VS Code shows "Tool unavailable" in the chat without explaining that the server is down vs the tool failed.
The monitoring gap is similar to Lambda: VS Code only sees the tool call result, not the underlying protocol health. An MCP server can return HTTP 200 on a liveness endpoint while the initialize handshake fails or tools/list returns an empty array — VS Code users experience this as "Copilot can't find any tools" with no error message.
AliveMCP runs a full protocol probe — initialize + tools/list + a sentinel tool call — every 60 seconds. When the probe fails, you get an alert before the first developer opens their VS Code and wonders why their MCP tools stopped working. Add your MCP server URL to AliveMCP monitoring as part of your server's deployment checklist.
Frequently asked questions
Which VS Code version added MCP support?
VS Code 1.99 (released April 2025) added the initial MCP server integration. The .vscode/mcp.json configuration format and the MCP Output panel were included in this release. Earlier versions do not support MCP — developers on VS Code <1.99 need to update before MCP tools appear in Copilot Chat.
Do I need GitHub Copilot to use MCP tools in VS Code?
Yes. MCP tool invocation in VS Code is exposed through GitHub Copilot Chat's agent mode. VS Code discovers and connects to MCP servers without Copilot, but the tool call surface is only available in the Copilot Chat panel. You cannot invoke MCP tools directly from the VS Code command palette without Copilot.
How do I restart an MCP server without restarting VS Code?
Open the Command Palette (Ctrl+Shift+P / Cmd+Shift+P) and run MCP: Restart Server. VS Code shows a list of connected servers — select the one to restart. This closes the existing connection, re-reads the configuration, and re-connects. For stdio servers, VS Code kills and restarts the subprocess. For HTTP servers, VS Code re-sends initialize to the same URL.
Can I connect to an MCP server running on localhost during development?
Yes. Set "type": "http", "url": "http://localhost:3000/mcp" and VS Code connects to your local server. VS Code does not apply the same-origin restriction for MCP connections. If you're developing the MCP server, use the Output panel to watch live protocol messages as you add tools. Note: if the local server is not running, VS Code logs a connection error and continues — it does not block VS Code startup.
What CORS headers does my MCP server need for VS Code?
For HTTP-type MCP servers, VS Code sends requests from an extension-host origin (not a browser origin). Strict CORS enforcement from the server side is not required — VS Code's extension host is not subject to browser CORS restrictions. However, if your MCP server also serves browser clients, configure CORS headers correctly for browser use cases. The extension host MCP connection is not affected either way.
How do I share MCP server configuration with my team without exposing API keys?
Use the inputs array in .vscode/mcp.json with "password": true for sensitive values. The file is safe to commit because it contains only the input variable reference (${input:api-key}), not the actual value. Each developer is prompted once when they first open the workspace and VS Code stores the value in OS-level secure storage. If a developer's key rotates, they use MCP: Clear Stored Inputs to clear cached values and are re-prompted on the next connection.
Further reading
- MCP server with GitHub Copilot — agent mode tool configuration
- MCP server in Cursor — configuring MCP tools for AI-assisted development
- MCP server in Claude Desktop — claude_desktop_config.json setup
- MCP server in Zed editor — context_servers configuration
- MCP tool annotations — readOnlyHint, destructiveHint, and idempotentHint
- Debugging MCP servers — protocol traces, inspector, and runtime errors
- AliveMCP — continuous protocol monitoring for MCP servers