# Bring your own MCP servers

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:

| Approach                                                                                                   | Who sets it up                                       | Lasts across chats                             | Authentication                                  |
| ---------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- | ---------------------------------------------- | ----------------------------------------------- |
| Workspace `.mcp.json`                                                                                      | You                                                  | Yes, for every chat in the workspace           | Environment variables (stdio) or headers (HTTP) |
| [Organization MCP servers](/docs/ai-coder/agents/extending-agents/#connect-to-mcp-servers)                 | An organization admin registers it, then you connect | Yes, based on the server's availability policy | OAuth2, API key, custom headers, or OIDC        |
| [Inline MCP servers](/docs/ai-coder/agents/platform-controls/mcp-servers/#inline-mcp-servers-experimental) | You, through the chats API, once an admin enables it | No, you declare it on each chat                | Headers 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 `_`.

```json
{
  "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](/docs/user-guides/user-secrets/) 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`:

```sh
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.

```sh
#!/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](/docs/user-guides/workspace-dotfiles/), 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](https://registry.coder.com/modules/dotfiles).
* **Personalize script**: save it as `~/personalize`, then run `chmod +x ~/personalize`.
  The template must include the [personalize module](https://registry.coder.com/modules/personalize).

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:

```sh
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:

```sh
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`](https://code.claude.com/docs/en/mcp) writes to `~/.claude.json`, which Coder doesn't read.
To write `.mcp.json` instead, run it with `--scope project` from the working directory:

```sh
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](#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.

<Callout type="info" title="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.
</Callout>

## Learn more

* [Workspace MCP tools](/docs/ai-coder/agents/extending-agents/#workspace-mcp-tools)
* [MCP servers](/docs/ai-coder/agents/platform-controls/mcp-servers/) for administrators
* [User secrets](/docs/user-guides/user-secrets/)
* [Dotfiles](/docs/user-guides/workspace-dotfiles/)
