Discover and run MCP servers
This issue is for tracking the initial work to support installing/discovering Model Context Protocol servers in VS Code. This does not involve much UI work yet. Other work around MCP should be tracked elsewhere under the chat-mcp tag.
Let's support MCP in VS Code. This issue tracks the following goals
- Allowing users to configure how those MCP servers are launched
- Discovering and installing servers
- Launching the MCP servers and maintaining their basic status
Configuring
In an ideal world, we would have something like DAP servers with deterministic, structured input the user can set to configure how the server is launched. Unfortunately, how you launch an MCP server is intrinsically linked to how you configure that server. That is likely to eventually change with things like server registries, but that's where we are today, so we cannot do much better than e.g. Cursor's experience with free-form argument and environment variables. (Though we could possibly use LM goodness to augment this.)
We will want MCP servers configurable both globally and per-workspace, so treating this like we do tasks make sense. Repos can have .vscode/mcp.json, and folks can also have an mcp section of their workspace, remote, or global settings.json. This would follow a similar form as Claude et al. already use, for example claude_desktop_config.json:
{
"mcpServers": {
"postgres-server": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "postgresql://localhost/mydb"],
"env": {}
}
}
}
As an augmentation, we should allow ${variable references}. In tasks.json and launch.json we already have inputs with password options, and so for mcp.json we would can do the same for secrets for example:
{
"servers": {
"postgres-server": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-postgres", "${input:postgresConnectionStr}"],
"env": {}
}
},
"inputs": [
{
"type": "promptString",
"id": "postgresConnectionStr",
"description": "Postgres connection string",
"default": "postgresql://localhost/mydb",
"password": true
}
]
}
Today tasks and debug only memoize the last input, but for MCP we should save it in the appropriate workspace/global/secret storage location and allow actions (e.g. code lenses) to edit saved values.
Discovery/Install
There are several ways MCP servers are installed today, often through 3rd party registries like Opentools or mcp-get. While we will certainly want MCP servers to eventually be installable via extensions for a number of reasons, we don't want this to be mandatory. Installers today often install to Claude Desktop locations by default, and it may make sense to natively read this and other common installation strategies so that MCP servers work across tools without extra fiddling.
In the above configuration model, MCP servers can be installed both globally and per-workspace. We should add a VS Code CLI command, such as code --add-mcp <json> [--workspace <path>], such that registry tools like npx opentools i can hook into VS Code and install them into settings in a deterministic way.
So in summary, the tools available for any given workspace are:
- Tools ambiently discovered on the machine (e.g. from
claude_desktop_config.json) (in the UI -- consent should be obtained before any of these servers are executed) - Tools referenced in the user's settings.json
mcpsection (in the UI) - Tools referenced in the remote's settings.json
mcpsection (in the remote workspace) - Tools referenced in the workspace
.vscode/mcp.json(in the remote workspace)
In the future we are also likely to allow for dynamic registration via an extension to work with cloud MCP registries (of which there are several already, like mcp.run).
Launching Servers
There is some discussion whether the MCP server processes would be owned by the extension host process, or the main process / remote server process. In either case we need to do some proxying as we cannot launch processes from the renderer, and eventual remote MCP connections would need to be owned either by the renderer or extension host to work in https://vscode.dev. It may also be necessary with extension-driven cloud registries. For this reason I favor the extension host, at least to get off the ground.
I think the actual handling of messages beyond JSON deserialization should happen in the renderer so that we can move things around easily -- again similar to DAP. We might optimize things if the MCP server being called is serving a request from the same extension host, but that's easy, and MCP is not very chatty compared to things like DAP, LSP, or fs/doc operations.
So excited 🎉
Couple thoughts/comments:
- Try out the MCP server installation method Cline uses. It's so seamless using the AI in agent mode to set up the JSON file, read the docs, verify the install by trying a command, etc. Much preferable over the Cursor MCP installation method where you basically have to figure it out on your own and no good way to verify it's actually working.
- Config in the repo is good 👍. Config in my user settings would also be good 👍. I work in a wonderful world where I have to hop between around 200 small repos. I don't want to have to copy past my MCP servers into every one of those.
@connor4312 this is amazing addition.
Was this developed for helping users connect MCP servers to their 3rd party agent extensions (such as CLINE, Roo, Aider, etc.) ?
As a CLINE user, I am trying to figure out if I should keep using their own MCP Marketplace?