VS Code MCP setup, and the one key that trips everyone up

by The Spinrun team3 min readGuides

Almost every MCP client copied Claude Desktop's configuration shape, which means almost every client reads its servers out of a mcpServers object.

VS Code does not. It uses servers.

That one word is the single most common reason a VS Code MCP setup silently does nothing: the file parses, the editor starts, and no tools appear, because nothing ever looked at the key you wrote.

The config

mcp.json
json
{
  "servers": {
    "spinrun": {
      "url": "https://spinrun.ai/mcp",
      "headers": {
        "x-spinrun-client": "vscode"
      }
    }
  }
}

The file lives in your user profile:

Where mcp.json lives
macOS   ~/Library/Application Support/Code/User/mcp.json
Linux   ~/.config/Code/User/mcp.json

Or run spinrun install vscode and let the CLI merge it in — it knows which key this client wants, which is exactly the detail worth not remembering.

There is no credential in that block. VS Code opens a browser the first time it connects to the gateway; you choose a workspace there and the tools appear.

What the header does

x-spinrun-client is attribution and carries nothing secret. It is what makes the activity log able to say a call came from VS Code, which matters the first time you are trying to work out which of your agents did something. The tools VS Code then sees are exactly the connections you can see, because it is authorized as you.

What you get

Your workspace's connected apps, as tools named TOOLKIT_VERB_OBJECTGITHUB_CREATE_ISSUE, JIRA_UPDATE_ISSUE, SLACK_SEND_MESSAGE. One HTTP endpoint, no local processes, no per-vendor OAuth application to register, and nothing to keep running on your machine.

Rules first, tools second

Set each app's policy before you point an agent at it. Three rows per app — read, write, destructive — each allowed or blocked, enforced at the gateway on every call rather than suggested to the model.

Blocked tools are not offered to VS Code at all, and are refused again if called anyway. The refusal names the rule, which turns "it didn't work" into "that is blocked and here is why".

A worked example

With a tracker and Slack connected:

Read the failing test in this file, open an issue describing it against the api repository, and drop the link in #eng.

VS Code reads the test from your working copy, and calls GITHUB_CREATE_ISSUE and SLACK_SEND_MESSAGE for the rest. The part that used to require three context switches is now one sentence.

If it still does not work

Three checks, in order:

  1. The key. servers, not mcpServers. It is nearly always this.
  2. The file. User profile, not workspace. A .vscode/mcp.json in a project is a different file with different scope.
  3. The restart. VS Code reads this at startup. Reload the window.

Next

Before anything loads

We would like to switch on analytics, so we can see which pages are worth keeping. Nothing has loaded yet and nothing will until you choose. The cookies that keep you signed in and remember your language are not part of this.

Read the Cookie Policy