Choose a configuration and apply changes

This reference targets v2.4.0. Configuration is one YAML document with strict field checking. Do not paste management API JSON directly into YAML: YAML headers is a map; API headers is an object array. A managed backend's API enabled field is not a YAML backend field. HTTP tool groups and OpenAPI imports are managed only through the UI/API.

Scenario Starting point Prerequisites
Default local console + SQLite Default template, startup steps 2 variables: MCP URL and encryption key; add backends in the UI
Advanced YAML-only deployment Independent example Fill each backend address and credential in YAML; explicitly publish reviewed read tools
Local administration, SQLite Complete configuration, startup steps MCP URL, persistent encryption key
Remote team administration Deployment examples and variables Admin URL, SQLite or PostgreSQL, HTTPS proxy; enterprise identity is optional
SSO / Vault personal accounts SSO, Vault Add the required modules to a managed deployment; Feishu also needs real-tenant acceptance
Configuration Edit in Takes effect through
server.listen, server.public_url, all auth, admin, client_authorization, vault YAML / service environment Restart; SIGHUP rejects changes to these fields
Other server fields YAML SIGHUP; see reload
backends with admin disabled YAML SIGHUP; at least one backend is required
backends with admin enabled YAML for initial import, then UI/API Imported once into an empty database; later YAML backends and their variables do not override stored records
HTTP tool groups, OpenAPI, user policies, client grant records Admin UI / user portal / API Save or complete required approval; never imported from YAML

Strict YAML decoding still applies in managed mode: unknown fields in ignored backend entries remain errors. Do not delete the database to force a reimport; it also stores tool groups, grants and audit history.

Environment variables and secrets

${NAME} expands only in the fields below, not in every string. Values come from the MCPHub process environment. Exporting variables in another shell cannot change a running service's environment; update its service environment and restart for rotation. MCPHub does not automatically load .env.

Expansion location Fields
server, auth server.listen, public_url, allowed_origins[]; auth.issuer
admin listen, mode, public_url, client_id, client_secret_env, database_driver, database_dsn_env, database_path, encryption_key_env, required_scopes[]
admin.approvals required_scopes[], step_up_acr_values[]; policy_changes.required_scopes[], policy_changes.subjects[]; notifications.url, notifications.secret; audit_archive.url, audit_archive.signing_key, audit_archive.key_id
client_authorization client_id, client_secret_env
YAML backends[] id, url, headers values, required_scopes[], published_tools[]; all oauth strings and scopes[]
YAML backends[].tool_rules[] match, required_scopes[]; resource_rules[].argument, allowed_values[]

auth.sso.*, vault.*, backends[].credentials.*, tool_rules[].approval.* and effect do not expand; supply literal settings. Durations, numbers and booleans do not accept placeholders. Names must match [A-Za-z_][A-Za-z0-9_]*; unset variables in supported fields fail validation. ${NAME:-default} and $NAME are not fallback/expansion syntax and may remain literal; do not use them.

A *_env field contains an environment variable name, not the secret:

# Fragment: place each setting in its respective section.
admin:
  encryption_key_env: MCPHUB_CONFIG_KEY # Read the Base64-encoded 32-byte key from this variable.
# Backend credentials are configured separately for each service in the UI.

Do not write encryption_key_env: ${MCPHUB_CONFIG_KEY}: that treats the key value as a variable name. Generate the database encryption key once and retain it with backups. SSO and Vault likewise reference secrets through their *_env fields; inject values through the service manager or secret store.

required: false only tolerates connection failure. That backend's URL, OAuth fields and supported environment variables must still be valid and complete; it does not disable the configuration. Remove an unused backend entry from the active YAML.

server

Field Default Description
listen :8080 HTTP listen address. It cannot be changed by SIGHUP; restart to change it.
public_url none Required absolute HTTPS URL with an MCP path (for example, https://hub.example.com/mcp), with no query or fragment. The path may not contain percent-encoded characters and may not be /healthz, /readyz, or /.well-known/oauth-protected-resource. It is both the MCP URL and the JWT audience. It cannot be changed by SIGHUP.
page_size 1000 Aggregated MCP catalog page size; must be positive.
request_timeout 60s Default timeout for ordinary requests and backend calls; must be positive. Long-lived subscriptions use a separate lifecycle after body reading; see below.
drain_timeout 15s Maximum wait for active requests during SIGTERM/SIGINT shutdown and while replacing the old runtime after SIGHUP; must be positive.
refresh_interval 5m Maximum backend catalog refresh interval. A shorter backend TTL causes an earlier refresh, with an effective interval no shorter than 5 seconds. Must be positive.
catalog_ttl 30s Private TTL advertised for MCP catalog/discovery results; may be zero but not negative.
max_request_body_bytes 4194304 (4 MiB) MCP POST body limit; larger requests return 413. Must be positive.
allowed_origins [] Additional browser HTTPS Origins. Each entry must be https://authority only, with no path, query, fragment, or wildcard. The origin of MCPHub's own public_url is allowed automatically.

Durations use Go time.ParseDuration syntax, such as 500ms, 60s, and 5m. The path of public_url is the MCP entry path; the example uses /mcp. Percent-encoded path characters are rejected, and /healthz, /readyz, and /.well-known/oauth-protected-resource are reserved paths.

Request deadlines and long-lived subscriptions

Default timeout for ordinary MCP requests and backend calls; must be positive. Every MCP-listener HTTP route keeps a request-body read deadline from this value until the body is consumed or closed, including unauthenticated and rejected requests. Once MCP handling proceeds, ordinary MCP POSTs also apply it to response-write deadlines and the request context; the newer subscriptions/listen POST keeps long-lived connection semantics after its body is read and is not given those ordinary response-write or context timeouts. Runtime or client context cancellation still expires its underlying write deadline, so a slow subscription write is interrupted when its generation drains or the client disconnects.

vault

Optional; requires managed database storage. address is required; mount defaults to secret, prefix to mcphub, and auth_mount to approle. Choose either role_id_env + secret_id_env or token_env, never both. All fields are literal and do not expand ${...}; every global Vault setting requires a restart.

Put credential references in backends[].credentials. Personal mode also requires the user portal and that backend's own require_client_grant: true; global enforcement alone does not satisfy this configuration check. See the Vault guide for fields, paths, policies and callbacks.

admin

The default template enables administration. It serves an embedded UI and JSON API from a separate listener. Local mode is loopback-only and requires built-in account sign-in; remote mode uses built-in accounts or optional enterprise authentication over HTTPS. It manages backend and tool-group configuration; process settings remain in YAML with the restart/reload rules in the ownership table.

Field Default Description
enabled false Enables management with the selected database as configuration source of truth.
mode local local is loopback-only and still requires built-in account sign-in; remote uses built-in or enterprise authentication over HTTPS.
listen 127.0.0.1:8081 Numeric loopback in local mode; remote mode may bind a private address behind an HTTPS proxy.
public_url none Required HTTPS admin origin in remote mode, with no path or trailing slash; also the admin JWT audience.
client_id none Browser OAuth client ID; built-in mode defaults to mcphub-admin.
client_secret_env none Optional confidential-client secret environment variable; defaults to a public client.
required_scopes [mcphub:admin] All required admin scopes; cannot be empty for built-in or remote administration.
database_driver sqlite sqlite or postgres.
database_dsn_env MCPHUB_DATABASE_URL PostgreSQL DSN environment variable. Do not also set database_path for PostgreSQL.
database_path none Required for SQLite. Relative paths resolve from the YAML directory.
encryption_key_env MCPHUB_CONFIG_KEY Environment variable containing a Base64-encoded 32-byte AES key. Losing or changing this key makes stored secrets unreadable.
request_retention 720h (30 days) Retention for completed MCP POST request history, from 24h to 8760h; expired records are pruned automatically. See administrator diagnostics and export.

On the first start with an empty database, expanded YAML backends are imported in one transaction. The selected database becomes the sole backend source after the bootstrap marker is written; later YAML backend edits have no effect. Header values and OAuth client secrets are encrypted with AES-256-GCM and are never returned by the management API. SQLite reserves its write lock before each read-modify-write transaction; concurrent local administration waits up to five seconds, then reports failure without replaying the change.

The UI is available at http://127.0.0.1:8081/ by default. It can register, probe, edit, enable, disable, and delete backends without restarting the process. Required backend failures reject a change without replacing the current runtime; an unavailable optional backend is saved and continues reconnecting in the background.

The JSON API is rooted at /api/v1. Individual backend responses include an ETag; update and delete requests must send that revision in If-Match, and stale writes fail with 409 revision_conflict. Secret fields are returned only as configured markers. Omitting a secret value during an edit preserves it, while omitting the Header or OAuth configuration removes it. Audit actors are the authenticated user subject, local for unauthenticated advanced local management or system for background refreshes; outcomes are redacted.

Other configuration guides

Configuration or task Reference
auth.sso, user policies, department/group synchronization and administrator recovery SSO and user management
client_authorization, portal callbacks and required client grants Administrator manual: client authorization
vault, backends[].credentials, shared/personal accounts Vault configuration and operations
HTTPS, remote administration and databases Deployment guide
Complete YAML Base example, remote SQLite, remote PostgreSQL

Starting from the full YAML example

The default config.example.yaml is identical in the v2.4.0 server archive and online template. It enables the local console and SQLite with backends: []. Only MCPHUB_PUBLIC_URL and MCPHUB_CONFIG_KEY are required; configure addresses and credentials per service in the UI.

Download the template into a new private deployment directory, replace the two HTTPS addresses, and start:

curl -fL https://samuelsupe.github.io/mcphub/examples/config.example.yaml -o config.yaml
export MCPHUB_PUBLIC_URL=https://hub.example.com/mcp
umask 077
mkdir -p secrets
test -f secrets/config.key || openssl rand -base64 32 > secrets/config.key
export MCPHUB_CONFIG_KEY="$(cat secrets/config.key)"
mcphub validate --config config.yaml
mcphub init-admin --config config.yaml
mcphub serve --config config.yaml

Open http://127.0.0.1:8081/ and follow start the console → add backends → test connections → publish tools. Configure a separate service URL and Header/OAuth credential for each backend. Publish only reviewed tools explicitly classified as read, and assign required scopes. Restarting with the same key restores service settings, credentials and tool policies from SQLite.

validate is a read-only configuration check; it does not verify identity login, backend connections or tool calls. serve initializes fresh storage. Check /healthz and /readyz, then make a real call using deployment verification.

Remote administrators use the remote templates, adding an administrator origin, registered identity client and scopes. Deployments without a console use the separate advanced YAML-only example; fill each backend's actual address, credential, publication list and read policy in the file.

Users do not hold direct permissions. Create groups in Users & groups, assign roles, scopes, tools and resources to groups, then add users as members. Initialization creates the Administrators group. The identity API separates user membership updates from group permission updates; see groups and API contracts.

Name and URI mapping

  • Tools and prompts are exposed as <backend-id>.<original-name>. Original names must match [A-Za-z0-9_.-]{1,128}. If the namespaced name exceeds 128 characters, MCPHub truncates it while retaining the backend prefix and appends a short SHA-256 suffix of the original name. Invalid metadata and mapped collisions are omitted and logged.
  • Static resources and ResourceLink/embedded resources in results are encoded as mcphub://<backend-id>/r/<base64url-no-padding(original-uri)>. MCPHub decodes that URI to read from the corresponding backend and recursively rewrites resource URIs in results.
  • Resource templates are encoded as mcphub://<backend-id>/t/<sha256(original-template)>, retaining URI-template variables as a query expression (for example, {?id}). Reads and completions restore the backend's original template.
  • ResourceLink, EmbeddedResource, and ResourceContents URIs in backend tool, prompt, or resource results are rewritten and recorded as issued resources for that backend. They remain readable and subscribable while their URI digest is retained, but are not added individually to the public resources catalog. Each backend retains at most 16,384 distinct issued-URI SHA-256 digests; once the oldest digest is evicted, a new read or subscription for that URI may fail, while existing subscription cancellation and session cleanup still use the session map. resource updated notifications never create catalog entries.
  • Resource subscriptions are tracked and deduplicated per upstream MCP session, while backend references are shared and reference-counted by original URI; an unpaired unsubscribe is ignored, session close cleans up, and reconnect restores all tracked subscriptions, waiting for notifications/subscriptions/acknowledged when the backend protocol supports that acknowledgement, before the backend is marked ready. An acknowledgement's subscription ID maps back to the original subscription URI(s), so a 2026 resource update fans out to those URIs even when the event URI differs; timeout, cancellation, and session/reconnect cleanup remove the mapping. When a modern subscriptions/listen stream is canceled or disconnects, cleanup detaches from the canceled upstream context but remains bounded by backend request_timeout and session lifecycle, so legacy backends still receive resources/unsubscribe. If any restore fails, the backend remains unavailable and the reconnect loop tries again.

Each token's scope set selects an independent backend view, so one MCP connection sees only the capabilities allowed for that token.