MCP Depot Documentation

MCP Depot is a self-hosted hub that lets any MCP-compatible AI assistant (Claude, OpenCode, Cursor, Zed) connect to all your tools through a single endpoint - running entirely on your own infrastructure.

Open source: MCP Depot is AGPL-3.0-licensed. All code is on GitHub. Contributions welcome.


Installation

MCP Depot supports three deployment options. Choose the one that fits your setup:

Option 1 — npm

Prerequisites: Node.js 18+. No Docker required. Data stored in SQLite — ideal for personal use.

bash
npx mcp-depot

Opens the admin UI at http://localhost:3000. Admin credentials are printed to the console on first run.

Option 2 — Docker Compose

Prerequisites: Docker and Docker Compose. Recommended for teams — includes PostgreSQL.

  1. Clone the repository
    bash
    git clone https://github.com/mcp-depot/mcp-depot.git
    cd mcp-depot
  2. Configure environment variables

    Copy the example env file and set your values:

    bash
    cp .env.example .env

    At minimum, set JWT_SECRET and ENCRYPTION_KEY to strong random strings. See Environment Variables for the full reference.

  3. Start the stack
    bash
    docker compose up -d

    The admin UI will be available at http://localhost:5173.

Production tip: Put MCP Depot behind a reverse proxy (nginx, Caddy, Traefik) with HTTPS. Your AI clients will connect over the internet, so TLS is important.

Option 3 — Kubernetes (Helm)

Prerequisites: kubectl and helm installed, a running Kubernetes cluster (Rancher Desktop, minikube, k3s, EKS, GKE, etc.), and Docker images built locally or pushed to a registry. Ideal for production-grade, scalable deployments.

Install

Pre-built images are published to GitHub Container Registry on every release. No local build needed:

bash
helm install mcp-depot oci://ghcr.io/mcp-depot/helm/mcp-depot \
  --namespace mcp-depot \
  --create-namespace

Or install from a local clone:

bash
helm install mcp-depot ./helm/mcp-depot \
  --namespace mcp-depot \
  --create-namespace

Access the UI

Port-forward (quickest, no ingress controller needed):

bash
kubectl port-forward -n mcp-depot svc/mcp-depot-client 8080:80
# Open http://localhost:8080

Ingress (persistent, requires an ingress controller such as Traefik or NGINX):

bash
helm upgrade mcp-depot ./helm/mcp-depot \
  --namespace mcp-depot \
  --set ingress.enabled=true \
  --set ingress.className=traefik \
  --set ingress.host=mcp-depot.local

Then add 127.0.0.1 mcp-depot.local to your /etc/hosts (or C:\Windows\System32\drivers\etc\hosts on Windows) and open http://mcp-depot.local.

Key values

ValueDefaultDescription
image.server.repositoryghcr.io/mcp-depot/mcp-depot-serverServer image repository
image.server.taglatestServer image tag — use a sha-<short> tag to pin
image.client.repositoryghcr.io/mcp-depot/mcp-depot-clientClient image repository
image.mcpRunner.repositoryghcr.io/mcp-depot/mcp-depot-mcp-runnermcp-runner sidecar image repository — isolates stdio external MCP server execution from the main server container
mcpRunner.enabledtrueRun stdio external MCP servers in the isolated mcp-runner sidecar instead of the server container
secrets.mcpRunnerTokenauto-generatedShared token the server uses to authenticate to the mcp-runner sidecar
postgres.enabledtrueDeploy bundled PostgreSQL
externalDatabase.url""External DB connection string (set when postgres.enabled=false)
ingress.enabledfalseCreate an Ingress resource
ingress.className""Ingress class (traefik, nginx, …)
ingress.hostmcp-depot.localHostname for the ingress rule
secrets.jwtSecretauto-generatedJWT signing secret (set explicitly for production)
secrets.existingSecret""Name of a pre-existing Secret to use instead of the chart-managed one
env.ALLOW_REGISTRATION"false"Allow public user registration
extraEnv[]Extra env vars injected into the server container — use for OIDC, feature flags, etc.
extraEnvFrom[]Extra envFrom sources (ConfigMap or Secret refs) for the server container
autoscaling.enabledfalseEnable Horizontal Pod Autoscaler

Production tip: Always set secrets.jwtSecret, secrets.sessionSecret, and secrets.encryptionKey explicitly. Auto-generated values are preserved across upgrades but setting them explicitly gives you full control.

Upgrade & uninstall

bash
# Upgrade
helm upgrade mcp-depot ./helm/mcp-depot --namespace mcp-depot

# Uninstall (PVCs are not deleted automatically)
helm uninstall mcp-depot --namespace mcp-depot
kubectl delete namespace mcp-depot

First Login

On first startup, MCP Depot creates a default admin account. You will be prompted to change the password on first login.

  1. Open the admin UI in your browser. The URL depends on your deployment method:
    DeploymentURL
    npmhttp://localhost:3000
    Docker Composehttp://localhost:5173
    Kubernetes — port-forwardhttp://localhost:8080 (or whichever local port you forwarded)
    Kubernetes — ingresshttp://<your-ingress-host> (e.g. http://mcp-depot.local)
  2. Log in with the default credentials shown in the startup logs (or set via ADMIN_EMAIL / ADMIN_PASSWORD env vars).
  3. Go to Settings → API Keys to generate your personal API key. You will need this to configure your AI clients.

Connecting a Client

MCP Depot exposes an MCP endpoint at /mcp. You connect your AI client by installing the mcp-depot CLI proxy, which acts as a stdio bridge to your server.

Step 1 - Install the CLI

bash
npm install -g mcp-depot

Step 2 - Authenticate

Run this once per machine. It saves your server URL and API key locally so the MCP proxy can connect on startup.

bash
mcp-depot --login

--login stores credentials in your local config file. Re-run it any time you change your server URL or rotate your API key.

Step 3 - Configure your AI client

Claude Desktop

Edit ~/Library/Application Support/Claude/claude_desktop_config.json (macOS) or %APPDATA%\Claude\claude_desktop_config.json (Windows):

json
{
  "mcpServers": {
    "mcp-depot": {
      "command": "mcp-depot",
      "args": ["--mcp"],
      "env": {
        "MCP_DEPOT_URL": "http://localhost:3000",
        "MCP_DEPOT_API_KEY": "your-api-key-here"
      }
    }
  }
}

Claude Code (CLI)

bash
claude mcp add mcp-depot -- mcp-depot --mcp \
  --env MCP_DEPOT_URL=http://localhost:3000 \
  --env MCP_DEPOT_API_KEY=your-api-key-here

Claude Code (CLI)

bash
claude mcp add mcp-depot -- mcp-depot --mcp

Cursor

Add to .cursor/mcp.json in your project (same JSON format as Claude Desktop above).


Architecture

MCP Depot sits between your AI clients and all your tools — one hub, one configuration, any number of clients.

Claude Desktop SSE / stdio Cursor HTTP transport OpenCode / Zed HTTP transport VS Code stdio / HTTP Any MCP Client any transport AI Clients MCP Depot Hub MCP Protocol Layer · stdio · SSE · HTTP/REST Tool Registry REST integrations Meta-tools API key auth Health monitoring Tool namespacing Skills & Personas Prompt templates Saved contexts Persona config Composite tools Skill sharing Session Manager Client tracking Tool monitoring Usage analytics Channels Context store PostgreSQL · persistent data store Integrations Jira Issue tracking GitHub / Bitbucket Code & PRs Confluence Docs & wikis Slack / Teams Messaging Custom HTTP APIs any endpoint

AI clients connect directly to MCP Depot over stdio, SSE, or HTTP — no extra proxy needed. The hub handles:

Transport layer

MCP Depot supports all three MCP transport types from a single server process. You choose the transport per AI client — they can all coexist on the same instance.

TransportBest forHow to connect
stdio AI clients on the same machine or inside the same Docker network Point the client at the mcp-depot wrapper binary; it forwards over stdin/stdout
HTTP + SSE Remote clients, MCP Inspector, most IDE extensions http://host:3000/mcp — SSE stream for server→client pushes
Streamable HTTP Modern clients (Claude.ai, Claude Code) Same /mcp endpoint; client negotiates the protocol automatically

Tool types

Every tool exposed over MCP is one of four internal types. The MCP Router determines which executor to call based on how the tool was registered.

TypeWhat it doesConfigured in
REST tool Calls an HTTP endpoint on an integration. Injects the integration's stored credentials, maps AI-provided parameters into the request, and returns the response as MCP content. Tools page → New Tool
External MCP proxy Connects to a remote or local MCP server (stdio or HTTP) and forwards tool calls through a persistent connection pool. The external server's tools are namespaced under its configured name. Settings → External MCP
Skill A saved prompt template exposed as a tool. The AI calls it with parameters; MCP Depot returns the rendered prompt as text content. Skills page
Meta-tool Built-in tools provided by MCP Depot itself: channel read/write, context store/retrieve, list integrations, fetch URL, and others. Always present, no configuration needed. Always enabled

External MCP proxy

The External MCP Proxy is what lets you use MCP Depot as a hub for other MCP servers — Jira, GitHub, Bitbucket, or any custom MCP server you run.

The Discover tab in Settings lets you browse the official MCP registry and add external servers with pre-filled configuration — including required environment variables — pulled directly from the registry metadata.

Sidecar isolation: stdio external MCP servers can run arbitrary local commands and install their own packages. Rather than run that inside the main server container, MCP Depot spawns them in a separate, minimal mcp-runner container with no access to DATABASE_URL, JWT_SECRET, or ENCRYPTION_KEY. On a plain npm install (no container boundary to speak of), it falls back to spawning directly.

Request flow — REST tool call

Step-by-step trace of what happens when an AI client calls a REST-backed tool:

  1. Transport receives the call — the AI client sends a tools/call MCP request over its active transport (stdio, SSE, or Streamable HTTP).
  2. Auth middleware validates — the request carries a JWT bearer token or API key. The middleware verifies it before the request proceeds; unauthenticated calls are rejected immediately.
  3. MCP Router dispatches — the router looks up the tool by its namespaced name, determines it is a REST tool, and hands off to the REST Tool Executor.
  4. Credentials injected — the Tool Executor loads the integration's encrypted credentials from the database (decrypted in memory, never written to logs), assembles the outbound HTTP request, and injects the stored auth header.
  5. Integration API called — the request is sent to the configured endpoint (e.g. Jira, GitHub, your own API). AI-provided parameters are mapped to query params, path segments, or the request body per the tool definition.
  6. Response transformed — the API response is converted into MCP content format (text, JSON, or error) and returned to the router.
  7. Session logged — the Session Manager records the call: tool name, client ID, latency, and success/failure. Visible in the Monitoring dashboard.
  8. Result returned — the MCP response travels back over the same transport to the AI client.

Data & security

DataStorageProtection
Integration credentials (API keys, tokens, passwords)DatabaseAES-256 encrypted at rest; decrypted in memory only at call time
External MCP server auth tokensDatabaseAES-256 encrypted at rest
User passwordsDatabasebcrypt hashed; plaintext never stored
Tool definitions, integrations, skills, personasDatabaseNo special encryption; no credentials in these records
Session channels & contextsDatabasePlain text; do not store secrets in channel messages or contexts
Database engineSQLite (default) or PostgreSQLSQLite is suitable for single-node; use PostgreSQL for multi-instance or high-volume
Stdio external MCP server executionIsolated mcp-runner sidecar containerNo access to DATABASE_URL, JWT_SECRET, or ENCRYPTION_KEY — arbitrary spawned commands can't reach the main server's secrets

The encryption key is derived from ENCRYPTION_KEY in your .env. Back it up — losing the key means losing access to all stored credentials.


Tools

A Tool in MCP Depot is an MCP-callable action backed by an HTTP endpoint. You define it once in the admin UI; all connected AI clients can then call it.

Creating a tool

  1. In the admin UI, go to Tools → New Tool.
  2. Enter the tool name (snake_case, e.g. get_user_profile) and description - the description is what the AI sees when deciding which tool to use, so make it clear and specific.
  3. Set the endpoint: method (GET/POST/PUT/DELETE), URL, and any required headers (e.g. Authorization: Bearer ...).
  4. Define parameters - the inputs the AI can provide. Each parameter has a name, type, and optional description.
  5. Save and the tool is immediately available to connected clients.

Tool parameters

Field Type Description
name required Unique tool identifier (snake_case). Shown to AI clients.
description required What the tool does. Used by the AI to choose when to call it.
endpoint.method required HTTP method: GET, POST, PUT, PATCH, DELETE.
endpoint.path required Full URL or path relative to integration base URL.
endpoint.headers optional Static headers to include (e.g. auth tokens). Stored encrypted.
endpoint.params optional Named parameters exposed to the AI. Path params substitute into URL.
isActive optional Whether the tool is visible to AI clients. Default: true.

Integrations

An Integration is a group of tools sharing a base URL and common auth. For example, a Jira integration holds the Jira URL and API token - all Jira tools inherit these automatically.

Credentials stored in integrations are encrypted at rest using AES-256-GCM. They are never exposed to AI clients.

External MCP Server integrations

Beyond HTTP tools, MCP Depot can proxy entire external MCP servers. Under Integrations → New MCP Server, provide:

MCP Depot starts and manages the server process — in Docker Compose and Kubernetes deployments, this happens inside the isolated mcp-runner sidecar (see External MCP proxy) rather than the main server container — proxying all its tools through your unified endpoint.


Sessions

A session is an active connection between an AI client and MCP Depot. Sessions are tracked automatically when an AI client connects via the CLI proxy.

The Connected Clients panel in the admin UI shows all active sessions with the client name (e.g. claude-code, cursor), version, and connection time. Stale sessions expire automatically after ~150 seconds of inactivity.


Session Channels

Session Channels are append-only message streams that any AI session or human can post to and read from. They are designed for coordination between AI assistants and team members - posting progress updates, sharing findings, or leaving instructions for the next session.

Common use cases

Channel MCP Tools

append-to-channel

Post a message to a named channel. Creates the channel if it does not exist.

json
// Input
{ "channel": "dev-notes", "message": "Build passed. Deployed to staging." }

// Output
{ "id": "abc123", "channel": "dev-notes", "createdAt": "2026-05-04T09:00:00Z" }

read-channel

Read messages from a channel. Pass since to get only new messages - useful for incremental reads in long sessions.

json
// Input
{ "channel": "dev-notes", "since": "2026-05-04T08:00:00Z" }

// Output
{ "messages": [...], "count": 3 }

list-channels

List all existing channels with message count and last activity.

clear-channel

Delete all messages from a channel. Irreversible - use with care.

watch-channel

Long-poll a channel until a new message arrives, then return it. Use this when you want the AI to wait for a reply without polling repeatedly. Returns { timedOut: true } if no message arrives within the timeout.

json
// Input
{ "channel": "dev-notes", "timeout": 60 }

// Output (when message arrives)
{ "message": "...", "postedAt": "2026-05-04T09:01:00Z", "timedOut": false }

// Output (if timeout expires)
{ "timedOut": true }

Tip: Use read-channel with a since timestamp at the start of each session to catch up on anything posted since you last connected. This is more token-efficient than reading the full channel history every time.


Session Contexts

Session Contexts let AI sessions save named snapshots of their working state - the current task, relevant file paths, decisions made, questions to follow up on. Contexts persist across sessions and can be shared with other AI clients.

How it works

Context MCP Tools

store-session-context

Save a named context. Overwrites if a context with the same name already exists.

json
{
  "name": "current-task",
  "content": "Working on the pool balance API. PR #47 pending review.",
  "ttlHours": 48,
  "shared": false
}

get-session-context

Retrieve a context by name.

list-session-contexts

List all contexts visible to the current user (own + shared). Includes TTL countdown.

delete-session-context

Delete a context by name.


Environment Variables

VariableRequiredDescription
JWT_SECRET required Secret for signing JWT tokens. Use a long random string.
ENCRYPTION_KEY required 32-byte key for AES-256-GCM encryption of stored credentials.
PORT optional HTTP port for the server. Default: 3000.
ADMIN_EMAIL optional Email for the default admin account created on first start.
ADMIN_PASSWORD optional Password for the default admin account. Change after first login.
DATABASE_PATH optional Path to SQLite database file. Default: ./data/mcp-depot.db.
LOG_LEVEL optional Logging verbosity: error, warn, info, debug. Default: info.
CORS_ORIGINS optional Comma-separated allowed CORS origins. Default: *.
OIDC_ENABLED optional Set to true to show the SSO login button. Requires OIDC_ISSUER_URL and OIDC_CLIENT_ID. See SSO / OIDC.
OIDC_ISSUER_URL optional Internal OIDC issuer URL (used server-side for token exchange and discovery).
OIDC_ISSUER_PUBLIC_URL optional Public-facing issuer URL (used by the browser for the authorization redirect). Defaults to OIDC_ISSUER_URL.
OIDC_CLIENT_ID optional OIDC client ID registered in your identity provider.
OIDC_CLIENT_SECRET optional OIDC client secret. Store in a Secret (Kubernetes) or .env file, not plain config.
OIDC_DISPLAY_NAME optional Label on the SSO login button. Default: Login with SSO.
CLIENT_URL optional Public URL of the MCP Depot UI (e.g. https://mcp-depot.yourcompany.com). Required when using OIDC — used to build the redirect_uri sent to the identity provider. Defaults to http://localhost:5173, which will cause OIDC login to fail in any non-local deployment.
MCP_RUNNER_URL optional URL of the mcp-runner sidecar that isolates stdio external MCP server execution from the main server container. Set automatically in Docker Compose and Helm; unset in the plain npm install (spawns directly instead — there's no container boundary to isolate from).
MCP_RUNNER_TOKEN optional Shared token authenticating the server to the mcp-runner sidecar. Auto-generated in Docker Compose and Helm.

SSO / OIDC

MCP Depot supports single sign-on via any OIDC-compatible provider — Keycloak, Okta, Auth0, Azure AD, and others. When enabled, a Login with SSO button appears on the login page alongside email/password login.

Required env vars

VariableRequiredDescription
OIDC_ENABLED required Set to true to enable the SSO login button.
OIDC_ISSUER_URL required OIDC issuer URL used by the server for token exchange and discovery. Use the internal hostname when running in Docker or Kubernetes (e.g. http://keycloak:8080/realms/myrealm).
OIDC_CLIENT_ID required Client ID registered in your OIDC provider.
OIDC_CLIENT_SECRET required Client secret from your OIDC provider. Store in a Secret, not a ConfigMap.
OIDC_ISSUER_PUBLIC_URL optional Public-facing issuer URL used by the browser for the authorization redirect. Set this when the internal and public hostnames differ (e.g. internal: http://keycloak:8080/realms/myrealm, public: https://auth.yourcompany.com/realms/myrealm). Falls back to OIDC_ISSUER_URL if not set.
OIDC_DISPLAY_NAME optional Label shown on the SSO login button. Default: Login with SSO.

Kubernetes (Helm) example — Keycloak

Create a Secret with the client secret, then pass OIDC vars via extraEnv and extraEnvFrom:

bash
kubectl create secret generic mcp-depot-oidc \
  --namespace mcp-depot \
  --from-literal=OIDC_CLIENT_SECRET=<your-secret>

helm upgrade mcp-depot ./helm/mcp-depot \
  --namespace mcp-depot \
  --reuse-values \
  --set "extraEnv[0].name=OIDC_ENABLED" \
  --set-string "extraEnv[0].value=true" \
  --set "extraEnv[1].name=OIDC_ISSUER_URL" \
  --set "extraEnv[1].value=http://keycloak.mcp-depot.svc.cluster.local:8080/realms/myrealm" \
  --set "extraEnv[2].name=OIDC_ISSUER_PUBLIC_URL" \
  --set "extraEnv[2].value=https://auth.yourcompany.com/realms/myrealm" \
  --set "extraEnv[3].name=OIDC_CLIENT_ID" \
  --set "extraEnv[3].value=mcp-depot-client" \
  --set "extraEnv[4].name=OIDC_DISPLAY_NAME" \
  --set "extraEnv[4].value=Login with Keycloak" \
  --set "extraEnv[5].name=CLIENT_URL" \
  --set "extraEnv[5].value=https://mcp-depot.yourcompany.com" \
  --set "extraEnvFrom[0].secretRef.name=mcp-depot-oidc"

Docker Compose example

yaml
services:
  server:
    environment:
      OIDC_ENABLED: "true"
      OIDC_ISSUER_URL: http://keycloak:8080/realms/myrealm        # internal
      OIDC_ISSUER_PUBLIC_URL: https://auth.yourcompany.com/realms/myrealm  # browser
      OIDC_CLIENT_ID: mcp-depot-client
      OIDC_CLIENT_SECRET: <your-secret>
      OIDC_DISPLAY_NAME: Login with Keycloak
      CLIENT_URL: https://mcp-depot.yourcompany.com

Two things must match: (1) Set CLIENT_URL to your MCP Depot public URL — this becomes the redirect_uri sent to the identity provider. (2) In your Keycloak client settings, add https://your-mcp-depot-host/* to Valid Redirect URIs. If these don't agree, the authorization redirect will fail silently (login button spins indefinitely).


MCP Client Configuration Reference

Environment variables for the CLI proxy

VariableRequiredDescription
MCP_DEPOT_URL required Base URL of your MCP Depot server, e.g. https://mcp.yourcompany.com.
MCP_DEPOT_API_KEY required Your personal API key from Settings → API Keys.

All supported clients

json — Claude Desktop / Cursor / Windsurf
{
  "mcpServers": {
    "mcp-depot": {
      "command": "mcp-depot",
      "args": ["--mcp"],
      "env": {
        "MCP_DEPOT_URL": "https://your-server.com",
        "MCP_DEPOT_API_KEY": "your-api-key"
      }
    }
  }
}
bash — Claude Code CLI
claude mcp add mcp-depot -- mcp-depot --mcp \
  --env MCP_DEPOT_URL=https://your-server.com \
  --env MCP_DEPOT_API_KEY=your-api-key

FAQ

Do I need Docker?

Docker Compose is the recommended way to run MCP Depot. You can also run the Node.js server and React frontend directly if you prefer - see the repository's CONTRIBUTING.md for the development setup.

Can I use a remote server?

Yes. Set MCP_DEPOT_URL to your server's public URL (HTTPS recommended). The CLI proxy on each client machine connects to the remote server.

Is SQLite suitable for production?

SQLite works well for personal and small-team use. For larger teams with concurrent write-heavy workloads, set the DATABASE_URL environment variable to a PostgreSQL connection string - MCP Depot will use it automatically instead of SQLite.

How are credentials protected?

All integration credentials (API keys, tokens, passwords) are encrypted at rest with AES-256-GCM using the ENCRYPTION_KEY you provide. They are never sent to AI clients or logged.

What happens when I disconnect an AI client?

Session entries are cleaned up automatically - direct HTTP sessions are removed immediately on disconnect, and CLI proxy sessions expire within ~150 seconds if the heartbeat stops.

Can multiple users share the same MCP Depot instance?

Yes. Each user has their own account and API key. Tool access is scoped per user. Session contexts are private by default but can be shared. Channels are accessible to all users.