MCPGateway Guide

Overview

MCPGateway is a Kubemoot CRD that deploys an MCP gateway - the central hub that routes agent tool calls to MCPServers. Agents connect to the gateway (not individual MCPServers), and the gateway discovers and manages connections to all matching MCPServers.

Architecture

Agent Runtime
    │
    │  KUBEMOOT_GATEWAY_ENDPOINT
    ▼
MCPGateway (Deployment + Service)
    │
    ├── MCPServer: kubernetes-mcp  (via label selector)
    ├── MCPServer: nats-mcp        (via label selector)
    ├── MCPServer: github-mcp      (via label selector)
    └── MCPServer: fetch-mcp       (via label selector)

The gateway:

  1. Discovers MCPServers matching its mcpServerSelector
  2. Connects to each server’s HTTP/SSE endpoint (provided by the mcp-bridge for stdio servers)
  3. Performs MCP protocol initialization (initialize → notifications/initialized → tools/list)
  4. Aggregates all discovered tools and exposes them to agents
  5. Routes tool calls from agents to the correct MCPServer

Implementations

ImplementationDescription
kubemoot (default)Kubemoot’s native gateway (Java/Spring WebFlux)
contextforgeIBM ContextForge MCP Gateway (legacy)
microsoftMicrosoft MCP Gateway
dockerDocker MCP Gateway

The reference deployment uses the kubemoot implementation. The gateway image comes from KubemootConfig (spec.images.mcpGateway).

Spec Reference

FieldTypeDefaultDescription
implementationenumkubemootGateway implementation to deploy
mcpServerSelectorLabelSelectorSelects MCPServers to register (all in namespace if empty)
portint328080Gateway service port
replicasint321Number of gateway instances
authMCPGatewayAuthAuthentication configuration
adminUIbooltrueEnable admin interface; unset means enabled, an explicit false turns it off
resourcesResourceRequirementsCPU/memory requests and limits
imagePullSecrets[]LocalObjectReferenceImage pull secrets
registriesMCPRegistriesConfigExternal MCP registry sources
toolIndexToolIndexConfigVector store for semantic tool search
metaToolsMetaToolsConfigDiscovery meta-tools (search_tools, load_tools)
credentialPolicies[]CredentialPolicyCredentials for dynamically discovered MCPs
qualityPolicyRefstringMCPQualityPolicy for filtering discovered MCPs
catalogRefs[]stringMCPCatalog resources for server discovery

MCPGatewayAuth

FieldTypeDefaultDescription
enabledboolfalseRequire authentication
typeenumnoneAuth type: jwt, basic, none
jwtSecretRefstringSecret with JWT signing key
basicAuthBasicAuthConfigBasic auth credentials

MetaToolsConfig

FieldTypeDefaultDescription
enabledboolfalseEnable search_tools and load_tools meta-tools
maxResultsPerSearchint10Max results from search_tools
discoveryDiscoveryConfigOn-demand tool discovery via catalog agent

CredentialPolicy

Credential policies define how dynamically discovered MCP servers get authentication. First matching policy wins. Explicit MCPServer CRs always take priority.

FieldTypeDescription
categories[]stringCategories this policy applies to (use ["*"] as catch-all)
transportenumOverride transport protocol for matched servers
serviceAccountNamestringK8s service account for RBAC
secretRefstringSecret for env vars
secretVolumes[]SecretVolumeSecrets mounted as files
emptyDirVolumes[]EmptyDirVolumeEmptyDir volumes

Status

FieldTypeDescription
phasestringPending, Deploying, Indexing, Ready, Error
readyboolGateway accepting connections
endpointstringGateway service URL
adminEndpointstringAdmin UI URL (if enabled)
registeredServersintCount of registered MCPServers
mcpServers[]stringNames of registered servers
toolIndexToolIndexStatusTool index readiness and count
catalogSyncCatalogSyncStatusCatalog sync progress

Server Selection

The gateway discovers MCPServers via mcpServerSelector. The most common pattern uses Helm release labels:

spec:
  mcpServerSelector:
    matchLabels:
      app.kubernetes.io/instance: homelab-pilot

This selects all MCPServers deployed by the same Helm release. MCPServers must also have registry.enabled: true (the default).

If mcpServerSelector is empty, all MCPServers in the namespace are registered.

MCP Protocol Initialization

The gateway must complete the MCP protocol handshake before tools are available:

  1. Gateway sends initialize request to the MCPServer (via bridge SSE endpoint)
  2. Server responds with capabilities
  3. Gateway sends notifications/initialized notification
  4. Gateway sends tools/list to discover available tools

For stdio/bridge transport, the gateway uses fire-and-forget initialization: send initialize, wait 500ms, send notifications/initialized, wait 500ms, then discover tools. A retry (2s delay) runs if 0 tools are found initially.

Tool List Changes

A server’s tools can change while its URL stays the same: its pod is replaced with new arguments (for example --toolsets=core,config,helm adds helm_list), its container restarts with a new image, or the server changes its tools at runtime. The gateway lists the tools again when one of these states shows:

SignalWhat the gateway does
A stdio (bridge) server sends notifications/tools/list_changed on its SSE streamLists the tools again on the open session. The gateway does not hold a listening stream to streamable HTTP servers, so for them the signals below apply
The MCP server process restarts inside its podThe mcp-bridge sends notifications/tools/list_changed to its SSE clients once the new process has completed its handshake, which re-lists as above
The SSE stream to the server ends (the pod was replaced or went away)Marks the server DISCONNECTED; the next tool call or operator re-registration opens a new session and lists the tools on it
A streamable HTTP server answers 400, 401 or 404 for the sessionOpens a new session, retries the call, and lists the tools on the new session
The operator re-registers a server that is DISCONNECTED, ERROR, or has no toolsConnects again and lists the tools. A burst of re-registrations starts one connect
The operator re-registers a connected server whose list is older than mcp.gateway.tool-list-max-age (default 10m)Lists the tools again. This is a safety net for a change that none of the signals above reported

A re-list that returns no tools, or fails, keeps the tools already known (a replacement server may still be starting) and marks the server ERROR, so the next re-registration connects again. When several re-lists overlap, only the most recently started one updates the list. GET /admin/tools and GET /admin/servers/{id}/tools always serve the latest list.

Agents read GET /admin/tools when they start and again before an evaluation once their copy is older than 60 seconds (10 seconds while it is empty). A tool that is added, removed, or given a new description or input schema reaches running agents that way, without restarting them.

Example

Example Gateway

apiVersion: kubemoot.ai/v1alpha1
kind: MCPGateway
metadata:
  name: example-gateway
spec:
  implementation: kubemoot
  port: 8080
  replicas: 1
  mcpServerSelector:
    matchLabels:
      app.kubernetes.io/instance: homelab-pilot
  adminUI: true
  auth:
    enabled: false
    type: none

Gateway with Tool Index and Meta-Tools

apiVersion: kubemoot.ai/v1alpha1
kind: MCPGateway
metadata:
  name: gateway-with-search
spec:
  implementation: kubemoot
  mcpServerSelector:
    matchLabels:
      app.kubernetes.io/instance: homelab-pilot
  toolIndex:
    vectorStore:
      type: pgvector
      host: pgvector.pgvector
      port: 5432
      database: vectors
      secretRef: pgvector-credentials
    embeddingModel:
      provider: ollama
      endpoint: http://ollama.ollama-a:11434
      model: nomic-embed-text
  metaTools:
    enabled: true
    maxResultsPerSearch: 10

Gateway with Credential Policies

apiVersion: kubemoot.ai/v1alpha1
kind: MCPGateway
metadata:
  name: gateway-with-creds
spec:
  implementation: kubemoot
  credentialPolicies:
    - categories: ["kubernetes", "k8s"]
      serviceAccountName: mcp-k8s-access
    - categories: ["github"]
      secretRef: github-token
    - categories: ["*"]
      transport: stdio

The MCPGateway works with three related CRDs for dynamic server discovery and quality filtering. These are used by the autonomic onboarding system (see onboarding-guide.md).

MCPCatalog

MCPCatalog defines an external MCP server registry to discover servers from. The gateway references catalogs via catalogRefs.

Short name: mcpcat

FieldTypeDefaultDescription
typeenum(required)official-registry, smithery, glama, docker, npm, agent
urlstring(required)Catalog API endpoint or starting URL
agentRefstringAgent CR for navigation (required for type: agent)
queries[]stringCapabilities to search for (empty = all)
syncIntervalstring24hRe-discovery interval
maxServersint100Max servers to discover
authCatalogAuthAuthentication for the catalog API
qualityPolicyRefstringMCPQualityPolicy to evaluate discovered servers

Status tracks serversDiscovered, serversAllowed, serversBlocked, and per-server details including quality decisions.

apiVersion: kubemoot.ai/v1alpha1
kind: MCPCatalog
metadata:
  name: official-registry
spec:
  type: official-registry
  url: https://registry.modelcontextprotocol.io/v0/servers
  syncInterval: "24h"
  maxServers: 50
  qualityPolicyRef: quality-policy

Warning: Auto-syncing the official MCP registry can deploy community servers that crash. Keep MCPCatalog disabled until quality filtering is robust enough to prevent broken deployments.

MCPQualityPolicy

MCPQualityPolicy defines trust and quality filtering for discovered MCP servers. Evaluation order: allowing → blocking → tested → considering (AI evaluation).

Short name: mqp

Evaluation Pipeline

Discovered Server
    │
    ├── Allowing list match? ──── YES → Allow immediately
    │                              (essential infrastructure MCPs)
    ├── Blocking list match? ──── YES → Block immediately
    │                              (known bad, deprecated, suspicious)
    ├── Tested (MCPServerReport)? ── successRate > threshold → Allow
    │                              ── verdict: "avoid" → Block
    │
    └── Considering (AI eval) ──── agentRef evaluates via LLM
                                   ── confidence > threshold → decision
                                   ── below threshold → fallbackAction
FieldTypeDescription
allowing[]AllowingEntryServers accepted without evaluation (name or author match)
blocking[]BlockingEntryServers rejected without evaluation (supports glob/regex, semver)
testedTestedConfigUse MCPServerReport trial history for decisions
consideringConsideringConfigAI-based evaluation for remaining servers

ConsideringConfig

FieldTypeDefaultDescription
enabledbooltrueEnable AI evaluation
agentRefstring(required)Quality evaluator Agent CR
criteriastringInline evaluation criteria
criteriaFromConfigMapConfigMapKeyRefLoad criteria from ConfigMap
confidenceThresholdstring0.7Min confidence to accept AI decision
timeoutSecondsint30AI evaluation timeout
fallbackActionenumdenyAction when AI unavailable: allow or deny
apiVersion: kubemoot.ai/v1alpha1
kind: MCPQualityPolicy
metadata:
  name: quality-policy
spec:
  allowing:
    - author: "modelcontextprotocol"  # Official MCP servers
    - name: "mcp/kubernetes"          # Essential infrastructure
  blocking:
    - name:
        type: glob
        value: "*crypto*"
      reason: "Cryptocurrency-related servers not needed"
    - author:
        type: exact
        value: "known-bad-actor"
      reason: "Known malicious publisher"
  tested:
    enabled: true
    minSuccessRate: "0.8"
    blockBroken: true
  considering:
    enabled: true
    agentRef: quality-evaluator
    confidenceThreshold: "0.7"
    fallbackAction: deny

MCPServerReport

MCPServerReport tracks deployment and runtime experience for an MCP server. One report per server accumulates trial records, enabling Kubemoot to learn which servers work reliably and which to avoid.

Short name: mcprpt

Verdicts

VerdictMeaning
useWorking reliably, recommended
cautionFlaky or mixed results
avoidBroken, should not be deployed
untestedNo trials recorded yet

Trial Phases

Each trial records where in the lifecycle it succeeded or failed:

PhaseDescription
deployContainer started successfully
connectGateway connected to the server
initializeMCP protocol handshake completed
discovertools/list returned tools
callA tool call executed successfully

Spec (identity + admin curation)

FieldTypeDescription
serverNamestringCanonical server name
githubUrlstringSource repository URL
registryTypestringPackage registry (npm, docker, pip)
packageIdentifierstringRegistry-specific package ID
adminVerdictenumHuman-pinned verdict override: use, caution, avoid
adminNotesstringExplanation for pinned verdict
adminAuthorstringWho pinned the verdict

Status (continuously updated)

FieldTypeDescription
verdictenumComputed or admin-pinned verdict
recommendedTransportstringMost reliable transport
recommendedVersionstringMost recent successful version
successCountint64Total successful trials
failureCountint64Total failed trials
successRatestringComputed ratio (0.0-1.0)
trials[]TrialRecordRecent trial history (capped at 20)
chroniclerNotesstringAI-generated analysis
apiVersion: kubemoot.ai/v1alpha1
kind: MCPServerReport
metadata:
  name: kafka-mcp-report
spec:
  serverName: kafka-mcp
  githubUrl: https://github.com/example/kafka-mcp
  registryType: docker
status:
  verdict: use
  recommendedTransport: stdio
  successRate: "0.95"
  successCount: 19
  failureCount: 1

Admin override example:

kubectl patch mcprpt kafka-mcp-report --type=merge -p \
  '{"spec":{"adminVerdict":"avoid","adminNotes":"Crashes under load","adminAuthor":"ops-team"}}'

Troubleshooting

0 tools discovered

Check the full chain:

  1. MCPServer pods are Running and Ready
  2. MCPServer status.endpoint is set
  3. MCPGateway’s mcpServerSelector matches the server’s labels
  4. Gateway pod logs for initialization errors

Gateway caches failed connections

If the gateway fails to connect to an MCPServer on first registration, it caches the failure and never retries. Delete the gateway pod to force re-connection:

kubectl delete pod -l kubemoot.ai/mcpgateway=<name>

Agent has no tools

Verify the agent’s KUBEMOOT_GATEWAY_ENDPOINT env var points to the correct gateway service:

kubectl exec deploy/<agent-name> -- env | grep GATEWAY

MCP bridge session ID missing

The bridge SSE endpoint event must include sessionId= in the data field. If the gateway logs show “skipping initialization” for a server, check the bridge version - older versions may not include the session ID.