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

Runtime Configuration

🗂️ Config boundary: process config tells the gateway how to run. Runtime user config tells each request where it may route. Plugin config controls the optional CPEX hook runtime.

Runtime configuration

The gateway consumes configuration from three places. Keeping them separate is important because they change at different times and are used by different parts of the dataplane.

Config Surfaces

SurfaceSourceLoadedMain user
Process ConfigCLI flags and CONTEXTFORGE_GATEWAY_RS_* env vars parsed by clap.Startup.Listener setup, JWT decoder keys, Redis connection, upstream HTTP client, telemetry, runtime shape.
UserConfigUserConfigStore, currently Redis through RedisUserConfigStore.Per request after JWT validation.Virtual host and backend selection.
RuntimePluginConfigDocumentRedis key ContextForgeGatewayRuntimePluginConfig when runtime plugins are enabled.Startup and watcher reload.CPEX tool pre/post hooks.

The control plane owns durable authoring. This repo owns reading those values and applying them on the request path.

UserConfig Shape

The API crate defines the shared runtime routing model:

UserConfig
  virtual_hosts: HashMap<String, VirtualHost>

VirtualHost
  backends: HashMap<String, BackendMCPGateway>

BackendMCPGateway
  name: String
  url: Url
  transport: Transport
  passthrough_headers: Vec<String>
  allowed_tool_names: Vec<String>
  allowed_resource_names: Vec<String>
  allowed_prompt_names: Vec<String>

Transport currently declares:

STREAMABLEHTTP
SSE
STDIO

The Rust types above are easier to picture as JSON. For a complete, working UserConfig document, see the seed request body in Run the Gateway Locally.

What The MCP Dataplane Uses Today

The struct is already wider than the current MCP routing code. That is useful, but the distinction should stay explicit:

Config fieldCurrent MCP dataplane behavior
UserConfig.virtual_hostsRequired. VirtualHostId from the path selects one entry.
VirtualHost.backends map keyRequired. Selects backend session state and becomes the public prefix when a multi-backend identifier has no explicit alias.
BackendMCPGateway.urlRequired. Used to build the upstream StreamableHttpClientTransport.
BackendMCPGateway.namePresent in the model. Current routing uses the backend map key, not this field, as the namespace.
transportPresent in the model. Current upstream code always builds a streamable HTTP client transport.
passthrough_headersPresent in the model. Current MCP routing does not apply header pass-through policy from this field.
allowed_tool_namesPresent in the model. Current list/call routing does not enforce it.
tool_name_aliasesOptional exact {downstream_alias: upstream_original} mapping used by tool list/call routing before the single-versus-multi-backend fallback.
allowed_resource_namesPresent in the model. Current resource routing does not enforce it.
allowed_prompt_namesPresent in the model. Current prompt routing does not enforce it.

The current route selection is:

JWT subject
  -> User::new(subject)
  -> UserConfig
  -> path VirtualHostId
  -> VirtualHost
  -> backend map key
  -> BackendMCPGateway.url

Expected config growth beyond the current fields:

  • route selection across multiple MCP endpoints
  • principal/virtual-host filters for tools, resources, and prompts
  • backend auth/TLS material references
  • request/response header pass/add/remove rules
  • plugin/CPEX hook settings
  • pagination/SSE behavior where protocol handling needs config
  • future A2A and LLM routing/provider settings

Redis Storage

RedisUserConfigStore stores user routing config as MessagePack:

ItemEncoding
Redis keyMessagePack-encoded User::new(jwt_subject).
Redis valueMessagePack-encoded UserConfig.
Cache keyRaw subject string through User::key().
Cache valueDecoded UserConfig.

The in-process cache is an implementation detail:

SettingValue
Entries50,000
Expiry--user-config-cache-expiry-seconds, default 60 seconds; 0 disables caching
Redis connection retries1,000

Routing code should stay behind the UserConfigStore trait. That keeps Redis, MessagePack, and cache behavior out of MCP method handling.

Plugin Runtime Config

When runtime_plugins_enabled is true, startup builds a CpexRuntimeRegistry::with_redis_config(...). The registry reads a separate runtime plugin document:

Redis key: ContextForgeGatewayRuntimePluginConfig

RuntimePluginConfigDocument
  version: 1
  cpex: CpexConfig

The plugin config loader accepts JSON bytes or MessagePack bytes. It rejects documents with the wrong version, missing cpex config, unsupported CPEX features, or an unavailable config store.

Current supported plugin scope is deliberately narrow:

CPEX featureCurrent support
TOOL_PRE_INVOKESupported for call_tool before backend invocation.
TOOL_POST_INVOKESupported for call_tool responses and backend progress events.
CPEX routingRejected. Gateway routing is owned by UserConfig and MCP routing code.
Plugin conditionsRejected.
Plugin dirs, global policies, global defaultsRejected.
Other hook typesRejected.

The registry has a watcher interval of 10 minutes. On valid reloads it swaps in the new runtime. On invalid reloads it marks the runtime failed so new plugin calls return an internal MCP error until a valid config is applied.

Process Config

Process Config is not per-user routing state. It is parsed once and controls the gateway shell:

AreaExamples
Listeneraddress, tls_address, downstream TLS certificate and private key.
AuthenticationRSA public key path or HMAC secret for JWT verification.
RedisHost, port, plain/TLS/mTLS mode, trust bundle, client cert, client key.
Upstream HTTP clientPlaintext/TLS/mTLS mode, upstream trust bundle, client cert, client key.
TelemetryOpenTelemetry traces, metrics endpoint/protocol, OTLP headers, service name.
Runtime shapeCPU count and single-runtime versus multi-runtime mode.
PluginsWhether runtime plugins are enabled.
LoggingLog name and rotation.

The process config decides whether the gateway can start and what dependencies it can reach. It does not decide which backend a specific MCP caller may use; that remains in UserConfig.

Selection Invariant

Backend selection should only happen after these facts exist:

authenticated subject
  + loaded UserConfig
  + path VirtualHostId
  + MCP session context

That invariant is why runtime config access is in middleware and MCP validators, not hidden inside individual backend calls.