# MCP servers

> Register MCP servers in config.toml (stdio and HTTP), timeouts and env, enable/disable, OAuth-related MCP credentials, and grok mcp management commands.

- Repository: xai-org/grok-build
- GitHub: https://github.com/xai-org/grok-build
- Human docs: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458
- Complete Markdown: https://grok-wiki.com/public/docs/xai-org-grok-build-90205de50458/llms-full.txt

## Source Files

- `crates/codegen/xai-grok-pager/docs/user-guide/07-mcp-servers.md`
- `crates/codegen/xai-grok-mcp/src/servers.rs`
- `crates/codegen/xai-grok-mcp/src/lib.rs`
- `crates/codegen/xai-grok-mcp/src/wire.rs`
- `crates/codegen/xai-grok-shell/src/util/config/mcp.rs`
- `crates/codegen/xai-grok-pager/src/mcp_cmd.rs`
- `crates/codegen/xai-grok-shell/src/mcp_doctor.rs`

---

---
title: "MCP servers"
description: "Register MCP servers in config.toml (stdio and HTTP), timeouts and env, enable/disable, OAuth-related MCP credentials, and grok mcp management commands."
---

Grok Build loads MCP (Model Context Protocol) servers from `config.toml` and vendor-compatible files, spawns or connects them at session start, and exposes their tools to the model as namespaced integration tools via `search_tool` and `use_tool`. Native configuration lives under `[mcp_servers.<name>]` in `~/.grok/config.toml` and optional project `.grok/config.toml` files. CLI management is `grok mcp` (`list`, `add`, `remove`, `doctor`). OAuth tokens for remote MCP servers are stored separately from xAI login credentials in `$GROK_HOME/mcp_credentials.json` (default `~/.grok/mcp_credentials.json`).

## Architecture

```mermaid
flowchart TB
  subgraph sources [Config sources]
    UT["~/.grok/config.toml<br/>[mcp_servers]"]
    PT[".grok/config.toml<br/>project chain"]
    CL["~/.claude.json"]
    CU[".cursor/mcp.json"]
    MJ[".mcp.json"]
    PL["plugins"]
  end

  subgraph shell [Shell load and merge]
    MERGE["load_mcp_servers / merge<br/>name winner by priority"]
    DIS["disabled_mcp_servers<br/>enabled = false"]
  end

  subgraph runtime [MCP runtime xai-grok-mcp]
    STDIO["stdio TokioChildProcess"]
    HTTP["HTTP / SSE streamable"]
    OAUTH["OAuth + mcp_credentials.json"]
    CLIENT["McpClient<br/>handshake + tools"]
  end

  subgraph model [Model surface]
    ST["search_tool"]
    UT2["use_tool<br/>server__tool"]
  end

  UT --> MERGE
  PT --> MERGE
  CL --> MERGE
  CU --> MERGE
  MJ --> MERGE
  PL --> MERGE
  MERGE --> DIS
  DIS --> CLIENT
  CLIENT --> STDIO
  CLIENT --> HTTP
  HTTP --> OAUTH
  CLIENT --> ST
  CLIENT --> UT2
```

Merge priority for name conflicts (higher wins; lower sources use insert-if-absent):

| Priority | Source | Notes |
|----------|--------|--------|
| Highest | `config.toml` chain | User `~/.grok/config.toml`, then project `.grok/config.toml` from repo root toward cwd (closest wins) |
| | `~/.claude.json` | Gated by `[compat.claude] mcps`; skipped after Claude import marker |
| | Cursor `.cursor/mcp.json` | Project then global; gated by `[compat.cursor] mcps` |
| Lowest | `.mcp.json` | Walk cwd → git root; skipped after Claude import marker |

Same-name project entries **replace** user entries entirely (no field-level merge). Plugins can also contribute MCP servers; `grok mcp doctor` reports them as `plugin: <name>` sources.

## Prerequisites

- Grok installed and on `PATH` (`grok --version`)
- For remote OAuth servers: a browser-capable session (interactive TUI or doctor with interactive OAuth)
- For stdio servers: the command binary on `PATH` (or an absolute path)
- Project-scoped MCP from working-tree configs may require folder trust before spawn

## Register a server

### stdio (local process)

```toml
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/dir"]
env = { API_KEY = "${MY_API_KEY}" }
enabled = true
startup_timeout_sec = 30
tool_timeout_sec = 6000
tool_timeouts = { slow_op = 120 }
```

| Field | Type | Default | Description |
|-------|------|---------|-------------|
| `command` | string | required | Executable or bare launcher (`npx`, `uvx`, …) |
| `args` | string[] | `[]` | Arguments after the command |
| `env` | map | optional | Extra environment for the child process |
| `cwd` | string | optional | Working directory (MCP JSON supports it; ACP stdio may not forward it) |
| `enabled` | bool | `true` | When false, server is omitted from the live set |
| `startup_timeout_sec` | u64 | global default **30** | Handshake timeout (seconds) |
| `tool_timeout_sec` | u64 | **6000** | Default per-tool call timeout (seconds) |
| `tool_timeouts` | map | optional | Per bare tool name → seconds |
| `expose_image_base64` | bool | `false` | Keep raw base64 in tool-result text for path-based forwarding |

String fields (`command`, `args`, `env` values, `url`, `headers` values) expand `${VAR}` / `${VAR:-default}` at load time.

On Windows, bare commands such as `npx` resolve via `PATH` / `PATHEXT` (including `.cmd` shims) before spawn.

### HTTP / SSE (remote)

```toml
[mcp_servers.sentry]
url = "https://mcp.sentry.dev/mcp"
enabled = true
headers = { "Authorization" = "Bearer ${SENTRY_TOKEN}" }

# Or inject a bearer token from an env var without putting it in headers:
# bearer_token_env_var = "SENTRY_TOKEN"
```

| Field | Type | Description |
|-------|------|-------------|
| `url` | string | Remote MCP endpoint (`http://` or `https://`) |
| `type` | string | Set to `"sse"` for SSE; also inferred when URL ends with `/sse` |
| `headers` | map | Static HTTP headers (values support `${VAR}`) |
| `bearer_token_env_var` | string | Env var whose value becomes `Authorization: Bearer …` |
| `oauth_client_id` | string | BYO OAuth client ID (non-DCR providers) |
| `oauth_client_secret_env_var` | string | Env var holding client secret |
| `oauth_scopes` | string[] | Scopes requested during authorization |
| `oauth` | table | Alternate OAuth block: `client_id`, `client_secret_env_var`, `scopes`, `callback_port` |

Prefer native `url` over wrapping a remote endpoint in a stdio proxy (`npx mcp-remote …`). Grok handles HTTP/SSE and OAuth directly.

Session-id placeholder in headers (when supported by the host wiring):

```toml
headers = { "x-mcp-session-id" = "{{session_id}}" }
```

### Global MCP knobs

```toml
[mcp]
startup_timeout_sec = 60   # global handshake default (seconds)
max_output_bytes = 40000   # inline tool-result cap (bytes)
```

**Startup timeout precedence** (global fallback; per-server `startup_timeout_sec` still wins):

1. `requirements.toml` `[mcp].startup_timeout_sec`
2. Env: `MCP_TIMEOUT` (milliseconds, rounded up) then `GROK_MCP_STARTUP_TIMEOUT_SECS` (seconds)
3. Effective `config.toml` `[mcp].startup_timeout_sec`
4. Remote settings
5. Built-in **30** seconds

**Inline MCP output cap** (default **20_000** bytes; full payload may spill under the session `mcp/` folder):

1. `requirements.toml` `[mcp] max_output_bytes`
2. Env: `GROK_MAX_MCP_OUTPUT_BYTES` then `MAX_MCP_OUTPUT_BYTES` (Grok-native wins if both set)
3. Effective / project `config.toml` `[mcp] max_output_bytes` (cwd-aware path when applicable)
4. Remote settings
5. Default 20_000

Related feature flags (defaults true unless requirements/managed override):

| Config / env | Effect |
|--------------|--------|
| `[features] mcp_liveness_watchers` / `GROK_MCP_LIVENESS_WATCHERS` | Liveness watchers |
| `[features] mcp_auto_restart` / `GROK_MCP_AUTO_RESTART` | Auto-restart failed servers |
| `[features] mcp_push_server_status` / `GROK_MCP_PUSH_SERVER_STATUS` | Push server status to UI |
| `[features] mcp_recursive_config_watch` / `GROK_MCP_RECURSIVE_CONFIG_WATCH` | Recursive config file watch |

## CLI: `grok mcp`

### List

```bash
grok mcp list
grok mcp list --json
```

Lists servers from user and project scopes (nearest project definition wins). Human output marks `(project)` and `(disabled)`.

### Add

```bash
# stdio — everything after -- is the server command (flags like -y reach the server)
grok mcp add filesystem -- npx -y @modelcontextprotocol/server-filesystem /path/to/dir

# stdio with env (-e / --env is one KEY=value per flag)
grok mcp add postgres -e DATABASE_URL=postgres://localhost/mydb -- npx -y @modelcontextprotocol/server-postgres

# HTTP / SSE
grok mcp add --transport http sentry https://mcp.sentry.dev/mcp
grok mcp add --transport http api https://mcp.example.com/mcp --header "Authorization: Bearer TOKEN"
grok mcp add --transport sse linear https://mcp.linear.app/sse

# Project config (./.grok/config.toml)
grok mcp add --scope project github -- npx -y @modelcontextprotocol/server-github
```

| Flag | Description |
|------|-------------|
| `--transport` / `-t` | `stdio` (default), `http`, or `sse` |
| `--scope` / `-s` | `user` (default → `~/.grok/config.toml`) or `project` (→ `./.grok/config.toml`) |
| `-e` / `--env` | Repeatable `KEY=value` (stdio only) |
| `-H` / `--header` | Repeatable `Name: value` (HTTP/SSE only) |

Constraints:

- Server names: letters, numbers, hyphens, underscores only
- Default transport is stdio; a URL-looking command without `--transport http` stays stdio and emits a warning
- `--env` is not greedy (`-e A=1 -e B=2`, not multi-value `--env`)
- Newly added servers start enabled and are cleared from `disabled_mcp_servers` if present

### Remove

```bash
grok mcp remove github
grok mcp remove github --scope user
grok mcp remove github --scope project
```

- Exit **0** when the entry is removed
- Exit **1** if the name is missing, or defined in **both** scopes without `--scope`
- Also removes matching OAuth credentials for that server name from the global credential store
- If a definition remains in another scope/ancestor, stderr notes that the name still resolves

### Doctor

```bash
grok mcp doctor
grok mcp doctor github
grok mcp doctor --json
```

Discovers servers from the same merge path as a session (including plugins and, when eligible, managed sources), then for each server:

1. Command exists (stdio) or URL present (remote)
2. Start / connect
3. Handshake
4. `tools/list` (fails the check if zero tools)

Exit **1** when any checked server fails or a named server is not found. Use doctor to catch cold-start `npx`/`uvx` timeout issues before a session.

## Project-scoped servers

```
my-project/
  .grok/
    config.toml    # [mcp_servers.*] shared with the team
  src/
```

| Location | Scope | Override strength |
|----------|-------|-------------------|
| `~/.grok/config.toml` | User | Lowest |
| `<repo-root>/.grok/config.toml` | Project | Medium |
| `<cwd>/.grok/config.toml` | Project (nearest) | Highest |

Project files contribute `[mcp_servers]`, `[plugins]`, and `[permission]` (most other sections stay user-global). Prefer `${VAR}` secrets in committed project configs.

## Enable, disable, and tool filters

Two disable paths:

1. Per-server `enabled = false` under `[mcp_servers.<name>]`
2. Top-level list:

```toml
disabled_mcp_servers = ["filesystem", "github"]
```

Runtime toggles from the TUI `/mcps` modal write `disabled_mcp_servers` (and may also flip `enabled` on local TOML servers). Disabled tool names persist under:

```toml
[disabled_mcp_tools]
github = ["create_issue", "delete_repo"]
```

### Runtime modal (`/mcps`)

- Open with `/mcps`, or via the plugins/MCP UI tab (`Ctrl+L` on non–VS Code family terminals)
- Space: enable/disable
- Expand: list tools
- `r`: refresh after editing config
- `i`: authenticate OAuth servers
- `a` / `x`: add / remove

## Tool naming and model tools

Qualified tool names use the delimiter `__`:

| Server | Tool | Model-facing name |
|--------|------|-------------------|
| `filesystem` | `read_file` | `filesystem__read_file` |
| `github` | `create_issue` | `github__create_issue` |

Built-in meta tools:

| Tool | Role |
|------|------|
| `search_tool` | Keyword/schema discovery across enabled MCP servers (status may be `"partial"` while servers still connect) |
| `use_tool` | Invoke a discovered tool by fully qualified name; arguments go in `tool_input` |

Permissions treat the **underlying** MCP tool as the effective tool for hooks and allow/deny rules when the call goes through `use_tool`.

## OAuth and credentials

Remote MCP OAuth is separate from xAI `auth.json` login:

| Item | Location / behavior |
|------|---------------------|
| Credential store | `$GROK_HOME/mcp_credentials.json` (keys `"{server_name}:{server_url}"`) |
| Lock file | `mcp_credentials.json.lock` (cross-process safe insert) |
| Flow | Prefer refresh; else browser consent on loopback `http://127.0.0.1:<port>/callback` |
| BYO client | `oauth_client_id` / `oauth.*` plus optional `callback_port` |
| Cleanup | `grok mcp remove` deletes credentials for that server name |

```toml
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
enabled = true

# Providers without Dynamic Client Registration:
# oauth_client_id = "…"
# oauth_client_secret_env_var = "LINEAR_MCP_CLIENT_SECRET"
# oauth_scopes = ["read", "write"]
```

Authenticate from `/mcps` (`i`) or during an interactive connect. After editing `config.toml`, refresh the modal with `r`.

## Vendor compatibility

| Source | Format | Gate |
|--------|--------|------|
| Grok TOML | `[mcp_servers]` | Always |
| Claude | `mcpServers` in `~/.claude.json` (+ per-project) | `[compat.claude] mcps` / `GROK_CLAUDE_MCPS_ENABLED` |
| Cursor | `mcpServers` in `.cursor/mcp.json` | `[compat.cursor] mcps` / `GROK_CURSOR_MCPS_ENABLED` |
| MCP JSON | project `.mcp.json` | Loaded until Claude import is marked |

Inspect resolved load with:

```bash
grok inspect
grok inspect --json
```

## Example configurations

<Tabs>
  <Tab title="Hosted HTTP + OAuth">

```toml
[mcp_servers.linear]
url = "https://mcp.linear.app/mcp"
enabled = true

[mcp_servers.sentry]
url = "https://mcp.sentry.dev/mcp"
enabled = true
```

  </Tab>
  <Tab title="Static bearer">

```toml
[mcp_servers.internal-tools]
url = "https://mcp.internal.example.com/mcp"
enabled = true
headers = { "Authorization" = "Bearer ${INTERNAL_MCP_TOKEN}" }
```

  </Tab>
  <Tab title="Local stdio">

```toml
[mcp_servers.filesystem]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/dir"]
startup_timeout_sec = 60

[mcp_servers.postgres]
command = "npx"
args = ["-y", "@modelcontextprotocol/server-postgres", "postgresql://user:pass@localhost/db"]
env = { PGSSLMODE = "disable" }
```

  </Tab>
</Tabs>

Confirm current package names and OAuth endpoints with each provider before shipping project config.

## Verification

```bash
# 1. Config parses and lists
grok mcp list

# 2. Connectivity and tools/list
grok mcp doctor --json

# 3. Resolved multi-source view
grok inspect

# 4. Manual stdio smoke (outside Grok)
npx -y @modelcontextprotocol/server-filesystem /path
```

Expected doctor outcome for a healthy server: command/URL check pass, start under the startup timeout, handshake OK, non-zero tool count.

## Troubleshooting

| Symptom | What to check |
|---------|----------------|
| Server missing from session | `enabled`, `disabled_mcp_servers`, project override shadowing user, folder trust, compat gates |
| Startup timeout | Raise `startup_timeout_sec` or global `MCP_TIMEOUT` / `GROK_MCP_STARTUP_TIMEOUT_SECS`; cold `npx`/`uvx` often needs >30s |
| Spawn / handshake fail (stdio) | `~/.grok/logs/mcp/<server>.stderr.log` (truncated each launch) |
| OAuth loop / unauthorized | Re-auth in `/mcps`; inspect `mcp_credentials.json`; verify `bearer_token_env_var` is set |
| Empty tool list | Server config or upstream connector; doctor reports `0 tools discovered` |
| Large results truncated | Raise `[mcp] max_output_bytes` or `GROK_MAX_MCP_OUTPUT_BYTES` |
| Remove fails with multi-scope | Pass `--scope user` or `--scope project` |
| Debug log stream | `RUST_LOG=debug GROK_LOG_FILE=/tmp/grok.log grok` and filter lines containing `mcp` |

```bash
tail -f ~/.grok/logs/mcp/filesystem.stderr.log
```

## Related pages

<CardGroup>
  <Card title="Configure Grok" href="/configure-grok">
    Config precedence, feature flags, and `grok inspect`.
  </Card>
  <Card title="Configuration reference" href="/configuration-reference">
    Schema keys for `[mcp_servers]`, `[mcp]`, `[compat]`, and env counterparts.
  </Card>
  <Card title="Permissions and safety" href="/permissions-and-safety">
    How MCP / `use_tool` calls enter the allow–deny–ask pipeline.
  </Card>
  <Card title="Plugins" href="/plugins">
    Plugins that bundle additional MCP servers.
  </Card>
  <Card title="CLI reference" href="/cli-reference">
    Full `grok mcp` surface and shared runtime flags.
  </Card>
  <Card title="Troubleshooting" href="/troubleshooting">
    MCP startup failures and broader diagnostics.
  </Card>
</CardGroup>
