Skip to content

Latest commit

 

History

History
721 lines (586 loc) · 31.6 KB

File metadata and controls

721 lines (586 loc) · 31.6 KB

< Back to README

Config Reference

Config File Location

Auto-discovery order:

  1. $CALLMUX_CONFIG environment variable
  2. ~/.config/callmux/config.json

Works on Linux, macOS, and Windows.

Add $schema for editor autocomplete (VS Code, JetBrains, etc.):

{
  "$schema": "https://raw.githubusercontent.com/edimuj/callmux/main/schema.json"
}

Embedders can import the installed package schema instead of pinning a raw GitHub URL:

import schema from "callmux/schema.json" with { type: "json" };
import type { CallmuxConfig } from "callmux";

callmux also accepts MCP-compatible format ({ "mcpServers": { ... } }) so you can adopt it without changing config structure.


Global Options

Field Type Default Description
servers object (required) Map of server name -> server config
recipes object - Named reusable callmux workflows (details)
cacheTtlSeconds integer 0 Cache TTL in seconds (0 = disabled)
cachePolicy object - Global cache allow/deny rules (see Caching)
maxConcurrency integer 20 Service-wide max concurrent downstream calls across direct and meta-tool requests
connectTimeoutMs integer 30000 Timeout for downstream startup connect + list-tools
callTimeoutMs integer 180000 Timeout for downstream tool calls
reloadDrainTimeoutMs integer longest call timeout + 1000 Maximum time to drain active calls from an old upstream generation after hot reload
reconnectPolicy object retry forever Downstream reconnect/backoff policy (see Resilience)
sessionCwdIdleTtlSeconds integer 600 Idle TTL for listener-mode session-cwd stdio clients (0 = close after each call)
listenerSessionInactivityTtlSeconds integer 1800 Inactivity TTL for streamable HTTP MCP sessions (0 disables expiry)
listenerMaxSessions integer 1000 Maximum live listener MCP sessions; idle sessions are evicted LRU-first at capacity
maxScopedClients integer 64 Service-wide cap for cached or connecting cwd/header-scoped downstream clients
maxScopedClientsPerServer integer 16 Default per-server cap for cached or connecting cwd/header-scoped downstream clients
fileReferenceRoots string[] - Local roots listener-origin $file/$jsonFile/$yamlFile references may read; relative roots resolve beside the config file
requestBodyMaxBytes integer 1048576 Global max inbound request payload bytes (0 = unlimited)
allowRequestBodyMaxOverride boolean false Allow per-request x-callmux-max-body-bytes header override
allowInsecureRemoteListener boolean false Permit non-loopback listener startup without auth (unsafe)
auth object - Listener authentication config (details)
authorization object - Listener authorization policy (details)
abuseControls object - Rate limits, in-flight caps, CIDR allowlist (details)
auditLog object - Structured per-request audit logging (details)
metrics object - Prometheus metrics endpoint (details)
dashboard object disabled Read-only listener dashboard (details)
eventStore object disabled SQLite per-call event history (details)
management object disabled Standalone listener management API
strictStartup boolean false Fail startup if any server fails to connect
maxCacheEntries integer 1000 Max cached entries before LRU eviction
maxCacheEntryBytes integer 8388608 Max serialized bytes retained for one cached result
maxCacheBytes integer 134217728 Max serialized bytes retained across all cached results
metaOnly boolean false Hide proxied tools, expose only meta-tools (details)
exposeMetaTools boolean true Expose callmux_* meta-tools in tools/list; set false to list only proxied downstream tools
descriptionMaxLength integer - Default max chars for tool descriptions in callmux_status
outputFormat "json", "toon", or "auto" "json" Model-facing text format for callmux-owned structured results
responseShield object enabled Response truncation, stored-result refs, and per-tool shielding rules
schemaCompression object balanced Tool schema description compression for prompt-token reduction

Tool-call timeout precedence is: meta-tool timeoutMs, then servers.<name>.callTimeoutMs, then global callTimeoutMs, then the built-in default. Session-cwd precedence is: explicit meta-tool cwd, request _meta cwd, existing session cwd/header, then MCP roots when no session cwd exists. Call admission is persistent across requests: direct calls, parallel/batch fan-out, and pipeline steps share the same global and per-server limits. Queued calls retain arrival order among calls eligible to run and are cancelled when the caller's MCP request is cancelled.

metaOnly and exposeMetaTools control opposite halves of the exposed tool list. metaOnly: true hides proxied downstream tools and keeps meta-tools. exposeMetaTools: false hides meta-tools and keeps proxied downstream tools. Setting both would expose no tools, so config loading rejects that combination.

outputFormat controls the model-facing text for callmux-owned structured results. JSON mode keeps structuredContent; when "toon" or "auto" actually emits non-JSON TOON text, callmux omits final structuredContent so clients do not display the JSON payload instead. Cache keys, stored results, dashboard state, and pipeline $json mapping stay JSON-native internally. Use "toon" for explicit TOON rendering, or "auto" to choose TOON only for larger tabular payloads where it is materially smaller than pretty JSON.


Stdio Server Config

Local process servers use command to launch:

Field Type Required Description
command string yes Command to launch the MCP server
args string[] - Arguments passed to the command
env object - Literal environment variables for the process; avoid for secrets
envRefs object - Secret environment values loaded at process start from env:NAME or file:PATH; overrides the same key in env
cwd string - Working directory
cwdMode "global" or "session" - Listener-mode cwd behavior. Omit for session/project cwd when available; use "global" to force configured/process cwd
requireSessionCwd boolean false For session-cwd servers (path-sensitive tools like tokenlean): refuse a call with an actionable error when the caller's working directory can't be resolved (no roots, no x-callmux-cwd header, no _meta.callmux.cwd), instead of silently running relative paths against callmux's own cwd ($HOME for a daemon). Unresolved calls are always counted under unresolvedSessionCwd in runtime diagnostics regardless of this flag
tools string[] - Whitelist of tool names to expose (omit = all)
alwaysLoad string[] - Tool names the MCP client should eagerly load (sets _meta anthropic/alwaysLoad)
prefix string - Override the multi-server sub-prefix for this server's tools (default = server key; "" drops it). See Multi-Server Tool Naming
maxConcurrency integer - Max concurrent calls to this server
maxScopedClients integer - Max cached or connecting cwd/header-scoped clients for this server (omit = global per-server default)
callTimeoutMs integer - Timeout for tool calls to this server (omit = global)
requestBodyMaxBytes integer - Inbound payload cap for calls targeting this server (0 = unlimited, omit = global)
cachePolicy object - Per-server cache allow/deny rules
responseShield object - Per-server response shielding overrides
schemaCompression object - Per-server schema compression overrides

Keep downstream credentials out of the main config with envRefs:

{
  "servers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "envRefs": {
        "GITHUB_TOKEN": "env:GITHUB_TOKEN",
        "SECONDARY_TOKEN": "file:./secrets/github-secondary-token"
      }
    }
  }
}

Environment references must use a valid variable name. Relative file paths resolve from the config directory. Referenced values are loaded only when the downstream process starts, and missing or empty values fail that server's connection without exposing the secret in status or audit output. callmux setup prefers an existing environment variable; when you enter a credential instead, it stores the value in a private sidecar file and writes only its absolute file: reference to config.


HTTP Server Config

Remote servers use url instead of command:

Field Type Required Description
url string yes URL of the remote MCP server
transport string - "streamable-http" or "sse" (auto-detected if omitted)
headers object - HTTP headers (e.g. authorization)
forwardHeaders string[] - Incoming listener headers to pass through per client session (opaque; no validation/authz by callmux)
tools string[] - Whitelist of tool names to expose (omit = all)
alwaysLoad string[] - Tool names the MCP client should eagerly load (sets _meta anthropic/alwaysLoad)
prefix string - Override the multi-server sub-prefix for this server's tools (default = server key; "" drops it). See Multi-Server Tool Naming
maxConcurrency integer - Max concurrent calls to this server
maxScopedClients integer - Max cached or connecting cwd/header-scoped clients for this server (omit = global per-server default)
callTimeoutMs integer - Timeout for tool calls to this server (omit = global)
requestBodyMaxBytes integer - Inbound payload cap for calls targeting this server (0 = unlimited, omit = global)
cachePolicy object - Per-server cache allow/deny rules
responseShield object - Per-server response shielding overrides
schemaCompression object - Per-server schema compression overrides

Transport is auto-detected: callmux tries Streamable HTTP first (the current MCP spec), then falls back to SSE for older servers. Force a specific transport with "transport": "sse" or "transport": "streamable-http".

Startup is degraded by default: if one downstream server fails to connect, callmux still starts with the healthy servers and reports failures in callmux_status.failedServers. Set "strictStartup": true or pass --strict-startup to fail startup when any downstream server fails.


Resilience

callmux keeps configured downstream servers as first-class targets even when they are down. A call routed to a configured-but-unavailable server returns a structured downstream_unavailable error with retry metadata instead of server_not_found.

By default, failed downstream servers reconnect forever with jittered exponential backoff. Calls during a scheduled backoff window fast-fail so a down server does not block every request on a fresh connect attempt. Use forceReconnect: true on callmux_call to bypass that window for an explicit recovery attempt.

{
  "reconnectPolicy": {
    "initialDelayMs": 250,
    "maxDelayMs": 10000,
    "jitterRatio": 0.2,
    "maxAttempts": null,
    "fastFailDuringBackoff": true
  }
}
Field Type Default Description
initialDelayMs integer 250 Initial reconnect backoff
maxDelayMs integer 10000 Maximum reconnect backoff
jitterRatio number 0.2 Random jitter applied to reconnect delays (0 disables jitter)
maxAttempts integer or null null Maximum failed reconnect attempts before stopping; null retries forever
fastFailDuringBackoff boolean true Return downstream_unavailable during scheduled backoff instead of blocking on reconnect

When a connected server disconnects or a tool call hits a transport/session/protocol/timeout failure, callmux retires that client and allows the next call to try reconnecting immediately. For cacheable safe calls, callmux may retry once after reconnecting.

Tool suites are refreshed on reconnect. listTools is dynamic, callmux meta-tools stay present, and callmux_status reports toolSuiteGeneration plus per-server addedTools and removedTools. Calling a tool that disappeared after reconnect returns tool_removed_after_reconnect with the current alternatives.

Listener deployments expose two status endpoints:

  • /health always reports listener liveness and session counts.
  • /ready reports operational readiness and returns HTTP 503 when configured downstream servers are unavailable.

Caching

Enable with cacheTtlSeconds or --cache <seconds>. Error results are never cached.

{
  "cacheTtlSeconds": 60,
  "maxCacheEntries": 1000,
  "maxCacheEntryBytes": 8388608,
  "maxCacheBytes": 134217728,
  "cachePolicy": {
    "allowTools": ["get_*", "list_*", "search_*"],
    "denyTools": ["get_secret"]
  }
}
  • allowTools: only matching tools are cacheable (whitelist)
  • denyTools: matching tools are never cached (blacklist)
  • Supports exact names and * wildcards
  • Per-server policies combine with the global policy
  • Oldest cache entries are evicted after maxCacheEntries (default: 1000)
  • Entries larger than maxCacheEntryBytes are not stored, and oldest entries are evicted before the cache exceeds maxCacheBytes
  • Concurrent misses for an identical cacheable call share one in-flight downstream invocation; one disconnected waiter does not cancel peers, the shared invocation is cancelled when its last waiter leaves, and rejected calls are removed from the in-flight map
  • callmux_cache_clear invalidates manually

Response Shielding

Response shielding is enabled by default. When a tool result is too large, callmux stores the full result in memory and returns a compact preview with _callmux.ref, _callmux.shape, and _callmux.retrieval. Use callmux_get_result to page through the stored result. If an MCP client defers that tool, call callmux_call with tool: "callmux_get_result" and the same retrieval arguments. A result above either stored-result byte ceiling is never retained; its preview reports retained: false and intentionally has no unusable retrieval ref.

Defaults:

Field Default Description
enabled true Enable response shielding
maxResultBytes 65536 Store and compact responses larger than this many serialized bytes
maxStringChars 8192 Truncate individual string fields longer than this
maxArrayItems 50 Truncate arrays longer than this
maxStoredResults 100 Global stored-result capacity before oldest refs are evicted
maxStoredResultBytes 67108864 Max serialized bytes retained for one full result
maxStoredBytes 268435456 Max serialized bytes retained across all full results
allowTools - Only shield matching tools when set
denyTools - Never shield matching tools; takes precedence

Global example:

{
  "responseShield": {
    "maxResultBytes": 32768,
    "maxStringChars": 4000,
    "maxArrayItems": 25,
    "maxStoredResults": 200,
    "maxStoredResultBytes": 67108864,
    "maxStoredBytes": 268435456,
    "denyTools": ["download_*"]
  }
}

Per-server override:

{
  "servers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "responseShield": {
        "enabled": false
      }
    }
  }
}

Retrieve a stored result:

{
  "ref": "r_...",
  "offset": 0,
  "limit": 50,
  "fields": ["id", "name"],
  "search": "failed",
  "outputFormat": "auto"
}

Schema Compression

schemaCompression reduces system-prompt bloat from verbose MCP tool schemas. It only rewrites description fields on exposed tool definitions and input schemas; names, types, required fields, enums, defaults, bounds, and the actual argument contract are preserved.

Balanced mode is the default. It drops descriptions that only restate compact field names, caps retained descriptions, and keeps guidance for ambiguous fields such as ref, cursor, sha, type, state_reason, and market.

Field Default Description
enabled true Enable schema compression
mode "balanced" "off", "balanced", or "aggressive"
maxDescriptionChars 160 Maximum chars for retained descriptions
{
  "schemaCompression": {
    "mode": "balanced",
    "maxDescriptionChars": 160
  },
  "servers": {
    "github": {
      "command": "github-mcp-server",
      "args": ["stdio"],
      "schemaCompression": { "mode": "aggressive" }
    },
    "docs": {
      "command": "docs-mcp",
      "schemaCompression": { "enabled": false }
    }
  }
}

callmux_status includes schemaCompression diagnostics with original/compressed schema bytes and estimated savings.

callmux_status includes responseStore stats: active stored refs, capacity, stored bytes, and total stored results since startup.


Dashboard

The dashboard is disabled by default. Enable it only for listener deployments where the HTTP endpoint is trusted or protected by listener auth:

{
  "dashboard": {
    "enabled": true,
    "path": "/dashboard",
    "maxEvents": 500
  }
}
Field Type Default Description
enabled boolean false Serve the read-only dashboard endpoints
path string "/dashboard" Dashboard base path
maxEvents integer 500 Bounded in-memory event history size

path can be /, /dashboard, or a reverse-proxy prefix such as /relay/. Non-root trailing slashes are normalized, and the UI resolves data and events relative to the loaded page URL.

When auth is configured, dashboard requests use the same listener authentication as /mcp.


Event Store

eventStore enables additive, queryable per-call history backed by Node 24's built-in node:sqlite. It is disabled by default; when disabled, callmux does not open SQLite and does not create a database file.

{
  "eventStore": {
    "enabled": true,
    "path": "/var/lib/callmux/callmux-events.sqlite",
    "maxRows": 100000,
    "retentionDays": 14,
    "pruneEvery": 100
  }
}
Field Type Default Description
enabled boolean false Enable SQLite-backed per-call event history
path string callmux-events.sqlite beside the config file SQLite database path
maxRows integer 100000 Maximum retained call event rows (0 = age-only retention)
retentionDays integer 14 Maximum retained event age (0 = row-count-only retention)
pruneEvery integer 100 Completed calls between retention prune passes

The store uses WAL mode, NORMAL synchronous mode, indexed target tables for drill-down, and periodic pruning. Inserts are batched in a dedicated worker thread, and dashboard analytical queries run in that worker as well, so optional SQLite observability does not block listener request processing. Forwarded-header audit rows store header names, session id, principal, downstream server, and tool only; raw credential values are never stored.

When the dashboard is enabled, /dashboard/drilldown reads this store for per-server, per-tool, per-session, and forwarded-header audit breakdowns.


Management API

The management API is disabled by default and only applies to standalone listener mode. It exposes versioned HTTP+JSON endpoints under /management/v1 by default. Mutations require management bearer auth and persist to a callmux-owned overlay file, leaving the base config file untouched.

{
  "management": {
    "enabled": true,
    "path": "/management/v1",
    "statePath": "/var/lib/callmux/managed-overlay.json",
    "auth": {
      "mode": "bearer",
      "tokens": [{ "id": "admin", "tokenRef": "env:CALLMUX_MANAGEMENT_TOKEN" }]
    }
  }
}
Field Type Default Description
enabled boolean false Serve management endpoints
path string "/management/v1" Management API base path
statePath string <config>.management.json Persistent managed overlay path
auth bearer auth object - Management bearer tokens. Required for mutations
allowUnauthenticatedRead boolean false Allow read-only management endpoints without any auth
allowAuthenticatedRead boolean false When no management auth is set, allow any globally authenticated MCP principal to read management endpoints. A tool-calling principal is not granted management read by default

Security note: Mutations always require a management auth token — a global MCP principal can never write. Management write registers arbitrary servers (including stdio command servers), so treat the management write token as shell-equivalent.

Initial endpoints:

Endpoint Purpose
GET /management/v1/status Runtime status, management state, and servers
GET /management/v1/config/effective Redacted effective config
GET /management/v1/servers Redacted server config plus runtime state
POST /management/v1/servers Add or replace a managed server
PATCH /management/v1/servers/:id Update tools, disabled state, or full server config
DELETE /management/v1/servers/:id Remove a server via overlay
POST /management/v1/servers/:id/restart Reconnect using the current effective config
POST /management/v1/cache/clear Clear cache globally or by server/tool

File References

Any argument object can use file references. callmux reads the file and replaces the reference with file content before forwarding to the downstream MCP tool.

File-backed references from a shared HTTP listener are disabled by default because their paths refer to the callmux host, not the remote caller. Enable them explicitly with local roots:

{
  "fileReferenceRoots": ["./agent-inputs", "/var/lib/callmux/uploads"]
}

Relative roots are resolved beside the config file. callmux resolves both roots and requested files through realpath, so symlinks cannot be used to escape a configured root. Local stdio-mode callers retain unrestricted legacy file-reference behavior when fileReferenceRoots is omitted; when roots are configured, they constrain local and listener calls alike.

Each tool call is limited to 32 file-backed references, 16 MB of file content in total, and four concurrent reads. Each file is opened and read only through maxBytes + 1, so a file that grows after validation cannot force an unbounded allocation. References that resolve to the same real path share one filesystem read, including symlink aliases. Every occurrence still counts toward the reference and 16 MB expanded-content budgets and keeps its own maxBytes, so reuse cannot amplify the downstream request past those limits.

Use the reference that matches the downstream field shape:

Need Use Do not use
Markdown or plain string fields such as GitHub issue body, description, comment, text, or content $file or $text $jsonFile unless the JSON file contains a JSON string
Full structured argument objects, arrays, or nested payload fields $jsonFile or $yamlFile $file if the downstream tool expects an object/array
Previous pipeline step output inputMapping with $text, $json, or $json.path Literal "$json" inside downstream arguments

$json is not a file reference. It is valid only in callmux_pipeline inputMapping, where it means "parse the previous step's text as JSON". If you put "$json" directly under downstream arguments, callmux treats it as a suspicious literal and callmux_dry_run reports a warning.

$file - Raw file content

{
  "body": { "$file": "/tmp/issue-body.md" }
}

Optional maxBytes override per reference:

{
  "body": { "$file": "/tmp/issue-body.md", "maxBytes": 2000000 }
}
  • maxBytes defaults to 1000000 (1 MB) when omitted
  • Hard cap for maxBytes is 10000000 (10 MB)

Works on string-typed fields too. Prefer the object form { "body": { "$file": "..." } }. But when a field's schema types the value as string (e.g. an issue body), the MCP client may coerce the object into a JSON string ("{\"$file\": \"...\"}") before callmux sees it. callmux detects a lone-ref string and resolves it the same way, so $file/$jsonFile/$yamlFile/$text work on string fields as well. A normal string that merely contains $file, or JSON with other keys alongside the ref, is left untouched.

$jsonFile / $yamlFile - Parsed file content

{
  "payload": { "$jsonFile": "/tmp/payload.json" },
  "config": { "$yamlFile": "/tmp/config.yaml" }
}

Both support optional maxBytes like $file.

$jsonFile and $yamlFile forward the parsed value as-is. That is right for structured fields:

{
  "metadata": { "$jsonFile": "/tmp/metadata.json" }
}

For string fields, use $file or $text instead:

{
  "tool": "github__create_issue",
  "arguments": {
    "title": "Bug report",
    "body": { "$file": "/tmp/issue-body.md" }
  }
}

Avoid this unless /tmp/issue-body.json contains a JSON string:

{
  "tool": "github__create_issue",
  "arguments": {
    "title": "Bug report",
    "body": { "$jsonFile": "/tmp/issue-body.json" }
  }
}

$text - Inline text composition

Skip writing a temp file for long multi-line text:

{
  "body": {
    "$text": {
      "lines": [
        "## Summary",
        "",
        "| Field | Value |",
        "| --- | --- |",
        "| A | B |"
      ]
    }
  }
}

$text forms:

  • {"$text": "literal string"}
  • {"$text": {"lines": ["line1", "line2"], "join": "\n"}} (join defaults to newline)

Like $file, $text is resolved by callmux before forwarding, so downstream MCP servers receive a normal string.


Multi-Server Tool Naming

Single server: tools keep original names (create_issue, search)

Multiple servers: tools are automatically namespaced (github__create_issue, linear__list_issues)

Meta-tools: always prefixed callmux_ to avoid collisions

The server field in meta-tool calls lets you target specific servers:

{
  "calls": [
    { "server": "github", "tool": "get_issue", "arguments": { "number": 42 } },
    { "server": "linear", "tool": "get_issue", "arguments": { "id": "ENG-123" } }
  ]
}

Shortening the sub-server prefix

The namespaced segment defaults to the server key, which can be redundant — every tokenlean tool already starts with tl_, so tokenlean__tl_diff encodes "tokenlean" twice. Set a per-server prefix to shorten or drop it (multi-server mode only; single-server tools always keep their original names):

{
  "servers": {
    "tokenlean": { "command": "tokenlean-mcp", "prefix": "" },   // -> tl_diff
    "github":    { "command": "gh-mcp",        "prefix": "gh" }  // -> gh__search_code
  }
}

prefix accepts letters, digits, and underscores (or "" to drop the prefix entirely). Both forms resolve when calling a tool — the emitted name (tl_diff, gh__search_code) and the original server-qualified form (tokenlean__tl_diff, github__search_code) — so existing recipes and server-qualified calls keep working. Authorization rules are always evaluated against the canonical server__tool name, so a shortened prefix cannot dodge a policy written against the real server key.

Collision safety: if shortening would make two tools collide (e.g. two servers both exposing status with prefix: ""), callmux keeps both reachable by falling back to the full server__tool names for the clashing tools and logs a warning to stderr — it never silently shadows a tool.

The mcp__<key>__ prefix is client-controlled

Claude Code builds the leading mcp__callmux__ segment from your client's MCP server key (the key under mcpServers in .mcp.json), not from anything callmux emits. callmux cannot change it. To shorten it (e.g. mcp__cx__tl_diff), rename the key in your client config — there is no callmux setting for this.


Full Example Config

{
  "$schema": "https://raw.githubusercontent.com/edimuj/callmux/main/schema.json",
  "servers": {
    "github": {
      "command": "npx",
      "args": ["-y", "@modelcontextprotocol/server-github"],
      "envRefs": { "GITHUB_TOKEN": "env:GITHUB_TOKEN" },
      "tools": ["create_issue", "get_issue", "list_issues", "search_issues"],
      "maxConcurrency": 5,
      "cachePolicy": { "allowTools": ["get_*", "list_*"] }
    },
    "remote-api": {
      "url": "https://mcp.example.com/mcp",
      "headers": { "Authorization": "Bearer sk-..." },
      "transport": "streamable-http",
      "forwardHeaders": ["authorization"]
    }
  },
  "recipes": {
    "open_bug": {
      "description": "Create a labeled bug issue",
      "mode": "call",
      "server": "github",
      "tool": "create_issue",
      "arguments": {
        "title": { "$param": "title" },
        "body": { "$param": "body" },
        "labels": ["bug"]
      }
    }
  },
  "cacheTtlSeconds": 60,
  "maxCacheEntries": 1000,
  "maxCacheEntryBytes": 8388608,
  "maxCacheBytes": 134217728,
  "cachePolicy": { "denyTools": ["create_*"] },
  "maxConcurrency": 20,
  "connectTimeoutMs": 30000,
  "callTimeoutMs": 180000,
  "reloadDrainTimeoutMs": 181000,
  "strictStartup": false,
  "metaOnly": false,
  "exposeMetaTools": true,
  "descriptionMaxLength": 80,
  "outputFormat": "json",
  "responseShield": {
    "maxResultBytes": 65536,
    "maxStoredResults": 100,
    "maxStoredResultBytes": 67108864,
    "maxStoredBytes": 268435456,
    "denyTools": ["download_*"]
  },
  "schemaCompression": {
    "mode": "balanced",
    "maxDescriptionChars": 160
  },
  "auth": {
    "mode": "bearer",
    "tokens": [{ "id": "ops", "hash": "scrypt$16384$8$1$<salt>$<derivedKey>" }],
    "allowUnauthenticatedHealth": false
  },
  "authorization": {
    "defaultEffect": "deny",
    "rules": [
      { "id": "ops-all", "effect": "allow", "principals": ["bearer:ops"], "tools": ["*"] },
      { "id": "agents-read", "effect": "allow", "principals": ["oidc:*"], "tools": ["github__get_*", "github__list_*"] }
    ]
  },
  "abuseControls": {
    "globalRequestsPerMinute": 1200,
    "principalRequestsPerMinute": 240,
    "principalMaxInFlight": 20,
    "cidrAllowlist": ["127.0.0.1/32", "::1/128"]
  },
  "auditLog": {
    "enabled": true,
    "includeRequestBody": false,
    "maxPayloadChars": 4096
  },
  "metrics": {
    "enabled": true,
    "path": "/metrics",
    "allowUnauthenticated": false
  },
  "dashboard": {
    "enabled": true,
    "path": "/dashboard",
    "maxEvents": 500
  },
  "eventStore": {
    "enabled": true,
    "path": "/var/lib/callmux/callmux-events.sqlite",
    "maxRows": 100000,
    "retentionDays": 14
  }
}

See Also