A distributed platform for managing, executing, and proxying MCP (Model Context Protocol) servers. It handles tool discovery, authentication, and deployment of MCP server instances across containerized environments, and serves as the gateway for clients like Cursor, LibreChat, and Claude Desktop.

High-Level Design

The platform is a set of cooperating Go microservices behind a single MCP endpoint, coordinated through an event-driven architecture:

  • Proxy: the MCP protocol handler. Speaks pure JSON-RPC 2.0 over HTTP (protocol versions 2024-11-05 and 2025-03-26), validates JWT bearer tokens via JWKS, discovers and filters tools per user, and routes tools/call requests to the right running server instance.
  • Registry: the source of truth. Catalogs server metadata, group-based access rules, secret templates, and tenant quotas in PostgreSQL; publishes lifecycle events when servers are created, updated, or deleted.
  • Execution: the orchestrator. Consumes registry events and provisions MCP server instances on a pluggable backend (Nomad or Kubernetes, selected per server), with Consul handling service registration and health checks.
  • Identity: the federation gateway. Fronts an IDP federation layer (Ory Polis) so each customer tenant can bring their own identity provider (Keycloak, Azure AD, Okta, Google Workspace) over SAML or OIDC, and runs a SCIM-based directory sync worker that provisions users and groups automatically.

State changes propagate through Redis Streams events (registry.server.approved, execution.server.started, …) rather than direct service-to-service calls. Server and tool caches are persistent, with no expiry-based invalidation, and updated purely by events, with a periodic Consul reconciliation pass as a safety net against missed events.

Architecture

  flowchart TB
    clients["MCP Clients<br/>Cursor · Claude Desktop · LibreChat"]
    web["Web App (React)<br/>role-based UI: users · tenant admins · operators"]
    proxy["Proxy Service<br/>MCP protocol · auth · routing"]
    registry["Registry Service<br/>metadata · groups · tenants"]
    redis[("Redis<br/>Streams + cache")]
    pg[("PostgreSQL<br/>tenant-scoped")]
    vault[("Vault<br/>tenant-isolated secrets")]
    exec["Execution Service<br/>multi-backend dispatch"]
    orch["Nomad / Kubernetes"]
    mcp["MCP Server Instances<br/>HTTP-native & stdio-wrapped"]
    consul["Consul<br/>discovery + health checks"]
    identity["Identity Service<br/>directory sync worker"]
    polis["IDP Federation<br/>SAML · OIDC · SCIM"]
    idp["Customer IDPs<br/>Keycloak · Azure AD · Okta"]

    clients -- "JSON-RPC 2.0 / HTTP" --> proxy
    web -- REST --> registry
    web -- REST --> proxy
    registry --> pg
    registry -. events .-> redis
    redis -. events .-> exec
    redis -. events .-> proxy
    proxy -- "tool calls" --> mcp
    proxy --> vault
    exec --> orch
    orch -- schedules --> mcp
    mcp -- registers --> consul
    consul -. reconciliation .-> exec
    identity -- polls --> polis
    identity --> pg
    idp -- "SCIM 2.0 push" --> polis
    idp <-- "SAML / OIDC login" --> polis

Data Flows

Server provisioning: an admin imports a server from the official MCP registry; the registry stores it and, on approval, publishes registry.server.approved; the execution service picks the configured backend (Nomad job or Kubernetes Deployment + Service), starts the container, and registers it in Consul; execution.server.started then flows back through Redis so the proxy adds the new tools to its cache. The next tools/list from any client sees them: no polling, no restarts.

  sequenceDiagram
    participant W as Web App
    participant R as Registry
    participant E as Execution
    participant O as Nomad / K8s
    participant C as Consul
    participant P as Proxy

    W->>R: import server
    R--)E: registry.server.approved (Redis Streams)
    E->>O: schedule instance
    O->>C: register service + health check
    E--)P: execution.server.started (Redis Streams)
    Note over P: cache updated, tools visible<br/>on next tools/list

Tool execution: a client POSTs a JSON-RPC tools/call to /mcp; the proxy validates the JWT, resolves the tool name to a server instance from its cache, checks group permissions and tenant isolation, injects user secrets from Vault if the server requires them, and forwards the call to the instance’s registered endpoint.

User-specific discovery: servers requiring credentials (e.g. a GitHub server needing a personal token) are excluded from the global tool list. When a user authenticates, the proxy checks Vault for that user’s secrets and merges matching servers into their personal tool list, with secrets injected per-request from templates. Two users of the same GitHub server see their own repositories.

Identity & directory sync: customer IDPs push users and groups over SCIM 2.0 to the federation layer; a sync worker polls and reconciles them into PostgreSQL per tenant, so group-based tool access stays current as people join and leave the organization.

Multi-Tenancy & Security

Single shared deployment serves all tenants. Isolation is enforced at every layer: the tenant ID is derived from the user’s email domain and carried as a JWT claim, every database query filters on tenant_id, Redis cache keys are tenant-scoped, and Vault policy templates confine each tenant to secret/{tenant}/* paths; cross-tenant secret reads fail at the policy level, not in application code. Tenant onboarding includes DNS TXT-record domain verification before SSO can be configured.

A React web app fronts the platform, with features shown or hidden based on the roles and groups carried in the user’s JWT. End users get a self-service area to manage their profile, enable or disable servers, supply their own authentication secrets (stored in Vault), and browse the tools, prompts, and resources their groups grant them. Tenant admins land in a management console for servers, users, SSO connections, and directory sync, and platform operators get system-level administration: tenant onboarding with the DNS verification flow, quotas, and a fleet-wide view of servers and users across all tenants. The platform supports protocol versioning so multiple MCP server implementations, HTTP-native and stdio-based (wrapped via an HTTP↔stdio gateway), coexist behind a single endpoint.

Go Redis Streams PostgreSQL HashiCorp Vault Consul Docker Nomad Kubernetes MCP Protocol

Freelance/contract project, March 2025 – June 2026.