Skip to content

Configuration reference

All commands are installed by uv sync as console scripts. --socket defaults to the SPIFFE_ENDPOINT_SOCKET environment variable.

The flag tables, fixed values and policy schema below are generated from the code by scripts/gen_config_docs.py. CI fails if they drift.

mcp-svid-authz

JWT-SVID to access token bridge (POC)

Flag Default Meaning
--issuer required Public base URL, used as iss and as the required SVID aud; trailing slash removed
--policy required Path to the policy YAML file; reloaded on SIGHUP and when its content changes
--policy-poll-seconds 5.0 How often to check the policy file for changes; 0 disables polling (SIGHUP only)
--token-exchange off Enable RFC 8693 on-behalf-of exchange for tokens this server issued; off by default
--host 127.0.0.1 Listen address
--port 8100 Listen port
--static-jwks none Test mode: JWT-SVID bundle as a JWKS file instead of the Workload API
--socket none Workload API socket for JWT bundles, default SPIFFE_ENDPOINT_SOCKET
--audit-log none Audit log file (JSON lines, appended), default stderr
--max-svid-lifetime 300 Reject JWT-SVIDs whose exp - iat exceeds this many seconds
--svid-replay reject Reject: require a JWT-SVID jti and accept each (sub, jti) once; allow-reuse-within-lifetime: jti optional, a reused SVID is accepted until it expires
--replay-cache-size 10000 Global live assertion cache limit; refuses when full
--replay-subject-limit 1000 Maximum live assertion entries per verified subject
--token-requests-per-second 50 Per-process token endpoint requests per fixed one-second window
--subject-requests-per-second 10 Exchanges per authorized subject per fixed one-second window
--signing-rotation-seconds 3600 Planned signing rotation interval; zero disables, minimum 60 seconds

Fixed values:

Setting Value
Token form byte limit 16384
Signing key prepublication 60s
Retired signing keys at most 6
Access token TTL 300s
Access token algorithm ES256
SVID clock leeway 30s
Trust bundle cache (Workload API) 30s

mcp-svid-notes

Notes MCP server (POC)

Flag Default Meaning
--name notes Server name, also used in the audit component (mcp_server:<name>)
--resource required Canonical URI, e.g. http://host:8101/mcp; must equal the token aud
--issuer required Authorization server issuer URL
--jwks-uri none JWKS location, default <issuer>/jwks.json
--host 127.0.0.1 Listen address
--port 8101 Listen port
--audit-log none Audit log file (JSON lines, appended), default stderr
--upstream-resource none Upstream MCP server for notes.search_upstream, called with this server's own token
--upstream-scope notes:read Scope requested for the upstream token
--trusted-issuer none Authorization server allowed for the upstream token; required with an upstream
--socket none Workload API socket for the upstream SVID, default SPIFFE_ENDPOINT_SOCKET
--allow-http off Dev only: accept plain http URLs for discovery, token requests and tool calls
--allow-private-network off Dev only: accept hosts that resolve to private, loopback or link-local addresses

Fixed values:

Setting Value
Tools (scope) notes.search (notes:read), notes.write (notes:write)
JWKS cache 60s
Unknown kid refetch at most every 10s
Token verification budget 256/second/process
Access token byte limit 8192
JWKS decoded byte limit 65536
JWKS key count limit 16
Request body limit 1048576 bytes
Accepted token typ at+jwt
Accepted token algorithms ES256

mcp-svid-agent

Agent that authenticates with its JWT-SVID

Flag Default Meaning
--resource required MCP server canonical URI
--scope required Space separated scopes, e.g. 'notes:read notes:write'
--trusted-issuer required Authorization server issuer the agent may mint SVIDs for; repeatable, exact match
--allow-http off Dev only: accept plain http URLs for discovery, token requests and tool calls
--allow-private-network off Dev only: accept hosts that resolve to private, loopback or link-local addresses
--spiffe-id none Sent as client_id
--socket none Workload API socket, default SPIFFE_ENDPOINT_SOCKET
--call none Tool to call with JSON arguments; repeatable; default notes.search then notes.write
--steal-token none Demo: send the token issued for --resource to OTHER_RESOURCE instead

mcp-svid-stdio

Run a stdio MCP server with a short-lived token from the Workload API

Flag Default Meaning
--resource required Upstream resource the child calls
--scope required Space separated scopes
--trusted-issuer required Authorization server issuer the wrapper may mint SVIDs for; repeatable, exact match
--allow-http off Dev only: accept plain http URLs for discovery, token requests and tool calls
--allow-private-network off Dev only: accept hosts that resolve to private, loopback or link-local addresses
--socket none Workload API socket, default SPIFFE_ENDPOINT_SOCKET
--refresh-margin 60 Seconds before expiry to refresh
--export-token-env off Also set MCP_ACCESS_TOKEN (weaker: visible in the environment); the child is stopped when that token expires
-- command [args...] required Child command, after --

Fixed values:

Setting Value
Exit code when the token cannot be refreshed 75
Grace between SIGTERM and SIGKILL for the child on expiry 5s

mcp-svid-audit-verify

Verify the hash chain of audit log files written with --audit-log

Flag Default Meaning
files required Audit log files, one chain each
--checkpoint none Verify one complete log against a separately trusted terminal checkpoint
--write-checkpoint none Create a new terminal checkpoint for one valid log; refuses to overwrite

Fixed values:

Setting Value
Chain prev_hash = SHA-256 of the JCS form of the previous record, hex

Policy file

Example from deploy/policy.yaml:

# Which SPIFFE IDs may get access tokens for which MCP servers, with which scopes.
# Anything not listed is denied.
trust_domain: example.org
clients:
  - spiffe_id: spiffe://example.org/agent/research
    resources:
      http://notes-a:8101/mcp: [notes:read, notes:write]
      http://notes-b:8102/mcp: [notes:read]
  # notes-a calls notes-b with its own token for notes.search_upstream.
  - spiffe_id: spiffe://example.org/mcp/notes-a
    resources:
      http://notes-b:8102/mcp: [notes:read]
# spiffe://example.org/agent/intruder has a valid SVID but is deliberately not listed.
Key Type Meaning
trust_domain string, required Only SVIDs from this trust domain are accepted
clients list One entry per SPIFFE ID
clients[].spiffe_id string, required Exact SPIFFE ID (SVID sub)
clients[].resources map Resource URI to list of allowed scopes. Trailing slashes are ignored when matching.

Behaviour notes

  • mcp-svid-agent calls notes.search then notes.write. It exits with code 2 and prints a token_denied event when the token request fails.
  • mcp-svid-stdio is described in stdio wrapper.

Policy decisions

The policy is an allowlist of which SPIFFE IDs may get access tokens for which MCP servers, with which scopes. Anything not listed is denied.

Decision order:

Condition Error
SPIFFE ID not listed unauthorized_client
Resource not listed for that ID invalid_target
Requested scopes not a subset of the listed scopes invalid_scope

The token carries exactly the requested scopes, not the full grant.