MCP Integration
The Model Context Protocol (MCP) is an open standard for connecting LLM applications to external tool servers. Cruvero's MCP bridge discovers tools from external MCP servers and registers them as native tools in the tool registry, so agents can call MCP-provided tools the same way they call built-in tools.
This document covers MCP configuration, the bridge architecture, dynamic discovery, and security controls. It is intended for operators adding MCP servers and developers extending the bridge.
Source: internal/mcp/*, internal/tools/mcp_bridge.go
Architecture
The MCP integration has three layers:
-
Configuration (
internal/mcp/config.go) — Parses environment variables intoServerConfigstructs. Each config defines a server name, command, arguments, environment variables, and an optional endpoint allowlist. -
Client (
internal/mcp/client.go) — Wraps themcp-golibrary to provide a stdio-based MCP client. Each client connects to one MCP server process, lists its tools, and executes tool calls. -
Bridge (
internal/tools/mcp_bridge.go) — Manages multiple MCP clients (static and dynamic). Registers MCP tools into the Cruvero tool registry withmcp.<server>.<tool>naming. Routes tool execution requests to the correct server and enforces endpoint security policies.
Static vs Dynamic Servers
- Static servers are configured via environment variables and started when the worker boots. They are always available.
- Dynamic servers are discovered at runtime via the event bus (
MCPAnnounce,MCPHeartbeat,MCPDeregisterevents). The bridge monitors health and marks stale servers as unhealthy.
Configuration
Environment Variables
| Variable | Format | Description |
|---|---|---|
CRUVERO_MCP_SERVERS | name=cmd|arg1|arg2;;name2=cmd|arg1 | Static MCP server definitions. Semicolons separate servers, pipes separate command arguments. |
CRUVERO_MCP_ENV_<NAME> | KEY=value,KEY2=value2 | Extra environment variables passed to the named MCP server process. |
CRUVERO_MCP_DEBUG | true|false | Enable verbose MCP protocol logging. |
CRUVERO_MCP_ENDPOINTS_<NAME> | https://api.example.com,https://other.example.com | Comma-separated allowed endpoint URLs for the named server. |
CRUVERO_MCP_STRICT_ENDPOINTS | true|false (default true) | When enabled, tool calls with URL arguments are denied if the target is not in the server's endpoint allowlist. |
Example Configuration
# Register two MCP servers
export CRUVERO_MCP_SERVERS="github=npx|@modelcontextprotocol/server-github;;filesystem=npx|@modelcontextprotocol/server-filesystem|/data"
# Pass GitHub token to the github server
export CRUVERO_MCP_ENV_GITHUB="GITHUB_PERSONAL_ACCESS_TOKEN=ghp_abc123"
# Restrict the github server to GitHub API endpoints
export CRUVERO_MCP_ENDPOINTS_GITHUB="https://api.github.com"
# Enable debug logging
export CRUVERO_MCP_DEBUG=true
Usage
Adding an MCP Server
- Set the
CRUVERO_MCP_SERVERSenvironment variable with the server definition. - Optionally set
CRUVERO_MCP_ENV_<NAME>for server-specific credentials. - Optionally set
CRUVERO_MCP_ENDPOINTS_<NAME>to restrict allowed endpoints. - Restart the worker. The bridge initializes clients on startup.
How Tools Appear in the Registry
When the tool registry is seeded (cmd/seed-registry), the bridge calls ListTools() on each connected MCP server. Each tool is registered with a namespaced name following the pattern mcp.<server>.<tool>. For example, a tool named create_issue from the github server becomes mcp.github.create_issue.
MCP tool schemas (JSON Schema for inputs) are preserved and used for agent decision context and input validation.
How Agents Discover MCP Tools
Agents discover MCP tools the same way they discover built-in tools — through the tool registry. When tool search is enabled (--tool-search), the agent's decision context includes relevant MCP tools alongside native tools, ranked by semantic similarity to the current task.
Endpoint Security
The endpoint policy system prevents MCP tools from accessing unauthorized hosts. When CRUVERO_MCP_STRICT_ENDPOINTS is enabled (default), the bridge:
- Extracts all URL-like strings from tool call arguments (including nested JSON).
- Validates each URL against the server's configured endpoint allowlist.
- Rejects the tool call if any URL target is outside the allowlist.
Allowlist matching supports subdomain wildcards: configuring https://api.github.com also allows https://uploads.api.github.com.
Enterprise Architecture (Phase 25)
Phase 25 extends the MCP integration from stdio-only subprocess management to a gateway-mediated Streamable HTTP architecture with per-integration scaling, persistent server registry, shared caching, circuit breakers, Vault-backed credential isolation, and Kubernetes-native deployment.
Transport Modes
The CRUVERO_MCP_TRANSPORT variable selects the transport layer:
| Mode | Description |
|---|---|
stdio (default) | Subprocess MCP servers on the worker host. Current behavior. |
http | Streamable HTTP via mcp-go. MCP servers are network-reachable HTTP endpoints. |
gateway | All calls routed through AgentGateway with federation, RBAC, and centralized auth. |
All transports implement a unified Transport interface, so the bridge, caching, circuit breaker, and retry layers work identically regardless of transport mode.
Persistent Server Registry
When CRUVERO_MCP_REGISTRY_ENABLED=true, MCP server configurations are stored in PostgreSQL (mcp_servers table, migration 0030). The bridge loads from both environment variables and the database, with database records taking precedence for duplicates.
The admin UI (/admin/mcp) provides live status badges and CRUD operations for managing servers without restarts.
Response Caching
When CRUVERO_MCP_CACHE_ENABLED=true, the CachingTransport decorator wraps the base transport with Dragonfly-backed response caching:
- Only read-only tools are cached (configurable per-tool).
- Cache keys:
mcp:\{server\}:\{tool\}:\{argHash\}(SHA-256 of sorted, compacted JSON arguments). - Default TTL: 60s (
CRUVERO_MCP_CACHE_TTL). - Cache errors never block calls.
Reliability Stack
Temporal Activity Retry (outer)
→ CircuitBreaker (per-server, application-level)
→ RetryTransport (exponential backoff + jitter, transient errors only)
→ CachingTransport (Dragonfly, read-only tools)
→ HTTPTransport (TLS-enabled, connection pooling)
Circuit breaker: Per-server state machine (closed/half-open/open). Defaults to open after 5 failures in 30s, probes every 10s in half-open state, and closes after 3 consecutive successes. Tunable via CRUVERO_MCP_CIRCUIT_*.
Retry: Exponential backoff with ±25% jitter. Retries on HTTP 502/503/504 and connection errors. Never retries 4xx or context.Canceled.
Vault Credential Isolation
When CRUVERO_MCP_VAULT_ENABLED=true, MCP server credentials are resolved from Vault at path admin/tenant-\{id\}/kv/\{integration\} instead of flat environment variables. Falls back to EnvCredentialProvider (CRUVERO_MCP_ENV_<NAME>) when Vault is unreachable or disabled.
TLS
When CRUVERO_MCP_TLS_ENABLED=true, HTTP and gateway transports use TLS 1.3 with:
- CA bundle validation (
CRUVERO_MCP_TLS_CA_CERT) - Optional mTLS with client certificates (
CRUVERO_MCP_TLS_CERT,CRUVERO_MCP_TLS_KEY) - cert-manager-managed certificates in Kubernetes
Kubernetes Deployment
MCP servers deploy as individual Kubernetes Deployments in the mcp-servers namespace with:
- KEDA ScaledObjects: Per-integration autoscaling based on Temporal task queue depth and CPU utilization.
- Three-tier QoS: Guaranteed (min 2 replicas), Burstable (min 1), BestEffort (min 0, scale-to-zero).
- NetworkPolicy: Ingress from gateway only; no cross-server communication.
- AgentGateway: Centralized routing, federation, and RBAC in the
mcp-infranamespace.
Observability
- OTel spans on every MCP call with
mcp.server,mcp.tool,mcp.transport,mcp.latency_ms,mcp.cachedattributes. - Prometheus metrics:
mcp_call_total,mcp_call_latency_seconds,mcp_cache_hit_total,mcp_cache_miss_total,mcp_circuit_breaker_state,mcp_server_health,mcp_active_connections. - Tenant events for code execution routing:
- Metric
code_exec_routewith fieldstool,outcome(attempt|success|fallback|failed) and optionalreason,latency_ms,fallback_disabled. - Audit events
code_exec_route_fallbackandcode_exec_route_failed.
- Metric
- Grafana dashboard with 6 panels: call rates, latency percentiles, cache hit rates, circuit breaker state, scale-to-zero transitions, error heatmap.
Enterprise Configuration
| Variable | Default | Description |
|---|---|---|
CRUVERO_MCP_TRANSPORT | stdio | Transport mode: stdio, http, gateway |
CRUVERO_MCP_GATEWAY_URL | — | AgentGateway endpoint URL |
CRUVERO_MCP_GATEWAY_AUTH | — | Gateway auth: jwt, apikey, none |
CRUVERO_MCP_GATEWAY_DEFS_CACHE_TTL | 30s | TTL for cached federated tools/list definitions in gateway mode |
CRUVERO_MCP_GATEWAY_INIT_REQUIRE_AUTH | false | Require auth token for initialize and tools/list calls in gateway mode |
CRUVERO_MCP_HTTP_TIMEOUT | 30s | HTTP transport timeout |
CRUVERO_MCP_HTTP_MAX_CONNS | 100 | Max HTTP connections per server |
CRUVERO_MCP_CIRCUIT_ENABLED | true | Enable per-server circuit breaker |
CRUVERO_MCP_CIRCUIT_FAILURE_THRESHOLD | 5 | Failures in window before opening |
CRUVERO_MCP_CIRCUIT_SUCCESS_THRESHOLD | 3 | Half-open probe successes required to close |
CRUVERO_MCP_CIRCUIT_FAILURE_WINDOW | 30s | Rolling failure window |
CRUVERO_MCP_CIRCUIT_HALF_OPEN_INTERVAL | 10s | Time before half-open probe is allowed |
CRUVERO_MCP_RETRY_MAX | 3 | Max retry attempts |
CRUVERO_MCP_RETRY_BACKOFF | 1s | Initial retry backoff |
CRUVERO_MCP_CACHE_ENABLED | false | Enable response caching |
CRUVERO_MCP_CACHE_TTL | 60s | Cache TTL |
CRUVERO_MCP_CACHE_ADDR | — | Cache address (defaults to CRUVERO_DRAGONFLY_ADDR) |
CRUVERO_MCP_CACHE_READONLY_<SERVER> | — | Comma-separated read-only tool names allowed for cache hits (for example list_issues,get_issue) |
CRUVERO_MCP_REGISTRY_ENABLED | false | Enable persistent registry |
CRUVERO_CODE_EXEC_MCP_REQUIRED | false | Fail closed for python_exec/bash_exec if mcp-code-exec is unavailable in `http |
CRUVERO_MCP_VAULT_ENABLED | false | Enable Vault credentials |
CRUVERO_MCP_VAULT_PATH | admin/tenant-\{id\}/kv | Vault path template |
CRUVERO_MCP_TLS_ENABLED | false | Enable TLS |
CRUVERO_MCP_TLS_CA_CERT | — | CA certificate path |
CRUVERO_MCP_TLS_CERT | — | Client certificate path (mTLS) |
CRUVERO_MCP_TLS_KEY | — | Client private key path (mTLS) |
Gateway Troubleshooting
401or403from gateway fortools/list:- Verify
CRUVERO_MCP_GATEWAY_AUTHmatches gateway policy (jwt,apikey, ornone). - If gateway requires auth during startup listing, set
CRUVERO_MCP_GATEWAY_INIT_REQUIRE_AUTH=trueand ensure a token is propagated in context.
- Verify
- Tools missing or stale in gateway mode:
- Reduce
CRUVERO_MCP_GATEWAY_DEFS_CACHE_TTLto force more frequent refresh. - Ensure tenant context is set correctly; gateway definitions cache is tenant-scoped.
- Reduce
- Cache enabled but no hits:
- Ensure read-only tools are explicitly allowlisted with
CRUVERO_MCP_CACHE_READONLY_<SERVER>. - Verify
CRUVERO_MCP_CACHE_ADDRpoints to a reachable Dragonfly/Redis endpoint.
- Ensure read-only tools are explicitly allowlisted with
- Incorrect backend routing:
- Confirm tool names are fully namespaced (
mcp.<server>.<tool>). Gateway routing uses the<server>segment.
- Confirm tool names are fully namespaced (
Limitations
- MCP servers communicate over stdio by default. The server process must be reachable from the worker host.
- In HTTP/gateway modes, MCP servers must expose Streamable HTTP endpoints.
- MCP tool schemas are captured at registry seed time. If an MCP server updates its tool schemas, the registry must be re-seeded with a new version.
- Dynamic discovery requires an event bus (NATS). Without it, only static servers are available.
- MCP server startup failures prevent the bridge from initializing. Ensure all configured servers are available before starting the worker.