Remote MCP OAuth/OIDC
Remote MCP is disabled by default. Enabling it requires both the remote MCP configuration block and the explicit safety flag, so an incomplete deployment fails closed instead of exposing unauthenticated broker tools.
The CLI YAML config uses safety.remote_mcp_enabled. The public Rust
GatewayConfiguration struct exposes the equivalent SDK-facing field as
safety.remote_public_mcp_enabled.
Minimum configuration fields:
remote_mcp.enabled: trueremote_mcp.resource: public protected-resource identifier for the gatewayremote_mcp.issuer: expected OIDC issuerremote_mcp.jwks_url: JWKS endpoint used for token signature checksremote_mcp.audiences: accepted token audiences/resourcesremote_mcp.allowed_scopes: gateway scopes that may be granted remotelyremote_mcp.token_id_hmac_secret_env: environment variable containing the deployment secret used to hash token ids for audit correlationremote_mcp.rate_limit_max_requests: per-client authorization attempts per windowremote_mcp.rate_limit_window_seconds: rate-limit window durationremote_mcp.max_connections: concurrent HTTP connections accepted by the transport before returning503safety.remote_mcp_enabled: true
The CLI HTTP transport is functional when --describe is omitted:
ibkr-agent --config config/remote.example.yaml mcp serve --transport http --enable-remote-mcp --bind 0.0.0.0:8080
It binds the configured address, serves protected-resource metadata at
/.well-known/oauth-protected-resource, and accepts JSON-RPC MCP requests at
POST /mcp. The implementation intentionally uses a small bounded HTTP/1.1
server: it validates Content-Length, caps headers and bodies, returns one
response per connection, caps concurrent connections, applies configured rate
limits, and routes authorized requests through the same tool handlers as stdio.
Broker authentication remains separate from MCP client authorization; MCP bearer tokens must never be forwarded to IBKR.
Example configuration shape:
remote_mcp:
enabled: true
bind_address: 0.0.0.0:8080
resource: https://gateway.example.com/mcp
issuer: https://auth.example.com/
jwks_url: https://auth.example.com/.well-known/jwks.json
metadata_url: https://auth.example.com/.well-known/openid-configuration
audiences:
- https://gateway.example.com/mcp
allowed_scopes:
- ibkr:health:read
- ibkr:accounts:read
- ibkr:portfolio:read
- ibkr:positions:read
- ibkr:marketdata:read
- ibkr:orders:read
- ibkr:audit:read
clock_skew_seconds: 60
rate_limit_max_requests: 120
rate_limit_window_seconds: 60
max_connections: 64
token_id_hmac_secret_env: IBKR_REMOTE_TOKEN_HMAC_SECRET
safety:
remote_mcp_enabled: true
Request behavior:
- missing, malformed, expired, wrong issuer, wrong audience, or bad signature:
401withWWW-Authenticateand protected-resource metadata - valid token with missing tool scope:
403 - repeated authorization attempts from the same forwarded IP or MCP session:
429 - concurrent connections beyond
remote_mcp.max_connections:503 - valid token with the required scope: the request is authorized and the token is not included in downstream broker calls or audit payloads
- valid UUID
x-request-idandmcp-session-idheaders are preserved for request/session correlation; missing or malformed values are replaced with gateway-generated ids initializeandtools/listvalidate the bearer token without a single required tool scope; visible tools are filtered to the token scopes allowed byremote_mcp.allowed_scopestools/callrequires the called tool scope before any backend, order writer, or audit workflow access
Production builds validate RS256 JWTs against RSA JWKS keys. HS256 and JWKS
oct key material are compiled only with the unstable-internal-test-support
feature for deterministic local and CI coverage. Adding ES256 provider keys
should stay inside the OAuth verifier without changing broker-core crates or
tool schemas.