Warmplane is the local control plane that keeps MCP sessions warm.
It runs multiple upstream MCP servers behind one local process, keeps those sessions persistent, and exposes a compact interaction surface for tools/resources/prompts. The goal is concrete and measurable: reduce startup latency, reduce payload size, and keep behavior deterministic.
Most agent stacks overpay in tokens and latency by eagerly surfacing large tool catalogs and detailed schemas that are never used.
Warmplane shifts that model to lazy, compact interaction:
- Discover compact indexes first.
- Fetch detail only when needed.
- Execute through normalized envelopes.
This improves:
- token efficiency
- time-to-first-useful-tool-call
- cross-client consistency
- observability and policy control
One runtime, three access modes:
- HTTP facade (
/v1/...) - CLI facade commands
- MCP facade server mode (
mcp-server) for MCP-native clients
All three modes share the same backend state, aliases, policy checks, and timeout behavior.
- list: compact capability index
- describe: on-demand detail for one capability
- call: normalized execution envelope
- list: compact resource index
- read: normalized read envelope
- list: compact prompt index
- get: normalized prompt rendering envelope
cargo install --path .cargo install warmplane is not available yet because the crate has not been published to crates.io.
Validate and lint configuration before startup:
warmplane validate-config --config mcp_servers.jsonExample success output:
{"ok":true,"config":"mcp_servers.json","servers":3}Create mcp_servers.json:
{
"port": 9090,
"toolTimeoutMs": 15000,
"authStorePath": "/Users/you/.local/share/opencode/mcp-auth.json",
"capabilityAliases": {
"sqlite.read_query": "db.query"
},
"resourceAliases": {
"filesystem.file:///tmp/readme.txt": "fs.readme"
},
"promptAliases": {
"github.code_review": "prompt.code-review"
},
"policy": {
"allow": ["db.*", "fs.*", "prompt.*"],
"deny": ["fs.secret"],
"redactKeys": ["token", "api_key", "password"]
},
"mcpServers": {
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "./test.db"]
},
"remote_docs": {
"url": "https://mcp.example.com/mcp",
"protocolVersion": "2025-11-25",
"allowStateless": false,
"headers": {
"X-Tenant": "acme"
},
"auth": {
"type": "bearer",
"tokenEnv": "REMOTE_DOCS_MCP_TOKEN"
}
},
"figma": {
"url": "https://mcp.figma.com/mcp",
"auth": {
"type": "oauth",
"scope": "file_content:read",
"tokenStoreKey": "figma"
}
},
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"]
}
}
}Per mcpServers.<id>, transport selection is strict and inferred:
- stdio upstream: set
command(plus optionalargs,env) - HTTP/SSE upstream: set
url(plus optionalprotocolVersion,allowStateless,headers,auth) - exactly one of
commandorurlmust be set
No legacy config fallback is supported.
authStorePath is optional. When omitted, Warmplane reads and writes the shared MCP auth store at the existing OpenCode locations, preferring ~/.local/share/opencode/mcp-auth.json and falling back to ~/.config/opencode/mcp-auth.json.
auth.type = "bearer":
{
"type": "bearer",
"tokenEnv": "MCP_TOKEN"
}auth.type = "basic":
{
"type": "basic",
"username": "svc-user",
"passwordEnv": "MCP_PASSWORD"
}For bearer/basic, exactly one direct secret (token/password) or env-backed secret (tokenEnv/passwordEnv) is required.
auth.type = "oauth":
{
"type": "oauth",
"clientId": "optional-pre-registered-client-id",
"clientName": "warmplane",
"clientSecretEnv": "MCP_CLIENT_SECRET",
"redirectUri": "http://127.0.0.1:8788/callback",
"scope": "files:read files:write",
"tokenStoreKey": "figma",
"authorizationEndpoint": "https://linear.app/oauth/authorize",
"tokenEndpoint": "https://api.linear.app/oauth/token",
"codeChallengeMethodsSupported": ["S256"]
}Warmplane currently uses the shared mcp-auth.json store for OAuth-backed upstream bearer injection. Manage those entries with the auth commands below.
OAuth contract notes:
clientIdis optional when the upstream authorization server supports dynamic client registration;auth start/auth logincan register a client first and persist the returnedclientInfoin the shared auth store.clientNameis optional and defaults towarmplaneduring dynamic registration.redirectUriis optional and defaults tohttp://127.0.0.1:8788/callbackfor loopback callback handling.clientSecretandclientSecretEnvremain mutually exclusive.tokenStoreKeycontrols which shared auth-store entry Warmplane reads and writes.- Discovery metadata (
resourceMetadataUrl, authorization server metadata, registration/token endpoints, PKCE support) is persisted in the shared auth store afterauth discover. - For providers that do not expose RFC 9728 / RFC 8414 discovery metadata, you can preconfigure
authorizationEndpoint,tokenEndpoint, optionalregistrationEndpoint, andcodeChallengeMethodsSupportedas explicit fallback metadata.
Discover upstream OAuth metadata before importing tokens:
warmplane auth discover --config mcp_servers.json figmaThis resolves protected-resource and authorization-server metadata, then persists the normalized discovery record in the shared auth store so later status checks can show whether discovery has already succeeded.
Build a PKCE authorization URL and persist state/codeVerifier in the shared auth store:
warmplane auth start --config mcp_servers.json figmaRun the integrated login flow with best-effort browser launch plus loopback callback capture:
warmplane auth login --config mcp_servers.json figmaExchange the callback code and state using the stored PKCE verifier:
warmplane auth exchange --config mcp_servers.json figma --code <CODE> --state <STATE>Inspect upstream OAuth readiness:
warmplane auth status --config mcp_servers.jsonImport tokens into the shared auth store without editing JSON by hand:
warmplane auth import --config mcp_servers.json figma --access-token-env FIGMA_ACCESS_TOKEN --refresh-token-env FIGMA_REFRESH_TOKENRefresh stored OAuth credentials using the discovered token endpoint and refresh token:
warmplane auth refresh --config mcp_servers.json figmaRemove stored credentials for one upstream:
warmplane auth logout --config mcp_servers.json figmaRecommended bootstrap flow for a new OAuth-capable upstream:
warmplane auth discover --config mcp_servers.json <server>warmplane auth login --config mcp_servers.json <server>warmplane auth status --config mcp_servers.json <server>
Fallback/manual flow when browser automation or callback capture is not convenient:
warmplane auth discover --config mcp_servers.json <server>warmplane auth start --config mcp_servers.json <server>- Complete the browser flow yourself and capture the callback
code+state warmplane auth exchange --config mcp_servers.json <server> --code <CODE> --state <STATE>warmplane auth status --config mcp_servers.json <server>
Recovery and cleanup:
warmplane auth refresh --config mcp_servers.json <server>to rotate expired credentials manually- Long-lived HTTP upstream sessions now re-check OAuth state at request time and refresh expired tokens automatically before sending the next MCP call when refresh prerequisites are present.
warmplane auth logout --config mcp_servers.json <server>to remove stored state and tokenswarmplane validate-config --config mcp_servers.jsonbefore retrying if auth bootstrap behaves unexpectedly
Troubleshooting and rollback diagnostics:
warmplane auth status --config mcp_servers.json <server>to inspect discovery readiness, client readiness, refresh-token availability, and auth statuswarmplane auth logout --config mcp_servers.json <server>followed bywarmplane auth login --config mcp_servers.json <server>to recover from bad or stale local auth state- Remove or restore the shared
mcp-auth.jsonentry for the affectedtokenStoreKeyif you need to roll back to a clean pre-auth state
- Figma is the standards-first target: discovery, dynamic client registration, PKCE
S256, token exchange, and refresh all align with Warmplane's native flow. - Linear is supported natively with explicit fallback metadata in config because its OAuth endpoints are documented but not published through the well-known discovery chain.
- Notion remains a compatibility exception until live validation confirms PKCE and loopback redirect support for its MCP path. Treat it as an explicit shim case instead of silently assuming standards parity.
- If an upstream does not advertise PKCE
S256, does not expose the expected discovery metadata, or requires non-standard redirect handling, treat that as an explicit compatibility gap rather than silently falling back to wrapper behavior.
warmplane daemon --config mcp_servers.jsonEndpoints:
GET /v1/capabilitiesGET /v1/capabilities/:idPOST /v1/tools/callGET /v1/resourcesPOST /v1/resources/readGET /v1/promptsPOST /v1/prompts/get
warmplane mcp-server --config mcp_servers.jsonMCP clients can point directly to this process.
Synthetic lightweight tools exposed:
capabilities_listcapability_describecapability_callresources_listresource_readprompts_listprompt_get
Native MCP methods also supported:
- resources:
resources/list,resources/read - prompts:
prompts/list,prompts/get
# capabilities
warmplane list-capabilities
warmplane describe-capability db.query
warmplane call-capability db.query --params '{"query":"SELECT 1"}'
# resources
warmplane list-resources
warmplane read-resource fs.readme
# prompts
warmplane list-prompts
warmplane get-prompt prompt.code-review --arguments '{"code":"fn main() {}"}'{
"mcpServers": {
"fast-facade": {
"command": "warmplane",
"args": ["mcp-server", "--config", "mcp_servers.json"]
}
}
}Run an end-to-end stdio MCP smoke test:
./scripts/smoke_mcp_server.shIt validates:
- MCP
initialize tools/listincludes all synthetic lightweight facade toolsresources/listandprompts/listreturn valid responses
- Upstream MCP compatibility remains intact.
- Client-facing schemas are intentionally small and stable.
- Policy and aliasing are enforced consistently across modes.
- Timeout and error envelopes are normalized for deterministic orchestration.
- Runtime logs are structured JSON for auditability.
- OpenTelemetry trace export is supported via OTLP.
Warmplane emits structured JSON logs by default (tracing + tracing-subscriber).
Example controls:
RUST_LOG=info,warmplane=debugset verbosityWARMPLANE_OTEL_ENABLED=trueenable OpenTelemetry exportOTEL_EXPORTER_OTLP_ENDPOINT=http://127.0.0.1:4317set OTLP collector endpointWARMPLANE_OTEL_ENDPOINT=http://127.0.0.1:4317fallback OTLP endpoint ifOTEL_EXPORTER_OTLP_ENDPOINTis unsetWARMPLANE_SERVICE_NAME=warmplane-prodoverride service name
Operational notes:
- Logs include structured request/capability/resource/prompt fields for audit trails.
trace_idin execution envelopes can be correlated with logs and distributed traces.- When OTEL is enabled, traces are exported via OTLP gRPC and local structured logs remain active.
For detailed request/response contracts, see docs/spec.md.
Additional references:
- OpenAPI:
docs/openapi.yaml - Config schema:
docs/config.schema.json - Install/distribution:
docs/INSTALL.md - Deployment runbook:
docs/DEPLOYMENT.md - Observability:
docs/OBSERVABILITY.md - Rollout follow-ups:
docs/MCP0_FOLLOW_UPS.md