For the complete documentation index, see llms.txt. This page is also available as Markdown.

Config file reference

Location

Default: ~/.config/mcp/servers.json

Override: MCP_CONFIG_PATH environment variable.

Schema

{
  "mcpServers": {
    "<name>": <ServerConfig>,
    ...
  }
}

ServerConfig

Three variants, distinguished by their fields:

Stdio server

{
  "command": "npx",
  "args": ["-y", "package-name"],
  "env": {
    "KEY": "value"
  }
}
Field
Type
Default
Description

command

string

required

Executable to spawn

args

string[]

[]

Arguments passed to the command

env

object

{}

Environment variables for the process

tool_acl

object

null

Manual read/write classification overrides (see Tool ACL overrides)

idle_timeout

string

"adaptive"

Idle shutdown policy (see Idle timeout)

min_idle_timeout

string

"1m"

Minimum idle timeout for adaptive mode

max_idle_timeout

string

"5m"

Maximum idle timeout for adaptive mode

HTTP server

Field
Type
Default
Description

url

string

required

Server endpoint URL

headers

object

{}

HTTP headers for every request

tool_acl

object

null

Manual read/write classification overrides (see Tool ACL overrides)

idle_timeout

string

"adaptive"

Idle shutdown policy (see Idle timeout)

min_idle_timeout

string

"1m"

Minimum idle timeout for adaptive mode

max_idle_timeout

string

"5m"

Maximum idle timeout for adaptive mode

CLI server

Field
Type
Default
Description

command

string

required

CLI executable to wrap

cli

bool

required

Must be true — marks this as a CLI server

cli_help

string

"--help"

Flag used to discover subcommands and options

cli_depth

number

2

How deep to recurse into subcommands for flag discovery

cli_only

string[]

[] (all)

Whitelist of subcommands to expose

args

string[]

[]

Base arguments prepended to every invocation

env

object

{}

Environment variables for the CLI process

tools

array

[]

Preset tool definitions (skips auto-discovery when set)

tool_acl

object

null

Manual read/write classification overrides (see Tool ACL overrides)

idle_timeout

string

"adaptive"

Idle shutdown policy (see Idle timeout)

min_idle_timeout

string

"1m"

Minimum idle timeout for adaptive mode

max_idle_timeout

string

"5m"

Maximum idle timeout for adaptive mode

See the CLI as MCP guide for discovery details and examples.

Idle timeout

Controls when the proxy shuts down idle backend connections to reclaim resources. Applies to both stdio and HTTP backends in proxy mode (mcp serve).

Policy values

Value
Behavior

"adaptive" (default)

Timeout adjusts based on usage frequency — frequently used backends stay alive longer

"never"

Never shut down — backend stays connected for the entire proxy lifetime

"<duration>"

Fixed timeout (e.g. "3m", "30s", "1h")

Duration format: number followed by s (seconds), m (minutes), or h (hours). Plain numbers are treated as seconds.

Adaptive mode

When idle_timeout is "adaptive" (the default), the proxy tracks how often each backend is used and assigns a timeout tier:

Usage tier
Requests/hour
Idle timeout

Hot

> 20

5 min

Warm

5–20

3 min

Cold

< 5

1 min

The tier is computed from the backend's total request count divided by its uptime. The result is clamped between min_idle_timeout (default 1m) and max_idle_timeout (default 5m).

When a backend is shut down due to inactivity, its tools remain visible in tools/list. On the next tools/call, the proxy reconnects automatically (lazy initialization). Usage history is preserved across reconnections so the adaptive algorithm has continuity.

Examples

Tool ACL overrides

The proxy ships with an automatic classifier that labels every tool of every upstream MCP as read, write, or ambiguous (treated as write, fail-safe). The classifier is auditable — run mcp acl classify to see the verdict, confidence, source, and reasons for each tool.

When the classifier is wrong (or when you just want to be explicit), add tool_acl to any server and pin individual tools to read or write using the same glob syntax as the ACL rules (*, prefix, suffix, contains).

Semantics:

  • Both read and write are optional — omit either or both.

  • Overrides run before the classifier. A tool that matches an override is never scored.

  • The same pattern string may not appear in both read and write for the same server — that fails loudly at load time.

  • If two different globs on the same server both match a single tool name (e.g. get_* in read and *_thing in write both match get_thing), the proxy fails safe to write at classification time. Narrow your globs to avoid this.

  • Overrides are never cached — they are re-read from the config on every startup.

For the full redesign plan and the token/description dictionaries the classifier uses, see docs/acl-redesign-plan.md.

Type detection

The config uses serde's untagged enum deserialization. The type is inferred from the fields:

  • Has command + cli: true → CLI

  • Has command (without cli) → Stdio

  • Has url → HTTP

CLI is checked first, then Stdio, then HTTP.

Environment variable substitution

Any ${VAR_NAME} in a string value is replaced with the env var's value at load time.

Missing env vars resolve to empty string "".

Reserved names

These names cannot be used as server names:

  • search

  • add

  • remove

  • list

  • help

  • version

Using a reserved name won't break the config, but you'll get a warning and the server may be shadowed by built-in commands.

Server authentication (serverAuth)

Optional. Configures authentication for mcp serve --http. Ignored for direct CLI usage.

Providers

providers is an array of provider names, evaluated in order as a chain. The first provider that accepts the request wins; if all reject, the chain returns the error of the first provider configured (oracle-resistant). Empty array (or omitted) is equivalent to anonymous access.

Value
Description

"none"

Anonymous identity (subject: "anonymous", no roles). Useful for testing.

"bearer"

Static bearer token validation. Reads from bearer sub-config.

"forwarded"

Trust reverse proxy header (e.g. X-Forwarded-User). Reads from forwarded sub-config.

"oauth_as"

OAuth 2.0 Authorization Server with Dynamic Client Registration — the route required by Claude.ai / ChatGPT / Cursor. Reads from oauthAs sub-config. See the OAuth AS how-to.

Schema change. Earlier versions used a single provider: "..." string. The new schema is the array providers: [...]. Configs carrying the legacy field deserialize into an empty providers list and boot as NoAuth — silently weakening auth, so an explicit migration is required. Booting with a missing sub-config (e.g. "bearer" listed without a bearer block) fails at startup rather than degrading.

Bearer config

Required when "bearer" is in providers. Each entry in tokens accepts two shapes:

  • Legacy (string): "<token>": "<subject>" — subject only, no roles.

  • Extended (object): "<token>": { "subject": "<subject>", "roles": ["<role>", ...] } — subject plus roles used by ACL evaluation.

Both forms can coexist in the same file.

Field
Type
Description

tokens

object

Map of token → subject string or {subject, roles} object

Each extended entry:

Field
Type
Default
Description

subject

string

required

User identity for this token

roles

string[]

[]

Roles assigned to this identity (used by ACL rules)

Forwarded config

Optional when "forwarded" is in providers. Reads the authenticated user from a header set by a trusted reverse proxy, and optionally reads a groups header to populate roles.

Field
Type
Default
Description

header

string

"x-forwarded-user"

Header name to read the authenticated user from

groups_header

string

"x-forwarded-groups"

Header name to read roles from (comma-separated, oauth2-proxy convention)

Groups header value is parsed as a comma-separated list: each entry is trimmed and empty entries are dropped. Missing header yields empty roles (not an error). Role matching is case-sensitive.

Only use forwarded behind a trusted reverse proxy. The proxy must strip these headers from incoming client requests — otherwise a client could forge identity and roles.

OAuth AS config (oauthAs)

Required when "oauth_as" is in providers. Turns mcp serve into an OAuth 2.0 Authorization Server with Dynamic Client Registration so Claude.ai, ChatGPT, Cursor and other AI clients can connect. User authentication is delegated to a trusted reverse proxy (oauth2-proxy / Cloudflare Access / Pomerium) — mcp serve never handles passwords.

Field
Type
Required
Default
Description

issuerUrl

string

yes

Public origin the AS advertises in metadata and embeds as iss in JWTs. Must match what clients reach.

jwtSecret

string

yes

HMAC-SHA256 signing key. Must be ≥ 32 bytes — boot fails otherwise.

trustedUserHeader

string

no

"x-forwarded-user"

Header read at /authorize to identify the human.

trustedGroupsHeader

string

no

"x-forwarded-groups"

Comma-separated → JWT groups claim.

trustedSourceCidrs

string[]

yes

CIDRs allowed to reach /authorize. Empty list rejected at boot — without it any client could spoof the trusted user header.

accessTokenTtlSeconds

number

no

3600

JWT lifetime.

refreshTokenTtlSeconds

number

no

2592000 (30d)

Refresh-token lifetime.

authorizationCodeTtlSeconds

number

no

60

Authorization-code lifetime.

scopesSupported

string[]

no

[]

Scopes advertised in metadata.

redirectUriAllowlist

string[]

yes

Patterns clients may register. Trailing * allowed (for ChatGPT-style URIs).

injectedRoles

string[]

no

[]

Roles always added to issued JWTs — useful as a "came in via OAuth" marker for ACL discrimination.

State (registered clients via DCR + refresh tokens) persists to auth_server.json in the config directory; override with MCP_AUTH_SERVER_PATH or inline via MCP_AUTH_SERVER_CONFIG. See the OAuth AS how-to for setup, security notes, and troubleshooting.

ACL config

Optional. Controls which users can access which tools. Supports two schemas: role-based (recommended) and legacy (backward compatible). Detection is automatic — see schema detection.

Field
Type
Default
Description

default

"allow" | "deny"

"allow"

Policy when no grant matches

strictClassification

bool

false

Block ambiguous tools entirely (require explicit override)

roles

object

{}

Map of role name → list of grants

subjects

object

{}

Map of subject → { roles, extra }

Grant

Field
Type
Default
Description

server

string or string[]

required

Server alias(es) to match ("*" = any)

access

"read" | "write" | "*"

required

Access level

tools

string[]

[] (all tools)

Tool name globs to narrow the grant

resources

string[]

[] (all resources)

Resource URI globs to narrow the grant. Same * syntax as tools.

prompts

string[]

[] (all prompts)

Prompt name globs to narrow the grant. Same * syntax as tools.

deny

bool

false

Turns grant into explicit deny (always wins over allows)

Subject config

Field
Type
Default
Description

roles

string[]

[]

Roles assigned to this subject (merged with token roles)

extra

Grant[]

[]

Additional per-subject grants

Access expansion

access

Read tools

Write tools

Ambiguous tools

Ambiguous (strict)

"read"

allowed

denied

denied

denied

"write"

denied

allowed

allowed

denied

"*"

allowed

allowed

allowed

denied

Evaluation model

  1. Collect all grants from all roles (token roles + subject config roles) + extra

  2. Filter to grants matching the target server and tool

  3. If any matching grant has deny: truedeny

  4. If any matching allow grant covers the access level → allow

  5. No match → apply default

Union-based, order-independent. Deny always wins.

Legacy schema

Field
Type
Default
Description

default

"allow" | "deny"

"allow"

Default policy when no rule matches

rules

array

[]

Ordered list of ACL rules (first match wins)

Legacy ACL rule

Field
Type
Default
Description

subjects

string[]

[] (match all)

User subjects to match (* = any)

roles

string[]

[] (match all)

Roles to match (* = any)

tools

string[]

required

Tool name patterns (supports * wildcards — prefix, suffix, middle, multiple)

policy

"allow" | "deny"

required

Action when rule matches

Both subjects and roles must match for a rule to apply. Empty means "match all".

Schema detection

JSON keys present
Schema used

roles (as object) or subjects (as object)

Role-based

rules (as array)

Legacy

Both rules and roles/subjects

Config error

Neither

Legacy with default allow

Tool pattern glob syntax

The tools field supports glob patterns with * wildcards (both schemas):

Pattern
Matches
Example

sentry__*

Anything starting with sentry__

sentry__search_issues

*_issues

Anything ending with _issues

search_issues, sentry__list_issues

*admin*

Anything containing admin

admin_panel, user_admin_tools

sentry__*_admin__*

Multiple wildcards

sentry__team_admin__delete

my_tool

Exact match (no wildcards)

my_tool

*

Everything

any tool

Auth store

Tokens and OAuth client registrations are stored separately in:

Keys are normalized server URLs (trailing slash removed).

Last updated

Was this helpful?