Skip to main content

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:

  1. Configuration (internal/mcp/config.go) — Parses environment variables into ServerConfig structs. Each config defines a server name, command, arguments, environment variables, and an optional endpoint allowlist.

  2. Client (internal/mcp/client.go) — Wraps the mcp-go library to provide a stdio-based MCP client. Each client connects to one MCP server process, lists its tools, and executes tool calls.

  3. Bridge (internal/tools/mcp_bridge.go) — Manages multiple MCP clients (static and dynamic). Registers MCP tools into the Cruvero tool registry with mcp.<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, MCPDeregister events). The bridge monitors health and marks stale servers as unhealthy.

Configuration

Environment Variables

VariableFormatDescription
CRUVERO_MCP_SERVERSname=cmd|arg1|arg2;;name2=cmd|arg1Static MCP server definitions. Semicolons separate servers, pipes separate command arguments.
CRUVERO_MCP_ENV_<NAME>KEY=value,KEY2=value2Extra environment variables passed to the named MCP server process.
CRUVERO_MCP_DEBUGtrue|falseEnable verbose MCP protocol logging.
CRUVERO_MCP_ENDPOINTS_<NAME>https://api.example.com,https://other.example.comComma-separated allowed endpoint URLs for the named server.
CRUVERO_MCP_STRICT_ENDPOINTStrue|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

  1. Set the CRUVERO_MCP_SERVERS environment variable with the server definition.
  2. Optionally set CRUVERO_MCP_ENV_<NAME> for server-specific credentials.
  3. Optionally set CRUVERO_MCP_ENDPOINTS_<NAME> to restrict allowed endpoints.
  4. 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:

  1. Extracts all URL-like strings from tool call arguments (including nested JSON).
  2. Validates each URL against the server's configured endpoint allowlist.
  3. 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:

ModeDescription
stdio (default)Subprocess MCP servers on the worker host. Current behavior.
httpStreamable HTTP via mcp-go. MCP servers are network-reachable HTTP endpoints.
gatewayAll 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-infra namespace.

Observability

  • OTel spans on every MCP call with mcp.server, mcp.tool, mcp.transport, mcp.latency_ms, mcp.cached attributes.
  • 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_route with fields tool, outcome (attempt|success|fallback|failed) and optional reason, latency_ms, fallback_disabled.
    • Audit events code_exec_route_fallback and code_exec_route_failed.
  • Grafana dashboard with 6 panels: call rates, latency percentiles, cache hit rates, circuit breaker state, scale-to-zero transitions, error heatmap.

Enterprise Configuration

VariableDefaultDescription
CRUVERO_MCP_TRANSPORTstdioTransport mode: stdio, http, gateway
CRUVERO_MCP_GATEWAY_URLAgentGateway endpoint URL
CRUVERO_MCP_GATEWAY_AUTHGateway auth: jwt, apikey, none
CRUVERO_MCP_GATEWAY_DEFS_CACHE_TTL30sTTL for cached federated tools/list definitions in gateway mode
CRUVERO_MCP_GATEWAY_INIT_REQUIRE_AUTHfalseRequire auth token for initialize and tools/list calls in gateway mode
CRUVERO_MCP_HTTP_TIMEOUT30sHTTP transport timeout
CRUVERO_MCP_HTTP_MAX_CONNS100Max HTTP connections per server
CRUVERO_MCP_CIRCUIT_ENABLEDtrueEnable per-server circuit breaker
CRUVERO_MCP_CIRCUIT_FAILURE_THRESHOLD5Failures in window before opening
CRUVERO_MCP_CIRCUIT_SUCCESS_THRESHOLD3Half-open probe successes required to close
CRUVERO_MCP_CIRCUIT_FAILURE_WINDOW30sRolling failure window
CRUVERO_MCP_CIRCUIT_HALF_OPEN_INTERVAL10sTime before half-open probe is allowed
CRUVERO_MCP_RETRY_MAX3Max retry attempts
CRUVERO_MCP_RETRY_BACKOFF1sInitial retry backoff
CRUVERO_MCP_CACHE_ENABLEDfalseEnable response caching
CRUVERO_MCP_CACHE_TTL60sCache TTL
CRUVERO_MCP_CACHE_ADDRCache 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_ENABLEDfalseEnable persistent registry
CRUVERO_CODE_EXEC_MCP_REQUIREDfalseFail closed for python_exec/bash_exec if mcp-code-exec is unavailable in `http
CRUVERO_MCP_VAULT_ENABLEDfalseEnable Vault credentials
CRUVERO_MCP_VAULT_PATHadmin/tenant-\{id\}/kvVault path template
CRUVERO_MCP_TLS_ENABLEDfalseEnable TLS
CRUVERO_MCP_TLS_CA_CERTCA certificate path
CRUVERO_MCP_TLS_CERTClient certificate path (mTLS)
CRUVERO_MCP_TLS_KEYClient private key path (mTLS)

Gateway Troubleshooting

  • 401 or 403 from gateway for tools/list:
    • Verify CRUVERO_MCP_GATEWAY_AUTH matches gateway policy (jwt, apikey, or none).
    • If gateway requires auth during startup listing, set CRUVERO_MCP_GATEWAY_INIT_REQUIRE_AUTH=true and ensure a token is propagated in context.
  • Tools missing or stale in gateway mode:
    • Reduce CRUVERO_MCP_GATEWAY_DEFS_CACHE_TTL to force more frequent refresh.
    • Ensure tenant context is set correctly; gateway definitions cache is tenant-scoped.
  • Cache enabled but no hits:
    • Ensure read-only tools are explicitly allowlisted with CRUVERO_MCP_CACHE_READONLY_<SERVER>.
    • Verify CRUVERO_MCP_CACHE_ADDR points to a reachable Dragonfly/Redis endpoint.
  • Incorrect backend routing:
    • Confirm tool names are fully namespaced (mcp.<server>.<tool>). Gateway routing uses the <server> segment.

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.