This document provides the complete field-by-field reference for MCP Gateway configuration.
For the upstream specification, see the MCP Gateway Configuration Reference.
MCP Gateway supports two configuration formats:
- JSON stdin — Use with
--config-stdinflag (primary format for containerized deployments) - TOML file — Use with
--configflag for file-based configuration
TOML configuration requires command = "docker" for stdio-based MCP servers to ensure containerization:
[gateway]
port = 3000
api_key = "your-api-key"
[servers.github]
command = "docker"
args = ["run", "--rm", "-e", "GITHUB_PERSONAL_ACCESS_TOKEN", "-i", "ghcr.io/github/github-mcp-server:latest"]
[servers.github.guard_policies.allow-only]
repos = ["github/gh-aw-mcpg", "github/gh-aw"]
min-integrity = "unapproved"
[servers.safeoutputs]
command = "docker"
args = ["run", "--rm", "-i", "ghcr.io/github/safe-outputs:latest"]
[servers.safeoutputs.guard_policies.write-sink]
Accept = ["private:github/gh-aw-mcpg", "private:github/gh-aw"]Important: Per MCP Gateway Specification Section 3.2.1, all stdio-based MCP servers MUST be containerized. The gateway rejects configurations where command is not "docker".
For HTTP-based MCP servers, use the url field instead of command:
[servers.myhttp]
type = "http"
url = "https://example.com/mcp"Format note: JSON format uses
"guard-policies"(with hyphen), TOML usesguard_policies(with underscore).
JSON configuration is the primary format for containerized deployments. Pass via stdin:
{
"mcpServers": {
"github": {
"type": "stdio",
"container": "ghcr.io/github/github-mcp-server:latest",
"env": {
"GITHUB_PERSONAL_ACCESS_TOKEN": ""
},
"guard-policies": {
"allow-only": {
"repos": ["github/gh-aw-mcpg", "github/gh-aw"],
"min-integrity": "unapproved"
}
}
},
"safeoutputs": {
"type": "stdio",
"container": "ghcr.io/github/safe-outputs:latest",
"guard-policies": {
"write-sink": {
"accept": ["private:github/gh-aw-mcpg", "private:github/gh-aw"]
}
}
}
},
"gateway": {
"port": 8080,
"apiKey": "${MCP_GATEWAY_API_KEY}",
"domain": "localhost"
}
}The gateway provides fail-fast validation with precise error locations (line/column for TOML parse errors), unknown key detection (catches typos like prot instead of port), and environment variable expansion validation. Check log files for warnings after startup.
Run ./awmg --help for full CLI options. Key flags:
./awmg --config config.toml # TOML config file
./awmg --config-stdin < config.json # JSON stdin
./awmg --config config.toml --routed # Routed mode (default)
./awmg --config config.toml --unified # Unified mode
./awmg --config config.toml --log-dir /path # Custom log directory-
type(optional): Server transport type"stdio"- Standard input/output transport (default)"http"- HTTP transport (fully supported)"local"- Alias for"stdio"(backward compatibility)
-
container(required for stdio in JSON format): Docker container image (e.g.,"ghcr.io/github/github-mcp-server:latest")- Automatically wraps as
docker run --rm -i <container> - Note: The
commandfield is NOT supported in JSON stdin format (stdio servers must usecontainerinstead) - TOML format uses
commandandargsfields -commandmust be"docker"for stdio servers
- Automatically wraps as
-
entrypoint(optional): Custom entrypoint for the container- Overrides the default container entrypoint
- Applied as
--entrypointflag to Docker
-
entrypointArgs(optional): Arguments passed to container entrypoint- Array of strings passed after the container image
-
args(optional): Additional Docker runtime arguments inserted before the container image name- Array of strings passed to
docker runbefore the container image - Example:
["--network", "host", "--privileged"] - Useful for advanced Docker configurations
- Array of strings passed to
-
mounts(optional): Volume mounts for the container- Array of strings in format
"source:dest:mode" source- Host path to mount (can use environment variables with${VAR}syntax)dest- Container path where the volume is mountedmode- Either"ro"(read-only) or"rw"(read-write)- Example:
["/host/config:/app/config:ro", "/host/data:/app/data:rw"]
- Array of strings in format
-
env(optional): Environment variables- Set to
""(empty string) for passthrough from host environment - Set to
"value"for explicit value - Use
"${VAR_NAME}"for environment variable expansion (fails if undefined)
- Set to
-
url(required for http): HTTP endpoint URL fortype: "http"servers -
headers(optional): HTTP headers to include in requests (fortype: "http"servers)- Map of header name to value (e.g.,
{"Authorization": "Bearer token"})
- Map of header name to value (e.g.,
-
tools(optional): List of tool names to expose from this server- If omitted or empty, all tools are exposed
- Example:
["get_file_contents", "search_code"]
-
registry(optional): Informational URI to the server's entry in an MCP registry- Used for documentation and discoverability purposes only; not used at runtime
-
guard(optional): Name of the guard to use for this server (DIFC)- References a guard defined in the top-level
[guards]section - Enables per-server DIFC guard assignment independent of
guard-policies - Example:
guard = "github"(uses the guard namedgithubfrom[guards.github])
- References a guard defined in the top-level
-
working_directory(optional, TOML format only): Working directory for the server process- Note: This field is parsed and stored but not yet implemented in the launcher; it has no runtime effect currently
Guard policies provide access control at the MCP gateway level. A server's guard-policies must contain either allow-only or write-sink, not both.
allow-only: Restricts which repositories a guard allows (used for GitHub MCP server)write-sink: Marks a server as a write-only output channel that accepts writes from agents with matching secrecy labels
Format note: JSON format uses
"guard-policies"(with hyphen), TOML usesguard_policies(with underscore).
Controls repository access with the following structure:
"guard-policies": {
"allow-only": {
"repos": ["github/gh-aw-mcpg", "github/gh-aw"],
"min-integrity": "unapproved"
}
}TOML equivalent:
[servers.github.guard_policies.allow-only]
repos = ["github/gh-aw-mcpg", "github/gh-aw"]
min-integrity = "unapproved"-
repos: Repository access scope"all"- All repositories accessible by the token"public"- Public repositories only- Array of patterns:
"owner/repo"- Exact repository match"owner/*"- All repositories under owner"owner/prefix*"- Repositories with name prefix under owner
-
min-integrity: Minimum integrity level required. Integrity levels are determined by the GitHub MCP server based on theauthor_associationfield of GitHub objects and whether the object is reachable from the main branch:"none"- No integrity requirements (includes objects with author_association: FIRST_TIME_CONTRIBUTOR, FIRST_TIMER, NONE)"unapproved"- Unapproved contributor level (includes objects with author_association: CONTRIBUTOR, FIRST_TIME_CONTRIBUTOR)"approved"- Approved contributor level (includes objects with author_association: OWNER, MEMBER, COLLABORATOR)"merged"- Merged to main branch (any object reachable from the main branch, regardless of authorship)
-
Meaning: Restricts the GitHub MCP server to only access specified repositories. Tools like
get_file_contents,search_code, etc. will only work on allowed repositories. Attempts to access other repositories will be denied by the guard policy.
Marks a server as a write-only output channel. Write-sink is required for ALL output
servers (e.g., safeoutputs) when DIFC guards are enabled on any other server. Without
it, the output server gets a noop guard that classifies operations as reads with empty
labels, causing integrity violations when the agent has integrity tags from other guards.
When an agent reads from a guarded server (e.g., GitHub with allow-only), it acquires
secrecy and integrity labels. The write-sink guard solves this by classifying all
operations as writes and accepting writes from agents whose secrecy labels match the
configured accept patterns.
For exact repos (repos=["owner/repo1", "owner/repo2"]):
"guard-policies": {
"write-sink": {
"accept": ["private:owner/repo1", "private:owner/repo2"]
}
}For prefix wildcard repos (repos=["owner/prefix*"]):
"guard-policies": {
"write-sink": {
"accept": ["private:owner/prefix*"]
}
}For broad access (repos="all" or repos="public"):
"guard-policies": {
"write-sink": {
"accept": ["*"]
}
}TOML equivalents:
# Exact repos
[servers.safeoutputs.guard_policies.write-sink]
Accept = ["private:owner/repo1", "private:owner/repo2"]
# Prefix wildcard repos
[servers.safeoutputs.guard_policies.write-sink]
Accept = ["private:owner/prefix*"]
# Broad access (repos="all" or repos="public")
[servers.safeoutputs.guard_policies.write-sink]
Accept = ["*"]-
accept: Array of secrecy tags the sink accepts (exact string match against agent secrecy tags — not glob patterns)"*"- Wildcard: Accept writes from agents with any secrecy (must be the sole entry). Use forrepos="all"orrepos="public"."private:owner/repo"- Matches agent secrecy tag fromrepos=["owner/repo"](exact repo)"private:owner/prefix*"- Matches agent secrecy tag fromrepos=["owner/prefix*"](prefix wildcard — the*is a literal character in the tag)"private:owner"- Matches agent secrecy tag fromrepos=["owner/*"](owner wildcard — bare owner, no/*suffix)"public:owner/repo*"- Matches agent secrecy tag for public repos matching a prefix"internal:owner/repo*"- Matches agent secrecy tag for internal repos matching a prefix
-
How it works: The write-sink classifies all operations as writes. For DIFC write checks:
- Resource secrecy is set to the
acceptpatterns → agent secrecy ⊆ resource secrecy passes - Resource integrity is left empty → no integrity requirements for writes
- Resource secrecy is set to the
-
When to use: Required for all output servers (
safeoutputs, etc.) when DIFC guards are enabled on any server in the configuration
The write-sink accept entries must match the secrecy tags the GitHub guard assigns
to the agent via label_agent. The mapping depends on the repos configuration:
allow-only.repos |
Agent secrecy tags | write-sink.accept |
|---|---|---|
"all" |
[] (none) |
["*"] (wildcard) |
"public" |
[] (none) |
["*"] (wildcard) |
["owner/repo"] |
["private:owner/repo"] |
["private:owner/repo"] |
["owner/*"] |
["private:owner"] |
["private:owner"] |
["owner/prefix*"] |
["private:owner/prefix*"] |
["private:owner/prefix*"] |
["O/R1", "O/R2"] |
["private:O/R1", "private:O/R2"] |
["private:O/R1", "private:O/R2"] |
["O1/*", "O2/R"] |
["private:O1", "private:O2/R"] |
["private:O1", "private:O2/R"] |
Key rules:
repos="all"orrepos="public"→ no secrecy tags → useaccept: ["*"](wildcard)- Write-sink is required for ALL output servers when DIFC guards are enabled (prevents noop guard integrity violations)
accept: ["*"]is a special wildcard that accepts writes from agents with any secrecy; it must be the sole entryrepos=["owner/*"](owner wildcard) → bare owner tag"private:owner"(no/*suffix)repos=["owner/prefix*"](prefix wildcard) →"private:owner/prefix*"(suffix preserved)repos=["owner/repo"](exact) →"private:owner/repo"- Multi-entry repos produce one tag per entry;
acceptmust include all of them acceptcan be a superset of the agent's secrecy (extra entries are harmless)min-integritydoes not affect these rules (it only changes integrity labels)
The customSchemas top-level field allows you to define custom server types beyond the built-in "stdio" and "http" types. Each custom type maps to an HTTPS schema URL that describes its configuration format.
{
"customSchemas": {
"myCustomType": "https://example.com/schemas/my-custom-type.json"
},
"mcpServers": {
"myServer": {
"type": "myCustomType"
}
}
}Validation Rules for customSchemas:
- Custom type names must not conflict with reserved types (
stdio,http) - Schema URLs must use
https://(HTTP URLs are not permitted) - If a server's
typereferences a custom type not listed incustomSchemas, validation fails with a helpful error message
- JSON stdin format:
- Stdio servers must specify
container(required) - HTTP servers must specify
url(required) - The
commandfield is not supported - stdio servers must usecontainer
- Stdio servers must specify
- TOML format:
- Uses
commandandargsfields directly (e.g.,command = "docker")
- Uses
- Common rules (both formats):
- Empty/"local" type automatically normalized to "stdio"
- Variable expansion with
${VAR_NAME}fails fast on undefined variables - All validation errors include JSONPath and helpful suggestions
- Mount specifications must follow
"source:dest:mode"formatsourcemust be an absolute path (e.g.,/host/data)destmust be an absolute path (e.g.,/app/data)modemust be either"ro"or"rw"- Both source and destination paths are required (cannot be empty)
| Field | Description | Default |
|---|---|---|
port |
HTTP port (1-65535) | From --listen flag |
apiKey |
API key for authentication | (disabled) |
domain |
Gateway domain ("localhost", "host.docker.internal", or "${VAR}") |
localhost |
startupTimeout |
Seconds to wait for backend startup | 60 |
toolTimeout |
Seconds to wait for tool execution | 120 |
payloadDir |
Directory for large payload files | /tmp/jq-payloads |
TOML-only / CLI-only options (not available in JSON stdin):
| Option | CLI Flag | Env Var | Default |
|---|---|---|---|
| Payload size threshold | --payload-size-threshold |
MCP_GATEWAY_PAYLOAD_SIZE_THRESHOLD |
524288 |
| Payload path prefix | --payload-path-prefix |
MCP_GATEWAY_PAYLOAD_PATH_PREFIX |
(empty) |
| Sequential launch | --sequential-launch |
— | false |
| Guards mode | --guards-mode |
MCP_GATEWAY_GUARDS_MODE |
strict |
Environment Variable Features:
- Passthrough: Set value to empty string (
"") to pass through from host - Expansion: Use
${VAR_NAME}syntax for dynamic substitution (fails if undefined)