Skip to main content
Docs / CORE FEATURES

Model Context Protocol (MCP)

MCP server integration for coc.nvim to allow AI agents like OpenAI Codex to interact with the editor.

coc-mcp.txt *coc-mcp* MCP server integration for coc.nvim

MCP (Model Context Protocol) integration lets agents like OpenAI Codex connect to the running coc.nvim instance, read editor buffers (including unsaved changes), explore code through the language servers and apply edits that stay in sync with Vim/Neovim.

TABLE OF CONTENTS

Overview coc-mcp-overview Enable coc-mcp-enable Codex setup coc-mcp-codex Commands coc-mcp-commands Configuration coc-mcp-configuration Result limits coc-mcp-result-limits Security coc-mcp-security Troubleshooting coc-mcp-troubleshooting Remote over SSH coc-mcp-ssh Resources and notifications coc-mcp-resources

1. OVERVIEW

coc.nvim starts a local MCP server over a loopback socket (TCP or Unix socket). Codex spawns a tiny stdio bridge (coc-mcp) which forwards MCP messages between Codex and the socket.

Protocol versions: the server accepts initialize requests for MCP 2024-11-05, 2025-06-18 and 2025-11-25 and echoes the requested version back when supported. Sessions on 2024-11-05 (which predates structured tool output and tool metadata) receive tools/list entries without title, annotations or outputSchema and tools/call results without structuredContent, so older clients only see the fields their schema defines; newer versions get the full metadata.

2. ENABLE

The server is not started by default. Enable auto start in coc-settings.json:

jsonc snippet
1{
2 "mcp": {
3 "autoStart": true
4 }
5}

When it is disabled, you can still start it manually for the current session with :CocCommand mcp.start (which ignores mcp.autoStart), and stop it again with :CocCommand mcp.stop.

By default (mcp.transport: "auto") the server listens on a per-process Unix socket on macOS/Linux (no local TCP port is exposed) and falls back to loopback TCP with a random port on Windows. The connection info (address + per-start token) is written to ~/.coc/mcp/coc-<pid>.json, keyed by the vim pid; the file (and the unix socket, when used) is removed when coc.nvim exits, and stale files from crashed instances are cleaned up by the bridge whenever it scans the directory.

Use :CocCommand mcp.status to show the current address, working directory and connected clients (each with its pid and connect/last-activity time) and the full list of available MCP tools. :CocInfo includes the same MCP status section.

3. CODEX SETUP

Add this stdio server to ~/.codex/config.toml:

toml snippet
1[mcp_servers.coc]
2command = "node"
3args = ["/path/to/coc.nvim/bin/coc-mcp.js"]
4enabled = true

Before starting Codex, start the coc.nvim MCP server with "mcp.autoStart": true or :CocCommand mcp.start. Run codex mcp list to verify the configuration. See doc/coc-mcp-example.toml for timeout and tool-filter examples.

The bridge discovers coc.nvim instances through ~/.coc/mcp and needs no environment variables. COC_MCP_DIR overrides the discovery directory for tests or remote mounts. During initialization, the bridge waits up to five seconds for a connection before returning an error and exiting. Set COC_MCP_STARTUP_TIMEOUT_MS to change this timeout, and keep Codex's startup_timeout_sec at least as large.

Multiple editor instances

Each instance publishes ~/.coc/mcp/coc-<pid>.json. The bridge selects one as follows:

  • default (--match-cwd): first instance whose workspace contains the bridge working directory, with symlinks normalized;
  • --match-first: first available instance, ignoring the working directory.

The bridge reconnects automatically after :CocRestart.

4. COMMANDS

:CocCommand mcp.start Start the MCP server :CocCommand mcp.stop Stop the MCP server :CocCommand mcp.status Show address, cwd, connected clients (pid and connect/last-activity time) and the full tool list

5. CONFIGURATION

All options live under the mcp section. Open all MCP settings in the Config explorer. Highlights:

6. RESULT LIMITS

LSP list tools cap the number of returned items so large results do not flood the agent or cost unnecessary memory/time (the server still counts all results). Each affected tool accepts an optional maxResults argument (minimum 1, hard maximum 1000):

text snippet
1lsp/references, lsp/definition, lsp/declaration,
2lsp/type_definition, lsp/implementation default 200
3lsp/document_symbols, lsp/workspace_symbols default 500
4lsp/diagnostics, lsp/code_actions default 100
5lsp/batch default 200 per method

When the result was truncated, the structured output adds returned (number actually returned) and truncated (true) next to count (total found), and the text content notes how many results were shown. The truncation happens before the results are serialized, so asking for fewer results also makes the call faster. lsp/batch applies maxResults to every listed method (the default is 200 per method).

7. SECURITY

  • The server binds loopback only and requires a per-start random token.
  • Tools are hidden behind a whitelist: tools/list only returns the names in mcp.allowedTools, and calls to other tools are rejected. The default is empty, so enable the tools you want explicitly in coc-settings.json (see coc-config-mcp-allowedTools for the full list).
  • Public-key client auth: set mcp.authClientPublicKey (PEM) and give the bridge the matching private key via COC_MCP_AUTH_KEY_FILE (a path to a PEM file). When configured, the client authenticates by signing a server-issued nonce (coc/challenge) and the per-start token is no longer required. This is mandatory for --connect (see coc-mcp-ssh) so the token-bearing discovery file never has to leave the remote host. Generate a keypair with node bin/coc-mcp.js --generate-key. Off by default (token-only).
  • Path access is restricted to workspace roots and opened documents unless mcp.allowedPaths extends it; mcp.deniedPaths always wins.
  • Mutating tool calls are serialized across clients by a global write lock; read-only tools run in parallel.
  • Destructive tools carry MCP destructiveHint annotations so Codex prompts for confirmation before invoking them.

8. TROUBLESHOOTING

codex mcp list shows the server but tools do not appear:

  • Start coc.nvim with "mcp.autoStart": true, or run :CocCommand mcp.start, before starting Codex. The bridge exits if it cannot connect within five seconds.
  • Check that ~/.coc/mcp/coc-<pid>.json exists and its workspace contains the bridge working directory. Use --match-first to ignore the workspace.
  • If mcp.authClientPublicKey is configured but the bridge has no COC_MCP_AUTH_KEY_FILE, authentication fails: set the private key file or clear the config.

Edits are not visible on disk:

  • workspace/apply_edit saves all modified buffers with :wa after applying, so edited files are on disk when the tool returns (saved in the result). If saveError is present, save manually with document/write or :wa.

9. REMOTE OVER SSH

The server binds loopback only, so it cannot be reached over the network directly. To use it on a remote host, forward the MCP port over SSH and run the bridge locally. Public-key auth is required for this scenario: never copy the per-instance discovery file (coc-<pid>.json) to the local machine - it contains the per-start token, which also changes every time coc.nvim restarts.

Setup on the remote host:

bash snippet
1# generate a keypair once (do this locally or remotely, keep the
2# private key on the local machine):
3node /path/to/coc.nvim/bin/coc-mcp.js --generate-key
4 
5# set mcp.authClientPublicKey in the remote coc-settings.json to the
6# generated public key, then restart coc.nvim. Read the port from
7# ~/.coc/mcp/coc-<pid>.json.

On the local machine, forward the port and configure the bridge to connect to it with --connect, authenticated by the private key (no discovery file, no token):

bash snippet
1ssh -N -L 127.0.0.1:PORT:127.0.0.1:PORT user@remote-host
toml snippet
1[mcp_servers.coc]
2command = "node"
3args = ["/path/to/coc.nvim/bin/coc-mcp.js", "--connect=127.0.0.1:PORT"]
4enabled = true
5startup_timeout_sec = 30
6 
7[mcp_servers.coc.env]
8COC_MCP_AUTH_KEY_FILE = "/home/user/.config/coc-mcp/key.pem"

--connect accepts host:port (or a bare port, defaulting to 127.0.0.1; IPv6 like [::1]:port works too). The bridge sends coc/challenge, signs the nonce with the private key from COC_MCP_AUTH_KEY_FILE (path to a PEM file) and authenticates; the remote server must have mcp.authClientPublicKey set to the matching public key. Keep the private key private; never forward it to untrusted hosts.

10. RESOURCES AND NOTIFICATIONS

The server exposes read-only coc:// resources and push notifications for editor events. Both are opt-in: resources use the standard MCP requests, and notifications are only sent to sessions that subscribed.

RESOURCES

Use the standard MCP requests resources/list, resources/read and resources/templates/list:

text snippet
1coc://documents/{uri} Text content of an editor document (buffer
2 first, unsaved changes included); `{uri}` is
3 the URL-encoded file URI.
4coc://diagnostics Workspace diagnostics as JSON.
5coc://services Language server state and initialize
6 capabilities as JSON.
7coc://workspace Workspace root, folders and cwd as JSON.

Reading an unknown resource returns MCP error -32002.

NOTIFICATIONS

Subscribe per session with the coc/subscribe request:

jsonc snippet
1{ "jsonrpc": "2.0", "id": 1, "method": "coc/subscribe",
2 "params": { "events": ["coc/document_saved", "coc/diagnostics_changed"] } }

coc/unsubscribe removes subscriptions. Only subscribed sessions receive the events, and event names that do not start with coc/ are ignored:

text snippet
1coc/diagnostics_changed { uri, bufnr, version, diagnostics }
2coc/document_changed { uri, version, changes }
3coc/document_saved { uri, version, languageId }
4coc/workspace_folders_changed { added, removed }
5coc/editor_state_changed { uri, bufnr, languageId }
6coc/service_state_changed { id, state, languageIds }

The server also broadcasts notifications/tools/list_changed to all initialized sessions when the registered tool set changes (for example an extension registers a tool). Query the current server status at any time with the coc/status request (address, clients, tool list and protocol version).

See also: coc-config-mcp. vim:tw=78:nosta:noet:ts=8:sts=0:ft=help:noet:fen: