Skip to content

Course · Copilot · Extending: MCP

Trust before it runs

You have added the service-registry entry to .vscode/mcp.json (the previous lesson covers how), and VS Code has noticed it. Notice what did not happen: it did not connect. An MCP server (a tool service speaking the Model Context Protocol) is a program - possibly one you did not write - about to reach into systems outside your editor on Copilot’s behalf. That is exactly the kind of thing that wants a deliberate yes in front of it, not a silent default.

When you add a server, or change its configuration, Copilot asks you to confirm trust before it starts that server (trust, the reset command, the sandbox keys, the client approval behavior, and the platform limits below are per the VS Code MCP documentation, verified October 2026). Until you do, the entry sits in the file inert. This is the same instinct as the diff-review habit from your first change: nothing reaches outside a boundary until a human keystroke says it can. Adding the config is describing a connection; trusting it is opening it.

If you ever want to revisit that decision - a server you stopped using, or one you trusted in a hurry - run “MCP: Reset Trust” from the Command Palette. It clears your trust decisions so the next start prompts you fresh. Cheap to reach for; reach for it whenever a server’s behavior surprises you.

Trusting the server to start is one gate. Tool-call approval depends separately on the client, your permission settings, the server, and organization policy. When Copilot wants to call a tool the registry exposes - “list the consumers of this library” - the client may ask for confirmation, auto-approve it, or apply a sandbox or policy rule. Check the tools list in your chat’s tool configuration instead of assuming every call behaves identically.

When confirmation is enabled, it is a useful default, because a tool call can have effects you did not anticipate. But confirmation behavior is configurable, and a trusted server may still be subject to different policies in different clients.

The sandbox: a controlled exception, with limits

Section titled “The sandbox: a controlled exception, with limits”

A server entry can declare a sandbox, which lets its tool calls run without per-call confirmation by constraining what they are allowed to do. Two keys govern it, with a hard limit on scope: sandboxing applies only to locally-running stdio servers (local processes VS Code launches), is not available on Windows, and on macOS and Linux constrains filesystem access and network domains through the sandbox rules.

  • sandboxEnabled - turns the sandbox on for that server.
  • sandbox - the rules the sandbox enforces, nested by area. filesystem.allowWrite lists the paths the server may write to, and filesystem.denyRead and filesystem.denyWrite list paths it may not read or write. network.allowedDomains lists the domains it may reach (wildcards such as *.example.com work), and network.deniedDomains lists domains it may not. Allow rules are honored only in a server’s own sandbox object; a top-level sandbox object beside servers and inputs accepts deny rules only. Reads are not boxed by default: nothing is denied unless you list it in filesystem.denyRead.

That stdio-only limit matters for the registry: it is an http server (a URL, not a local process), so it cannot be sandboxed. A sandbox candidate looks different - a local stdio server you run yourself, like a file-search helper launched with "command": "npx". Box that into “write only under these paths, reach that host, nothing else, and deny the sensitive paths it has no business reading,” and its calls can run unprompted inside those walls. The trade is explicit either way: you are swapping per-call confirmation for up-front constraints - the control principle in miniature, guidance traded for a boundary - and it only works where the server is a local process the sandbox can actually contain. A server that can write anywhere, or that runs remotely, is one you think harder about before lifting the confirmation prompt; and because reads are open unless you list paths in filesystem.denyRead, a local server that reads sensitive files (a .env reader, say) is one to keep behind per-call confirmation even sandboxed.

It is tempting to file all of this under “Agent,” since that is where you watch Copilot call the registry mid-loop. Do not. MCP is not agent-only. The tools, resources, and prompts a server contributes are available across chat and agents alike - the chat roles (Ask, Plan, Agent) can read a registry resource to answer a question, any role can pull in a server-provided prompt. What the Agent role adds is autonomy: it executes those tool calls itself, in a loop, which is why the confirmation gates matter most there. The trust model is general; it just bites hardest where Copilot is acting on its own.

You have a trusted server. But it is wired at the workspace level - every conversation in this repo can reach it. For a high-consequence library repo, that is broader than you want. Next: scope it to one agent.