Skip to content
Coder Docs

Bring your own MCP servers

Add your own MCP servers to Coder Agents chats with a workspace .mcp.json file, without admin registration.

On this page

This page is for developers who want their own MCP servers in Coder Agents chats without asking an administrator to register each one. If you use claude mcp add in Claude Code, a .mcp.json file in your workspace is the closest equivalent. You write the file once, from your dotfiles or a startup script, and chats in that workspace get its tools.

Choose an approach

Coder Agents can get MCP tools from three places:

ApproachWho sets it upLasts across chatsAuthentication
Workspace .mcp.jsonYouYes, for every chat in the workspaceEnvironment variables (stdio) or headers (HTTP)
Organization MCP serversAn organization admin registers it, then you connectYes, based on the server's availability policyOAuth2, API key, custom headers, or OIDC
Inline MCP serversYou, through the chats API, once an admin enables itNo, you declare it on each chatHeaders only

Use an organization MCP server when the provider needs a browser sign-in. A workspace .mcp.json file has no OAuth2 flow.

The rest of this page covers the workspace .mcp.json file.

Before you begin

The agent reads .mcp.json from one place: the top level of the workspace agent's working directory. The template sets this directory with the dir argument of its coder_agent resource, for example /home/coder/project.

  • If the template sets dir, put your file at <dir>/.mcp.json.
  • If the template doesn't set dir, the agent reads no .mcp.json at all. Ask your template administrator to set dir.

The agent doesn't read ~/.claude.json, subdirectories, or parent directories. When the template sets dir, a new workspace terminal opens in that directory.

Write the file

.mcp.json uses the same mcpServers format as Claude Code. Each key is a server name, and Coder names that server's tools <server>__<tool>. A server name can't contain __ or start or end with _.

{
  "mcpServers": {
    "github": {
      "command": "github-mcp-server",
      "args": ["stdio"]
    },
    "example": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer <token>" }
    }
  }
}

A server with command runs as a local process (stdio) in the workspace, started in the working directory. A server with url connects over streamable HTTP from the workspace, so it can reach anything the workspace can reach, including localhost. To choose the transport explicitly, set type to stdio, http, or sse.

Keep tokens out of the file

Store each token as a user secret with an environment variable target. Coder injects user secrets into every workspace you own, and stdio servers inherit them from the agent. If the workspace is already running when you create the secret, restart the workspace so its servers get the new variable.

For example, the GitHub MCP server reads GITHUB_PERSONAL_ACCESS_TOKEN. A user secret that targets that variable needs no entry in .mcp.json:

echo -n "$TOKEN" | coder secret create github-mcp-token \
  --env GITHUB_PERSONAL_ACCESS_TOKEN

Set the secret's --env target to the variable your server expects. The env block in .mcp.json can't map one name to another here. Coder expands ${VAR} in env values from the agent's own process environment, which doesn't include user secrets. An env entry that references a user secret, such as "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}", expands to an empty string and replaces the secret's value, so remove it. Coder doesn't expand variables in command, args, url, or headers.

HTTP servers take credentials in headers, which Coder reads literally. To keep those tokens out of your dotfiles repository, have your startup script copy them from a user secret into the file, as the next section shows. The token then sits in plain text in the workspace, so the script restricts the file to your user.

Create the file when the workspace starts

A script that writes .mcp.json at startup gives every workspace you create the same servers. User secrets are available to startup scripts, so the script can fill in header tokens.

This script writes both servers from the earlier example. Set WORKDIR to your agent's working directory, and replace EXAMPLE_API_TOKEN with your user secret's environment variable. The script waits for the template to clone a repository into the working directory, because a clone skips a directory that isn't empty. If your template doesn't clone into the working directory, remove the wait loop.

#!/usr/bin/env bash
set -euo pipefail

WORKDIR="$HOME/project"

# Wait for the template to clone the repository into the working directory.
# Writing first makes the directory non-empty, which stops the clone.
for _ in $(seq 60); do
  [ -d "$WORKDIR/.git" ] && break
  sleep 5
done
[ -d "$WORKDIR" ] || exit 0

umask 077
cat > "$WORKDIR/.mcp.json" <<JSON
{
  "mcpServers": {
    "github": {
      "command": "github-mcp-server",
      "args": ["stdio"]
    },
    "example": {
      "type": "http",
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer ${EXAMPLE_API_TOKEN}" }
    }
  }
}
JSON
chmod 600 "$WORKDIR/.mcp.json"

Run the script from one of these places:

  • Dotfiles: save it as install.sh in your dotfiles repository, or call it from your existing install script. Run chmod +x install.sh before you commit it, because coder dotfiles runs only an executable script. The template must run coder dotfiles, usually through the dotfiles module.
  • Personalize script: save it as ~/personalize, then run chmod +x ~/personalize. The template must include the personalize module.

The agent connects to the servers after startup scripts finish. It also watches the file, so a .mcp.json that appears later still loads without a workspace restart.

If the working directory is a Git repository

The script writes into your project, so keep the file out of commits:

grep -qxF ".mcp.json" "$WORKDIR/.git/info/exclude" || echo ".mcp.json" >> "$WORKDIR/.git/info/exclude"

If the repository already commits a .mcp.json, do not overwrite it. Merge your servers into the committed file, then tell Git to ignore your local change:

jq -s '.[0] * .[1]' "$WORKDIR/.mcp.json" ~/my-mcp.json > ~/.mcp.merged.json
chmod 600 ~/.mcp.merged.json
mv ~/.mcp.merged.json "$WORKDIR/.mcp.json"
git -C "$WORKDIR" update-index --skip-worktree .mcp.json

In this example, ~/my-mcp.json holds your own mcpServers entries.

Reuse a Claude Code setup

By default, claude mcp add writes to ~/.claude.json, which Coder doesn't read. To write .mcp.json instead, run it with --scope project from the working directory:

cd ~/project
claude mcp add --scope project --transport http example https://mcp.example.com/mcp

A file from Claude Code works in Coder with three differences:

  • Coder accepts type values of stdio, http, and sse. If a file uses streamable-http, change it to http.
  • Coder expands variables only in env values, as Keep tokens out of the file describes.
  • Coder doesn't ask you to approve the servers in .mcp.json. The agent starts every server the file lists as soon as it finds the file, so a repository in the working directory runs the commands in its .mcp.json. Review a repository's .mcp.json before you clone it into the working directory.

Check that your servers loaded

Start a new chat in the workspace and, outside plan mode, ask the agent which tools it has. Tools from your file have names like github__<tool>.

If a server is missing:

  1. Confirm that .mcp.json sits at the top level of the working directory.
  2. Run jq . .mcp.json in the working directory to confirm that the file is valid JSON.
  3. Run the server's command in a workspace terminal to confirm that it starts.
  4. Read the workspace agent log, /tmp/coder-agent.log by default on Linux and macOS, for the connection error.

One invalid entry, such as a server name that breaks the naming rules, makes the agent skip every server in the file. The agent skips a server that doesn't connect within 30 seconds, and the other servers still load. Each tool call has a 60 second timeout and needs a running workspace.

Update your servers

Edit .mcp.json at any time. The agent reloads its servers without a workspace restart, and new chats get the new tools right away.

Existing chats in the workspace get the change too. After the agent reloads, each chat that uses the workspace gets the new tools from its next message. An MCP-only change doesn't mark a chat out of date, so you don't need to refresh its context.

Note

Template administrators can point the agent at other files with the experimental CODER_AGENT_EXP_MCP_CONFIG_FILES variable, set in the agent's process environment. It takes a comma-separated list of paths and replaces the default .mcp.json. Relative paths resolve against the working directory, and absolute or ~/ paths work even when the template doesn't set dir. For example, .mcp.json,~/.coder/mcp.json keeps the project file and adds one in your home directory.

Learn more