Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Configuration reference

One file, TOML, meant to be edited by hand. mcpgw's own write commands go through a syntax tree rather than a serializer, so your comments and ordering survive add, remove, enable and disable.

Location

~/.config/mcpgw/config.toml

The same path on Linux, macOS and Windows — the dev-CLI convention that git, gh and ripgrep use, rather than platform-native config directories. Resolution order:

  1. $MCPGW_CONFIG — a full path to the file, not a directory. Wins over everything.
  2. $XDG_CONFIG_HOME/mcpgw/config.toml
  3. $HOME/.config/mcpgw/config.toml (%USERPROFILE% on Windows)

A missing file is the normal first-run state, not an error.

Shape

# mcpgw canonical config — the single source of truth for your MCP servers.
version = 1

[servers.github]
type = "stdio"
command = "npx"
args = ["-y", "@modelcontextprotocol/server-github"]

[servers.linear]
type = "http"
url = "https://mcp.linear.app/mcp"

version

Required, currently 1. It's read before anything else, so a config from a future mcpgw fails with "unsupported version" instead of a confusing field-level parse error.

[servers.NAME]

NAME must match [a-z0-9-_], and may not contain __ — that sequence is reserved as the gateway's server__tool separator.

Common to both transports:

KeyTypeDefaultMeaning
type"stdio" | "http"requiredwhich transport
enabledbooltruefalse keeps the entry but skips it everywhere
tagslist of strings[]free-form grouping

type = "stdio":

KeyTypeDefaultMeaning
commandstringrequiredexecutable, resolved on PATH
argslist of strings[]passed verbatim
envtable of strings{}added to the child's environment

type = "http":

KeyTypeDefaultMeaning
urlstringrequiredStreamable HTTP endpoint
headerstable of strings{}sent on every request

Everything at once

version = 1

[servers.postgres]
type = "stdio"
command = "mcp-server-postgres"
args = ["--readonly"]
tags = ["work", "db"]

  [servers.postgres.env]
  PGHOST = "localhost"
  PGDATABASE = "app"

[servers.staging]
type = "http"
url = "https://staging.example.com/mcp"
enabled = false
tags = ["work"]

  [servers.staging.headers]
  Authorization = "Bearer sk-…"

Values must come before sub-tables within a section — that's TOML, not mcpgw. env and headers are sub-tables, so they go last.

State directory

~/.local/share/mcpgw/
├── managed.json          which server names mcpgw wrote into which client
├── backups/
│   └── cursor/           timestamped copies, newest 5 kept per client
└── traffic/
    └── 2026-09-01.jsonl  daily capture log, mode 0600

Resolution order:

  1. $MCPGW_STATE_DIR
  2. $XDG_DATA_HOME/mcpgw
  3. $HOME/.local/share/mcpgw

managed.json is how sync knows what it owns. Deleting it is safe: every client entry then counts as unmanaged, and sync stops touching them until you re-adopt them with import.

backups/ is written before every client file is rewritten. The five most recent per client are kept; mcpgw sync --rollback restores the newest.

traffic/ is the capture log — see Watching traffic.

Environment variables

VariableEffect
MCPGW_CONFIGfull path to the canonical config file
MCPGW_STATE_DIRoverrides the state directory
MCPGW_NO_UPDATE_CHECKany non-empty value switches the version notice off
XDG_CONFIG_HOMEbase for the config path when MCPGW_CONFIG is unset
XDG_DATA_HOMEbase for the state dir when MCPGW_STATE_DIR is unset

The mcpgw-specific variables are ignored when set to the empty string. Setting the pair of them is the clean way to run mcpgw against a scratch environment:

MCPGW_CONFIG=/tmp/try/config.toml MCPGW_STATE_DIR=/tmp/try/state mcpgw list