Repository navigation
VS Code
This page describes the contract between Visual Studio Code (VS Code) and the vscode bridge. VS Code 1.140 has two MCP clients, and both can speak revision 2025-11-251. The executable mcp-bridge-vscode is a stdio server for these clients, and it speaks revision 2026-07-282 to the upstream server. For each requirement of VS Code, the page tells what the bridge does and which milestone added it. It also gives the configuration, the sign-in, the embedded bridge and the steps to find the cause of a problem. The Translation Rules page gives the rule for each message.
Applies to: zig-bridge-sdk 0.1.2 on main at commit 3cbc34b (the tag v0.1.2), with the milestones M0 to M6. The vscode bridge, VS Code 1.140.0, and zig-sdk 0.5.1 at commit 52fc1b8.
VS Code 1.140.0 has two MCP clients. Both get the same launch configuration from mcp.json.
| Client | Protocol | Notes |
|---|---|---|
| Local harness (the workbench client) | Revision 2025-11-25 only. It starts each connection with initialize, and it has no fallback to a later revision. |
It operates the MCP user interface of VS Code: the server list, the prompts, the resources, the mcp-resource: editors and the Output channel. A zig-sdk server on stdio answers its initialize with -32601. This client is the primary target of the bridge. |
| Copilot harness (the Agent Host, Copilot runtime 1.0.89-7 and later) | It sends server/discover first. After the error -32601, it sends initialize of revision 2025-11-253. It also sends initialize after a time limit, or after a discover result that it cannot use4. |
Copilot runtime 1.0.89 has no support for multi round-trip requests (MRTR). Thus it can list and call the tools of a zig-sdk server, but a tool that asks for input cannot complete. Copilot runtime 1.0.95 has MRTR5, and the manual check of 2026-10-12 observed the rounds on the modern path. |
The setting chat.defaultToCopilotHarness is false by default, and VS Code enables the Copilot harness by experiment. In VS Code 1.142, the chat input has a harness picker with the entries Copilot, Local, Cloud, Claude and Codex. The command code-insiders chat starts a new chat with the Copilot harness. VS Code marks the Local harness as "will be removed in a future release". The issue for revision 2026-07-28 in VS Code is open, with the label agent-host-mitigated6.
The Local harness needs these properties of a server7. The last column names the milestone that added the behavior of the bridge.
| Item | What VS Code needs | What the bridge does | Milestone |
|---|---|---|---|
| R1 |
initialize gets a result with a capabilities object, and serverInfo with name and version. Without capabilities.tools, VS Code never sends tools/list. VS Code ignores the protocolVersion of the result. |
The bridge makes the result from the server/discover result of the upstream server. Without server information from the upstream server, serverInfo has the value of --name and the version of the bridge. The default name is the file name of the upstream command without its extension, or the host of the URL. VS Code makes the ids of the tools and the keys of the tool approvals from this name. |
M1, M4 |
| R2 | VS Code sends notifications/initialized. logging/setLevel can come before it. |
The bridge answers ping and logging/setLevel itself in each state. notifications/initialized opens the listen stream when the initialize result declares a list change or resources.subscribe. |
M1, M3 |
| R3 | The requests have no _meta keys for the protocol version or the capabilities. The _meta of tools/call has only progressToken (a unique string), vscode.conversationId, vscode.requestId, traceparent and tracestate. A list request has {} and an optional cursor. |
The client of zig-sdk adds the _meta keys of revision 2026-07-28 to each upstream request. The progress token stays with the bridge. From M3, traceparent, tracestate and the vscode. keys go to the upstream server. |
M1, M3 |
| R4 | No nextCursor: null. VS Code asks for the next page while the result has nextCursor, also when its value is null. |
A list result loses each nextCursor that is not a string. |
M1 |
| R5 | A tools/call result has a content array. |
The bridge adds an empty content array when the upstream result has none. |
M1 |
| R6 | VS Code examines the tool schemas two times. (a) The workbench checks each inputSchema against JSON Schema draft-07. It omits each tool that fails, and it shows a Warning notification. (b) The Copilot extension stops each chat request when a schema has type: "array" and an items value that is false in JavaScript8. One such schema stops the requests for all tools of all servers. |
For check (b), the bridge gives items: {} to each such array schema of a tool inputSchema. It writes one log line with the JSON pointer of the schema. It also removes each outputSchema whose root does not have type: "object". The bridge does not do check (a). |
M1 |
| R7 | Stdout has only JSON-RPC lines, and the logs go to stderr9. The server exits soon after the end of stdin. | The bridge writes only JSON-RPC messages to stdout and each log line to stderr. At the end of stdin, it stops in a bounded time. The section The stop sequence of VS Code gives the steps. | M1 |
| R8 | VS Code never gets the input_required result of revision 2026-07-2810. |
The bridge does the rounds of each InputRequiredResult itself. VS Code gets only a complete result or an error. |
M2 |
| Item | What the harness does | What the bridge does | Milestone |
|---|---|---|---|
| C1 | Its first frame can be server/discover11, before initialize. |
The bridge answers server/discover with -32601 at once, in each state, and never sends it to the upstream server. Before the initialize result, a request with the _meta key io.modelcontextprotocol/protocolVersion also gets -32601. Then the bridge accepts the initialize that comes next. |
M1 |
| C2 | Its initialize comes from copilot-cli with revision 2025-11-25. It declares sampling and elicitation with form and url, but no roots. |
The bridge declares the same input kinds to the upstream server. The upstream server then refuses a roots input request with -32021, and the bridge forwards the error. | M1, M2 |
| C3 | Each request has _meta.progressToken: 0. |
The integer 0 is a valid progress token. The bridge keys the progress by request, and never by token. | M1 |
| C4 | A late answer to server/discover can come before an initialize on the same connection. |
The bridge answers server/discover at once, without the upstream server. Thus no late answer occurs. |
M1 |
A zig-sdk server that calls vscode.serveStdio answers server/discover itself by default. The section The Copilot harness and an embedded server gives the details.
VS Code uses these features when the server declares them.
| Item | What VS Code does | What the bridge does | Milestone |
|---|---|---|---|
| O1 |
elicitation/create in form mode and in URL mode12. A URL elicitation needs an elicitationId, and VS Code reads notifications/elicitation/complete. |
The bridge sends each elicitation of the upstream server to VS Code. A URL elicitation gets an elicitationId, and VS Code gets notifications/elicitation/complete after the step. From M4, a sign-in after notifications/initialized is also a URL elicitation. |
M2, M4 |
| O2 |
sampling/createMessage, with the consent of the user, and only text, image and audio content. VS Code ignores tools and includeContext. A refusal of the user gives the error -32000. |
The bridge refuses a sampling request with tools, toolChoice or a tool content block, because VS Code declares no sampling.tools. It does not examine includeContext. The error of VS Code goes to the original request. |
M2 |
| O3 |
roots/list gives the folders of the workspace as file:// URIs. |
The upstream server gets only the roots with a file:// URI. |
M2 |
| O4 | Progress with the token of VS Code. | The progress of all rounds goes to VS Code with the token of VS Code. Without a token, the bridge drops the progress. | M1 |
| O5 |
logging/setLevel and notifications/message. |
The bridge sends the level with each upstream request. VS Code gets each log message of the upstream server at that level or above. | M3 |
| O6 |
notifications/tools/list_changed and notifications/prompts/list_changed. VS Code then lists the tools or the prompts again. |
A listen stream13 gets the list changes of the upstream server, and the bridge sends them to VS Code. notifications/resources/list_changed also goes to VS Code. |
M3 |
| O7 |
resources/subscribe and notifications/resources/updated. VS Code never sends resources/unsubscribe. |
The bridge adds the URI to the listen stream, and it sends each update of the resource to VS Code. It also accepts resources/unsubscribe. |
M3 |
| O8 | VS Code sends completion/complete also when the server does not declare completions. |
The bridge always forwards completion/complete. An upstream error, for example -32601, goes to VS Code without a change. |
M1 |
| O9 | Prompts and resources. | The bridge forwards prompts/list, prompts/get, resources/list, resources/templates/list and resources/read. From M2, prompts/get and resources/read can ask for input. |
M1, M2 |
| O10 |
instructions, title and the annotations. An icon with a data: URI always shows. VS Code drops the http: and https: icons of a stdio server, and it gives file: icons the trust of a local process. |
The initialize result keeps instructions, and the title, icons, description and websiteUrl of the server information. The results keep the annotations and the icons. For an upstream server at a URL, VS Code gets only the icons with a data: URI. |
M1, M4 |
| O11 | MCP Apps14: the extension io.modelcontextprotocol/ui and the ui:// resources. |
The extensions go to the two sides, except the Tasks extension. structuredContent stays as the upstream server sent it, because an app reads it. A ui:// resource reaches VS Code with resources/read. |
M1 |
Revision 2026-07-28 has no ping from the server to the client, and the bridge never sends one. The bridge answers each ping of VS Code with {}.
| Quirk of VS Code | What the bridge does | Milestone |
|---|---|---|
When VS Code gets notifications/cancelled, it cancels its own request with that id. The ids of VS Code and of the upstream server are integers, thus they can be equal. |
VS Code never gets a notifications/cancelled of the upstream server. The requests of the bridge to VS Code have string ids, such as b-1. |
M1, M2 |
VS Code ignores a notifications/cancelled for a request that it got. After a stop in the chat, a question of the bridge can stay open, and VS Code can answer late. An approved sampling request still runs the model. |
The bridge sends the cancellation. Then it drops the late answer with only a debug line. | M2 |
| VS Code cannot show a form without a property, for example a confirmation. | VS Code gets a form with the message of the server and one choice, Continue. | M2 |
VS Code does not examine the accepted content of a form against its schema. It also opens URLs with other schemes, such as file:, vscode: and command:. |
The bridge examines the content before the upstream server gets it. It sends a URL to VS Code only with https, or with http and a loopback host. |
M2 |
VS Code opens each URL in the data.elicitations of the error -32042. |
An upstream -32042 becomes -32603 without data. |
M1 |
| After a tool call, VS Code lists the tools again before the next turn only when the list change comes before the result15. | On a stdio upstream, the bridge writes the events of the listen stream in the order of the upstream server, relative to the responses. On an upstream server at a URL, the listen stream and the call are different HTTP requests, and the bridge gives no such order. | M3 |
VS Code lists the tools and the prompts at the same time as notifications/initialized. A change before the first listen stream reaches no stream. |
After the acknowledgment of the first stream, VS Code gets one list change for each declared list. | M3 |
VS Code reads the updates of a resource only after the response to resources/subscribe. |
The response {} goes out before the first update of the new listen stream. |
M3 |
VS Code 1.140 marks .vscode/mcp.json "Deprecated" in Add Server. It also reads .mcp.json and $COPILOT_HOME/mcp-config.json. |
The examples on this page use .vscode/mcp.json, because only this file has password inputs for secrets. |
M1 (documentation) |
VS Code does not give its setting http.proxy to a stdio server. |
The URL form reads the proxy from HTTPS_PROXY, ALL_PROXY and NO_PROXY. |
M4 |
The bridge never starts the upstream server again itself. When the upstream command stops and stdin is still open, the bridge writes an error line to stderr. It answers each request in flight with -32603 and the message of the cause upstream_exited. Then it exits with code 1. VS Code shows "Process exited with code 1", and it starts the bridge again at the next tool call.
An upstream server at a URL never stops the bridge. A failed request gets an error, and the next requests continue.
VS Code stops a stdio server in these steps16. The bridge stops itself before the first signal of VS Code.
| Time after the end of stdin | What VS Code does | What the bridge does |
|---|---|---|
| 0 s | VS Code closes the stdin of the bridge. | The bridge starts its watchdog. It cancels each request in flight without a response, a sign-in that waits for the browser, and the listen stream. After at most shutdown_grace (2 s), it stops the upstream server and exits with code 0. |
| 8 s | Nothing. | If the bridge did not stop yet, the watchdog writes a warning line. On POSIX, it sends a kill signal to the process group of the upstream server. Then the bridge exits with code 0. |
| 10 s | On POSIX, VS Code sends a termination signal to each process of the tree. On Windows, VS Code does nothing at this time. | The bridge has no handler for the termination signal. |
| 20 s | VS Code stops the process tree by force. | Nothing. |
A second stop of VS Code stops the tree at once. The exit of the bridge cancels the kill that VS Code planned. When the extension host removes the handler, for example at a reload, VS Code clears its timers, and no kill follows. On Windows, the job object of libuv still stops the bridge. On POSIX, nothing stops it. Thus the bridge has its own watchdog.
To stop the upstream server, zig-sdk closes its stdin. A child process that does not exit in shutdown_grace gets a termination signal, and after one more shutdown_grace a kill signal. On POSIX, the signals go to the process group of the child process. On Windows, the job object of the child process stops the other processes of the job.
This file is the primary configuration of VS Code. Put the entry in .vscode/mcp.json of the workspace, or in the user configuration with MCP: Open User Configuration17:
{
"servers": {
"demo": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["--", "/abs/path/your-server", "--its-args"]
}
}
}The -- separates the options of the bridge from the upstream command. The bridge starts that command and speaks revision 2026-07-28 to it over stdio. It does not examine the arguments after --. Write absolute paths. This file accepts the variables of VS Code, such as ${workspaceFolder}. On Windows, write each backslash two times, for example "C:\\tools\\mcp-bridge-vscode.exe".
VS Code writes the full command line to the Output channel at the Debug level, and the process list of the host also shows it. Thus never write a secret in args. Give the secret to the upstream server in an environment variable from a password input:
{
"inputs": [
{ "type": "promptString", "id": "api-key", "description": "API key", "password": true }
],
"servers": {
"demo": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["--", "/abs/path/your-server"],
"env": { "API_KEY": "${input:api-key}" }
}
}
}The upstream server gets the environment of the bridge. The bridge writes no environment values to its log. The Copilot harness does not get the entries of .vscode/mcp.json that use ${input:...} or ${command:...}.
VS Code 1.140 also reads these files:
| File | Key of the servers | Variables |
|---|---|---|
.mcp.json in the root of the workspace |
mcpServers |
No ${...} variables and no inputs. Write absolute paths. |
$COPILOT_HOME/mcp-config.json |
mcpServers |
No ${input:...} variables. |
{
"mcpServers": {
"demo": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["--", "/abs/path/your-server", "--its-args"]
}
}
}The copilot command line tool and the Copilot harness also read these files. Never write a secret in a .mcp.json file that you commit to a repository.
With a URL in place of -- and a command, the bridge connects to the upstream server at the URL over Streamable HTTP18 (M4). The URL uses https, or http with a loopback host, for example localhost, a name that ends in .localhost, an address in 127.0.0.0/8 or [::1]. The URL has no user information and no fragment.
{
"servers": {
"remote": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["https://mcp.example.com/mcp"]
}
}
}When the server accepts a static token, give the token in an environment variable from a password input. Then name the variable with --header-env. Never write the token in args.
{
"inputs": [
{ "type": "promptString", "id": "api-auth", "description": "Authorization header", "password": true }
],
"servers": {
"remote": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["--header-env", "Authorization=API_AUTH", "https://mcp.example.com/mcp"],
"env": { "API_AUTH": "Bearer ${input:api-auth}" }
}
}
}A static Authorization header disables the sign-in. Without such a header, the bridge signs in when the server asks for it. The section Sign-in and accounts gives the rules.
The client secret of a pre-registered client comes from the environment variable MCP_BRIDGE_CLIENT_SECRET. A client with a secret needs --client-issuer, so that the secret goes only to that authorization server:
{
"inputs": [
{ "type": "promptString", "id": "client-secret", "description": "Client secret", "password": true }
],
"servers": {
"remote": {
"type": "stdio",
"command": "/abs/path/mcp-bridge-vscode",
"args": ["--client-id", "my-client", "--client-issuer", "https://auth.example.com", "https://mcp.example.com/mcp"],
"env": { "MCP_BRIDGE_CLIENT_SECRET": "${input:client-secret}" }
}
}
}The oauth keys of an HTTP server entry in mcp.json, for example clientId and enterpriseManaged, do not apply to the stdio entry of the bridge. The bridge does not support the enterprise-managed authorization of VS Code. When a server accepts a static token, use --header-env with Authorization in its place.
The command line has these forms:
mcp-bridge-vscode [options] -- <command> [args...]
mcp-bridge-vscode [options] <url>
mcp-bridge-vscode logout [--account <label>] <url>
mcp-bridge-vscode logout --all
These options apply to each form that serves VS Code:
| Option | What it does | Default |
|---|---|---|
--name <name> |
The server name for VS Code when the upstream server sends no name. | The file name of <command> without its extension, or the host of <url>
|
--log-level <level> |
The level of the log lines on stderr: err, warn, info or debug. |
info |
--discover-timeout <s> |
The time in seconds for the answer of the upstream server to server/discover. |
60 |
--max-line-bytes <n> |
The maximum length of one message in bytes, from 1 to 1073741824 (1 GiB). The limit applies to the messages of VS Code and of a stdio upstream server. | 67108864 (64 MiB) |
--help |
Show the usage on stdout. | |
--version |
Show the version on stdout. |
These options apply only to the URL form (M4):
| Option | What it does | Default |
|---|---|---|
--header-env <name>=<var> |
Send the header <name> with the value of the environment variable <var> of the bridge. Use it for a secret. A variable that is not set is an error. |
|
--header <name>:<value> |
Send the header <name> with <value>. VS Code writes the command line to its log, and the process list shows it. Never use it for a secret. |
|
--max-response-bytes <n> |
The maximum length of one JSON response or one event of a stream from the upstream server, from 1 to 1073741824 (1 GiB). | 67108864 (64 MiB) |
--ca-file <path> |
Also trust the CA certificates of this PEM file, for the MCP requests and for the requests of the sign-in. The bridge always trusts the CA certificates of the system. | |
--client-id <id> |
Use this client ID, which the authorization server issued. The client secret comes from MCP_BRIDGE_CLIENT_SECRET. |
|
--client-issuer <url> |
The issuer of --client-id. A client with a secret needs it. |
|
--client-metadata-url <url> |
Use the client ID metadata document at this URL. | The document of the bridge |
--redirect-port <port> |
The port of the redirect URI http://127.0.0.1:<port>/callback. |
41894 |
--account <label> |
The account of the stored sign-in: 1 to 64 letters, digits, -, ., _, @ or +. |
default |
--token-store <store> |
Where the bridge keeps the tokens: auto, keychain, file or memory. |
auto |
--token-key-file <path> |
The file with the key of the file store. Only your account can read it. | |
--no-browser |
Do not open a browser. Write the URL of the sign-in to stderr. After notifications/initialized, VS Code still gets the URL in a URL elicitation. We recommend it on a host that other users share (see Environment limits). |
|
--sign-in-timeout <s> |
The time in seconds for the sign-in in the browser. | 300 |
A static Authorization header (from --header-env or --header) and the sign-in options exclude each other. The bridge refuses the headers that zig-sdk or the HTTP connection sets, for example Host, Content-Type and Accept. It also refuses each name that starts with mcp-, and Proxy-Authorization. The executable has no option that permits http to other hosts, and no option that disables a check of the sign-in. An option of the URL form with -- and a command is an error.
logout takes --account, --all, --redirect-port, --token-store, --token-key-file, --ca-file and --log-level. An option takes its value from the next argument or after an equals sign, for example --name x or --name=x. When an option occurs two times, the last value applies. Each --header and each --header-env adds one header.
For the URL form, server/discover waits for --discover-timeout and --sign-in-timeout together when a sign-in can start. A sign-in can start when the store has no access token for the account that is valid for more than one minute.
| Exit code | Cause |
|---|---|
| 0 | Stdin ended, and the bridge stopped. The watchdog also exits with code 0. |
| 1 | The upstream command stopped, or the bridge has an internal error. An upstream server at a URL never stops the bridge. |
| 2 | The arguments are not valid. The code is also 2 for a missing variable of --header-env and for a --ca-file that the bridge cannot use. It is also 2 for a token store that does not open. The bridge writes the cause to stderr, and for arguments that are not valid also the usage. |
The exit code of logout is 0 when the deletion succeeded, also when there was nothing to delete. It is 1 when a deletion failed, and 2 for arguments that are not valid or for a --ca-file that the bridge cannot use. With --token-store keychain or file, a store that does not open also gives 2. With auto, logout skips a store that does not open.
An upstream server at a URL can ask for a sign-in with the status 401 or 403. The bridge then signs in at the authorization server of the upstream server with OAuth19 and PKCE. These steps occur:
- The bridge listens for the redirect on
http://127.0.0.1:<port>/callback, before the browser opens. For the browser, the same port also serves a one-time start URLhttp://127.0.0.1:<port>/start/<token>(M6). - It writes
mcp-bridge-vscode: sign in at <url>to stderr. The<url>is the authorization URL. The Output channel shows the line. - Before
notifications/initialized, the bridge opens the start URL in the browser. The start URL sends the browser to the authorization URL. With--no-browser, the bridge only writes the line. - After
notifications/initialized, VS Code gets a URL elicitation with the authorization URL. VS Code shows the full URL and asks you. - When you accept, VS Code opens the URL. The bridge never opens the browser itself then.
- You sign in, and the browser goes to the redirect URI. The bridge gets the code and the tokens.
The browser opener gets only the start URL, because other users of a POSIX host can read the arguments of a process. The start URL works one time, and only during the sign-in. A second request of the start URL stops the sign-in, because another program on the host possibly sent one of the two requests. That request gets a page that tells you to start the sign-in again. The request of VS Code that needs the sign-in gets an error with the cause sign_in_start_reused. A start URL with a different token gets 404 and does not stop the sign-in.
A sign-in has a time limit, --sign-in-timeout (300 s). When you decline a URL elicitation, the request gets an error. Then for 60 s, the bridge asks no new question, and each sign-in fails at once. When VS Code does not declare URL elicitation, a sign-in after notifications/initialized fails, and a restart of the server signs in again.
The authorization server shows the name mcp-bridge-vscode (zig-bridge-sdk) on its consent page. The bridge gets a client ID in this order20:
-
--client-id: a client that you registered with the exact redirect URI of the bridge. - A client ID metadata document. The default is the document of the bridge on GitHub Pages.
- Dynamic client registration, when the authorization server does not use the document.
The document of the bridge is at https://christianpresley.github.io/zig-bridge-sdk/vscode/client.json. It lists only the redirect URI http://127.0.0.1:41894/callback. Thus with a different --redirect-port and no registration option, the bridge uses dynamic client registration. --client-metadata-url gives a different document, which must list the redirect URI of the bridge. Two servers with a sign-in in one mcp.json need different redirect ports, because only one program can listen on a port.
The bridge keeps one stored sign-in for each server URL, account and redirect URI. --account <label> keeps the sign-ins of two accounts apart, for example a work account and a personal account.
- Run
mcp-bridge-vscode logout <url>with the URL and the options of the server entry. - For an account that is not
default, add--account <label>. - To delete each stored sign-in of the bridge, run
mcp-bridge-vscode logout --all. - Run MCP: Restart Server in VS Code. The next request signs in again.
logout does not tell the authorization server. When the store has no index entry for the URL, logout reads the protected resource metadata of the server. It deletes a sign-in only when the resource of that metadata covers the URL. When logout cannot run, delete the entries yourself:
| Store | How to delete the entries |
|---|---|
| Windows Credential Manager | Delete each generic credential whose name starts with zig-bridge-sdk/mcp-bridge-vscode/. A large entry has more credentials that end in /1, /2 and so on. cmdkey /list shows them, and cmdkey /delete:<name> deletes one. |
| macOS Keychain | Run security delete-generic-password -s zig-bridge-sdk/mcp-bridge-vscode until it finds no more items. |
| Secret Service on Linux | Run secret-tool clear service zig-bridge-sdk/mcp-bridge-vscode. |
| File store | Delete the token directory: %LOCALAPPDATA%\zig-bridge-sdk\vscode\tokens on Windows, else $XDG_STATE_HOME/zig-bridge-sdk/vscode/tokens or ~/.local/state/zig-bridge-sdk/vscode/tokens. |
When the authorization server forgets a stored client, the sign-in page can show an error and send no redirect. The sign-in then ends at its time limit, and the error tells you to run logout.
--token-store auto uses the first store that works:
- The keychain of the host: Windows Credential Manager, the macOS Keychain or the Secret Service on Linux.
- Encrypted files, with a key of 64 hexadecimal digits from outside the token directory.
- Memory. Then you sign in at each start of the server.
The keychain service is zig-bridge-sdk/mcp-bridge-vscode. On Linux without a display, the bridge shows no unlock prompt of the keychain. When the keychain stops to answer later, the bridge uses the next store for the rest of the process. The key of the file store comes from the environment variable MCP_BRIDGE_TOKEN_KEY, or from the file of --token-key-file. A password input can give the variable, and the key file must be private to your account.
--token-store keychain, file and memory use only that store. A store that does not open is then an error. At the start, the bridge writes one line that names the store. The file store protects the tokens against other accounts of the host and against a copy of the token directory alone. It does not protect them against a program that runs as your account.
- Proxies: the URL form uses the proxy of
HTTPS_PROXYorALL_PROXYfor the MCP requests and for the requests of the sign-in.NO_PROXYnames the hosts without a proxy, and a loopback host never uses a proxy. Put the user and the password of the proxy into the proxy URL. Set the variables in theenvof the server entry, or in the environment of VS Code. - The sandbox of VS Code: with
sandboxEnabled, the sandbox blocks the browser and the redirect port of the sign-in. The variableSANDBOX_RUNTIME=1tells the bridge. Without a usable stored token,initializethen fails at once with an error that names the sandbox. Disable the sandbox for a server at a URL that needs a sign-in. - Remote windows: a remote window of VS Code connects to a host over Secure Shell, to WSL or to a dev container. In such a window, a server of the workspace configuration or of the remote user configuration runs on the remote host. A server of the local user configuration runs on the local computer. For a server on the remote host, put the bridge and the upstream server on that host.
- The sign-in on a remote host: the bridge opens the start URL with the
$BROWSERhelper of VS Code. When the browser does not open, open the URL of the sign-in line in the Output channel. The start URL and the redirect both go to the redirect port on the remote host. VS Code can forward the port, or you can forward it in the Ports view. - Tokens on a remote host: the tokens of a server on a remote host stay on that host. A rebuild of a dev container deletes them, unless the token directory is on a volume.
- Shared hosts: on a POSIX host, other users can read the command lines of the processes. Thus the browser opener gets only the one-time start URL, and not the URL of the sign-in with its
stateand itscode_challenge(M6). But another user of the host can read the start URL and send the first request. When that user completes a sign-in with a different account before your browser opens the start URL, the bridge gets the code for that account. The same applies when your browser does not open. On a host that other users share, use--no-browser, and open the URL of the sign-in line yourself. - The limits of
--no-browseron a shared host: the option helps only when no program on that host gets the URL as an argument. For example, VS Code on that host startsxdg-openwith the URL of a link that you click. In a remote window, VS Code opens the link on your computer. Afternotifications/initialized, VS Code gets the URL in a URL elicitation, also with--no-browser. On Linux, the administrator can also mount/procwith the optionhidepid. Then users cannot read the command lines of other users. - VS Code for the Web cannot start stdio servers, thus the bridge does not operate there.
From M5, a server that you write with zig-sdk can put the bridge into its own executable. Call vscode.serveStdio in place of mcp.transport.stdio.serve. Then one executable serves the Local harness, the Copilot harness and each other client of revision 2026-07-28 on stdio. You do not need mcp-bridge-vscode for that server. The first request of the client selects the path of the connection:
-
initializeselects the bridge. Its upstream server is your server in the same process, throughmcp.transport.memory.ClientLinkof zig-sdk. - Each other message selects the stdio transport of zig-sdk, without the bridge. A line that is not valid JSON-RPC also selects it.
- A
server/discoverbefore the first other request selects no path. The optiondiscovertells who answers it.
- Add the package to the
build.zig.zonof your server withzig fetch --save-exact=bridge_sdk git+https://github.com/ChristianPresley/zig-bridge-sdk#v0.1.2. - In
build.zig, import the modulesvscodeandmcpof the package. - Take
mcponly from this package. A second pin of zig-sdk gives a secondmcpmodule. - In your
main, give your server tovscode.serveStdio. - In
.vscode/mcp.json, write the absolute path of your executable as thecommandof the entry.
The imports of build.zig:
const dep = b.dependency("bridge_sdk", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("vscode", dep.module("vscode"));
exe.root_module.addImport("mcp", dep.module("mcp"));The main of the server:
const std = @import("std");
const vscode = @import("vscode");
const mcp = @import("mcp");
pub fn main(init: std.process.Init) !void {
var server = try mcp.Server.init(init.gpa, init.io, .{ .info = .{ .name = "my-server", .version = "1.0.0" } });
defer server.deinit();
// Add the tools, the prompts and the resources here.
try vscode.serveStdio(init.io, init.gpa, &server, .{});
}The server entry has no args for the bridge:
{
"servers": {
"demo": { "type": "stdio", "command": "/abs/path/your-server" }
}
}The Package Model page tells why the mcp module must come from the package. The example in examples/consumer/ is a complete program. The CI job consumer builds it from a tarball of the package.
The two paths take their limits from server.options.limits of your server, not from the defaults of mcp-bridge-vscode. These limits are the line length, the depth, the requests in flight and shutdown_grace. The paths have these differences:
- On the legacy path, a line that is too long gets -32600. On the first line and on the modern path,
vscode.serveStdiodrops it without a response. - On the legacy path, a handler sees
ctx.kind == .memory, and the rate limits of your server see no caller (Peer.unknown). On the modern path, a handler sees.stdio, and the rate limits see one caller for the connection. - On the two paths, a request above the limit of requests in flight gets -32603 at once.
On the legacy path, these rules of the memory link apply:
- The handler of a request runs on the task of the request of VS Code. Only a cancellation of VS Code and the end of stdin stop it.
- A request has no time limit in the process. The wait for the answers of VS Code to an input request keeps its limit of 1 h.
- A change of your server, for example
setToolEnabled, writes its list change to VS Code on the task that makes the change, before the result. While VS Code does not read stdout, each event and each new listen stream of your server waits. - The listen stream, the subscriptions, the log level and the input requests operate as in
mcp-bridge-vscode.
At the end of stdin, vscode.serveStdio ends the listen streams and stops in a bounded time. On the legacy path, the bridge cancels the requests in flight at once, as mcp-bridge-vscode does. On the modern path, the requests in flight get at most shutdown_grace (2 s by default). Then the function fires the cancel tokens of the requests that are left. On the two paths, it then cancels the tasks that are left and returns.
Only a handler that does not examine its cancel token and has no cancel point can keep the process alive. mcp-bridge-vscode has a watchdog for this case, but vscode.serveStdio has none.
The Copilot harness sends server/discover first. By default (.discover = .answer), your server answers it, and the harness takes the modern path. From Copilot runtime 1.0.95, the harness does the MRTR rounds on that path5, and a tool that asks for input completes. The manual check of 2026-10-12 observed this with the consumer example. An older harness has no MRTR, thus a tool that asks for input cannot complete there. The other tools operate.
For an older harness, give .{ .discover = .refuse } to vscode.serveStdio. The harness then gets -32601 for server/discover and sends initialize, and the bridge does the MRTR rounds for it. With this option, a client of revision 2026-07-28 that needs a server/discover result cannot use your server. A client that sends its requests without server/discover still takes the modern path.
| Milestone | Local harness | Copilot harness |
|---|---|---|
| M1 | The bridge forwards the requests for tools, prompts, resources and completion. The harness lists and calls the tools. | The harness gets -32601 for server/discover, and then it sends initialize. It lists and calls the tools. |
| M2 | The tools, the prompts and the resources that ask for input complete. | The tools that ask for input complete through the bridge, also with a harness that has no MRTR. The harness declares no roots, thus a roots input request fails with -32021. |
| M3 | After a call that changes the tools, the harness lists the tools again before the next turn of the same chat request. An editor of a resource shows the changes of the resource. | The bridge sends the same notifications. In the manual check of 2026-10-12, the harness listed the tools again after a call that changed them, and the disabled tool left its list. |
| M4 | An upstream server at an HTTPS URL, with OAuth sign-in. The first sign-in opens the browser before notifications/initialized. A later sign-in is a URL elicitation. |
The same URL form, in the same entry of the configuration. The harness declares URL elicitation (C2), thus a later sign-in is also a URL elicitation. |
| M5 | A zig-sdk server that calls vscode.serveStdio serves the harness without mcp-bridge-vscode. The initialize of the harness goes to the bridge in the process of the server. |
By default, the embedded server answers server/discover itself, and the harness speaks revision 2026-07-28. From Copilot runtime 1.0.95, a tool that asks for input completes on that path5. The option .discover = .refuse sends an older harness to the bridge. |
| M6 | The first sign-in opens a one-time start URL in the browser, and the start URL sends the browser to the authorization URL. The sign-in line in the Output channel keeps the authorization URL. | The same URL form and the same start URL. A later sign-in is a URL elicitation with the authorization URL. |
The maintainer checked the bridge in a real VS Code on 2026-10-12. The automated transcript and process tests send fixed requests. This check sends the requests of VS Code itself, with the Local harness. The procedure is the smoke test of the README, with three servers in place of one.
| Item | Result |
|---|---|
| Environment | Windows 11 Pro. VS Code Insiders 1.121.0-insider, commit aa21abcbab6b9a929f7aad632d8377b6fa16bbcd. The Local harness: the MCP log says "Starting server from LocalProcess extension host". The bridge 0.1.2, built with -Doptimize=ReleaseSafe, with zig-sdk 0.5.1. |
| Servers | The workspace file .vscode/mcp.json has three stdio entries: (1) mcp-bridge-vscode.exe -- bridge-fixture-server.exe, (2) the consumer example consumer.exe, which calls vscode.serveStdio, and (3) bridge-embedded-server.exe. |
| Start | The code lens Start in mcp.json put the three servers in the state Running. VS Code listed 21 tools and 3 prompts for (1), 2 tools for (2), and 19 tools and 3 prompts for (3). The Output channel of each server showed initialize from Visual Studio Code - Insiders 1.121.0-insider and the upstream server is ready. VS Code logged "Tool list changed, refreshing tools..." and "Discovered N tools". |
| Tool calls | In the chat in agent mode, add with a=2 and b=3 through (1) gave 5. greet with the name "VS Code" through (2) gave Hello, VS Code.. echo with the text "hello" through (3) gave hello. Each call needed the tool approval of VS Code one time. |
| MRTR |
ask_url through (1). VS Code showed "Authorization Required – zig-bridge (MCP Server) / Sign in to continue. / Open this URL? https://example.com/auth" with the buttons "Open example.com" and "Cancel". After the accept, VS Code asked one more time to open the external website. The browser opened the page, and the tool result was url: accept. |
| Stop | After Stop in the code lens, the log showed "Stopping server" and then "Connection state: Stopped" in 100 ms. No bridge process and no upstream process stayed in the process list. After a reload of the window, the bridge and its upstream server ended with the extension host, and no process stayed. The reload came after a hang of the renderer of VS Code after a code-insiders chat command. The main log says "CodeWindow: detected unresponsive". The maintainer did not find the cause, and the servers do not run in the renderer. |
| Observation | With the two fixture servers (1) and (3) active at the same time, the tool names of the two are the same. VS Code used "Optimized tool selection", and the user also has other MCP servers. The model one time answered that add of zig-bridge "is not available". With only (1) active, add worked. This is a behavior of the tool selection of the chat, not of the bridge. |
| Not checked | The form elicitation (ask_form, ask_name), sampling, roots, and the URL form with the OAuth sign-in. The automated transcript and process tests cover them with fixed requests. |
The maintainer also checked the Copilot harness on 2026-10-12, with the same procedure. A small proxy between VS Code and each server wrote each frame to a file.
| Item | Result |
|---|---|
| Environment | Windows 11 Pro. VS Code Insiders 1.142.0-insider, commit ff00e788322523ee1e9e8ee2c9bb4b89af93a3c2. The Copilot harness is Copilot runtime 1.0.95-0, and its clientInfo.name is copilot-cli. The bridge 0.1.2 from main at ab920b4, built with -Doptimize=ReleaseSafe, with zig-sdk 0.5.1. |
| Servers | The workspace file .vscode/mcp.json has two stdio entries: (1) mcp-bridge-vscode.exe --log-level debug -- bridge-fixture-server.exe, and (2) the consumer example consumer.exe, which calls vscode.serveStdio. |
| Harness | The chat input of VS Code 1.142 has a harness picker: Copilot, Local, Cloud, Claude and Codex. Each request came from the command code-insiders chat, which starts a new chat with the Copilot harness. A workspace value false of chat.defaultToCopilotHarness did not change this. |
| Start | For each chat request, the harness started a new process of each server. To (1), it sent server/discover, got -32601, and sent initialize of revision 2025-11-25. That initialize declares sampling and elicitation with form and url, and no roots. After notifications/initialized, the bridge sent the three list changes, and the harness sent tools/list four times. To (2), it sent server/discover and then subscriptions/listen, and the server acknowledged the stream. |
| Tool calls |
toggle with enabled true through (1) gave tool toggled: enabled (no change), and toggled gave toggled. greet with the name "VS Code" through (2) gave Hello, VS Code.. Each call of a tool without the read-only annotation needed the tool approval of the harness one time. |
| MRTR through the bridge |
sample through (1): the harness asked for consent, and the result was model: Hello! from gpt-5.6-sol. ask_url: the harness showed "Authorization Required" with the URL, VS Code asked to open the external website, and the result was url: accept. ask_form: the harness showed the form with its four fields, one field at a time. A cancel of the form gave form: cancel. list_roots: the harness declares no roots, thus the upstream server answered -32021, and the bridge forwarded the error. |
| MRTR on the modern path |
ask_name through (2): the server answered input_required, and the harness showed the form "What is your name?". A cancel of the form gave a second tools/call with inputResponses, and the result was No name: cancel.. |
| List change |
toggle with enabled false through (1): the bridge sent notifications/tools/list_changed before the result tool toggled: disabled. The harness sent tools/list again, its new list had no toggled, and a call of toggled failed with "does not exist". |
| Stop | After the close of the window, no bridge process, no upstream process and no proxy process stayed in the process list. |
| Not checked | The accept of a form with typed text. The Local harness with the form elicitation, the sampling and the roots. The URL form with the OAuth sign-in. The automated transcript and process tests cover them with fixed requests. |
The maintainer also checked the bridge in the stable VS Code 1.141.0 on 2026-10-12, with the same procedure and the same proxy. The Local harness started and stopped the servers, and each chat request went to the Copilot harness.
| Item | Result |
|---|---|
| Environment | Windows 11 Pro. VS Code 1.141.0, commit 2a59476c9bfcb90b3ddc372c36762471b7dfad1c. The bundled Copilot Chat is 0.69.0. The Copilot harness runs as copilot-runtime.exe from the package copilot-sdk-win32-x64 of VS Code, and its clientInfo is copilot-cli 1.0.92-4.unstable.r37397886721.gad270aa. The bridge 0.1.2 from main at 87646eb, built with -Doptimize=ReleaseSafe, with zig-sdk 0.5.1. |
| Servers | The workspace file .vscode/mcp.json has three stdio entries: (1) mcp-bridge-vscode.exe --log-level debug -- bridge-fixture-server.exe, (2) the consumer example consumer.exe, which calls vscode.serveStdio, and (3) bridge-embedded-server.exe. A stdio proxy in Python sat between VS Code and each server, and it wrote each frame to a file. |
| Start | In the Extensions view, section "MCP Servers - Installed", the item Start Server of the gear menu put each server in the state Running. The initialize came with clientInfo "Visual Studio Code" 1.141.0. It declared roots, sampling, elicitation with form and url, tasks, and the extension io.modelcontextprotocol/ui. The logging/setLevel with info came before notifications/initialized. VS Code listed 21 tools and 3 prompts for (1), 2 tools for (2), and 19 tools and 3 prompts for (3). The Output channel showed initialize from Visual Studio Code 1.141.0, the upstream server is ready, "Tool list changed, refreshing tools..." and "Discovered 21 tools". The MCP server log of VS Code is the file mcpServer.mcp.config.ws0.<name>.log in the log directory of the window. |
| Harness | The chat mode picker of VS Code 1.141 has only Agent and "Configure Custom Agents...". Each request came from the command code chat -m agent -r, and each went to the Copilot harness. A workspace value false of chat.defaultToCopilotHarness did not change this. For each chat request, the harness started a new process of each server. To (1) and (3), it sent server/discover, got -32601, and sent initialize of revision 2025-11-25. That initialize declares sampling and elicitation with form and url, and no roots. After notifications/initialized, it got the three list changes, and it sent tools/list two or three times. |
| Tool calls |
add with a=2 and b=3 through (1) gave 5 without an approval, because the tool has readOnlyHint. echo with the text "hello" through (1) gave hello. greet with the name "VS Code" through (2) gave Hello, VS Code. after one approval. |
| MRTR through the bridge |
ask_url through (1): the harness showed "Authorization Required / Sign in to continue. / Open this URL? https://example.com/auth" with the button "Open example.com". VS Code then asked to open the external website, the browser opened the page, and the result was url: accept. The bridge sent elicitation/create with the id b-1, and then notifications/elicitation/complete. ask_form: the harness showed the form "Tell us about you" with its four fields, one field at a time (1/4). A cancel of the form gave form: cancel. sample: the harness answered sampling/createMessage in 4 ms with the stopReason cancelled and the text "The user cancelled the request.", without a consent dialog. The tool result was model: The user cancelled the request.. Copilot runtime 1.0.95 in VS Code Insiders asked for consent and gave the answer of a model. |
| MRTR on the modern path |
ask_name through (2): the server answered input_required, and the harness showed the form "What is your name?". A cancel of the form gave a second tools/call with inputResponses {name: {action: cancel}}, and the result was No name: cancel.. Thus Copilot runtime 1.0.92 already has MRTR. |
| List change |
toggle with enabled false through (1): the bridge sent notifications/tools/list_changed before the result tool toggled: disabled. The harness sent tools/list again, and a call of toggled failed with "does not exist". |
| Stop | The item Stop Server of the gear menu for (3) gave "Stopping server" and then "Connection state: Stopped" in 24 ms. The server got EOF on stdin, and it exited with code 0 in 17 ms. The close of the workspace window gave "Extension host shut down, server will stop", and the processes of the Local harness ended. The Copilot harness kept one process of each server for each chat until the last window of VS Code closed. After 8 chat requests, the process list had 10 bridge, 10 fixture, 10 consumer and 8 embedded processes. Then all of them and copilot-runtime.exe ended. No bridge process, no upstream process and no proxy process stayed. |
| Not checked | The accept of a form with typed text. The automated transcript and process tests cover it with fixed requests. The next table has the Local harness with the roots, a tool call and the OAuth sign-in. |
The maintainer also checked the Local harness of the stable VS Code 1.141.0 on 2026-10-12, through the extension API, with the OAuth sign-in. The environment is the same as in the table before, and the same proxy wrote each frame.
| Item | Result |
|---|---|
| Environment | Windows 11 Pro. VS Code 1.141.0, commit 2a59476c9bfcb90b3ddc372c36762471b7dfad1c. The bridge 0.1.2 from main at 87646eb, built with -Doptimize=ReleaseSafe, with zig-sdk 0.5.1. |
| Procedure | A new user data directory without a Copilot sign-in. In that profile, the harness picker shows "Local". An extension test with @vscode/test-electron ran on the installed Code.exe, and it called vscode.lm.invokeTool for each tool. VS Code names a tool mcp_<name>_<tool>, where <name> is the first 13 characters of serverInfo.name, for example mcp_bridge-fixtur_add. A second server with the same name gets the suffix fixtu2. |
| Servers | (1) mcp-bridge-vscode.exe --log-level debug -- bridge-fixture-server.exe. (4) mcp-bridge-vscode.exe --log-level debug --token-store memory --ca-file test/fixtures/tls/ca.crt --redirect-port 41895 https://127.0.0.1:44311/mcp, with bridge-fixture-server --https 44311 --oauth as the upstream server. The item Start Server of the gear menu in the Extensions view, section "MCP Servers - Installed", started each server. |
| Tool calls |
add with a=2 and b=3 through (1) gave 5, without a confirmation and without a model. echo with the text "hello" through (4) gave hello. |
| Roots |
list_roots through (1) gave roots: file:///c%3A/.../ws, the workspace folder. The bridge sent roots/list to VS Code as the request b-1, and VS Code answered with the folders. |
| Form |
ask_form through (1), outside the chat: VS Code showed the notification "Tell us about you." with the button Respond, and then the quick input "Name (1/4)". VS Code refuses the accept of the empty required field (minLength 1), thus the typed name stays unchecked. |
| Sampling |
sample through (1): the extension test host refuses dialogs ("DialogService: refused to show dialog in tests"). VS Code answered -32603, and the bridge forwarded the error. This is a limit of the extension tests, not of the bridge. |
| URL form with OAuth | At Start Server of (4), the bridge wrote "the client metadata document of the bridge has only the redirect port 41894. With --redirect-port 41895, the bridge uses dynamic client registration". It wrote the sign-in line, and it opened the default browser (Edge) at the one-time start URL. Edge arrived at https://127.0.0.1:44311/authorize?... with the client_id, the redirect_uri http://127.0.0.1:41895/callback, the state and the code_challenge. Edge showed a certificate warning, because Windows does not trust the test CA. The test browser of the fixture (bridge-fixture-server --browse) completed the sign-in on the URL of the sign-in line. VS Code then got the initialize result after 20 s, and it listed 23 tools. |
| Step-up |
guarded through (4): the upstream server answered 403 insufficient_scope. The bridge sent a URL elicitation "The upstream server needs a new sign-in. Open the URL and sign in at the authorization server." with the step-up URL (scope mcp:read mcp:write). VS Code showed it as a notification with the full URL and the button "Open URL". VS Code opened Edge at the URL at once, without the dialog of the external website. The test browser completed the step-up, the bridge sent notifications/elicitation/complete, and the result was guarded: allowed after 60 s. |
| Stop | The close of the test window ended the bridge, the upstream and the proxy processes. |
| Not checked | A form accepted with typed text, because VS Code needs a typed name. The sampling consent of the Local harness, because the test host shows no dialogs. The automated transcript and process tests cover them with fixed requests. |
VS Code writes the messages of each MCP server to an Output channel. The channel also has the stderr lines of the bridge and of the upstream server, each at the Warning level. Each line of the bridge has the form mcp-bridge-vscode: <scope>: <level>: <text>. The sign-in line has the form mcp-bridge-vscode: sign in at <url>. A line without one of these starts comes from the upstream server.
The scope bridge is the core module, and the scope vscode is the executable. The scopes mcp_client, mcp_stdio and mcp_router come from zig-sdk. An error line about the arguments names the option, the header or the environment variable, but never a value.
-
Run MCP: List Servers, select the server, then select Show Output.
-
In the Output view, set the level of the channel to Debug or Trace. At Debug, VS Code writes each JSON-RPC message.
-
As an alternative to step 2, add
"dev": {}to the server entry. VS Code then writes the JSON-RPC messages at the Info level. -
With
"dev": {}, VS Code also opens the channel when the server starts. -
Add
--log-level debugbefore the--inargs. Then the bridge also writes its debug lines:"args": ["--log-level", "debug", "--", "/abs/path/your-server"]
-
For the URL form, put
--log-level debugbefore the URL.
At the debug level, the bridge writes one line for each upstream exchange: upstream request <id> (<method>, round <n> of client request <id>): <outcome> after <ms> ms. The line has no content of a frame. Other debug lines name each request of the bridge to VS Code, such as request <id> (<method>): sent elicitation/create to the client as request "b-1". The debug lines never have a header value, a token or the query of the redirect.
| Message | What it means |
|---|---|
Waiting for server to respond to `initialize` request... |
VS Code sent initialize and has no response yet. The bridge answers after the upstream server answers server/discover, or after --discover-timeout (60 s). If the message stays, examine the stderr lines of the upstream server. |
mcp-bridge-vscode: bridge: info: initialize from <name> <version> |
VS Code sent initialize. The bridge starts the upstream command, or makes the HTTP client for the URL. |
mcp-bridge-vscode: bridge: info: the upstream server is ready |
The upstream server answered server/discover, and VS Code gets the initialize result. |
mcp-bridge-vscode: bridge: warning: cannot start the upstream command: <error> |
The bridge cannot start <command>, and VS Code gets an error for initialize. Examine the path of the command in args. |
mcp-bridge-vscode: bridge: warning: server/discover failed: <error> |
The upstream server did not answer server/discover, or it answered with an error. VS Code gets an error for initialize. For an upstream server at a URL, the line can end with the HTTP status, for example (HTTP status 404). |
mcp-bridge-vscode: bridge: info: still waiting for the upstream server: <method> (request <id>, <n> s) |
The upstream server did not answer the request in 10 s. The line comes again each 30 s until the response or the time limit. |
mcp-bridge-vscode: bridge: warning: request <id> (<method>): no response in <limit> |
The request had no response in its time limit. VS Code gets -32603. |
mcp-bridge-vscode: bridge: warning: rejected request <id> (<method>): too many requests in flight |
VS Code sent a new request at the limit of requests in flight. The request gets -32603 at once. |
mcp-bridge-vscode: bridge: info: tool '<name>': added an items schema at '<pointer>' of the input schema |
An array schema of the tool had no items, or its items was false in JavaScript. The bridge added items: {}, so that VS Code shows the tool. |
mcp-bridge-vscode: bridge: info: tool '<name>': the input schema at '<pointer>' is too deep to examine |
The schema is deeper than 64 levels. The bridge did not examine the deeper part. |
mcp-bridge-vscode: bridge: info: still waiting for the answers of the client to the input requests of <method> (request <id>, <n> s) |
The upstream server asked for input, and VS Code did not answer all input requests yet. Answer the form, the URL or the sampling request in VS Code. |
mcp-bridge-vscode: bridge: warning: input request '<key>': the bridge refused the URL of a URL elicitation, and the upstream server gets decline |
The URL does not use https, or it uses http to a host that is not a loopback host. The URL can also have a backslash, or user information before the host. VS Code did not get the URL. |
mcp-bridge-vscode: bridge: warning: request <id> (<method>): the input requests of the upstream server failed: <detail> |
An input request or an answer of VS Code did not pass a check, or VS Code did not answer in time. <detail> gives the cause. |
mcp-bridge-vscode: bridge: info: request <id> (<method>): the client answered an input request with an error: <message> |
VS Code answered a sampling or roots request with an error. For example, the user refused a sampling request. The request of VS Code gets that error. |
mcp-bridge-vscode: bridge: warning: request <id> (<method>): no answers of the client in <limit> (input requests without an answer: <n> of <m>) |
VS Code did not answer all input requests of a round in 1 h. |
mcp-bridge-vscode: bridge: warning: request <id> (<method>): the upstream server asked for input in <n> rounds |
The upstream server asked for input in each of 10 rounds. The request gets -32603. |
mcp-bridge-vscode: bridge: warning: request <id> (<method>): the upstream server refused the request state after an input wait of <time> |
The answers came after the time limit of the state of the request, or the upstream server started again. Send the request again. |
mcp-bridge-vscode: bridge: warning: cannot start the listen stream: <error>. The client gets no list changes. |
The bridge cannot open the listen stream after notifications/initialized. |
mcp-bridge-vscode: bridge: warning: the listen stream ended: <error>. A new stream follows in <time>. |
The listen stream stopped, for example after a lost connection. After the wait, VS Code gets one list change for each declared list and one update for each subscribed resource. |
mcp-bridge-vscode: bridge: info: the upstream server ended the listen stream. A new stream follows in <time>. |
The upstream server ended the listen stream with a result. The bridge opens a new stream after the wait. |
mcp-bridge-vscode: bridge: warning: the upstream server refused the listen stream with the error <code>: <message>. The bridge sends no more list changes. |
VS Code gets no more notifications until the next change of the subscriptions. |
mcp-bridge-vscode: bridge: warning: the listen stream for a change of the subscriptions failed: <error> |
The new stream of resources/subscribe or resources/unsubscribe failed before its acknowledgment. VS Code gets an error, and the old subscriptions stay. |
mcp-bridge-vscode: bridge: warning: the listen stream ended with the HTTP status <n>. A new stream follows in <time>. |
The upstream server or a proxy answered the listen stream with the status 408, 429 or 5xx, but not 501. The bridge opens a new stream after the wait. |
mcp-bridge-vscode: bridge: warning: the listen stream failed with the HTTP status <n>. A new stream follows after the next sign-in. |
The sign-in of the listen stream did not complete. The next sign-in of a different request opens the stream again. |
mcp-bridge-vscode: bridge: info: a sign-in completed. The bridge opens the listen stream again. |
A different request signed in after the listen stream failed with 401 or 403. |
mcp-bridge-vscode: mcp_stdio: warning: stdio server exited (exit code <n>); no restart is left |
zig-sdk writes this line when the upstream server stops. The line of the bridge about the exit comes after it. |
mcp-bridge-vscode: mcp_router: warning: dropped <frame>: <outcome> |
zig-sdk dropped a frame of the upstream server that it cannot read, for example a frame longer than --max-line-bytes. When the frame names a request in flight, that request fails. |
mcp-bridge-vscode: bridge: error: the upstream server exited with code <n> |
The upstream server stopped while VS Code was connected. The bridge answered each request in flight with -32603, and it exits with code 1. On POSIX, the line can name a signal instead of a code. |
mcp-bridge-vscode: vscode: warning: the bridge did not stop in 8 s after the end of stdin, thus it stops now |
A request did not stop after its cancellation. The watchdog stopped the bridge and the upstream server. |
mcp-bridge-vscode: vscode: error: the bridge stopped because of an internal error: <error> |
The bridge has an internal error, and it exits with code 1. |
mcp-bridge-vscode: vscode: error: the option <option> is not known |
The arguments are not valid. The usage comes after the line, and the bridge exits with code 2. The other texts name a missing command, a missing value, a refused header or a missing variable. |
mcp-bridge-vscode: vscode: error: cannot use the CA certificates of --ca-file: <error> |
The bridge cannot read the file of --ca-file, or the file has no certificate that the bridge can use. The bridge exits with code 2. |
mcp-bridge-vscode: vscode: warning: the value of the header <name> is on the command line. ... |
A --header has a name that usually carries a secret. Use --header-env. |
mcp-bridge-vscode: bridge: warning: cannot make the HTTP client for the upstream server: <error> |
The URL, a proxy variable or the CA certificates are not valid. VS Code gets an error for initialize. |
mcp-bridge-vscode: vscode: error: the token store did not open: <cause> |
The store of --token-store does not open, or the token key is not valid. <cause> tells what to correct. The bridge exits with code 2. |
mcp-bridge-vscode: bridge: info: the tokens are in the keychain of the host (service "zig-bridge-sdk/mcp-bridge-vscode") |
The store of the tokens. Other forms of the line name the token directory of the file store, or tell why the tokens stay in memory. |
mcp-bridge-vscode: bridge: warning: the keychain stopped to answer (<error>). For the rest of the process, ... |
The keychain refused a request after the start, for example because the user locked it. The bridge uses the next store until it stops. |
mcp-bridge-vscode: sign in at <url> |
The upstream server needs a sign-in. The <url> is the authorization URL. The browser gets a one-time start URL that sends it to this URL. When no browser opens, open the URL in a browser. After notifications/initialized, VS Code also shows the URL in a URL elicitation. |
mcp-bridge-vscode: bridge: warning: the browser did not open: open the URL of the sign-in line in a browser |
The bridge cannot start a browser, for example on a host without a desktop. Open the URL of the sign-in line yourself. |
mcp-bridge-vscode: bridge: warning: the bridge has no random data for the start URL, thus it does not open the browser |
The system gave the bridge no random data for the token of the start URL. The line about the browser that did not open comes next. Open the URL of the sign-in line yourself. |
mcp-bridge-vscode: bridge: warning: The start URL of the sign-in received a second request, ... |
The start URL got a second request before the redirect. Another program on the host of the bridge possibly opened it, thus the bridge stopped the sign-in. The request gets an error. Restart the server to sign in again. When this occurs again on a host that other users share, use --no-browser. |
mcp-bridge-vscode: bridge: warning: the start URL of the sign-in received a second request after the redirect. ... |
The start URL got a second request after the bridge received the redirect with a code. Another program possibly signed in with a different account. To be sure, run mcp-bridge-vscode logout <url>, and restart the server to sign in again. |
mcp-bridge-vscode: bridge: warning: the authorization URL is not valid (<error>), thus the bridge does not open it |
The authorization server sent a URL that is not safe to open. The bridge did not write it and did not open it. Other lines tell of a URL without state, or with a different redirect URI. |
mcp-bridge-vscode: bridge: warning: Another program uses the redirect port <n>. ... |
A different program, or a second server with a sign-in, listens on the redirect port. Set a different --redirect-port for one of them. |
mcp-bridge-vscode: bridge: warning: The sign-in did not complete in <n> s. |
Nobody completed the sign-in in the browser in the time limit. The error of the request tells what to do. |
mcp-bridge-vscode: bridge: info: The user declined the sign-in. |
You declined the URL elicitation of the sign-in. The request gets an error. For 60 s, the bridge then asks no new question. |
mcp-bridge-vscode: bridge: info: the client did not declare URL elicitation: a later sign-in fails, and a restart of the server signs in again |
This client of VS Code cannot ask you to open a URL. A sign-in after notifications/initialized fails at once. |
mcp-bridge-vscode: bridge: warning: The upstream server needs a sign-in, but the bridge runs in the sandbox of VS Code. ... |
SANDBOX_RUNTIME=1 is set, and the store has no token that the bridge can use. initialize fails. A later sign-in writes the line The bridge runs in the sandbox of VS Code, ... in its place. Disable the sandbox for this server. |
mcp-bridge-vscode: vscode: info: logout deleted the stored sign-in of the account "<label>". ... |
logout deleted the tokens and the client of the account for the URL. Restart the server in VS Code to sign in again. |
mcp-bridge-vscode: vscode: info: logout found no stored sign-in of the account "<label>" for the URL |
The stores have no sign-in of this account for the URL. Give the same URL, --account and --redirect-port as the server entry. |
Process exited with code <n> |
The bridge stopped. Code 1 means that the upstream command stopped, or that the bridge has an internal error. Code 2 means that the arguments are not valid, that the bridge cannot use --ca-file, or that the token store does not open. |
<n> tools have invalid JSON schemas and will be omitted |
VS Code checks the input schema of each tool against JSON Schema draft-07. It does not show the tools that fail. Correct the schemas in the upstream server. |
MPC <code>: <message> |
The server sent an error response. VS Code writes "MPC" in place of "MCP". The error table gives the messages of the bridge. A different message comes from the upstream server. |
The repository file bridges/vscode/README.md gives each message of the sign-in, of the token stores and of logout17.
- MCP specification 2025-11-25: Model Context Protocol Specification 2025-11-25.
- MCP-SPEC-2026-07-28: Model Context Protocol Specification 2026-07-28.
-
GitHub copilot-cli issue 4870: The Copilot runtime sends
initializeafter -32601 forserver/discover. -
GitHub copilot-cli issue 4888:
initializeafter a time limit or after a discover result that the runtime cannot use. The issue is open. - GitHub copilot-cli issue 4834: Multi round-trip requests in the Copilot runtime. The issue closed on 2026-10-12.
- VS Code issue 329848: Revision 2026-07-28 in VS Code.
-
mcpServerRequestHandler.ts: The requests of the Local harness to a server. -
VS Code issue 339276: An array parameter without
itemsblocks the tools of a server. - MCP-STDIO-2026-07-28: The stdio transport 2026-07-28.
- MCP-MRTR-2026-07-28: Multi round-trip requests 2026-07-28.
-
MCP-DISCOVER-2026-07-28:
server/discover2026-07-28. -
MCP elicitation 2025-11-25: Elicitation in revision 2025-11-25, with
elicitationIdfor the URL mode. - MCP-SUBSCRIPTIONS-2026-07-28: Subscriptions 2026-07-28.
- MCP-EXT-APPS: The MCP Apps extension.
- VS Code issue 261611: The tool list after a tool call that changes it.
- VS Code MCP source: The MCP client of VS Code.
-
bridges/vscode/README.md: The build, the configuration and the messages of thevscodebridge. - MCP-HTTP-2026-07-28: Streamable HTTP transport 2026-07-28.
- MCP-AUTH-2026-07-28: Authorization overview 2026-07-28.
- MCP-AUTH-CLIENTREG-2026-07-28: Client registration 2026-07-28.
zig-bridge-sdk uses the Apache License 2.0. The wiki uses the project profile of ASD-STE100 Simplified Technical English. Visual Studio Code and VS Code are trademarks of Microsoft Corporation. This project has no affiliation with Microsoft, and Microsoft does not endorse it.
zig-bridge-sdk
zig-sdk