# Authentication Source: https://docs.mcphub.app/api-reference/auth Manage users and authentication. > **[POST /api/auth/login](#login)** > > Log in to get a JWT token. > **[POST /api/auth/register](#register)** > > Register a new user. > **[GET /api/auth/user](#get-current-user)** > > Get the currently authenticated user. > **[POST /api/auth/change-password](#change-password)** > > Change the password for the current user. *** ### Login Authenticates a user and returns a JWT token along with user details. * **Endpoint**: `/api/auth/login` * **Method**: `POST` * **Body**: * `username` (string, required): The user's username. * `password` (string, required): The user's password. * **Request Example**: ```json theme={null} { "username": "admin", "password": "your-admin-password" } ``` * **Success Response**: ```json theme={null} { "success": true, "message": "Login successful", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "username": "admin", "isAdmin": true, "permissions": { ... } } } ``` * **Password login disabled**: while Better Auth's `disablePasswordLogin` is in effect, the endpoint answers `403` with `{"success": false, "message": "Password login is disabled. Sign in with SSO, or use a bearer key for API access."}`. See [SSO-only sign-in](/features/authentication#sso-only-sign-in). *** ### Register Registers a regular user and returns a JWT token. Requires `systemConfig.auth.allowRegistration: true` (default: false) and password login to be enabled; otherwise returns HTTP 403. * **Endpoint**: `/api/auth/register` * **Method**: `POST` * **Body**: * `username` (string, required): The desired username. * `password` (string, required): The desired password (at least 8 characters, including a letter, a number, and a special character). * Administrator privileges cannot be requested; `isAdmin` is ignored. * **Request Example**: ```json theme={null} { "username": "newuser", "password": "Password123!" } ``` * **Success Response**: ```json theme={null} { "success": true, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "username": "newuser", "isAdmin": false, "permissions": { ... } } } ``` *** ### Get Current User Retrieves the profile of the currently authenticated user. * **Endpoint**: `/api/auth/user` * **Method**: `GET` * **Authentication**: Bearer Token required. * **Success Response**: ```json theme={null} { "success": true, "user": { "username": "admin", "isAdmin": true, "permissions": { ... } } } ``` *** ### Change Password Allows the authenticated user to change their password. * **Endpoint**: `/api/auth/change-password` * **Method**: `POST` * **Authentication**: Bearer Token required. * **Body**: * `currentPassword` (string, required): The user's current password. * `newPassword` (string, required): The desired new password (must be at least 6 characters). * **Request Example**: ```json theme={null} { "currentPassword": "oldpassword", "newPassword": "newpassword123" } ``` * **Success Response**: ```json theme={null} { "success": true, "message": "Password updated successfully" } ``` *** ### Social Login (Better Auth) MCPHub integrates [Better Auth](https://www.better-auth.com/) to provide seamless social login capabilities (GitHub, Google, etc.). **Prerequisites**: 1. **Database Mode**: `DB_URL` environment variable must be configured (PostgreSQL is required for Better Auth persistence). 2. **Provider Configuration**: Better Auth bootstrap settings are read once at startup, so restart MCPHub after changing them. 3. **Environment Variables**: * **Global**: `BETTER_AUTH_ENABLED`, `BETTER_AUTH_URL`, optional `BETTER_AUTH_BASE_PATH`, optional `BETTER_AUTH_TRUSTED_ORIGINS`. * **GitHub**: `BETTER_AUTH_GITHUB_ENABLED`, `GITHUB_CLIENT_ID`, and `GITHUB_CLIENT_SECRET`. * **Google**: `BETTER_AUTH_GOOGLE_ENABLED`, `GOOGLE_CLIENT_ID`, and `GOOGLE_CLIENT_SECRET`. * **Local OIDC**: `BETTER_AUTH_OIDC_ENABLED`, optional `BETTER_AUTH_OIDC_PROVIDER_ID`, `BETTER_AUTH_OIDC_DISCOVERY_URL` (or legacy `OIDC_DISCOVERY_URL`), optional `BETTER_AUTH_OIDC_SCOPES`, optional `BETTER_AUTH_OIDC_PKCE`, optional `BETTER_AUTH_OIDC_PROMPT`, plus `OIDC_CLIENT_ID` and `OIDC_CLIENT_SECRET`. Non-secret Better Auth settings can also be stored in `systemConfig.auth.betterAuth`, but the `BETTER_AUTH_*` environment variables take precedence over stored config values. **Endpoints**: The Better Auth handler is mounted at `${BASE_PATH}${betterAuthConfig.basePath}`. The default `betterAuthConfig.basePath` is `/api/auth/better`; set `BETTER_AUTH_BASE_PATH` to override it at startup, or use `systemConfig.auth.betterAuth.basePath` as a fallback. `BETTER_AUTH_URL` takes precedence over `systemConfig.install.baseUrl` when MCPHub builds redirect URLs. If you also set `BASE_PATH`, prepend it to every example below. Key endpoints include: * **Initiate Login**: * `GET /api/auth/better/signIn/social?provider=github` * `GET /api/auth/better/signIn/social?provider=google` * **Session Management**: * `GET /api/auth/better/session` (Get current session) * `POST /api/auth/better/signOut` (Sign out) For a complete list of endpoints and usage details, refer to the [Better Auth API Documentation](https://www.better-auth.com/docs). **Disabling Better Auth**: Better Auth is automatically disabled if `DB_URL` is not configured, if `BETTER_AUTH_ENABLED=false`, or if no login providers are effectively enabled with their required credentials. # Config Source: https://docs.mcphub.app/api-reference/config Manage and retrieve system-wide configurations. > **[PUT /api/system-config](#update-system-config)** > > Update the main system configuration. > **[GET /api/settings](#get-all-settings)** > > Get all system settings, including servers and groups. > **[GET /config](#get-runtime-config)** > > Get public runtime configuration for the frontend. > **[GET /public-config](#get-public-config)** > > Get public configuration to check for auth skip. *** ### Update System Config Updates various parts of the system configuration. You only need to provide the keys for the sections you want to update. * **Endpoint**: `/api/system-config` * **Method**: `PUT` * **Body**: A JSON object containing one or more of the following top-level keys: `routing`, `install`, `smartRouting`, `toolResultCompression`, `mcpRouter`. #### Routing Configuration (`routing`) * `enableGlobalRoute` (boolean): Enable or disable the global `/api/mcp` route. * `enableGroupNameRoute` (boolean): Enable or disable group-based routing (e.g., `/api/mcp/group/:groupName`). * `enableBearerAuth` (boolean): Require bearer token authentication for MCP endpoints (default: `true`). * `bearerAuthKey` (string): Legacy bearer key (used only for one-time migration). * `bearerAuthHeaderName` (string): Header name used to receive MCP bearer credentials (default: `Authorization`). Change this when upstream passthrough headers also use `Authorization`. * `jsonBodyLimit` (string): Express JSON body size limit (default: `1mb`). This applies to large inline OpenAPI schema uploads too. * `skipAuth` (boolean): If true, skips dashboard/web API login requirements so the frontend can call `/api` routes without a session token. Unauthenticated dashboard users are treated as the built-in `guest` administrator, so enable this only in trusted environments. This does **not** disable MCP endpoint authentication. #### Install Configuration (`install`) * `pythonIndexUrl` (string): The base URL of the Python Package Index (PyPI) to use for installations. * `npmRegistry` (string): The URL of the npm registry to use for installations. * `baseUrl` (string): The public base URL of this MCPHub instance, used for MCP requests and OAuth callback URLs. #### Smart Routing Configuration (`smartRouting`) * `enabled` (boolean): Enable or disable the Smart Routing feature. * `dbUrl` (string): The database connection URL for storing embeddings. * `llmProviderBaseUrl` (string): The base URL for the OpenAI-compatible API for generating embeddings. * `llmProviderApiKey` (string): The API key for the embeddings service. * `embeddingModel` (string): The name of the embedding model to use. * `embeddingDimensions` (number, optional): The output dimensionality for providers that support configurable dimensions. Leave unset to use the model default. This value also declares the expected storage dimension, but it is only forwarded to the provider as the `dimensions` request parameter for models known to support Matryoshka (MRL) representations, or when `embeddingDimensionsApiPassthrough` is enabled (issue #1131). * `embeddingDimensionsApiPassthrough` (boolean, optional, default `false`): When `true`, always forward `embeddingDimensions` to the provider as the `dimensions` request parameter. Non-MRL models/backends (Qwen3-Embedding, BGE, vLLM/sglang) reject the parameter outright; enable only for other MRL-capable models. See also `EMBEDDING_DIMENSIONS_API_PASSTHROUGH`. #### Tool Result Compression Configuration (`toolResultCompression`) * `enabled` (boolean): Enable transparent compression for successful tool results. * `minTokens` (number): Only compress text blocks at or above this estimated token count. Default: `2000`. * `maxOutputTokens` (number): Target token budget for each compressed text block. Default: `1200`. * `strategy` (string): Compression strategy. Allowed values: `auto`, `json`, `log`, `search`, `diff`, `text`. Default: `auto`. Compression applies only to successful tool responses. `isError: true` responses and non-text content blocks are returned unchanged. Activity logging records the original upstream output before compression. #### MCP Router Configuration (`mcpRouter`) * `apiKey` (string): The API key for the MCP Router service. * `referer` (string): The referer header to use for MCP Router requests. * `title` (string): The title to display for this instance on MCP Router. * `baseUrl` (string): The base URL for the MCP Router API. * **Request Example**: ```json theme={null} { "routing": { "enableBearerAuth": true, "bearerAuthHeaderName": "X-MCP-Authorization", "jsonBodyLimit": "5mb", "skipAuth": false }, "smartRouting": { "enabled": true, "dbUrl": "postgresql://user:pass@host:port/db" }, "toolResultCompression": { "enabled": true, "minTokens": 2000, "maxOutputTokens": 1200, "strategy": "auto" } } ``` *** ### Get All Settings Retrieves the entire settings object for the instance, including all server configurations, groups, and system settings. This is a comprehensive dump of the `mcp_settings.json` file. * **Endpoint**: `/api/settings` * **Method**: `GET` *** ### Get Runtime Config Retrieves the essential runtime configuration required for the frontend application. This endpoint does not require authentication. * **Endpoint**: `/config` * **Method**: `GET` * **Success Response**: ```json theme={null} { "success": true, "data": { "basePath": "", "version": "1.0.0", "name": "MCPHub" } } ``` *** ### Get Public Config Retrieves public configuration, primarily to check if authentication is skipped. This allows the frontend to adjust its behavior accordingly before a user has logged in. This endpoint does not require authentication. * **Endpoint**: `/public-config` * **Method**: `GET` * **Success Response**: ```json theme={null} { "success": true, "data": { "skipAuth": false, "permissions": {} } } ``` ## Session rebuild Set `enableSessionRebuild` to `true` to recreate a missing Streamable HTTP session using its existing ID. This does not restore the client's negotiated capabilities. Single-upstream routes accept both raw and prefixed tool names, allowing cached ordinary tool calls to continue. MCP Apps metadata and app-only tools still require a fresh initialization that advertises Apps support. When rebuild is disabled, requests with an unknown session ID return HTTP 404 so the client can initialize a new session. Requests missing a required session ID continue to return HTTP 400. # Groups Source: https://docs.mcphub.app/api-reference/groups Manage server groups to organize and route requests. > **[GET /api/groups](#get-all-groups)** > > Get a list of all groups. > **[POST /api/groups](#create-a-new-group)** > > Create a new group. > **[GET /api/groups/:id](#get-a-group)** > > Get details of a specific group. > **[PUT /api/groups/:id](#update-a-group)** > > Update an existing group. > **[DELETE /api/groups/:id](#delete-a-group)** > > Delete a group. > **[POST /api/groups/:id/servers](#add-server-to-group)** > > Add a server to a group. > **[DELETE /api/groups/:id/servers/:serverName](#remove-server-from-group)** > > Remove a server from a group. > **[PUT /api/groups/:id/servers/batch](#batch-update-group-servers)** > > Batch update servers in a group. > **[GET /api/groups/:id/server-configs](#get-group-server-configs)** > > Get detailed server configurations in a group. > **[PUT /api/groups/:id/server-configs/:serverName/tools](#update-group-server-tools)** > > Update tool selection for a server in a group. *** ### Get All Groups Retrieves a list of all server groups. * **Endpoint**: `/api/groups` * **Method**: `GET` * **Success Response**: ```json theme={null} { "success": true, "data": [ { "id": "group-1", "name": "My Group", "description": "A collection of servers.", "servers": ["server1", "server2"], "owner": "admin" } ] } ``` *** ### Create a New Group Creates a new server group. * **Endpoint**: `/api/groups` * **Method**: `POST` * **Body**: * `name` (string, required): The name of the group. * `description` (string, optional): A description for the group. * `instructions` (string, optional): Sent as the MCP `initialize` instructions on the group's routes. See [Group instructions](/features/group-management#group-instructions). * `servers` (array, optional): A list of server names or capability-scoped server configuration objects. * **Request Example**: ```json theme={null} { "name": "My New Group", "description": "A description for the new group", "servers": [ "server1", { "name": "server2", "tools": ["toolA", "toolB"], "prompts": ["draft_prompt"], "resources": ["resource://docs/guide"] } ] } ``` *** ### Get a Group Retrieves details for a specific group by its ID or name. * **Endpoint**: `/api/groups/:id` * **Method**: `GET` * **Parameters**: * `:id` (string, required): The ID or name of the group. *** ### Update a Group Updates an existing group's name, description, instructions, or server list. * **Endpoint**: `/api/groups/:id` * **Method**: `PUT` * **Parameters**: * `:id` (string, required): The ID or name of the group to update. * **Body**: * `name` (string, optional): The new name for the group. * `description` (string, optional): The new description for the group. * `instructions` (string, optional): The new instructions for the group; an empty string removes them. * `servers` (array, optional): The new list of servers for the group. See [Batch Update Group Servers](#batch-update-group-servers) for format. * **Request Example**: ```json theme={null} { "name": "Updated Group Name", "description": "Updated description" } ``` *** ### Delete a Group Deletes a group by its ID or name. * **Endpoint**: `/api/groups/:id` * **Method**: `DELETE` * **Parameters**: * `:id` (string, required): The ID or name of the group to delete. *** ### Add Server to Group Adds a single server to a group. * **Endpoint**: `/api/groups/:id/servers` * **Method**: `POST` * **Parameters**: * `:id` (string, required): The ID or name of the group. * **Body**: * `serverName` (string, required): The name of the server to add. * **Request Example**: ```json theme={null} { "serverName": "my-server" } ``` *** ### Remove Server from Group Removes a single server from a group. * **Endpoint**: `/api/groups/:id/servers/:serverName` * **Method**: `DELETE` * **Parameters**: * `:id` (string, required): The ID or name of the group. * `:serverName` (string, required): The name of the server to remove. *** ### Batch Update Group Servers Replaces all servers in a group with a new list. The list can be simple strings or detailed configuration objects. * **Endpoint**: `/api/groups/:id/servers/batch` * **Method**: `PUT` * **Parameters**: * `:id` (string, required): The ID or name of the group. * **Body**: * `servers` (array, required): An array of server names (strings) or server configuration objects. * **Request Example (Simple)**: ```json theme={null} { "servers": ["server1", "server2"] } ``` * **Request Example (Detailed)**: ```json theme={null} { "servers": [ { "name": "server1", "tools": "all", "prompts": "all", "resources": "all" }, { "name": "server2", "tools": ["toolA", "toolB"], "prompts": ["draft_prompt"], "resources": ["resource://docs/guide"] } ] } ``` Each detailed server object accepts these optional capability selectors: * `tools`: `"all"` or an array of tool names. * `prompts`: `"all"` or an array of prompt names. * `resources`: `"all"` or an array of resource URIs. * `pinnedTools`: an array of bare tool names listed next to the meta-tools on the group's `$smart/{group}` endpoint (see [Pinned Tools](/features/smart-routing#pinned-tools)). Only pins that are also selected in `tools` take effect. *** ### Get Group Server Configs Retrieves the detailed configuration for all servers within a group, including which tools, prompts, and resources are enabled. * **Endpoint**: `/api/groups/:id/server-configs` * **Method**: `GET` * **Parameters**: * `:id` (string, required): The ID or name of the group. * **Success Response**: ```json theme={null} { "success": true, "data": [ { "name": "server1", "tools": "all", "prompts": "all", "resources": "all" }, { "name": "server2", "tools": ["toolA", "toolB"], "prompts": ["draft_prompt"], "resources": ["resource://docs/guide"] } ] } ``` *** ### Update Group Server Tools Updates the tool selection for a specific server within a group. * **Endpoint**: `/api/groups/:id/server-configs/:serverName/tools` * **Method**: `PUT` * **Parameters**: * `:id` (string, required): The ID or name of the group. * `:serverName` (string, required): The name of the server to update. * **Body**: * `tools` (string or array of strings, required): Either the string `"all"` to enable all tools, or an array of tool names to enable specifically. Pins (`pinnedTools`) of tools left out of the array are removed. * **Request Example**: ```json theme={null} { "tools": ["toolA", "toolC"] } ``` ## Visibility fields `POST /api/groups`, entries in `POST /api/groups/batch`, and `PUT /api/groups/:id` accept `visibility` (`private | group | public`) and `sharedWithUsers` (an array of non-empty usernames). New groups default to `private`; omitted fields on update remain unchanged. `shared` and `null` are not valid visibility values. An empty shared user list allows only the owner and admins. `GET /api/groups/:id/share-candidates` returns a sorted array of usernames, excluding the owner, and requires owner/admin access. Sharing grants discovery/use, not management. All group read responses filter inaccessible nested servers; replacement server edits preserve existing inaccessible entries. See [Group Management](/features/group-management) for legacy compatibility and database upgrade behavior. # Introduction Source: https://docs.mcphub.app/api-reference/introduction Welcome to the MCPHub API documentation. The MCPHub API provides a comprehensive set of endpoints to manage your MCP servers, groups, users, and more. The API is divided into two main categories: * **MCP Endpoints**: These are the primary endpoints for interacting with your MCP servers. They provide a unified interface for sending requests to your servers and receiving responses in real-time. * **Management API**: These endpoints are used for managing the MCPHub instance itself. This includes managing servers, groups, users, and system settings. All API endpoints are available under the `/api` path. For example, the endpoint to get all servers is `/api/servers`. Authentication is required for most Management API endpoints. See the [Authentication](/api-reference/auth) section for more details. # Logs Source: https://docs.mcphub.app/api-reference/logs Access and manage server logs. > **[GET /api/logs](#get-all-logs)** > > Get all logs. > **[DELETE /api/logs](#clear-logs)** > > Clear all logs. > **[GET /api/logs/stream](#stream-logs)** > > Stream logs in real-time. *** ### Get All Logs Retrieves all stored logs. * **Endpoint**: `/api/logs` * **Method**: `GET` * **Success Response**: ```json theme={null} { "success": true, "data": [ { "timestamp": "2023-10-27T10:00:00.000Z", "level": "info", "message": "Server started successfully.", "service": "system" } ] } ``` *** ### Clear Logs Deletes all stored logs. * **Endpoint**: `/api/logs` * **Method**: `DELETE` * **Success Response**: ```json theme={null} { "success": true, "message": "Logs cleared successfully" } ``` *** ### Stream Logs Streams logs in real-time using Server-Sent Events (SSE). The connection will remain open, and new log entries will be sent as they occur. * **Endpoint**: `/api/logs/stream` * **Method**: `GET` * **Response Format**: The stream sends events with a `data` field containing a JSON object. The first event has `type: 'initial'` and contains all historical logs. Subsequent events have `type: 'log'` and contain a single new log entry. * **Example Event**: ``` data: {"type":"log","log":{"timestamp":"2023-10-27T10:00:05.000Z","level":"debug","message":"Processing request for /api/some-endpoint","service":"mcp-server"}} ``` # MCP HTTP Endpoints Source: https://docs.mcphub.app/api-reference/mcp-http Connect to your MCP servers using the unified HTTP endpoint. MCPHub provides a unified streamable HTTP interface for all your MCP servers. This allows you to send requests to any configured MCP server and receive responses in real-time. **Security note**: MCP endpoints require authentication by default to avoid exposing connected tools and data. If you intentionally want public MCP access, disable **Enable Bearer Authentication** in the Keys settings. **Skip Authentication** only affects dashboard login. By default MCPHub reads Bearer credentials from the `Authorization` header, but this is configurable via **Bearer Auth Header Name** if upstream passthrough headers would otherwise conflict. ### Unified Endpoint This endpoint provides access to all enabled MCP servers. * **Endpoint**: `http://localhost:3000/mcp` * **Method**: `POST` ### Group-Specific Endpoint For targeted access to specific server groups, use the group-based HTTP endpoint. * **Endpoint**: `http://localhost:3000/mcp/{group}` * **Method**: `POST` * **Parameters**: * `{group}`: The ID or name of the group. ### Server-Specific Endpoint For direct access to individual servers, use the server-specific HTTP endpoint. * **Endpoint**: `http://localhost:3000/mcp/{server}` * **Method**: `POST` * **Parameters**: * `{server}`: The name of the server. > **Note**: If a server name and group name are the same, the group will take precedence. ## Protocol compatibility The same `/mcp` URL serves legacy `2025-11-25` and modern `2026-07-28` clients. Group and server URLs use the same protocol handling. Changing the URL from `/sse` to `/mcp` does not by itself upgrade the client's protocol revision. | Behavior | Legacy HTTP+SSE (`/sse`) | HTTP `2025-11-25` (`/mcp`) | HTTP `2026-07-28` (`/mcp`) | | - | - | - | - | | Status | Deprecated; still supported | Supported for compatibility | Supported; migration work continues | | Startup | `initialize` over the SSE transport | `initialize` over Streamable HTTP | No `initialize` handshake; SDK discovery | | Downstream session | SSE connection and session message endpoint | `Mcp-Session-Id` | No downstream transport session | | Routing headers | Legacy transport behavior | Legacy transport behavior | `Mcp-Method`; `Mcp-Name` for named calls and resource reads | | Stateful tools | Existing session lifecycle | Existing session lifecycle | Bearer auth plus explicit state ID or OpenAI conversation metadata | | MCP Apps capabilities | Declared during initialization | Declared during initialization | Declared in each request's `params._meta` | | List cache hints | Existing legacy response shape | Existing legacy response shape | Remaining upstream TTL, capped at 5 seconds; `private`; otherwise zero | | Resource-read cache hints | Existing upstream hints preserved | Existing upstream hints preserved | Valid remaining upstream TTL, restricted to `private`; otherwise zero | MCP Apps passthrough also depends on the route and visible upstream servers; `$smart` routes remain ordinary MCP routes. See [MCP Apps](/features/mcp-apps). Upstream Streamable HTTP connections default to automatic protocol negotiation: MCPHub probes `server/discover`, uses `2026-07-28` stateless HTTP when supported, and falls back to the legacy handshake according to the SDK's negotiation rules. The downstream protocol is independent of the upstream protocol. Set the per-server `protocolNegotiation: "legacy"` option to skip probing for a legacy server. Stdio and legacy SSE upstreams continue using legacy negotiation. | Upstream behavior | Modern Streamable HTTP | Legacy upstream | | - | - | - | | Startup | `server/discover`; no transport session or GET stream | `initialize`; existing transport lifecycle | | Lists | On-demand revalidation at the original upstream TTL expiry; concurrent refreshes coalesce | Existing list-changed notifications | | Missing/zero TTL or partial list | Revalidate on the next relevant request | Existing discovery behavior | | Health checks (opt-in) | Network `server/discover`, including servers without tools | `ping`, with existing method-availability fallback | | Elicitation-enabled isolated connection | Uses legacy negotiation while the relay requires legacy requests | Existing forwarding support | List revalidation also runs before tool calls, prompt retrieval and resource reads so newly added or removed entries affect routing. Resource templates are fetched on demand. A failed refresh excludes only that upstream from the current request; healthy upstreams and built-in content remain available. Partial lists advertise zero TTL, and later requests retry the failed upstream. No background list polling is introduced, and a list does not update until a relevant request occurs. Changed shared lists notify connected legacy downstream clients after refresh; personal lists remain scoped to their own runtime. Upstream list discovery remains single-page; a response with `nextCursor` receives no positive freshness hint. When `passthroughHeaders` are configured, modern discovery uses a disposable, request-local client and snapshots. Lists and pending refreshes are never shared between forwarded identities, even with positive TTLs or absent headers. This adds a discovery round trip per relevant request; those snapshots do not update global descriptors, embeddings or list-change broadcasts. Automatic negotiation does not treat every connection failure as evidence of a legacy server. HTTP probe timeouts, network outages and authentication errors can fail the connection rather than trigger fallback. See the [upstream configuration guide](/configuration/mcp-settings#upstream-protocol-negotiation). ## Elicitation forwarding For legacy clients, MCPHub forwards form and URL `elicitation/create` requests during tool calls when the upstream server has `perSessionClient: true`. The isolated connection advertises only the modes declared by its downstream client; the legacy empty `elicitation: {}` capability means form support only. Accept, decline and cancel responses are returned to the upstream unchanged. URL completion notifications are sent only to the owning downstream session. This works for direct calls and Smart Routing's `call_tool`. Calls on an elicitation-enabled upstream connection run sequentially within each downstream session because legacy requests do not provide a portable parent-call identifier. Other sessions use separate connections. Cancelling a call, ending the session, or a timeout aborts the pending interaction. A thrown request error discards the upstream connection without automatically replaying the call, so the next call creates a fresh connection and any upstream session state must be rebuilt. A completed tool response with `isError` is still returned normally. Shared upstream connections, personal-credential/OAuth runtimes, prompt/resource operations, and modern `2026-07-28` multi-round-trip elicitation are not supported in this initial implementation. These paths do not advertise elicitation. Enabling `X-MCPHub-State-Id` does not enable elicitation for modern clients. Form elicitation is for ordinary structured input. Passwords, API keys and access tokens must use URL mode, with sensitive input collected on the upstream service's page rather than in an MCP form. MCPHub does not add a dashboard input dialog; the connected MCP client provides the interaction UI. ## Migrating a client 1. Replace the legacy SSE client transport with an HTTP transport, and change `/sse` to `/mcp`, preserving any group, server or user route scope. Keep the same bearer credentials. A legacy HTTP client can make this transport change while retaining its initialization and session lifecycle. 2. To adopt `2026-07-28`, use a client that supports modern HTTP discovery and the per-request metadata envelope. The SDK v2 HTTP client supplies the protocol and routing headers. A custom client must send the normal JSON-RPC body with `params._meta["io.modelcontextprotocol/protocolVersion"]` set to `2026-07-28` and declare client capabilities under `params._meta["io.modelcontextprotocol/clientCapabilities"]`. Send matching `MCP-Protocol-Version` and `Mcp-Method` headers; `tools/call` and `prompts/get` require `Mcp-Name` matching `params.name`, and `resources/read` requires it matching `params.uri`. Use names returned by that route's list response. 3. For `perSessionClient` or OpenAPI `cookieSession` tools, create a UUID v4 for each independent workflow and send it as `X-MCPHub-State-Id` on tool calls. Authenticated OpenAI/ChatGPT tool calls that cannot set this extension may instead reuse their client-declared `_meta["openai/session"]` conversation hint when the explicit header is absent. MCPHub hashes that hint before use; it is correlation metadata, not authentication. An invalid explicit state header fails closed and never falls back. Stateless tools need neither value. Reuse is limited to the same bearer credential, user and route. State is process-local, expires after 30 idle minutes, and requires sticky routing across replicas. Restart, expiry or credential/route changes require rebuilding application state. See [explicit-state configuration](/configuration/mcp-settings#stateful-tools-over-mcp-2026-07-28). 4. Verify listing and calling tools with your actual permissions, then verify resource reads and MCP Apps if used. Modern Apps hosts must declare their UI capability on each request. Modern calls do not depend on `Mcp-Session-Id` or `enableSessionRebuild`; those remain legacy mechanisms. ## Cache behavior Modern list results combine upstream discovery snapshots with live configuration, permissions and per-request capabilities. Positive hints are private and bounded by the shortest remaining upstream freshness and a maximum five-second projection window. Discovery age is retained; repeated gateway lists do not renew it. Empty upstream lists also participate in the bound. Unknown/invalid TTLs, partial pages, failed or incomplete discovery, gateway-generated Smart Routing entries, built-in prompts/resources and unknown hosted permission freshness use zero. Every request rebuilds its projection. Configuration, membership, permission or runtime changes detected during projection force zero. List refreshes revoke the previous snapshot's freshness before awaiting a new envelope; failure, disconnect or replacement cannot establish new freshness. Template lists use the actual responses fetched for that request. No gateway response cache is added. Clients may retain an already-issued list for at most five seconds after a local configuration/permission change; server invalidation cannot retract client caches. Keep caches separate by endpoint, authenticated identity and per-request capabilities. A cached list never authorizes a call: MCPHub rechecks access on each request. Use zero when an upstream supplies no trustworthy freshness. Modern resource reads continue to bypass the SDK response cache, subtract handling time from a valid upstream TTL and restrict sharing to the requesting authorization context. Missing or invalid TTLs, built-in resources and gateway-generated read errors use zero. Legacy response shapes and session behavior are unchanged. ## Legacy SSE deprecation plan The public notice will accompany the next release that includes this plan. The release notes must record the actual notice date **N** and the earliest eligible removal date **N + 12 calendar months**. This page and the ADR do not establish an earlier notice date. Until those release notes are published, the notice date and removal date remain unannounced. The intended removal release is the first major release on or after that date that meets the readiness criteria below. If readiness is incomplete, postpone removal and publish the revised target; do not shorten the notice window. Publish the exact target release and date at least 90 days before removal, remind users 30 days before removal, and include the migration instructions in each announcement. Actual removal requires a separate reviewed change. ### Preserve route scope and authentication Prefix every URL below with the deployment's configured base path, if any. Group IDs remain preferable to names; retain the same route identifier and bearer header name/value. Group names take precedence over server names. | Existing SSE connection | Streamable HTTP replacement | SSE message endpoint being retired | | - | - | - | | `/sse` | `/mcp` | `/messages` | | `/sse/{group}` | `/mcp/{group}` | `/messages` | | `/sse/{server}` | `/mcp/{server}` | `/messages` | | `/{user}/sse` | `/{user}/mcp` | `/{user}/messages` | | `/{user}/sse/{group}` | `/{user}/mcp/{group}` | `/{user}/messages` | | `/{user}/sse/{server}` | `/{user}/mcp/{server}` | `/{user}/messages` | Smart Routing suffixes such as `$smart/{group}` are preserved as well. The SSE client follows its advertised message URL and session query; do not translate that message URL into a new HTTP endpoint. Replace the connection URL and let the HTTP transport manage its requests. First migrate the transport independently of the protocol revision. For example, with the legacy TypeScript SDK (`@modelcontextprotocol/sdk` 1.29.0, the version used by MCPHub's independent compatibility tests): ```typescript theme={null} import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const client = new Client({ name: 'my-client', version: '1.0.0' }); await client.connect( new StreamableHTTPClientTransport(new URL('https://hub.example.com/alice/mcp/my-group-id'), { requestInit: { headers: { Authorization: 'Bearer YOUR_EXISTING_KEY' } }, }), ); const tools = await client.listTools(); // Use the returned tool names; retain this client for the workflow. await client.close(); ``` This keeps the legacy initialization and `Mcp-Session-Id` lifecycle. Existing stateful tools keep session isolation; no `X-MCPHub-State-Id` is required for this transport-only migration. Modern discovery and explicit state are a separate upgrade described above. Existing SSE session IDs cannot be carried across transports: start a new HTTP workflow and repeat any login/tool setup. ### Readiness and removal boundaries Before announcing the exact removal target, maintainers must record: * The release containing the public notice and its actual date, plus proof that at least 12 calendar months will have elapsed at removal. * Passing legacy SSE and Streamable HTTP compatibility suites throughout the window, including authenticated global, group, server and user routes, session continuation/rebuild and stateful-tool isolation. * A migration checklist for each documented client integration: HTTP transport, preserved route/authentication, successful listing/calling, and resources, Apps or stateful workflows where used. Record tested client versions and unresolved blockers; there must be zero unresolved release-blocking migration defects. Operator confirmations may supplement this checklist; absence of telemetry is not evidence that nobody uses SSE. * Published release notes and documentation in both languages, the 90-day and 30-day reminders, and a support path for deployments that still use SSE. The removal inventory is limited to the SSE GET routes, their POST message routes, `handleSseConnection`, `handleSseMessage`, SSE transport construction, and SSE-only tests/imports. Audit every caller before removing shared helpers. Retain `/mcp` POST/GET/DELETE routes, legacy HTTP initialization, session maps and cleanup, session creation/rebuild locks, bearer/group authorization, `perSessionClient` isolation, OpenAPI cookie jars and modern explicit state. The `server-legacy` package also supplies shared legacy HTTP/OAuth functionality; SSE retirement alone is not grounds to remove the dependency. The plan is tracked in [#1272](https://github.com/samanhappy/mcphub/issues/1272). The notice release must open a separate removal task carrying the actual dates, client checklist and inventory. Legacy `2025-11-25` Streamable HTTP remains supported independently. # MCP SSE Endpoints (Deprecated) Source: https://docs.mcphub.app/api-reference/mcp-sse Connect to your MCP servers using the SSE endpoint. The legacy HTTP+SSE endpoint is deprecated but remains supported during migration. Use the [MCP HTTP endpoints and migration guide](/api-reference/mcp-http) for new integrations. No removal release or date has been announced. The staged-upgrade ADR calls for a deprecation window of at least 12 months. Removal requires a published schedule and a verified migration path. This does not deprecate the session lifecycle of legacy `2025-11-25` HTTP clients: both legacy and modern HTTP remain supported. **Security note**: MCP endpoints require authentication by default to avoid exposing connected tools and data. If you intentionally want public MCP access, disable **Enable Bearer Authentication** in the Keys settings. **Skip Authentication** only affects dashboard login. By default MCPHub reads Bearer credentials from the `Authorization` header, but this is configurable via **Bearer Auth Header Name** if upstream passthrough headers would otherwise conflict. ### Unified Endpoint * **Endpoint**: `http://localhost:3000/sse` * **Method**: `GET` ### Group-Specific Endpoint * **Endpoint**: `http://localhost:3000/sse/{group}` * **Method**: `GET` * **Parameters**: * `{group}`: The ID or name of the group. ### Server-Specific Endpoint * **Endpoint**: `http://localhost:3000/sse/{server}` * **Method**: `GET` * **Parameters**: * `{server}`: The name of the server. # OpenAPI Integration Source: https://docs.mcphub.app/api-reference/openapi Generate OpenAPI specifications from MCP tools for seamless integration with OpenWebUI and other systems # OpenAPI Generation for OpenWebUI Integration MCPHub now supports generating OpenAPI 3.0.3 specifications from MCP tools, enabling seamless integration with OpenWebUI and other OpenAPI-compatible systems without requiring MCPO as an intermediary proxy. ## Features * ✅ **Automatic OpenAPI Generation**: Converts MCP tools to OpenAPI 3.0.3 specification * ✅ **OpenWebUI Compatible**: Direct integration without MCPO proxy * ✅ **Real-time Tool Discovery**: Dynamically includes tools from connected MCP servers * ✅ **Dual Parameter Support**: Supports both GET (query params) and POST (JSON body) for tool execution * ✅ **No Authentication Required**: OpenAPI endpoints are public for easy integration * ✅ **Comprehensive Metadata**: Full OpenAPI specification with proper schemas and documentation ## API Endpoints ### OpenAPI Specification ```bash GET /api/openapi.json theme={null} curl "http://localhost:3000/api/openapi.json" ``` ```bash GET /api/openapi.yaml theme={null} curl "http://localhost:3000/api/openapi.yaml" ``` ```bash With Parameters theme={null} curl "http://localhost:3000/api/openapi.json?title=My MCP API&version=2.0.0" ``` Generates and returns the complete OpenAPI 3.0.3 specification for all connected MCP tools. The `.json` and `.yaml` variants accept the same query parameters. **Query Parameters:** **title** `string` Custom API title **description** `string` Custom API description **version** `string` Custom API version **serverUrl** `string` Custom server URL **includeDisabled** `boolean` (default: false) Include disabled tools **servers** `string` Comma-separated list of server names to include ### Group/Server-Specific OpenAPI Specification ```bash GET /api/:name/openapi.json theme={null} curl "http://localhost:3000/api/mygroup/openapi.json" ``` ```bash GET /api/:name/openapi.yaml theme={null} curl "http://localhost:3000/api/mygroup/openapi.yaml" ``` ```bash With Parameters theme={null} curl "http://localhost:3000/api/myserver/openapi.json?title=My Server API&version=1.0.0" ``` Generates and returns the OpenAPI 3.0.3 specification for a specific group or server in JSON or YAML format. If a group with the given name exists, it returns the specification for all servers in that group. Otherwise, it treats the name as a server name and returns the specification for that server only. **Path Parameters:** **name** `string` *required* Group ID/name or server name **Query Parameters:** Same as the main OpenAPI specification endpoint (title, description, version, serverUrl, includeDisabled). ### Available Servers ```bash GET /api/openapi/servers theme={null} curl "http://localhost:3000/api/openapi/servers" ``` Returns a list of connected MCP server names. ```json Example Response theme={null} { "success": true, "data": ["amap", "playwright", "slack"] } ``` ### Tool Statistics ```bash GET /api/openapi/stats theme={null} curl "http://localhost:3000/api/openapi/stats" ``` Returns statistics about available tools and servers. ```json Example Response theme={null} { "success": true, "data": { "totalServers": 3, "totalTools": 41, "serverBreakdown": [ {"name": "amap", "toolCount": 12, "status": "connected"}, {"name": "playwright", "toolCount": 21, "status": "connected"}, {"name": "slack", "toolCount": 8, "status": "connected"} ] } } ``` ### Tool Execution ```bash GET /api/tools/{serverName}/{toolName} theme={null} curl "http://localhost:3000/api/tools/amap/amap-maps_weather?city=Beijing" ``` ```bash POST /api/tools/{serverName}/{toolName} theme={null} curl -X POST "http://localhost:3000/api/tools/playwright/playwright-browser_navigate" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' ``` Execute MCP tools via OpenAPI-compatible endpoints. String array items submitted by the dashboard are converted according to the tool's `items.type` (`integer`, `number`, or `boolean`) before being forwarded to the MCP server. **Path Parameters:** **serverName** `string` *required* The name of the MCP server **toolName** `string` *required* The name of the tool to execute ## OpenWebUI Integration To integrate MCPHub with OpenWebUI: **1. Start MCPHub** Ensure MCPHub is running with your MCP servers configured **2. Get OpenAPI Specification** ```bash theme={null} curl http://localhost:3000/api/openapi.json > mcphub-api.json curl http://localhost:3000/api/openapi.yaml > mcphub-api.yaml ``` **3. Add to OpenWebUI** Import the OpenAPI specification file or point to the URL directly in OpenWebUI ### Configuration Example In OpenWebUI, you can add MCPHub as an OpenAPI tool by using: > **OpenAPI URL** > > `http://localhost:3000/api/openapi.json` or `http://localhost:3000/api/openapi.yaml` > **Base URL** > > `http://localhost:3000/api` ## Generated OpenAPI Structure The generated OpenAPI specification includes: ### Tool Conversion Logic * **Simple tools** (≤10 primitive parameters) → GET endpoints with query parameters * **Complex tools** (objects, arrays, or >10 parameters) → POST endpoints with JSON request body * **All tools** include comprehensive response schemas and error handling ### Example Generated Operation ```yaml theme={null} /tools/amap/amap-maps_weather: get: summary: "根据城市名称或者标准adcode查询指定城市的天气" operationId: "amap_amap-maps_weather" tags: ["amap"] parameters: - name: city in: query required: true description: "城市名称或者adcode" schema: type: string responses: '200': description: "Successful tool execution" content: application/json: schema: $ref: '#/components/schemas/ToolResponse' ``` ### Security * The OpenAPI specification and tool execution endpoints are mounted **outside** the authenticated router (see `app.use(${BASE_PATH}/api, router)` versus the `app.get(${BASE_PATH}/api/openapi*)` and `app.get(${BASE_PATH}/api/tools/...)` registrations in `src/routes/index.ts`). They do not require a JWT, bearer key, OAuth token, or Better Auth session. * For private deployments, gate these endpoints at your reverse proxy or only expose MCPHub on a trusted network. ## Benefits over MCPO > **Direct Integration** > > No need for intermediate proxy > **Real-time Updates** > > OpenAPI spec updates automatically as MCP servers connect/disconnect > **Better Performance** > > Direct tool execution without proxy overhead > **Simplified Architecture** > > One less component to manage ## Troubleshooting > **OpenAPI spec shows no tools** > > Ensure MCP servers are connected. Check `/api/openapi/stats` for server status. > **Tool execution fails** > > Verify the tool name and parameters match the OpenAPI specification. Check server logs for details. > **OpenWebUI can't connect** > > Ensure MCPHub is accessible from OpenWebUI and the OpenAPI URL is correct. > **Missing tools in specification** > > Check if tools are enabled in your MCP server configuration. Use `includeDisabled=true` to see all tools. # Prompts Source: https://docs.mcphub.app/api-reference/prompts Manage and execute MCP prompts. > **[POST /api/mcp/:serverName/prompts/:promptName](#get-a-prompt)** > > Execute a prompt on an MCP server. > **[POST /api/servers/:serverName/prompts/:promptName/toggle](#toggle-a-prompt)** > > Enable or disable a prompt. > **[PUT /api/servers/:serverName/prompts/:promptName/description](#update-prompt-description)** > > Update the description of a prompt. *** ### Get a Prompt Execute a prompt on an MCP server and get the result. * **Endpoint**: `/api/mcp/:serverName/prompts/:promptName` * **Method**: `POST` * **Authentication**: Required * **Parameters**: * `:serverName` (string, required): The name of the MCP server. * `:promptName` (string, required): The name of the prompt. * **Body**: ```json theme={null} { "arguments": { "arg1": "value1", "arg2": "value2" } } ``` * `arguments` (object, optional): Arguments to pass to the prompt. * **Response**: ```json theme={null} { "success": true, "data": { "messages": [ { "role": "user", "content": { "type": "text", "text": "Prompt content" } } ] } } ``` **Example Request:** ```bash theme={null} curl -X POST "http://localhost:3000/api/mcp/myserver/prompts/code-review" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "arguments": { "language": "typescript", "code": "const x = 1;" } }' ``` *** ### Toggle a Prompt Enable or disable a specific prompt on a server. * **Endpoint**: `/api/servers/:serverName/prompts/:promptName/toggle` * **Method**: `POST` * **Authentication**: Required * **Parameters**: * `:serverName` (string, required): The name of the server. * `:promptName` (string, required): The name of the prompt. * **Body**: ```json theme={null} { "enabled": true } ``` * `enabled` (boolean, required): `true` to enable the prompt, `false` to disable it. **Example Request:** ```bash theme={null} curl -X POST "http://localhost:3000/api/servers/myserver/prompts/code-review/toggle" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"enabled": false}' ``` *** ### Update Prompt Description Update the description of a specific prompt. * **Endpoint**: `/api/servers/:serverName/prompts/:promptName/description` * **Method**: `PUT` * **Authentication**: Required * **Parameters**: * `:serverName` (string, required): The name of the server. * `:promptName` (string, required): The name of the prompt. * **Body**: ```json theme={null} { "description": "New prompt description" } ``` * `description` (string, required): The new description for the prompt. **Example Request:** ```bash theme={null} curl -X PUT "http://localhost:3000/api/servers/myserver/prompts/code-review/description" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"description": "Review code for best practices and potential issues"}' ``` **Note**: Prompts are templates that can be used to generate standardized requests to MCP servers. They are defined by the MCP server and can have arguments that are filled in when the prompt is executed. # Servers Source: https://docs.mcphub.app/api-reference/servers Manage your MCP servers. > **[GET /api/servers](#get-all-servers)** > > List all MCP servers and their status. > **[GET /api/servers/:name](#get-a-server)** > > Read a single server's configuration. > **[POST /api/servers](#create-a-new-server)** > > Add a new MCP server. > **[POST /api/servers/batch](#batch-create-servers)** > > Add multiple servers in one request. > **[PUT /api/servers/:name](#update-a-server)** > > Update an existing server. > **[DELETE /api/servers/:name](#delete-a-server)** > > Remove a server. > **[POST /api/servers/:name/toggle](#toggle-a-server)** > > Enable or disable a server. > **[POST /api/servers/:name/reload](#reload-a-server)** > > Reconnect to an upstream server. > **[POST /api/servers/:serverName/tools/:toolName/toggle](#toggle-a-tool)** > > Enable or disable a tool. > **[PUT /api/servers/:serverName/tools/:toolName/description](#update-tool-description)** > > Override a tool description. > **[DELETE /api/servers/:serverName/tools/:toolName/description](#reset-tool-description)** > > Reset a tool description to upstream. > **[PUT /api/servers/:serverName/tools/:toolName/hidden-params](#hide-tool-parameters)** > > Hide top-level input parameters from models. > **[Prompts on servers](#prompts)** > > Toggle / describe / reset prompts. > **[Resources on servers](#resources)** > > Toggle / describe / reset resources. > **[GET /api/settings](#get-settings)** > > Read everything: servers, groups, system config. *** ### Get All Servers Retrieves a list of all configured MCP servers, including their status and available tools. Connected, enabled MCP servers include an optional `protocolVersion` field containing the current connection's negotiated MCP protocol version. It is omitted for disconnected, disabled, and OpenAPI servers, or when unavailable. This is separate from the server software `version`. * **Endpoint**: `/api/servers` * **Method**: `GET` * **Response**: ```json theme={null} { "success": true, "data": [ { "name": "example-server", "status": "connected", "tools": [ { "name": "tool1", "description": "Description of tool 1" } ], "config": { "type": "stdio", "command": "node", "args": ["server.js"] } } ] } ``` *** ### Get a Server Returns the configuration of a single server, including upstream tools/prompts/resources when connected. * **Endpoint**: `GET /api/servers/:name` * **Authentication**: Required. * Returns `404` if the server is not configured. *** ### Create a New Server Adds a new MCP server to the configuration. * **Endpoint**: `/api/servers` * **Method**: `POST` * **Body**: ```json theme={null} { "name": "my-new-server", "config": { "type": "stdio", "command": "python", "args": ["-u", "my_script.py"], "owner": "admin" } } ``` * `name` (string, required): The unique name for the server. * `config` (object, required): The server configuration object. * `type` (string): `stdio`, `sse`, `streamable-http`, or `openapi`. * `command` (string): Command to execute for `stdio` type. * `args` (array of strings): Arguments for the command. * `url` (string): URL for `sse`, `streamable-http`, or `openapi` types. * `openapi` (object): OpenAPI configuration. * `url` (string): URL to the OpenAPI schema. * `schema` (object): The OpenAPI schema object itself. * `headers` (object): Headers to send with requests for `sse`, `streamable-http`, and `openapi` types. * `keepAliveInterval` (number): Health check interval in milliseconds for remote servers. Defaults to 60000. * `enableKeepAlive` (boolean): Toggle periodic health checks for remote servers. Disconnected remote servers always retry automatically with bounded exponential backoff and jitter. * `enabled` (boolean): Defaults to `true`. Disabled servers stay in the configuration but are not connected. * `owner` (string): The owner of the server. Defaults to the current user or `'admin'`. * `passthroughHeaders` (string\[]): Inbound HTTP header names that should be forwarded to the upstream server. * `oauth`, `proxy`, `options`, `description`: Optional advanced settings — see [MCP Settings](/configuration/mcp-settings). *** ### Batch Create Servers Create several servers in a single request. The body contains a `servers` **array** of `{ name, config }` objects — the same shape `POST /api/servers` accepts, just batched: * **Endpoint**: `POST /api/servers/batch` * **Authentication**: Required. * **Body**: ```json theme={null} { "servers": [ { "name": "fetch", "config": { "type": "stdio", "command": "uvx", "args": ["mcp-server-fetch"] } }, { "name": "amap", "config": { "type": "sse", "url": "https://mcp.amap.com/sse?key=..." } } ] } ``` Each element is validated independently, and the response contains a per-server `{ name, success, message? }` record so partial batches do not abort the whole request. *** ### Update a Server Updates the configuration of an existing MCP server. * **Endpoint**: `/api/servers/:name` * **Method**: `PUT` * **Parameters**: * `:name` (string, required): The name of the server to update. * **Body**: ```json theme={null} { "config": { "type": "stdio", "command": "node", "args": ["new_server.js"] } } ``` * `config` (object, required): The updated server configuration object. See "Create a New Server" for details. *** ### Delete a Server Removes an MCP server from the configuration. * **Endpoint**: `/api/servers/:name` * **Method**: `DELETE` * **Parameters**: * `:name` (string, required): The name of the server to delete. *** ### Toggle a Server Enables or disables an MCP server. * **Endpoint**: `/api/servers/:name/toggle` * **Method**: `POST` * **Parameters**: * `:name` (string, required): The name of the server to toggle. * **Body**: ```json theme={null} { "enabled": true } ``` * `enabled` (boolean, required): `true` to enable the server, `false` to disable it. *** ### Reload a Server Forces MCPHub to disconnect and re-establish the upstream connection (or re-spawn the stdio child) for a single server. Use this when an upstream server has been updated out-of-band or appears stuck. * **Endpoint**: `POST /api/servers/:name/reload` * **Authentication**: Required. *** ### Batch Tool Status `POST /api/servers/:serverName/tools/toggle` enables or disables currently discovered tools in one request. It requires dashboard authentication and the same server configuration access as the single-tool endpoint. ```json theme={null} { "enabled": false, "tools": ["create_*", "delete_*", "specific_tool"] } ``` * `enabled` is a required boolean; repeated requests set the same state. * `tools` contains 1–100 non-empty names or patterns, up to 256 characters each. Patterns are case-sensitive full-name matches: `*` matches zero or more characters, `?` matches one; every other character is literal. * Exact names resolve runtime (server-prefixed) names first, then original upstream names. Patterns match either form using the configured name separator. * Matching tools are deduplicated and saved in one update. Descriptions and hidden parameters are preserved. The change applies across groups and takes effect for discovery and subsequent calls without reconnecting. * For personal OAuth or credential bindings, the inventory belongs to the authenticated caller; another user's credentials are never borrowed. If that inventory cannot be acquired, the request returns `409` without changing tool states. * Patterns are expanded once against the current inventory, not persisted as policies for future tools. An unavailable inventory returns `409`; an empty match succeeds without writing. The response lists matched runtime names: ```json theme={null} { "success": true, "data": { "matchedTools": ["example-create_item", "example-delete_item"], "enabled": false } } ``` The existing single-tool endpoint remains available. *** ### Toggle a Tool Enables or disables a specific tool on a server. * **Endpoint**: `/api/servers/:serverName/tools/:toolName/toggle` * **Method**: `POST` * **Parameters**: * `:serverName` (string, required): The name of the server. * `:toolName` (string, required): The name of the tool. * **Body**: ```json theme={null} { "enabled": true } ``` * `enabled` (boolean, required): `true` to enable the tool, `false` to disable it. *** ### Update Tool Description Updates the description of a specific tool. * **Endpoint**: `/api/servers/:serverName/tools/:toolName/description` * **Method**: `PUT` * **Parameters**: * `:serverName` (string, required): The name of the server. * `:toolName` (string, required): The name of the tool. * **Body**: ```json theme={null} { "description": "New tool description" } ``` * `description` (string, required): The new description for the tool. *** ### Reset Tool Description Drops the override stored in MCPHub and falls back to the upstream tool description. * **Endpoint**: `DELETE /api/servers/:serverName/tools/:toolName/description` * **Authentication**: Required. *** ### Hide Tool Parameters Sets the top-level input parameters of a tool that models do not see. They are removed from the input schema in `tools/list`, `search_tools` results, `describe_tool`, the Smart Routing embedding text, generated OpenAPI specs and context cost figures, and dropped from the arguments of a call before it is forwarded, so the upstream default applies. Use it for options a deployment cannot use, such as cloud-only features of a self-hosted backend. The dashboard keeps showing the full schema. * **Endpoint**: `/api/servers/:serverName/tools/:toolName/hidden-params` * **Method**: `PUT` * **Parameters**: * `:serverName` (string, required): The name of the server. * `:toolName` (string, required): The name of the tool, bare or server-prefixed. * **Body**: ```json theme={null} { "hiddenParams": ["profile", "zeroDataRetention"] } ``` * `hiddenParams` (string\[], required): Top-level parameter names. An empty array removes the setting. * **Errors**: `400` when a name is not a parameter of the tool or is listed in its `required`. These checks run when the server is connected; otherwise the list is stored as given, and a required parameter is still never hidden. *** ### Prompts MCPHub exposes the same toggle / describe / reset triple for upstream prompts. The body shape mirrors the tool endpoints above. | Method & Path | Purpose | | - | - | | `POST /api/servers/:serverName/prompts/:promptName/toggle` | Enable / disable a prompt. Body: `{ "enabled": true }`. | | `PUT /api/servers/:serverName/prompts/:promptName/description` | Override the prompt description. Body: `{ "description": "..." }`. | | `DELETE /api/servers/:serverName/prompts/:promptName/description` | Reset the description to the upstream value. | *** ### Resources | Method & Path | Purpose | | - | - | | `POST /api/servers/:serverName/resources/:resourceUri/toggle` | Enable / disable a resource. Body: `{ "enabled": true }`. The `:resourceUri` must be URL-encoded. | | `PUT /api/servers/:serverName/resources/:resourceUri/description` | Override the resource description. | | `DELETE /api/servers/:serverName/resources/:resourceUri/description` | Reset the description. | *** ### System Configuration System-wide configuration (routing, install, smart routing, mcpRouter, name separator, session rebuild, OAuth server) is managed through `PUT /api/system-config`. See [System Config API](/api-reference/config) for the full request schema and accepted top-level keys (`routing`, `install`, `smartRouting`, `mcpRouter`, `nameSeparator`, `enableSessionRebuild`, `oauthServer`). > [!WARNING] > > The legacy `openaiApiKey` / `modelName` / `temperature` / `maxTokens` request body is no longer accepted. Smart-routing model selection now lives under the nested `smartRouting` object documented in [System Config API](/api-reference/config). *** ### Get Settings Retrieves all server settings and configurations. * **Endpoint**: `/api/settings` * **Method**: `GET` * **Response**: ```json theme={null} { "success": true, "data": { "servers": [...], "groups": [...], "systemConfig": {...} } } ``` **Note**: For built-in prompt and resource management (not tied to an upstream server), see the [Prompts API](/api-reference/prompts) documentation. *** ## Other server-adjacent endpoints MCPHub exposes several other route families beyond pure server CRUD. Each lives on its own resource and is documented separately or, where no dedicated page exists yet, follows the schema described inline: | Family | Method & Path(s) | Notes | | - | - | - | | **Auth & users** | `POST /api/auth/login`, `POST /api/auth/register`, `GET /api/auth/user`, `POST /api/auth/change-password`, `/api/users/*`, `/api/users-stats` | See [Auth](/api-reference/auth) and [Users](/api-reference/users). | | **Bearer keys** | `GET /api/auth/keys`, `POST /api/auth/keys`, `PUT /api/auth/keys/:id`, `DELETE /api/auth/keys/:id` | System-level and user-level static MCP tokens. Only all-access system keys can authenticate dashboard APIs. See [Authentication](/features/authentication). | | **Groups** | `/api/groups`, `/api/groups/batch`, `/api/groups/:id/servers*`, `/api/groups/:id/server-configs*` | See [Groups](/api-reference/groups). | | **Templates** | `POST /api/templates/export`, `GET /api/templates/export/groups/:id`, `POST /api/templates/import` | Export / import `mcp_settings.json` snapshots and group templates. Rate-limited. | | **Activities** | `GET /api/activities`, `/api/activities/stats`, `/api/activities/filters`, `/api/activities/:id`, `DELETE /api/activities/cleanup`, `GET /api/activities/available` | Database mode only — see [Monitoring](/features/monitoring). | | **Logs** | `GET /api/logs`, `DELETE /api/logs`, `GET /api/logs/stream` | In-process log buffer + SSE stream. | | **Built-in prompts / resources** | `/api/prompts`, `/api/resources`, `POST /api/resources/read` | First-party prompts and resources that are not attached to an upstream server. | | **MCPB upload** | `POST /api/mcpb/upload` | Upload an MCPB bundle to register a new server. | | **Market / Cloud / Registry** | `/api/market/*`, `/api/cloud/*`, `/api/registry/*` | Read-only catalogs that back the dashboard's discovery pages. | | **OAuth clients (MCPHub server)** | `/api/oauth/clients`, `/api/oauth/clients/:clientId/regenerate-secret` | Manage clients of MCPHub's own authorization server. The flow itself lives at `/oauth/*` and is documented under [OAuth](/features/oauth). | | **Better Auth** | `GET /api/better-auth/user`, plus the Better Auth handler mounted at `${BASE_PATH}${betterAuthConfig.basePath}` (default `/api/auth/better`) | Optional GitHub / Google login. | | **OpenAPI** | `GET ${BASE_PATH}/api/openapi.{json,yaml}`, `GET ${BASE_PATH}/api/:name/openapi.{json,yaml}`, `GET ${BASE_PATH}/api/openapi/servers`, `GET ${BASE_PATH}/api/openapi/stats`, plus the OpenAPI tool execution routes | See [OpenAPI](/api-reference/openapi). | | **MCP settings export** | `GET /api/mcp-settings/export` | Download the live settings as JSON. | # Smart Routing Source: https://docs.mcphub.app/api-reference/smart-routing Intelligent tool discovery using vector semantic search. Smart Routing is MCPHub's intelligent tool discovery system that uses vector semantic search to automatically find the most relevant tools for any given task. ### HTTP Endpoint * **Endpoint**: `http://localhost:3000/mcp/$smart` * **Method**: `POST` ### SSE Endpoint (Deprecated) * **Endpoint**: `http://localhost:3000/sse/$smart` * **Method**: `GET` ### How it Works 1. **Tool Indexing**: All MCP tools are automatically converted to vector embeddings and stored in PostgreSQL with pgvector. 2. **Semantic Search**: User queries are converted to vectors and matched against tool embeddings using cosine similarity. 3. **Intelligent Filtering**: Dynamic thresholds ensure relevant results without noise. 4. **Precise Execution**: Found tools can be directly executed with proper parameter validation. ### Setup Requirements * PostgreSQL with pgvector extension * LLM Provider (OpenAI or compatible) API key * Enable Smart Routing in MCPHub settings ## Diagnostics & Maintenance API The following admin endpoints live under the authenticated Dashboard API and require a valid Dashboard session (JWT) or a system-level all-access bearer key (`Authorization: Bearer mcphub_...`), like any other `/api/*` route. Both are additionally **admin-only**: an authenticated non-admin identity receives `403 Admin privileges required`. ### Index Status & Performance Returns Smart Routing configuration, database connection state, vector store size (tool/server row counts, distinct servers, dimensions), per-model and per-server statistics, sync freshness, and index coverage. ```bash theme={null} curl http://localhost:3000/api/smart-routing/performance \ -H "Authorization: Bearer YOUR_JWT_OR_BEARER_KEY" ``` ### Reindex Force a full rebuild of the vector index: `vector_embeddings` is cleared first, then embeddings are regenerated for every enabled server. Runs synchronously and returns a summary; per-server progress is streamed over SSE `embedding-sync-progress` events. Servers that declare a personal credential template are never connected globally, so they are reached on behalf of the users who bound credentials (the server owner first) and the union of the tools those users can see is indexed. A server nobody bound credentials for is reported as skipped with a reason in the response `results`. Only one pass runs at a time: a call made while another pass is in flight is rejected with `409 A reindex pass is already running`. The `GET /api/smart-routing/performance` payload exposes the same state as `reindexing`, so a dashboard can disable its button while a pass is running. ```bash theme={null} curl -X POST http://localhost:3000/api/smart-routing/reindex \ -H "Authorization: Bearer YOUR_JWT_OR_BEARER_KEY" ``` # System Source: https://docs.mcphub.app/api-reference/system System and utility endpoints. > **[GET /health](#health-check)** > > Check the health status of the MCPHub server. > **[GET /oauth/callback](#oauth-callback)** > > OAuth callback endpoint for authentication flows. > **[POST /api/mcpb/upload](#upload-mcpb-file)** > > Upload a MCPB bundle file. > **[GET /api/mcp-settings/export](#export-mcp-settings)** > > Export MCP settings as JSON. *** ### Health Check Check the health status of the MCPHub server. * **Endpoint**: `/health` * **Method**: `GET` * **Authentication**: Not required * **Response**: ```json theme={null} { "status": "ok", "timestamp": "2024-11-12T01:30:00.000Z", "uptime": 12345 } ``` **Example Request:** ```bash theme={null} curl "http://localhost:3000/health" ``` *** ### OAuth Callback OAuth callback endpoint for handling OAuth authentication flows. This endpoint is automatically called by OAuth providers after user authorization. * **Endpoint**: `/oauth/callback` * **Method**: `GET` * **Authentication**: Not required (public callback URL) * **Query Parameters**: Varies by OAuth provider (typically includes `code`, `state`, etc.) **Note**: This endpoint is used internally by MCPHub's OAuth integration and should not be called directly by clients. *** ### Upload MCPB File Upload a MCPB (MCP Bundle) file to import server configurations. * **Endpoint**: `/api/mcpb/upload` * **Method**: `POST` * **Authentication**: Required (admin only) * **Content-Type**: `multipart/form-data` * **Body**: * `mcpbFile` (file, required): The MCPB bundle file to upload. **Security notes:** * **Admin only**: the uploaded bundle's code is extracted to the deterministic `data/uploads/mcpb/server-` path that existing stdio server configurations execute from, so non-admin uploads are rejected with `403` before the request body is staged on disk. * `manifest.json` must include a safe `name` value using only letters, numbers, `.`, `_`, and `-`. * Uploads are rejected if the manifest name contains path separators, `..`, or other unsafe path characters. **Example Request:** ```bash theme={null} curl -X POST "http://localhost:3000/api/mcpb/upload" \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "mcpbFile=@bundle.mcpb" ``` *** ### Export MCP Settings Export the current MCP settings configuration as a JSON file. * **Endpoint**: `/api/mcp-settings/export` * **Method**: `GET` * **Authentication**: Required * **Response**: Returns the `mcp_settings.json` configuration file. **Example Request:** ```bash theme={null} curl "http://localhost:3000/api/mcp-settings/export" \ -H "Authorization: Bearer YOUR_TOKEN" \ -o mcp_settings.json ``` **Note**: This endpoint allows you to download a backup of your MCP settings, which can be used to restore or migrate your configuration. # Tools Source: https://docs.mcphub.app/api-reference/tools Execute MCP tools programmatically. > **[POST /api/tools/call/:server](#call-a-tool)** > > Call a specific tool on an MCP server. *** ### Call a Tool Execute a specific tool on an MCP server with given arguments. * **Endpoint**: `/api/tools/call/:server` * **Method**: `POST` * **Parameters**: * `:server` (string, required): The name of the MCP server. * **Body**: ```json theme={null} { "toolName": "tool-name", "arguments": { "param1": "value1", "param2": "value2" } } ``` * `toolName` (string, required): The name of the tool to execute. Accepted in either form: * The bare upstream tool name (e.g. `weather`). MCPHub will route it to `:server`. * The fully qualified `` form (e.g. `amap-weather`). The separator defaults to `-` and is configurable via `systemConfig.nameSeparator` (see [System Config API](/api-reference/config)). Use the qualified form when an upstream tool name happens to contain the separator and you need to disambiguate. * `arguments` (object, optional): The arguments to pass to the tool. Defaults to an empty object. * **Response**: ```json theme={null} { "success": true, "data": { "content": [ { "type": "text", "text": "Tool execution result" } ], "toolName": "tool-name", "arguments": { "param1": "value1", "param2": "value2" } } } ``` The response keeps the `success` / `data` envelope. `data` preserves the tool result fields, including `structuredContent`, `isError`, and `_meta` when present, alongside `toolName` and the converted `arguments`. Missing `content` defaults to `[]`. A tool execution failure is indicated by `data.isError: true`; the envelope's `success: true` means the API returned a tool result, not that the tool succeeded. **Example Request:** ```bash theme={null} curl -X POST "http://localhost:3000/api/tools/call/amap" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "toolName": "amap-maps_weather", "arguments": { "city": "Beijing" } }' ``` **Notes:** * The tool arguments are automatically converted to the proper types based on the tool's input schema. * Use the `x-session-id` header to maintain session state across multiple tool calls if needed. * This endpoint requires authentication. *** ### Alternative: OpenAPI Tool Execution For OpenAPI-compatible tool execution without authentication, see the [OpenAPI Integration](/api-reference/openapi#tool-execution) documentation. The OpenAPI endpoints provide: * **GET** `/api/tools/:serverName/:toolName` - For simple tools with query parameters * **POST** `/api/tools/:serverName/:toolName` - For complex tools with JSON body These endpoints are designed for integration with OpenWebUI and other OpenAPI-compatible systems. # Users Source: https://docs.mcphub.app/api-reference/users Manage local user accounts in MCPHub. These endpoints manage the local username/password users stored in `mcp_settings.json` (or the `mcphub_user` table when the database backend is enabled). All endpoints require an authenticated admin (`isAdmin: true`). For OAuth/social accounts created via Better Auth, see `/api/better-auth/*` instead — those records live in a separate table and are not managed through this API. > **[GET /api/users](#get-all-users)** > > List all users. > **[GET /api/users/:username](#get-a-user)** > > Get a single user. > **[POST /api/users](#create-a-user)** > > Create a user. > **[PUT /api/users/:username](#update-a-user)** > > Update a user. > **[DELETE /api/users/:username](#delete-a-user)** > > Delete a user. > **[GET /api/users-stats](#get-user-statistics)** > > Aggregate user counts. *** ### Get All Users * **Endpoint**: `GET /api/users` * **Authentication**: Required (admin only) * **Response**: The `password` field is stripped from every record. ```json theme={null} { "success": true, "data": [ { "username": "admin", "isAdmin": true }, { "username": "alice", "isAdmin": false } ] } ``` *** ### Get a User * **Endpoint**: `GET /api/users/:username` * **Authentication**: Required (admin only) * **Response**: ```json theme={null} { "success": true, "data": { "username": "alice", "isAdmin": false } } ``` Returns `404` if the user does not exist. *** ### Create a User * **Endpoint**: `POST /api/users` * **Authentication**: Required (admin only) * **Body**: ```json theme={null} { "username": "alice", "password": "P@ssw0rd-123", "isAdmin": false } ``` | Field | Type | Required | Description | | - | - | - | - | | `username` | string | yes | Must be unique. | | `password` | string | yes | Must satisfy the configured password policy (see `src/utils/passwordValidation.ts`). | | `isAdmin` | boolean | no | Defaults to `false`. | * Returns `400` if `username`/`password` are missing, the password is too weak, or the username is already taken. * Returns `201` with the new user (password stripped) on success. > [!NOTE] > > The user model does not have per-user `servers` or `groups` lists. Server access is governed by each server's `owner`, `visibility`, and `sharedWithUsers` fields. See [Authentication & Security](/features/authentication) and [MCP Settings](/configuration/mcp-settings). *** ### Update a User * **Endpoint**: `PUT /api/users/:username` * **Authentication**: Required (admin only) * **Body** (at least one field is required): ```json theme={null} { "isAdmin": true, "newPassword": "Newp@ssw0rd" } ``` | Field | Type | Description | | - | - | - | | `isAdmin` | boolean | Promote/demote the user. | | `newPassword` | string | Replace the user's password. Must satisfy the password policy. | * Returns `400` if neither field is supplied, the password fails validation, or the request would demote the last remaining admin. * Returns `404` if the username does not exist. > [!WARNING] > > Username is the primary key — to rename a user, delete and recreate them. Other profile fields (email, display name, etc.) are not currently supported on the local user model. *** ### Delete a User * **Endpoint**: `DELETE /api/users/:username` * **Authentication**: Required (admin only) * **Restrictions**: * You cannot delete your own account. * You cannot delete the last admin. ```json theme={null} { "success": true, "message": "User deleted successfully" } ``` *** ### Get User Statistics * **Endpoint**: `GET /api/users-stats` * **Authentication**: Required (admin only) ```json theme={null} { "success": true, "data": { "totalUsers": 5, "adminUsers": 1, "regularUsers": 4 } } ``` The stats endpoint reports only aggregate counts; per-server or per-group breakdowns are not produced here. # Database Configuration Source: https://docs.mcphub.app/configuration/database-configuration Configuring MCPHub to use a PostgreSQL database as an alternative to the mcp_settings.json file. # Database Configuration for MCPHub ## Overview MCPHub supports storing configuration data in a PostgreSQL database as an alternative to the `mcp_settings.json` file. Database mode provides enhanced persistence and scalability for production environments and enterprise deployments. ## Why Use Database Configuration? **Core Benefits:** * ✅ **Better Persistence** - Configuration stored in a professional database with transaction support and data integrity * ✅ **High Availability** - Leverage database replication and failover capabilities * ✅ **Enterprise Ready** - Meets enterprise data management and compliance requirements * ✅ **Backup & Recovery** - Use mature database backup tools and strategies ## Environment Variables ### Required for Database Mode ```bash theme={null} # Database connection URL (PostgreSQL) # Simply setting DB_URL will automatically enable database mode DB_URL=postgresql://user:password@localhost:5432/mcphub # Or explicitly control with USE_DB (overrides auto-detection) # USE_DB=true ``` > [!NOTE] > > **Simplified Configuration**: You only need to set `DB_URL` to enable database mode. MCPHub will automatically detect and enable database mode when `DB_URL` is present. Use `USE_DB=false` to explicitly disable database mode even when `DB_URL` is set. ## Setup Instructions ### 1. Using Docker #### Option A: Using External Database If you already have a PostgreSQL database: ```bash theme={null} docker run -d \ -p 3000:3000 \ -v ./mcp_settings.json:/app/mcp_settings.json \ -e DB_URL="postgresql://user:password@your-db-host:5432/mcphub" \ samanhappy/mcphub ``` #### Option B: Using PostgreSQL as a separate service Create a `docker-compose.yml`: ```yaml theme={null} version: '3.8' services: postgres: image: pgvector/pgvector:pg17 environment: POSTGRES_DB: mcphub POSTGRES_USER: mcphub POSTGRES_PASSWORD: your_secure_password volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" mcphub: image: samanhappy/mcphub:latest environment: USE_DB: "true" DB_URL: "postgresql://mcphub:your_secure_password@postgres:5432/mcphub" PORT: 3000 ports: - "3000:3000" depends_on: - postgres volumes: pgdata: ``` Run with: ```bash theme={null} docker-compose up -d ``` ### 2. Manual Setup #### Step 1: Setup PostgreSQL Database ```bash theme={null} # Install PostgreSQL (if not already installed) sudo apt-get install postgresql postgresql-contrib # Create database and user sudo -u postgres psql < mcphub_backup.sql # Or using Docker docker exec postgres pg_dump -U mcphub mcphub > mcphub_backup.sql ``` ### Restore ```bash theme={null} # PostgreSQL restore psql -U mcphub mcphub < mcphub_backup.sql # Or using Docker docker exec -i postgres psql -U mcphub mcphub < mcphub_backup.sql ``` ## Switching Back to File-Based Config If you need to switch back to file-based configuration: 1. Set `USE_DB=false` or remove `DB_URL` and `USE_DB` environment variables 2. Restart MCPHub 3. MCPHub will use `mcp_settings.json` again Note: Changes made in database mode won't be reflected in the file unless you manually export them. ## Troubleshooting ### Connection Refused ``` Error: connect ECONNREFUSED 127.0.0.1:5432 ``` **Solution:** Check that PostgreSQL is running and accessible: ```bash theme={null} # Check PostgreSQL status sudo systemctl status postgresql # Or for Docker docker ps | grep postgres ``` ### Authentication Failed ``` Error: password authentication failed for user "mcphub" ``` **Solution:** Verify database credentials in `DB_URL` environment variable. ### Migration Failed ``` ❌ Migration failed: ... ``` **Solution:** 1. Check that `mcp_settings.json` exists and is valid JSON 2. Verify database connection 3. Check logs for specific error messages 4. Ensure database user has CREATE TABLE permissions ### Tables Already Exist Database tables are automatically created if they don't exist. If you get errors about existing tables, check: 1. Whether a previous migration partially completed 2. Manual table creation conflicts 3. Run with `synchronize: false` in database config if needed ## Environment Variables Reference | Variable | Required | Default | Description | | - | - | - | - | | `DB_URL` | Yes\* | - | Full PostgreSQL connection URL. Setting this automatically enables database mode | | `USE_DB` | No | auto | Explicitly enable/disable database mode. If not set, auto-detected based on `DB_URL` presence | | `MCPHUB_SETTING_PATH` | No | - | Path to mcp\_settings.json (for migration) | \*Required for database mode. Simply setting `DB_URL` enables database mode automatically ## Security Considerations 1. **Database Credentials:** Store database credentials securely, use environment variables or secrets management 2. **Network Access:** Restrict database access to MCPHub instances only 3. **Encryption:** Use SSL/TLS for database connections in production: ```bash theme={null} DB_URL=postgresql://user:password@host:5432/mcphub?sslmode=require ``` 4. **Backup:** Regularly backup your database 5. **Access Control:** Use strong database passwords and limit user permissions ## Performance Database mode offers better performance for: * Multiple concurrent users * Frequent configuration changes * Large number of servers/groups File mode may be faster for: * Single user setups * Read-heavy workloads with infrequent changes * Development/testing environments # Docker Setup Source: https://docs.mcphub.app/configuration/docker-setup Deploy MCPHub using Docker and Docker Compose # Docker Setup This guide covers deploying MCPHub using Docker, including development and production configurations. ## Quick Start with Docker ### Using Pre-built Image The standard MCPHub image includes Node.js/pnpm, Python, uv/uvx, Git, and build tools. Use the `latest-full` image if you need Cargo and the stable Rust toolchain for Rust-based MCP servers. ```bash theme={null} # Pull the latest image docker pull samanhappy/mcphub:latest # Run with default configuration docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ samanhappy/mcphub:latest ``` The image defaults to `/app/data/mcp_settings.json`. Mount the entire data directory to persist settings, users, credential bindings, and their encryption key. For custom servers, create `data/mcp_settings.json` before the first launch, or edit the existing file afterward. With detached mode (`-d`), run `docker logs mcphub` to see the generated initial admin password. Existing file mounts at `/app/mcp_settings.json` are detected automatically and take precedence over the data directory, so existing Docker commands keep working. An explicit `MCPHUB_SETTING_PATH` always takes precedence over both locations. For full persistence, migrate the settings file and its existing `.credentials.json` and `.credentials.key` sidecars together into the mounted data directory. Explicit `MCPHUB_SETTING_PATH` values remain supported. ### Building from Source ```bash theme={null} # Clone the repository git clone https://github.com/your-username/mcphub.git cd mcphub # Build the Docker image docker build -t mcphub:local . # Run the container docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ mcphub:local ``` ### Building with Extended Features The Docker image supports an `INSTALL_EXT` build argument to include additional tools. Published `-full` images are built with this enabled: ```bash theme={null} # Build with extended features (includes stable Rust/Cargo, Docker Engine, Chrome + Firefox for Playwright) docker build --build-arg INSTALL_EXT=true -t mcphub:extended . # Option 1: Run with automatic Docker-in-Docker (requires privileged mode) docker run -d \ --name mcphub \ --privileged \ -p 3000:3000 \ -v ./data:/app/data \ mcphub:extended # Option 2: Run with Docker socket mounted (use host's Docker daemon) docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ -v /var/run/docker.sock:/var/run/docker.sock \ mcphub:extended # Verify Docker is available docker exec mcphub docker --version docker exec mcphub docker ps ``` > [!NOTE] > > **What's included with INSTALL\_EXT=true:** > > * **Stable Rust + Cargo**: Installed through rustup for Rust-based MCP servers. > * **Docker Engine**: Full Docker daemon with CLI for container management. The daemon auto-starts when the container runs in privileged mode. > * **Chrome + Firefox for Playwright** (amd64 only): For browser automation tasks > > The extended image is larger but provides additional capabilities for advanced use cases. > [!WARNING] > > **Docker-in-Docker Security Considerations:** > > * **Privileged mode** (`--privileged`): Required for the Docker daemon to start inside the container. This gives the container elevated permissions on the host. > * **Docker socket mounting** (`/var/run/docker.sock`): Gives the container access to the host's Docker daemon. Both approaches should only be used in trusted environments. > * For production, consider using Docker socket mounting instead of privileged mode for better security. ## Docker Compose Setup ### Basic Configuration Create a `docker-compose.yml` file: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest # For local development, use: # build: . container_name: mcphub ports: - '3000:3000' environment: - NODE_ENV=production - PORT=3000 - TRUST_PROXY=1 - JWT_SECRET=${JWT_SECRET:-your-jwt-secret} - DB_URL=postgresql://mcphub:password@postgres:5432/mcphub volumes: - ./servers.json:/app/servers.json:ro - ./data:/app/data # Keep installed stdio server packages across container recreates. # See "Persisting the stdio package cache" below. - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv depends_on: postgres: condition: service_healthy restart: unless-stopped networks: - mcphub-network postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=password volumes: - postgres_data:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro ports: - '5432:5432' healthcheck: test: ['CMD-SHELL', 'pg_isready -U mcphub -d mcphub'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network volumes: postgres_data: mcphub_npm_cache: mcphub_uv_cache: networks: mcphub-network: driver: bridge ``` ### Persisting the stdio package cache `stdio` servers download and install their package on first use: `npx` through npm's caches, `uvx` and other `uv`-based commands through uv's. The image sets no `USER`, so it runs as `root` and those installs land in: | Path | Holds | | - | - | | `/root/.npm/_npx` | packages installed by `npx` | | `/root/.npm/_cacache` | npm's downloaded tarballs | | `/root/.cache/uv` | uv's download cache and the environments `uvx` creates | Environments created by `uv tool install` live in `~/.local/share/uv/tools` instead. Those are baked into the image layer and are not affected by any of this. If you run the image as a non-root user, `~` is not `/root`: adjust both the paths above and the mount targets below to that user's home directory. None of these are volumes by default, so they live in the container's writable layer. That layer is discarded whenever the container is **recreated** — an image bump, `docker compose down && up -d`, or any change to a service's configuration. The next start then reinstalls every `stdio` server from scratch, in parallel, before any of them can complete their MCP handshake. > [!WARNING] > > `docker restart` keeps the writable layer, so it does not lose the cache. > `docker compose down` followed by `up -d` removes and recreates the container, which > does. If restarting appears to reinstall everything, this distinction is usually why. Mounting the two paths keeps the installs on the host: ```yaml theme={null} services: mcphub: volumes: - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv volumes: mcphub_npm_cache: mcphub_uv_cache: ``` Named volumes survive `docker compose down`; only `down -v` removes them. A bind mount (`./data/npm-cache:/root/.npm`) also works and is easier to inspect or clear by hand, but it is not initialised from the image the way a named volume is — it hides whatever the image already holds at that path. On amd64 the `-full` image bakes a Playwright install into `/root/.npm/_npx`, so a bind mount there costs that download once. A few things worth knowing: * **Version pinning is not required.** An unpinned `npx -y ` reuses the package already installed in the cache instead of reinstalling it. npm still revalidates the package metadata against the registry on each run, so the registry (or your proxy) must stay reachable — the cache removes the install cost, not the network round trip. * **The first start after adding the volumes is still cold.** The volume begins empty, so that run pays the full install cost once; every recreate afterwards reuses it. * **Budget for the size.** Around 90 `stdio` servers occupy roughly 2 GB. * **To recover from a corrupted install**, use a server's *Reinstall* action — available for `npx` and `uvx` servers only. For `uvx` it refreshes just that package, through a `--refresh` flag on the next spawn. For `npx` it deletes the whole shared `/root/.npm/_npx` directory, so every npx server reinstalls on its next start, not only the one you targeted. The `_cacache` volume is what keeps that reinstall cheap. This matters most at scale. On a deployment with \~95 `stdio` servers, a cold recreate left 19 of them connected and 74 failing their connect with `MCP error -32001: Request timed out`, because every server was competing to install at once. The same restart with the cache mounted reached 73 connected and no timeouts — though that run also raised `INIT_TIMEOUT` from its 300000 default to 900000, so the two changes have to be read apart. Against the per-minute connect curve they split roughly as: the cache is worth about +45 servers inside the original 300-second budget, the longer timeout about +9. The dominant cost was reinstalling packages, not waiting. ### Production Configuration with Nginx ```yaml theme={null} version: '3.8' services: nginx: image: nginx:alpine container_name: mcphub-nginx ports: - '80:80' - '443:443' volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./ssl:/etc/nginx/ssl:ro - nginx_logs:/var/log/nginx depends_on: - mcphub restart: unless-stopped networks: - mcphub-network mcphub: image: samanhappy/mcphub:latest container_name: mcphub-app expose: - '3000' environment: - NODE_ENV=production - PORT=3000 - TRUST_PROXY=1 - JWT_SECRET=${JWT_SECRET} - DB_URL=postgresql://mcphub:${POSTGRES_PASSWORD}@postgres:5432/mcphub - OPENAI_API_KEY=${OPENAI_API_KEY} - REDIS_URL=redis://redis:6379 volumes: - ./servers.json:/app/servers.json:ro - ./data:/app/data - mcphub_logs:/app/logs - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped networks: - mcphub-network healthcheck: test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://localhost:3000/health'] interval: 30s timeout: 10s retries: 3 postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data - ./backups:/backups healthcheck: test: ['CMD-SHELL', 'pg_isready -U mcphub -d mcphub'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network redis: image: redis:7-alpine container_name: mcphub-redis command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data healthcheck: test: ['CMD', 'redis-cli', 'ping'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network volumes: postgres_data: redis_data: mcphub_logs: mcphub_npm_cache: mcphub_uv_cache: nginx_logs: networks: mcphub-network: driver: bridge ``` ### Environment Variables Create a `.env` file for Docker Compose: ```env theme={null} # Application NODE_ENV=production TRUST_PROXY=1 JWT_SECRET=your-super-secret-jwt-key-change-this # Database POSTGRES_PASSWORD=your-secure-database-password # Redis REDIS_PASSWORD=your-secure-redis-password # External APIs OPENAI_API_KEY=your-openai-api-key # Optional: Custom port # PORT=3000 ``` ## Development Setup ### Development Docker Compose Create `docker-compose.dev.yml`: ```yaml theme={null} version: '3.8' services: mcphub-dev: build: context: . dockerfile: Dockerfile.dev container_name: mcphub-dev ports: - '3000:3000' - '5173:5173' # Frontend dev server - '9229:9229' # Debug port environment: - NODE_ENV=development - PORT=3000 - DB_URL=postgresql://mcphub:password@postgres:5432/mcphub volumes: - .:/app - /app/node_modules - /app/frontend/node_modules depends_on: - postgres command: pnpm dev networks: - mcphub-dev postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres-dev environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=password ports: - '5432:5432' volumes: - postgres_dev_data:/var/lib/postgresql/data networks: - mcphub-dev volumes: postgres_dev_data: networks: mcphub-dev: driver: bridge ``` ### Development Dockerfile Create `Dockerfile.dev`: ```dockerfile theme={null} FROM node:20-alpine # Install pnpm RUN npm install -g pnpm # Set working directory WORKDIR /app # Copy package files COPY package.json pnpm-lock.yaml ./ COPY frontend/package.json ./frontend/ # Install dependencies RUN pnpm install # Copy source code COPY . . # Expose ports EXPOSE 3000 5173 9229 # Start development server CMD ["pnpm", "dev"] ``` ## Running the Application ### Development Mode ```bash theme={null} # Start development environment docker-compose -f docker-compose.dev.yml up -d # View logs docker-compose -f docker-compose.dev.yml logs -f mcphub-dev # Stop development environment docker-compose -f docker-compose.dev.yml down ``` ### Production Mode ```bash theme={null} # Start production environment docker-compose up -d # View logs docker-compose logs -f mcphub # Stop production environment docker-compose down ``` ## Configuration Management ### MCP Settings Volume Mount Before the first launch, create `data/mcp_settings.json`: ```json theme={null} { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] }, "amap": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "your-api-key" } } } } ``` ### Secrets Management For production, use Docker secrets: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest environment: - JWT_SECRET_FILE=/run/secrets/jwt_secret - DATABASE_PASSWORD_FILE=/run/secrets/db_password secrets: - jwt_secret - db_password secrets: jwt_secret: file: ./secrets/jwt_secret.txt db_password: file: ./secrets/db_password.txt ``` ## Data Persistence ### Database Backups Add backup service to your `docker-compose.yml`: ```yaml theme={null} services: backup: image: pgvector/pgvector:pg17 container_name: mcphub-backup environment: - PGPASSWORD=${POSTGRES_PASSWORD} volumes: - ./backups:/backups - ./scripts/backup.sh:/backup.sh:ro command: /bin/sh -c "chmod +x /backup.sh && /backup.sh" depends_on: - postgres profiles: - backup networks: - mcphub-network ``` Create `scripts/backup.sh`: ```bash theme={null} #!/bin/sh BACKUP_FILE="/backups/mcphub_$(date +%Y%m%d_%H%M%S).sql" pg_dump -h postgres -U mcphub -d mcphub > "$BACKUP_FILE" echo "Backup created: $BACKUP_FILE" # Keep only last 7 days of backups find /backups -name "mcphub_*.sql" -mtime +7 -delete ``` Run backup: ```bash theme={null} docker-compose --profile backup run --rm backup ``` ## Monitoring and Health Checks ### Health Check Endpoint Add to your application: ```javascript theme={null} // In your Express app app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), version: process.env.npm_package_version, }); }); ``` ### Docker Health Checks ```yaml theme={null} services: mcphub: # ... other config healthcheck: test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://localhost:3000/health'] interval: 30s timeout: 10s retries: 3 start_period: 60s ``` ### Monitoring with Watchtower Add automatic updates: ```yaml theme={null} services: watchtower: image: containrrr/watchtower container_name: mcphub-watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - WATCHTOWER_CLEANUP=true - WATCHTOWER_POLL_INTERVAL=3600 - WATCHTOWER_INCLUDE_STOPPED=true restart: unless-stopped ``` ## Troubleshooting ### Common Issues **Container fails to start**: Check logs with `docker-compose logs mcphub` **Rate-limit warning about `X-Forwarded-For`**: Set `TRUST_PROXY=1` when MCPHub is behind Nginx, Traefik, a load balancer, or another reverse proxy. Recent versions also default to trusting one proxy automatically inside Docker/Kubernetes, but keeping it explicit in production compose files is recommended. **Database connection errors**: Ensure PostgreSQL is healthy and accessible **Port conflicts**: Check if ports 3000/5432 are already in use **Volume mount issues**: Verify file paths and permissions ### Debug Commands ```bash theme={null} # Check container status docker-compose ps # View logs docker-compose logs -f [service_name] # Execute commands in container docker-compose exec mcphub sh # Check database connection docker-compose exec postgres psql -U mcphub -d mcphub # Restart specific service docker-compose restart mcphub # Rebuild and restart docker-compose up --build -d ``` ### Performance Optimization ```yaml theme={null} services: mcphub: # ... other config deploy: resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' ``` This Docker setup provides a complete containerized environment for MCPHub with development and production configurations. # Environment Variables Source: https://docs.mcphub.app/configuration/environment-variables Configure MCPHub using environment variables MCPHub reads the following environment variables at startup. The authoritative list is whatever appears under `process.env.*` in `src/`; this page summarizes everything currently read by the code on `main`. ## Core application | Variable | Default | Description | | - | - | - | | `PORT` | `3000` | HTTP server port. | | `BASE_PATH` | `''` | Mount-point prefix when MCPHub is served under a sub-path (e.g. `/mcphub`). | | `SMART_ROUTING_PATH` | `$smart` | Smart Routing path segment, e.g. `my_smart` for `/mcp/my_smart`. Use 1–64 ASCII letters, digits, hyphens or underscores. Restart required. The legacy `$smart` endpoint remains available. Server/group names and group IDs must not conflict with this value. | | `DEFAULT_REQUEST_TIMEOUT` | `60000` | Default request timeout (ms) for upstream MCP server requests when a server does not set `options.timeout`. | | `INIT_TIMEOUT` | `300000` | Initial timeout (ms) used when starting upstream MCP servers. | | `STARTUP_CONNECT_CONCURRENCY` | `0` | How many upstream servers may connect at once during startup. `0` (or unset) connects them all in parallel. On deployments with many `stdio` servers, a bound keeps them from competing for CPU, network and disk hard enough to exhaust `INIT_TIMEOUT`. Time spent queueing does not count against a server's timeout. | | `READONLY` | `false` | Reject mutating dashboard API requests. `GET` and `${basePath}/tools/*` remain allowed. | | `DISABLE_WEB` | `false` | Skip serving the bundled dashboard. The API and MCP transport routes still run. | | `TRUST_PROXY` | auto | Express `trust proxy` setting. Accepts `true`, `false`, a hop count (`1`, `2`, …), or names like `loopback,linklocal,uniquelocal`. Defaults to `1` inside Docker/Kubernetes, `false` otherwise. | | `MCPHUB_SETTING_PATH` | `mcp_settings.json` | Override the path of the settings file used by file-mode storage. | | `INSTALL_BASE_URL` | `http://localhost:3000` | Public installation base URL used when `systemConfig.install.baseUrl` is unset. The dashboard/API setting still takes precedence; this env var is useful for declarative Docker/Kubernetes deployments and OAuth callback URLs. | | `NODE_ENV` | `development` | `development` / `production` / `test`. | | `ENABLE_METRICS` | `false` | Enable the root `/metrics` Prometheus endpoint. | | `METRICS_TOKEN` | unset | Dedicated bearer token for metrics scraping. Without it, an enabled endpoint is public; restrict network access. | | `DEBUG` | `false` | Set to `true` to enable verbose startup logging for frontend-path discovery. | ### Custom Smart Routing URL For example, with `PORT=6001`, `BASE_PATH=/mcp_manage` and `SMART_ROUTING_PATH=my_smart`, connect using Streamable HTTP at `http://localhost:6001/mcp_manage/mcp/my_smart`. Append `/` to restrict Smart Routing to a group, or prepend `/` before `/mcp` for a user-scoped route. The same alias also works under `/sse` for legacy clients. The configured segment is reserved for Smart Routing. Startup rejects existing server/group name or group ID conflicts, and configuration writes reject new conflicts. Change the segment or rename the conflicting target first. Changing the segment requires a restart and updating clients using the previous custom URL; `$smart` remains available for compatibility, including CLI calls. This setting does not change authentication or permissions. ## Authentication | Variable | Default | Description | | - | - | - | | `ADMIN_PASSWORD` | random (`admin123` in `pnpm dev`) | Password seeded for the bootstrap `admin` user when no users exist yet. If unset, `pnpm dev` uses `admin123`; other starts generate a 24-character random password and print it to the logs. | | `JWT_SECRET` | random per-process | Secret used to sign JWTs issued by `/api/auth/login`. See the warning below — this **must** be set explicitly in any non-development deployment. | > [!WARNING] > > **`JWT_SECRET` must be set to a persistent, high-entropy value (≥ 32 random bytes) in staging and production.** If it is left unset, MCPHub generates a new random secret on every process start, which means: every restart invalidates all outstanding sessions and bearer tokens; horizontally-scaled instances cannot share sessions because each replica signs with its own secret; and CI / container restarts silently log everyone out. Generate one with `openssl rand -hex 32` and store it in your secret manager. Treat it like any other long-lived cryptographic key — rotation requires invalidating outstanding JWTs. ### Better Auth (optional social login) Better Auth is initialized once at process startup in `src/betterAuth.ts`. Because of that, changing any Better Auth setting still requires a restart. For non-secret Better Auth settings, MCPHub resolves values in this order: 1. `BETTER_AUTH_*` environment variables 2. `systemConfig.auth.betterAuth` (from `mcp_settings.json` or the database-backed system config) 3. Built-in defaults Provider client credentials remain environment-variable only. > [!NOTE] > > Better Auth currently requires PostgreSQL-backed storage in MCPHub. In practice that means `DB_URL` must be configured; file-only mode does not support Better Auth sessions. | Variable | Description | | - | - | | `BETTER_AUTH_ENABLED` | Master switch for Better Auth. Better Auth is still disabled automatically when MCPHub is not in database mode or when no provider has the required credentials. | | `BETTER_AUTH_URL` | Public base URL used to build Better Auth redirect URIs. Takes precedence over `systemConfig.auth.betterAuth.baseUrl` (configurable in Settings → Better Auth) and `systemConfig.install.baseUrl`. Its origin is also auto-added to the trusted origin list. | | `BETTER_AUTH_BASE_PATH` | Mount path for the Better Auth handler. Defaults to `/api/auth/better`. | | `BETTER_AUTH_TRUSTED_ORIGINS` | Optional additional origins allowed to start Better Auth login flows. Accepts a comma-separated list, whitespace-separated list, or JSON array. When omitted, MCPHub still auto-trusts the origins from `BETTER_AUTH_URL` and `systemConfig.install.baseUrl`. | | `BETTER_AUTH_DISABLE_PASSWORD_LOGIN` | Turn off username/password login (`POST /api/auth/login` and the login form) while at least one SSO provider is enabled (and was enabled at startup). Overrides the stored `disablePasswordLogin` setting in both directions, so `false` is also the recovery switch when the identity provider is unavailable. Defaults to `false`. | | `BETTER_AUTH_GOOGLE_ENABLED` | Enable or disable the Google provider after credentials are present. Defaults to `true`. | | `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | Google OAuth app credentials. | | `BETTER_AUTH_GITHUB_ENABLED` | Enable or disable the GitHub provider after credentials are present. Defaults to `true`. | | `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | GitHub OAuth app credentials. | | `BETTER_AUTH_OIDC_ENABLED` | Enable or disable the generic OIDC provider. Defaults to `false`. | | `BETTER_AUTH_OIDC_PROVIDER_ID` | Provider identifier passed to Better Auth's generic OAuth plugin. Defaults to `oidc`. | | `BETTER_AUTH_OIDC_DISCOVERY_URL` | Discovery URL for a local OIDC issuer (for example Keycloak, Authentik, or Dex). | | `OIDC_DISCOVERY_URL` | Legacy alias for `BETTER_AUTH_OIDC_DISCOVERY_URL`. It is also still useful for `${OIDC_DISCOVERY_URL}` interpolation inside `mcp_settings.json`. | | `BETTER_AUTH_OIDC_SCOPES` | Optional requested scopes for the local OIDC provider. Accepts a comma-separated list, whitespace-separated list, or JSON array. Defaults to `openid profile email`. | | `BETTER_AUTH_OIDC_PKCE` | Enable or disable PKCE for the local OIDC provider. Defaults to `true`. | | `BETTER_AUTH_OIDC_PROMPT` | Optional `prompt` parameter for the local OIDC provider (`login`, `consent`, `select_account`, etc.). | | `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | Client credentials for a local OIDC provider such as Keycloak, Authentik, or Dex. | You can still store the non-secret Better Auth settings in `systemConfig.auth.betterAuth`, but `BETTER_AUTH_*` variables now override those values at startup. A full env-only local OIDC example looks like this: ```env theme={null} BETTER_AUTH_ENABLED=true BETTER_AUTH_URL=https://mcphub.example.com BETTER_AUTH_BASE_PATH=/api/auth/better BETTER_AUTH_TRUSTED_ORIGINS=https://dashboard.example.com,https://admin.example.com BETTER_AUTH_OIDC_ENABLED=true BETTER_AUTH_OIDC_PROVIDER_ID=local-oidc BETTER_AUTH_OIDC_DISCOVERY_URL=https://auth.example.com/.well-known/openid-configuration BETTER_AUTH_OIDC_SCOPES=openid,profile,email BETTER_AUTH_OIDC_PKCE=true BETTER_AUTH_OIDC_PROMPT=login OIDC_CLIENT_ID=your-oidc-client-id OIDC_CLIENT_SECRET=your-oidc-client-secret ``` ### Forward auth Forward auth trusts a JWT issued to users by an upstream gateway and verifies it against the identity provider's JWKS. See [Authentication → Forward auth](/features/authentication#forward-auth-jwks-verified-gateway-identity) for the behavior. Values resolve in this order: `FORWARD_AUTH_*` environment variables, then `systemConfig.auth.forwardAuth`, then built-in defaults. Settings are read per request, so no restart is needed. | Variable | Description | | - | - | | `FORWARD_AUTH_ENABLED` | Enable forward auth. Defaults to `false`. | | `FORWARD_AUTH_MODE` | Verification mode. Only `jwks` is supported. | | `FORWARD_AUTH_JWKS_URI` | JWKS endpoint of the identity provider. Must be `https` (plain `http` only for `localhost`). Required. | | `FORWARD_AUTH_ISSUER` | Expected `iss` claim. Required. | | `FORWARD_AUTH_AUDIENCE` | Accepted `aud` value(s). Accepts a comma-separated list or JSON array; values may contain spaces (for example `My API`). Required. | | `FORWARD_AUTH_ALGORITHMS` | Allowed asymmetric algorithms. Defaults to `RS256,ES256`. `HS*` and `none` are never accepted. | | `FORWARD_AUTH_USERNAME_CLAIM` | Claim used for the username of newly created users. Defaults to `preferred_username`. | | `FORWARD_AUTH_EMAIL_CLAIM` | Claim used for the email of newly created users. Defaults to `email`. | | `FORWARD_AUTH_AUTO_CREATE` | Create a non-admin local user for a new identity. Defaults to `true`. | ## Storage MCPHub picks file or database storage at boot: ``` useDatabase = (USE_DB === 'true') OR (USE_DB unset AND DB_URL is set) ``` | Variable | Default | Description | | - | - | - | | `USE_DB` | unset | Force database mode (`true`) or file mode (`false`). | | `DB_URL` | unset | PostgreSQL connection URL. Required for database mode. | | `DB_POOL_SIZE` | `10` | Maximum connections **per pool**. Applied independently to the TypeORM pool and to the Better Auth pool, so it is not a process-wide cap — see the note below. | | `DB_POOL_IDLE_TIMEOUT` | `30000` | Idle pool connection timeout (ms). | | `DB_CONNECTION_TIMEOUT` | `60000` | Initial connect timeout (ms). | | `DB_CONNECTION_RETRY_DELAY` | `3000` | Base delay between automatic reconnection attempts (ms). | | `DB_MAX_CONNECTION_RETRIES` | `5` | Maximum automatic reconnection attempts. | | `DB_DESTROY_TIMEOUT` | `15000` | Timeout (ms) for tearing down the old pool during reconnection. Prevents a connection stuck in `idle in transaction` from wedging the service (#1205). | | `DB_STATEMENT_TIMEOUT` | `0` | Per-connection `statement_timeout` (ms) sent to PostgreSQL; `0` keeps the PostgreSQL default (disabled). | | `DB_IDLE_IN_TRANSACTION_TIMEOUT` | `0` | Per-connection `idle_in_transaction_session_timeout` (ms) sent to PostgreSQL; `0` keeps the PostgreSQL default (disabled). | | `DB_ENABLE_HEALTH_CHECK` | `true` | Run periodic DB health probes (consumed by `/health`). | | `DB_HEALTH_CHECK_INTERVAL` | `30000` | Health-probe interval (ms). | | `DB_HEALTH_CHECK_QUERY_TIMEOUT` | `5000` | Timeout (ms) for the `SELECT 1` health probe. | > [!NOTE] > > **`DB_POOL_SIZE` is a per-pool limit.** MCPHub opens one pool for TypeORM and, when Better Auth is enabled, a second one for Better Auth, each sized to `DB_POOL_SIZE`. When both are in use, budget roughly **2 × `DB_POOL_SIZE`** PostgreSQL connections per replica (for example `DB_POOL_SIZE=20` → about 40), plus headroom for other clients and ops tooling against the same database. > > This is the combined *configured* pool capacity, not a strict process-wide cap. During an automatic reconnection, the old pool is torn down with `DB_DESTROY_TIMEOUT`; if that teardown times out, connections from the old pool may linger briefly while the new pool is already open. See [Database Configuration](/configuration/database-configuration) for end-to-end setup. ## Smart Routing & embeddings Smart Routing performs vector search over upstream tools to surface only the few most relevant tools for a given query. It requires Postgres + pgvector and an embedding endpoint. | Variable | Default | Description | | - | - | - | | `SMART_ROUTING_ENABLED` | `false` | Canonical master switch for Smart Routing. `ENABLE_SMART_ROUTING` is accepted as a legacy alias; when both are set, this variable takes precedence. | | `SMART_ROUTING_EMBEDDING_PROVIDER` | `openai` | Embedding provider. Currently `openai` or `azure_openai`. | | `SMART_ROUTING_EMBEDDING_ENCODING_FORMAT` | provider default | Override the `encoding_format` passed to the embedding API. | | `SMART_ROUTING_BASE_PACING_DELAY_MS` | `0` | Base delay (ms) applied between embedding requests to avoid rate limits. | | `SMART_ROUTING_PROGRESSIVE_DISCLOSURE` | unset | When set, expose progressively-disclosed tool metadata (see Smart Routing docs). | | `SMART_ROUTING_SERVER_DESCRIPTION_MODE` | `names` | Control how `search_tools` lists available servers: `names` for server names only, `full` to include server descriptions/instructions when available. | | `EMBEDDING_MODEL` | `text-embedding-3-small` | Embedding model name. `OPENAI_API_EMBEDDING_MODEL` is accepted as a legacy alias; when both are set, this variable takes precedence. | | `EMBEDDING_DIMENSIONS` | unset | Optional output dimensionality for providers that support it. When unset, the provider's model default is used. | | `EMBEDDING_DIMENSIONS_API_PASSTHROUGH` | `false` | When `true`, always forward `embeddingDimensions` to the provider as the `dimensions` request parameter. By default MCPHub only forwards `dimensions` for models known to support Matryoshka (MRL) representations (`text-embedding-3-*`, `gemini-embedding-*`); many open models/backends (Qwen3-Embedding, BGE, vLLM/sglang) reject the parameter outright (issue #1131). Enable only for other MRL-capable models. | | `EMBEDDING_MAX_TOKENS` | per-model | Override token truncation limit before embedding. Useful to match a local inference server's batch size. | | `SMART_ROUTING_EMBEDDING_QUERY_PREFIX` | empty | Text prepended to `search_tools` queries before embedding, for asymmetric retrieval models trained with task prefixes (e.g. `task: search result \| query: ` for EmbeddingGemma). Used verbatim, including the trailing space. | | `SMART_ROUTING_EMBEDDING_DOCUMENT_PREFIX` | empty | Text prepended to tool and server documents before embedding (e.g. `title: none \| text: ` for EmbeddingGemma). Changing it re-embeds every server. | | `SMART_ROUTING_SIMILARITY_THRESHOLD` | unset | Minimum query-to-tool similarity (0-1) for a `search_tools` hit. Unset keeps the query-length heuristic (0.2 / 0.3 / 0.4). | | `SMART_ROUTING_FULL_SCHEMA_TOP_N` | unset | Only the first N `search_tools` hits carry their full definition; the rest carry name, description and score, and `describe_tool` is listed to fetch them. Unset returns every hit in full. Ignored with Progressive Disclosure. | | `SMART_ROUTING_TOOL_DEFINITION_FIELDS` | `title,annotations` | Comma-separated optional tool fields that full `search_tools` hits and `describe_tool` carry next to name, description and `inputSchema`: `title`, `annotations`, `outputSchema`, `execution`, `icons`, `_meta`. `none` sends none of them. | ### OpenAI | Variable | Description | | - | - | | `OPENAI_API_KEY` | API key. | | `OPENAI_API_BASE_URL` | Override the base URL (set this for any OpenAI-compatible endpoint, e.g. LocalAI). | ### Azure OpenAI | Variable | Description | | - | - | | `AZURE_OPENAI_API_KEY` | API key. | | `AZURE_OPENAI_ENDPOINT` | Resource endpoint (e.g. `https://my-resource.openai.azure.com`). | | `AZURE_OPENAI_API_VERSION` | API version (e.g. `2024-02-01`). | | `AZURE_OPENAI_EMBEDDING_DEPLOYMENT` | Deployment name. | | `AZURE_OPENAI_EMBEDDING_MODEL` | The underlying OpenAI model the deployment maps to (e.g. `text-embedding-3-small`). Used for tokenizer and limit selection. | > [!NOTE] > > **Environment variables win over the dashboard.** Every setting above overrides the matching field saved in the dashboard. Resolution order is **environment variable → dashboard setting → built-in default**, so a stale value left in `.env` silently shadows a key you just pasted into the UI — embeddings then fail with an authentication error while the form still shows the new key (issue #642). > > The dashboard now reports this: affected fields carry a warning naming the environment variable that is actually in use. To switch to the dashboard value, remove the variable from the environment and restart. > > `Smart Routing → Enabled` and `DB URL` follow the same order but are reflected directly by the UI instead of a warning — the toggle mirrors the env var, and the URL field renders a `${DB_URL}` placeholder. ## MCPRouter (optional upstream catalog) | Variable | Description | | - | - | | `MCPROUTER_API_KEY` | API key for the MCPRouter cloud catalog. | | `MCPROUTER_API_BASE` | Override the API base URL (default `https://api.mcprouter.to/v1`). | | `MCPROUTER_REFERER` | Optional `Referer` header sent on MCPRouter requests. | | `MCPROUTER_TITLE` | Optional `Title` header sent on MCPRouter requests. | ## Tool call guard (optional) | Variable | Default | Description | | - | - | - | | `TOOL_CALL_GUARD_ENABLED` | `false` | When set to exactly `true`, `tools/call` requests are inspected before being forwarded. Calls whose arguments contain high-confidence command-injection / reverse-shell patterns or cloud instance-metadata (credential-theft) targets are rejected fail-closed. Benign arguments are unaffected. Any other value leaves the guard off. | ## Configuration examples ### Development ```env theme={null} NODE_ENV=development PORT=3000 JWT_SECRET=dev-secret-key ``` ### Production with database + Smart Routing ```env theme={null} NODE_ENV=production PORT=3000 BASE_PATH= JWT_SECRET=replace-with-32+-random-bytes USE_DB=true DB_URL=postgresql://mcphub:secret@db:5432/mcphub SMART_ROUTING_ENABLED=true OPENAI_API_KEY=sk-... EMBEDDING_MODEL=text-embedding-3-small ``` ## Loading order At startup MCPHub reads one file, `.env` in the working directory. `.env.local` and `.env.{NODE_ENV}` (e.g. `.env.production`) are not read. A variable already set in the process environment (your shell, Docker `-e` / `env_file`, a Kubernetes manifest) takes precedence; `.env` never overrides it. Values in `.env` are used as written: `${VAR}` references inside `.env` are not expanded. `${VAR}` expansion is available in `mcp_settings.json` instead; see [MCP Settings](/configuration/mcp-settings). ## Security best practices 1. Never commit secrets — `.env*` files are gitignored by default; keep it that way. 2. Always set `JWT_SECRET` explicitly in production. 3. Rotate bearer keys (`/api/auth/keys`) and OAuth client secrets periodically. 4. Use Docker / Kubernetes secrets for container deployments instead of plain `-e`. # MCP Settings Configuration Source: https://docs.mcphub.app/configuration/mcp-settings Configure MCP servers and their settings for MCPHub # MCP Settings Configuration This guide explains how to configure MCP servers in MCPHub using the `mcp_settings.json` file and related configurations. ## Configuration Files Overview MCPHub uses several configuration files: * **`mcp_settings.json`**: Your personal configuration of active MCP servers and system config * **`servers.json`**: Metadata cataloging hundreds of available MCP servers * **`.env`**: Environment variables and secrets ## Basic MCP Settings Structure ### mcp\_settings.json ```json theme={null} { "mcpServers": { "server-name": { "command": "command-to-run", "args": ["arg1", "arg2"], "env": { "ENV_VAR": "value" } } } } ``` ### Example Configuration ```json theme={null} { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "env": { "USER_AGENT": "MCPHub/1.0" } }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] }, "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}", "SLACK_TEAM_ID": "${SLACK_TEAM_ID}" } } } } ``` ## Server Configuration Options A server entry has the shape `"": ServerConfig`. The relevant fields depend on the transport, which is selected by `type`. ### Top-level fields | Field | Type | Required | Description | | - | - | - | - | | `type` | `"stdio" \| "sse" \| "streamable-http" \| "openapi"` | Recommended | Transport. Defaults to `stdio` when omitted but a `command` is provided. | | `enabled` | boolean | no | Defaults to `true`. Disabled servers stay in settings but are not connected. | | `owner` | string | no | Username that owns the entry. Defaults to the creating user or `admin`. Used for user-scoped routes (`/:user/mcp/...`). | | `visibility` | `"private" \| "group" \| "public"` | no | Runtime access for non-admin users. Defaults to `private`; `group` grants access to `sharedWithUsers`. | | `sharedWithUsers` | string\[] | no | Local usernames allowed to discover and call a server whose `visibility` is `group`. Ignored for other visibility values. | | `description` | string | no | Free-form description displayed in the dashboard. | | `options` | object | no | Per-server runtime options (see `src/types/index.ts`). | | `proxy` | object | no | Optional HTTP proxy settings (`url`, `noProxy`, etc.). | | `oauth` | object | no | Upstream OAuth client settings (used when the upstream server requires OAuth). | | `enableKeepAlive` | boolean | no | For `sse` / `streamable-http`: enable periodic health checks. Disconnected remote servers always retry automatically with exponential backoff and jitter (5 seconds to 5 minutes); this interval only controls health probes. | | `keepAliveInterval` | number | no | Health check interval in milliseconds. Defaults to `60000`. | | `perSessionClient` | boolean | no | Create a dedicated upstream client per downstream MCP session instead of sharing one connection across all sessions. Use for stateful servers like Playwright. See [Per-session client isolation](#per-session-client-isolation-stateful-servers). | | `tools`, `prompts`, `resources` | object | no | Per-item overrides: `{ "": { "enabled": true, "description": "..." } }`, keyed by the bare upstream name or the server-prefixed name. A tool entry can also carry `hiddenParams`: top-level input parameters removed from the schema models receive and dropped from their calls (required ones are never hidden). `enabled` is required in every entry. Managed via the dashboard or the per-resource API endpoints. | ### `type: "stdio"` (default) | Field | Type | Required | Description | | - | - | - | - | | `command` | string | yes | Executable to spawn. | | `args` | string\[] | no | Command-line arguments. | | `env` | object | no | Extra environment variables for the child process. `${ENV}` placeholders in values are expanded against the MCPHub process environment. | ### `type: "sse"` or `type: "streamable-http"` | Field | Type | Required | Description | | - | - | - | - | | `url` | string | yes | Upstream endpoint URL. | | `headers` | object | no | Static headers sent on every request. | | `passthroughHeaders` | string\[] | no | Inbound HTTP header names to forward from the caller to the upstream server. | ### `type: "openapi"` | Field | Type | Required | Description | | - | - | - | - | | `openapi.url` | string | one of `url`/`schema` | URL of an OpenAPI document MCPHub will download. | | `openapi.schema` | object | one of `url`/`schema` | Inline OpenAPI document. | | `headers` | object | no | Static headers applied to outbound calls, including the spec download itself. | | `openapi.passthroughHeaders` | string\[] | no | Headers to forward from the caller. | The authoritative definition of every field is the `ServerConfig` type in [src/types/index.ts](src/types/index.ts) — consult it if you need behavior not described here. > [!NOTE] > > A protected `openapi.url` is downloaded through MCPHub's authenticated client, so the > configured `headers` and OpenAPI `security` credentials (HTTP Basic/Bearer, API key, > OAuth2 token) authenticate the fetch. Credentials are scoped to the document origin only — > they are **not** forwarded to external `$ref` resolutions or cross-origin redirects. If a > protected document references external `$ref`s that also require auth, inline the document > via `openapi.schema` instead. ## Common MCP Server Examples ### Web and API Servers #### Fetch Server ```json theme={null} { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "env": { "USER_AGENT": "MCPHub/1.0", "MAX_REDIRECTS": "10" } } } ``` #### Web Scraping with Playwright ```json theme={null} { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"], "timeout": 60000, "env": { "PLAYWRIGHT_BROWSERS_PATH": "/tmp/browsers" } } } ``` ### Per-session client isolation (stateful servers) By default MCPHub keeps a **single** upstream connection per server and shares it across every downstream MCP session. For stateful servers this means all clients share the same state — with Playwright, for example, every session drives the same browser instance and its tabs. Set `perSessionClient: true` to make MCPHub open a **dedicated upstream client per downstream session** instead. Each session gets its own connection (and, for stdio servers, its own child process), created lazily on first tool call and torn down automatically when the session ends. > [!NOTE] > > `perSessionClient` is a **MCPHub-side** setting: it controls how many upstream > connections MCPHub opens. It is distinct from Playwright MCP's own `--isolated` > flag, which controls browser-profile isolation on the Playwright side. The two > pair naturally — enable both for fully isolated browser sessions. ```json theme={null} { "mcpServers": { "playwright": { "type": "streamable-http", "url": "http://playwright-mcp:3000/mcp", "perSessionClient": true } } } ``` Or with a locally spawned Playwright MCP using its `--isolated` flag: ```json theme={null} { "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless", "--isolated"], "perSessionClient": true } } } ``` > [!TIP] > > Enable `perSessionClient` for servers that hold per-session state or need > [legacy tool-call elicitation forwarding](/api-reference/mcp-http#elicitation-forwarding). > It multiplies upstream connections (and stdio child processes) by the number of > concurrent sessions, so leave it off when neither feature is needed. ### Upstream protocol negotiation Streamable HTTP upstreams default to `protocolNegotiation: "auto"`. MCPHub probes `server/discover` and uses modern stateless HTTP when supported; legacy servers fall back to the existing handshake. HTTP probe timeouts and network or authentication failures can fail the connection rather than trigger fallback. To skip discovery for a known legacy endpoint, configure: ```json theme={null} { "mcpServers": { "legacy-gateway": { "type": "streamable-http", "url": "https://gateway.example.com/mcp", "protocolNegotiation": "legacy" } } } ``` The optional field accepts `"auto"` or `"legacy"`. `"auto"` requires `type: "streamable-http"`; stdio and legacy SSE retain legacy negotiation. Configure this advanced option through settings JSON, JSON import or the server API; dashboard form edits and duplication preserve it. It is supported in both file and database storage, templates and declarative settings sync. Editing it reconnects the server. Shared, isolated and personal connections use the same policy, except isolated connections forwarding legacy elicitation remain legacy until a modern `input_required` bridge is available. Modern upstreams have no server-push stream. Lists are revalidated on relevant requests after their upstream TTL expires, with concurrent refreshes coalesced. Missing, zero or unusable TTLs and partial pages are revalidated on the next relevant request. Health checks remain opt-in via `enableKeepAlive`; modern connections probe `server/discover` over the network instead of `ping`. See the [compatibility matrix](/api-reference/mcp-http#protocol-compatibility). Shared remote SSE and Streamable HTTP servers retry in the background after a connection failure or a closed connection, even when health checks are disabled. Retries use exponential backoff with jitter, from 5–10 seconds initially up to 150–300 seconds, without a retry limit. Successful connections reset the backoff; disabled servers and servers waiting for OAuth authorization do not retry. With health checks disabled, an outage is detected when a request fails or the connection closes. The dashboard shows the next retry and the last retry time/count through the runtime `recovery` status (`attempts`, `lastAttemptAt`, and `nextAttemptAt`; timestamps are epoch milliseconds). For modern upstreams with `passthroughHeaders`, discovery clients and snapshots are request-local rather than shared across forwarded identities. Each relevant request adds a discovery round trip; those descriptors are not published to the global cache or vector index. If an upstream refresh fails, only that upstream is excluded from the current request; healthy results remain available and partial lists have zero TTL. Later requests retry the failed upstream. ### Routing headers over MCP 2026-07-28 Modern requests use `MCP-Protocol-Version: 2026-07-28` and `Mcp-Method` matching the JSON-RPC method. `tools/call` and `prompts/get` also require `Mcp-Name` matching `params.name`; `resources/read` requires it to match `params.uri`. List methods do not require `Mcp-Name`. The SDK v2 HTTP client sets these headers automatically. For example, a tool call to `/mcp` with `params.name` set to `my-server-my-tool` sends: ```http theme={null} MCP-Protocol-Version: 2026-07-28 Mcp-Method: tools/call Mcp-Name: my-server-my-tool ``` These headers accompany the normal JSON-RPC body, including the modern `params._meta` protocol-version and client-capability envelope. They do not select a group or grant access: the URL and bearer permissions still determine the available servers. On a server-specific URL, use the tool name exposed by that URL in both the body and header. Non-ASCII names use the SDK's `=?base64??=` encoding in `Mcp-Name`. Missing required headers, header/body mismatches and invalid name encodings return HTTP 400 with JSON-RPC error `-32020` before dispatch. Legacy `2025-11-25` requests retain their existing handshake and session behavior and do not require these routing headers. ### Stateful tools over MCP 2026-07-28 Modern MCP requests do not create a downstream transport session. To call tools on servers with `perSessionClient: true` or OpenAPI `cookieSession: true`, enable bearer authentication and send `X-MCPHub-State-Id` with a client-generated UUID v4 on each request that belongs to the same application workflow. For example: ```http theme={null} Authorization: Bearer X-MCPHub-State-Id: 65b523af-e4d0-4d99-8e06-461a62a14967 ``` This is an MCPHub extension, independent of `Mcp-Session-Id` and the MCP handshake. The same state ID reuses isolated upstream clients and cookie jars only within the same bearer credential, user and route. Use a different UUID for each independent workflow. Authenticated OpenAI/ChatGPT tool calls that cannot set this extension may omit it and send the client-declared `_meta["openai/session"]` conversation hint that ChatGPT includes with tool calls. MCPHub hashes the hint before using it for state correlation. The hint never authenticates or authorizes a request, and reuse is still bounded by the verified bearer credential, user and route. If `X-MCPHub-State-Id` is present but invalid, MCPHub fails closed instead of falling back. If neither usable correlation value is present, stateful tools return a tool error. Other tools need no state ID. State lives in memory on one MCPHub process. It expires after 30 minutes without an active call; running calls pin their state until completion. Expiry and shutdown close isolated upstream clients and clear cookie jars. The process admits at most 1,000 modern state scopes; excess scopes receive a tool error until capacity is available. Restart, expiry, bearer rotation or a route change starts fresh state. Use sticky routing when running multiple replicas. Legacy `2025-11-25` clients continue using their existing downstream session lifecycle. ### File and System Servers #### Filesystem Server ```json theme={null} { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"], "env": { "ALLOWED_OPERATIONS": "read,write,list" } } } ``` #### SQLite Server ```json theme={null} { "sqlite": { "command": "uvx", "args": ["mcp-server-sqlite", "--db-path", "/path/to/database.db"], "env": { "SQLITE_READONLY": "false" } } } ``` ### Communication Servers #### Slack Server ```json theme={null} { "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}", "SLACK_TEAM_ID": "${SLACK_TEAM_ID}", "SLACK_APP_TOKEN": "${SLACK_APP_TOKEN}" } } } ``` ### Development and API Servers #### GitHub Server ```json theme={null} { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" } } } ``` ### Map and Location Services #### Amap (高德地图) Server ```json theme={null} { "amap": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "${AMAP_API_KEY}" } } } ``` > [!TIP] > > Looking for more servers? The bundled [Market catalog](https://docs.mcphub.app/features/server-management) in the dashboard lists hundreds of known MCP servers with copy-paste-ready configurations. ## Advanced Configuration ### Environment Variable Substitution MCPHub supports environment variable substitution using `${VAR_NAME}` syntax: ```json theme={null} { "mcpServers": { "api-server": { "command": "python", "args": ["-m", "api_server"], "env": { "API_KEY": "${API_KEY}", "API_URL": "${API_BASE_URL}/v1" } } } } ``` ### Proxy Configuration (proxychains4) MCPHub supports routing STDIO server network traffic through a proxy using **proxychains4**. This feature is available on **Linux and macOS only** (Windows is not supported). > [!NOTE] > > To use this feature, you must have `proxychains4` installed on your system: > > * **Debian/Ubuntu**: `apt install proxychains4` > * **macOS**: `brew install proxychains-ng` > * **Arch Linux**: `pacman -S proxychains-ng` #### Basic Proxy Configuration ```json theme={null} { "mcpServers": { "fetch-via-proxy": { "command": "uvx", "args": ["mcp-server-fetch"], "proxy": { "enabled": true, "type": "socks5", "host": "127.0.0.1", "port": 1080 } } } } ``` #### Proxy Configuration Options | Field | Type | Default | Description | | - | - | - | - | | `enabled` | boolean | `false` | Enable/disable proxy routing | | `type` | string | `socks5` | Proxy protocol: `socks4`, `socks5`, or `http` | | `host` | string | - | Proxy server hostname or IP address | | `port` | number | - | Proxy server port | | `username` | string | - | Proxy authentication username (optional) | | `password` | string | - | Proxy authentication password (optional) | | `configPath` | string | - | Path to custom proxychains4 config file | #### Proxy with Authentication ```json theme={null} { "mcpServers": { "secure-server": { "command": "npx", "args": ["-y", "@example/mcp-server"], "proxy": { "enabled": true, "type": "http", "host": "proxy.example.com", "port": 8080, "username": "${PROXY_USER}", "password": "${PROXY_PASSWORD}" } } } } ``` #### Using Custom proxychains4 Configuration For advanced use cases, you can provide your own proxychains4 configuration file: ```json theme={null} { "mcpServers": { "custom-proxy-server": { "command": "python", "args": ["-m", "custom_mcp_server"], "proxy": { "enabled": true, "configPath": "/etc/proxychains4/custom.conf" } } } } ``` > [!TIP] > > When `configPath` is specified, all other proxy settings (`type`, `host`, `port`, etc.) are ignored, and the custom configuration file is used directly. ## Group Management ### Group Configuration ```json theme={null} { "groups": { "production": { "name": "Production Tools", "description": "Stable production servers", "servers": ["fetch", "slack", "github"] }, "experimental": { "name": "Experimental Features", "description": "Beta and experimental servers", "servers": ["experimental-ai", "beta-search"] } } } ``` ## Editing mcp\_settings.json while running In file mode, MCPHub re-reads `mcp_settings.json` when it changes, without a restart: * **Server definitions and credentials** are picked up on the next server reload — `POST /api/servers/:name/reload`, the `reload` button in the dashboard, or `mcphub servers reload `. * **System settings** (`systemConfig`, e.g. `nameSeparator`) are picked up on the next read once the file has changed. * Settings that are read once at process startup still require a restart to take effect: the Better Auth bootstrap, `skipAuth`, the listening port, and anything driven by environment variables. In database mode, `mcp_settings.json` is a one-time seed by default. Set `MCPHUB_SETTINGS_SYNC=upsert` to apply server and group declarations on each startup, or `prune` to also delete removed managed entries; see [declarative database configuration](/configuration/database-configuration#declarative-servers-and-groups-gitops) for precedence and OAuth state handling. ## System Routing Settings You can also configure runtime routing and authentication behavior under `systemConfig.routing` in `mcp_settings.json`: ```json theme={null} { "systemConfig": { "routing": { "enableBearerAuth": true, "bearerAuthHeaderName": "X-MCP-Authorization", "jsonBodyLimit": "5mb", "skipAuth": false } } } ``` * `bearerAuthHeaderName` defaults to `Authorization` and controls which incoming header MCPHub reads for Bearer authentication. * Use a custom header such as `X-MCP-Authorization` when an upstream MCP/OpenAPI server also needs to passthrough `Authorization`. * `jsonBodyLimit` defaults to `1mb` and controls the Express JSON parser limit for API requests, including large inline OpenAPI schema uploads. ## Tool Result Compression MCPHub can transparently compress large text tool results before returning them to MCP clients. This is useful when upstream tools return large logs, diffs, search output, JSON arrays, or long text blobs that would otherwise consume too much model context. Tool result compression is disabled by default. Enable and tune it from **Settings → Tool Result Compression**, or configure it under `systemConfig.toolResultCompression`: ```json theme={null} { "systemConfig": { "toolResultCompression": { "enabled": false, "minTokens": 2000, "maxOutputTokens": 1200, "strategy": "auto" } } } ``` | Field | Type | Default | Description | | - | - | - | - | | `enabled` | boolean | `false` | Enable transparent compression for successful tool results. | | `minTokens` | number | `2000` | Only compress a text block when its estimated token count is at or above this threshold. | | `maxOutputTokens` | number | `1200` | Target token budget for each compressed text block. | | `strategy` | `"auto" \| "json" \| "log" \| "search" \| "diff" \| "text"` | `"auto"` | Compression strategy. `auto` detects common structured output formats. | Compression only applies to successful tool responses. Responses with `isError: true` are returned unchanged, non-text content blocks are preserved, and each text block is evaluated independently. When MCPHub compresses a result, it prepends a marker such as: ```text theme={null} [mcphub:compressed-tool-result strategy=log original_tokens=9000 compressed_tokens=1200 omitted_lines=240] ``` The activity log records the original upstream output before compression, so operators can inspect full results in MCPHub even when clients receive compressed content. ## Best Practices ### Security 1. **Use environment variables** for sensitive data: ```json theme={null} { "env": { "API_KEY": "${API_KEY}", "DATABASE_PASSWORD": "${DB_PASSWORD}" } } ``` This comprehensive guide covers all aspects of configuring MCP servers in MCPHub for various use cases and environments. ### Stdio response buffer Set `options.maxBufferSize` to a positive safe integer in bytes to allow larger stdio responses: ```json theme={null} { "type": "stdio", "command": "node", "args": ["server.js"], "options": { "maxBufferSize": 268435456 } } ``` Omitting this option retains the SDK default of 10 MiB (10485760 bytes). The limit applies to the read buffer containing the serialized MCP message, including its envelope. A response that exceeds the limit closes the transport. Raising the limit allows greater memory use; choose a finite limit appropriate for the server. Zero, negative values, fractions, and non-finite values are rejected. This option does not change HTTP transports or the SDK's large-message processing performance. ## Allowing internal upstream networks Administrators can maintain **Allowed upstream CIDR ranges** in Settings, or set `systemConfig.network.allowedCidrs` in JSON storage: ```json theme={null} { "systemConfig": { "network": { "allowedCidrs": ["192.168.1.0/24", "fd00:1234::/64"] } } } ``` The list defaults to empty. It permits all users' upstream MCP (SSE and Streamable HTTP), OpenAPI, and upstream OAuth requests to addresses in those ranges. Public addresses remain accessible; admin-owned servers retain their existing unrestricted internal-network access. Server visibility and sharing permissions are unchanged. Each entry must be an IPv4 or IPv6 address with a prefix length (for example `/32` for one IPv4 address or `/128` for one IPv6 address). All DNS answers, each redirect destination, and connection-time DNS answers are checked. Removing a range affects subsequent requests, but does not close existing connections or clear cached data. Anonymous OAuth client metadata (CIMD) URLs remain restricted to public addresses. Only admins can update the list through `PUT /api/system-config` with `{"network":{"allowedCidrs":["192.168.1.0/24"]}}`. Sending an empty array clears it. Invalid lists return HTTP 400 without saving. Database installations should use the dashboard or API; the field is stored in the system configuration table. Use the smallest necessary ranges. Broad ranges such as `0.0.0.0/0` or `::/0` also allow sensitive internal destinations, including loopback and cloud metadata endpoints. ## On-demand tool snapshots For stdio servers, `startOnDemand: true` keeps the child process asleep until it is needed. MCPHub persists the last successful raw tool discovery so a restart can restore `tools/list` without starting every on-demand server. An empty tool list is also a valid snapshot. The first tool call still connects to the upstream and refreshes discovery; tools removed upstream cannot execute from the snapshot. Failed calls trigger a discovery refresh without replaying the call. File mode stores snapshots beside the settings file as `.capabilities.json`; mount that directory on persistent storage to retain snapshots across container replacement. Database mode stores them in the `capability_snapshots` table. Snapshots stay outside settings exports and contain no launch configuration or credentials. Tool definitions themselves may contain private upstream metadata, so protect snapshot storage like other runtime data. The server-list and server-detail APIs expose `capabilityCache.state` (`fresh`, `stale`, or `missing`) and `capabilityCache.capturedAt` when available. Here `fresh` means configuration-matching last-known discovery, not guaranteed current upstream state. Launch configuration, effective child environment, and personal credential binding changes invalidate a snapshot. Server/group tool overrides and permissions are reapplied when listing; they are never frozen into the snapshot. Personal snapshots are isolated by authenticated user, and listing still requires a valid binding and current server access. Missing or incompatible snapshots retain live discovery (startup priming for shared stdio servers, first authenticated use for personal servers). No fleet-wide background refresh is scheduled. Unchanged configuration cannot detect an upstream package update or external permission change until the next live connection. Prompts and resources are not persisted in this phase; personal prompt/resource requests still connect live. # Nginx Configuration Source: https://docs.mcphub.app/configuration/nginx Configure Nginx as a reverse proxy for MCPHub # Nginx Configuration This guide explains how to configure Nginx as a reverse proxy for MCPHub, including SSL termination, load balancing, and caching strategies. ## Basic Reverse Proxy Setup ### Configuration File Create or update your Nginx configuration file (`/etc/nginx/sites-available/mcphub`): ```nginx theme={null} server { listen 80; server_name your-domain.com; # Redirect HTTP to HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; # SSL Configuration ssl_certificate /path/to/your/certificate.crt; ssl_certificate_key /path/to/your/private.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # Security Headers add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Gzip Compression gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml; # Main application location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 86400; } # API endpoints with longer timeout for MCP operations location /api/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 300; proxy_connect_timeout 60; proxy_send_timeout 60; } # Static assets caching location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://127.0.0.1:3000; proxy_cache_valid 200 1d; proxy_cache_valid 404 1m; add_header Cache-Control "public, immutable"; expires 1y; } } ``` ### Enable the Configuration ```bash theme={null} # Create symbolic link to enable the site sudo ln -s /etc/nginx/sites-available/mcphub /etc/nginx/sites-enabled/ # Test configuration sudo nginx -t # Reload Nginx sudo systemctl reload nginx ``` ## Load Balancing Configuration For high-availability setups with multiple MCPHub instances: ```nginx theme={null} upstream mcphub_backend { least_conn; server 127.0.0.1:3000 weight=1 max_fails=3 fail_timeout=30s; server 127.0.0.1:3001 weight=1 max_fails=3 fail_timeout=30s; server 127.0.0.1:3002 weight=1 max_fails=3 fail_timeout=30s; # Health check (Nginx Plus feature) # health_check interval=5s fails=3 passes=2; } server { listen 443 ssl http2; server_name your-domain.com; # SSL and other configurations... location / { proxy_pass http://mcphub_backend; # Other proxy settings... } } ``` ## Caching Configuration ### Browser Caching ```nginx theme={null} # Cache static assets location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://127.0.0.1:3000; expires 1y; add_header Cache-Control "public, immutable"; } # Cache API responses (be careful with dynamic content) location /api/public/ { proxy_pass http://127.0.0.1:3000; proxy_cache mcphub_cache; proxy_cache_valid 200 5m; proxy_cache_key "$scheme$request_method$host$request_uri"; add_header X-Cache-Status $upstream_cache_status; } ``` ### Nginx Proxy Cache Add to the `http` block in `nginx.conf`: ```nginx theme={null} http { # Proxy cache configuration proxy_cache_path /var/cache/nginx/mcphub levels=1:2 keys_zone=mcphub_cache:10m max_size=1g inactive=60m use_temp_path=off; # Other configurations... } ``` ## WebSocket Support For real-time features and SSE (Server-Sent Events): ```nginx theme={null} location /api/stream { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # Disable buffering for real-time responses proxy_buffering off; proxy_cache off; # Timeouts for long-lived connections proxy_read_timeout 24h; proxy_send_timeout 24h; } ``` ## Security Configuration ### Rate Limiting ```nginx theme={null} http { # Define rate limiting zones limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; server { # Apply rate limiting to API endpoints location /api/ { limit_req zone=api burst=20 nodelay; # Other configurations... } # Strict rate limiting for login endpoints location /api/auth/login { limit_req zone=login burst=5; # Other configurations... } } } ``` ### IP Whitelisting ```nginx theme={null} # Allow specific IPs for admin endpoints location /api/admin/ { allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; proxy_pass http://127.0.0.1:3000; # Other proxy settings... } ``` ## Monitoring and Logging ### Access Logs ```nginx theme={null} http { # Custom log format log_format mcphub_format '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time'; server { # Enable access logging access_log /var/log/nginx/mcphub_access.log mcphub_format; error_log /var/log/nginx/mcphub_error.log; # Other configurations... } } ``` ### Status Page ```nginx theme={null} location /nginx_status { stub_status; allow 127.0.0.1; deny all; } ``` ## Docker Integration When running MCPHub in Docker, update the proxy configuration: ```nginx theme={null} upstream mcphub_docker { server mcphub:3000; # Docker service name } server { location / { proxy_pass http://mcphub_docker; # Other proxy settings... } } ``` ## Complete Example Configuration Here's a production-ready example using the provided `nginx.conf.example`: ```bash theme={null} # Copy the example configuration cp nginx.conf.example /etc/nginx/sites-available/mcphub # Update the configuration with your domain and paths sudo nano /etc/nginx/sites-available/mcphub # Enable the site sudo ln -s /etc/nginx/sites-available/mcphub /etc/nginx/sites-enabled/ # Test and reload sudo nginx -t && sudo systemctl reload nginx ``` ## Troubleshooting ### Common Issues **502 Bad Gateway**: Check if MCPHub is running and accessible **504 Gateway Timeout**: Increase `proxy_read_timeout` for long-running operations **WebSocket connection failures**: Ensure proper `Upgrade` and `Connection` headers **Cache issues**: Clear proxy cache or disable for development ### Debug Commands ```bash theme={null} # Test Nginx configuration sudo nginx -t # Check Nginx status sudo systemctl status nginx # View error logs sudo tail -f /var/log/nginx/error.log # Check if MCPHub is responding curl -I http://localhost:3000 ``` ## Performance Optimization ### Worker Processes ```nginx theme={null} # In nginx.conf worker_processes auto; worker_connections 1024; ``` ### Buffer Sizes ```nginx theme={null} proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; ``` ### Keep-Alive ```nginx theme={null} upstream mcphub_backend { server 127.0.0.1:3000; keepalive 32; } location / { proxy_pass http://mcphub_backend; proxy_http_version 1.1; proxy_set_header Connection ""; } ``` This configuration provides a solid foundation for running MCPHub behind Nginx with proper security, performance, and reliability features. # Development Source: https://docs.mcphub.app/development Develop and contribute to MCPHub This page is a landing index. The full developer documentation lives under `docs/development/`. > **[Getting Started](/development/getting-started)** > > Clone the repo, install dependencies, run the dev servers, and find your way around the code. > **[Architecture](/development/architecture)** > > The actual moving parts: `AppServer`, the service layer, file vs. database storage, and request paths. > **[Embedding Lifecycle](/development/embedding-lifecycle)** > > How smart-routing embeddings are generated, persisted, and refreshed. > **[Contributing](/development/contributing)** > > Coding style, commit message conventions, and PR review expectations. ## Quick reference * **Source layout**: `src/server.ts` is the process entrypoint; HTTP routes live in `src/routes/index.ts`. See [Architecture](/development/architecture) for the full map. * **Dev servers**: `pnpm dev` runs the backend on `:3000` and the Vite frontend on `:5173`. The frontend proxies `/api`, `/mcp`, `/sse`, `/oauth`, `/health` to the backend (`frontend/vite.config.ts`). * **Tests**: `pnpm test` runs the Jest suite. Tests live under `src/**/*.test.ts` and `tests/`. * **Lint / format**: `pnpm lint`, `pnpm format`. * **Storage modes**: file (default, `mcp_settings.json`) or PostgreSQL — see [Database Configuration](/configuration/database-configuration). For deployment, dependencies, and full end-user installation, see [Installation Guide](/installation). # Architecture Source: https://docs.mcphub.app/development/architecture How MCPHub is laid out at runtime This page describes the actual moving parts of MCPHub today. Anything not listed here is not implemented — there is no built-in Redis, no Prometheus exporter, no WebSocket server, no plugin loader. ## Process model MCPHub runs as a single Node.js (Express) process. The entrypoint is `src/index.ts`, which delegates to `AppServer` in [src/server.ts](src/server.ts). `AppServer` is responsible for: 1. Initializing i18n (`src/utils/i18n.ts`). 2. Bootstrapping the default admin user (`src/models/User.ts:initializeDefaultUser`). 3. Initializing the optional **upstream-MCP OAuth client provider** (`src/services/oauthService.ts`) and the optional **MCPHub OAuth authorization server** (`src/services/oauthServerService.ts`). 4. Wiring middlewares (`src/middlewares/index.ts`) and HTTP routes (`src/routes/index.ts`). 5. Spawning / connecting all enabled upstream MCP servers via `initUpstreamServers()` in `src/services/mcpService.ts`. 6. Mounting the SSE / streamable-HTTP MCP routes (`/mcp/:group`, `/sse/:group`, and their user-scoped variants `/:user/mcp/...`, `/:user/sse/...`). 7. Serving the built frontend from `frontend/dist/` (unless `DISABLE_WEB=true`). Shutdown is handled in `AppServer.shutdown()`: stop the HTTP server, close all upstream MCP clients, then close the database pool if database mode is on. ## Top-level directories | Path | Purpose | | - | - | | `src/server.ts` | `AppServer` class — process lifecycle, MCP route registration. | | `src/index.ts` | CLI entrypoint that constructs and starts `AppServer`. | | `src/routes/index.ts` | All HTTP route definitions (single file). | | `src/middlewares/` | Auth (`auth.ts`), user context (`userContext.ts`), i18n, common middleware. | | `src/controllers/` | One controller per resource (servers, groups, users, tools, prompts, resources, logs, activities, OAuth, bearer keys, templates, market, cloud, registry, MCPB upload, health, config). | | `src/services/` | Long-lived domain services — see the table below. | | `src/dao/` | Data access layer with a file backend (default) and a TypeORM-based DB backend (`*DaoDbImpl.ts`). The factory in `src/dao/DaoFactory.ts` selects the implementation. | | `src/db/` | TypeORM `DataSource`, entities (`src/db/entities/`), repositories, subscribers. Loaded only in database mode. | | `src/models/` | Lightweight in-memory models (e.g. `User`, `OAuth`) that wrap the DAO layer for convenience. | | `src/clients/` | Upstream MCP client wrappers (stdio / SSE / streamable-http / OpenAPI). | | `src/types/` | Shared TypeScript types — start here when looking for config schemas. | | `src/utils/` | Helpers: rate limiting, JWT, vector helpers, file migration, bearer-token parsing, etc. | | `src/betterAuth.ts` | Optional [Better Auth](https://better-auth.com/) integration (GitHub/Google OAuth). | | `frontend/` | Vite + React dashboard. Talks to the API over `/api`. In dev, `frontend/vite.config.ts` proxies to `http://localhost:3000`. | ## Service layer The services in `src/services/` are the core domain layer. Notable members: | Service | Responsibility | | - | - | | `mcpService.ts` | Lifecycle of all upstream MCP clients (connect, reconnect, capability cache, tool/prompt/resource registries). | | `sseService.ts` | Handles HTTP requests on `/sse/*` and `/mcp/*` and proxies them to the right upstream client / group / smart route. | | `smartRoutingService.ts` | Vector-search-based tool discovery. Requires Postgres + pgvector and an OpenAI-compatible embedding endpoint. | | `vectorSearchService.ts` | Embedding store and similarity search backing smart routing. | | `oauthService.ts` | Acts as an **OAuth client** to upstream MCP servers that require OAuth. | | `oauthServerService.ts` | Acts as an **OAuth authorization server** for downstream MCP clients (uses `@node-oauth/oauth2-server`). | | `betterAuthConfig.ts` + `src/betterAuth.ts` | Optional social-login layer; mounted at `${basePath}${betterAuthConfig.basePath}` (default `/api/auth/better`). | | `activityLoggingService.ts` | Records request/response activities. Persists to the `mcphub_activity` table; only active in database mode. | | `logService.ts` | In-process log buffer that powers `/api/logs` and the SSE stream at `/api/logs/stream`. | | `templateService.ts` | Export / import of `mcp_settings.json` snapshots and group templates. | | `marketService.ts` / `cloudService.ts` / `registry.ts` | Read-mostly catalogs surfaced by the dashboard. | | `keepAliveService.ts` | Optional periodic ping for `sse` upstream servers. | ## Data layer MCPHub supports two storage backends. The choice is decided once at startup: ``` useDatabase = (USE_DB === 'true') OR (USE_DB unset AND DB_URL set) ``` ### File mode (default) All persistent state lives in `mcp_settings.json` (path configurable via `MCPHUB_SETTING_PATH`). Users, servers, groups, system config and OAuth state are read/written through `src/dao/*Dao.ts` file implementations. This is the simplest deployment mode and works for single-instance use. ### Database mode When enabled, MCPHub uses TypeORM against PostgreSQL. The schema is defined by entities in `src/db/entities/`: * `User`, `BearerKey`, `OAuthClient`, `OAuthToken` * `Server`, `Group`, `SystemConfig`, `UserConfig` * `BuiltinPrompt`, `BuiltinResource` * `Activity` — request/response logging (only populated in DB mode). * `VectorEmbedding` — pgvector-backed table used by smart routing. The DAO factory in `src/dao/DaoFactory.ts` swaps in the `*DaoDbImpl.ts` variants when database mode is on. A one-time migration script `src/scripts/migrate-to-database.ts` copies an existing `mcp_settings.json` into the database; the helper in `src/utils/migration.ts` runs the same path on first start. See [Database Configuration](/configuration/database-configuration) for setup details. ## Request paths There are three classes of HTTP route, all defined in `src/routes/index.ts` and mounted by `AppServer`: 1. **Public endpoints** (no auth): * `GET /health` * `GET ${basePath}/config`, `GET ${basePath}/public-config` * All `/oauth/*` and `/.well-known/oauth-*` endpoints (the OAuth flow itself). * All `${basePath}/api/openapi*` and `${basePath}/api/tools/...` OpenAPI endpoints. * `POST /api/auth/login`, `POST /api/auth/register`. 2. **Authenticated dashboard / management API** under `${basePath}/api/*`. Authentication is enforced by `src/middlewares/auth.ts`, which accepts (in order): * A forward-auth JWT from the configured issuer, verified against its JWKS (`src/services/forwardAuthService.ts`, when `systemConfig.auth.forwardAuth` is enabled). A token from that issuer that fails verification is rejected without trying the methods below. * A configured system-level bearer key (`/api/auth/keys`) whose `accessType` is `all`. * An OAuth access token issued by MCPHub's authorization server (when enabled). * A Better Auth session cookie (when enabled). * An `x-auth-token` JWT issued by `/api/auth/login`, or the same value as a `?token=` query. * If `systemConfig.routing.skipAuth` is `true`, unauthenticated dashboard API calls receive a synthetic `guest` user with admin privileges. 3. **MCP transport routes**: `${basePath}/mcp/:group?`, `${basePath}/sse/:group?`, plus their user-scoped variants `${basePath}/:user/mcp/...` and `${basePath}/:user/sse/...`. These are rate-limited via `mcpConnectionRateLimiter` and pass through `sseUserContextMiddleware` before being dispatched by `sseService.ts`. Forward-auth JWTs are checked first, with the same no-fallback rule as the dashboard API. System-level key scopes are enforced inside `sseService.ts`; user-level keys restore their owner's live user context so server visibility filtering applies. ## Observability What actually ships today: * `GET /health` — process & database & MCP-server connection status. * `GET /api/logs`, `DELETE /api/logs`, `GET /api/logs/stream` (Server-Sent Events of the in-memory log buffer). * `GET /api/activities*` — query the `mcphub_activity` table. **Database mode only.** There is no Prometheus exporter, no OpenTelemetry SDK initialization and no built-in distributed tracing. If you need those, scrape `/health`/`/api/logs/stream` or wrap the process at the infrastructure layer (sidecar, log shipper, etc.). ## Frontend The dashboard is a Vite + React + Tailwind app under `frontend/`. After `pnpm build` it is emitted to `frontend/dist/` and served by the backend as static files at `${basePath}/`. In development (`pnpm dev`), Vite runs on `:5173` and proxies `/api`, `/mcp`, `/sse`, `/oauth`, `/health` to the backend on `:3000` — see `frontend/vite.config.ts`. The frontend uses the same JWT / Bearer / Better Auth session that the management API expects; it does not have its own auth. # Contributing to MCPHub Source: https://docs.mcphub.app/development/contributing Learn how to contribute to the MCPHub project ## Welcome Contributors! 🎉 Thank you for your interest in contributing to MCPHub! This guide will help you get started with contributing to the project, whether you're fixing bugs, adding features, improving documentation, or helping with testing. ## Quick Start 1. **Fork the repository** on GitHub 2. **Clone your fork** locally 3. **Create a branch** for your changes 4. **Make your changes** following our guidelines 5. **Test your changes** thoroughly 6. **Submit a pull request** ## Ways to Contribute ### 🐛 Bug Reports Help us improve MCPHub by reporting bugs: * **Search existing issues** first to avoid duplicates * **Use the bug report template** when creating issues * **Provide detailed information** including steps to reproduce * **Include system information** (OS, Node.js version, etc.) ```markdown theme={null} ## Bug Report Template **Description** A clear description of what the bug is. **Steps to Reproduce** 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' 4. See error **Expected Behavior** What you expected to happen. **Actual Behavior** What actually happened. **Environment** - OS: [e.g. macOS 12.0] - Node.js: [e.g. 18.17.0] - MCPHub Version: [e.g. 1.2.3] - Browser: [e.g. Chrome 91.0] **Additional Context** Any other context about the problem. ``` ### ✨ Feature Requests We welcome feature suggestions: * **Check existing feature requests** to avoid duplicates * **Use the feature request template** * **Explain the use case** and why it would be valuable * **Consider implementation complexity** ### 🔧 Code Contributions Ready to write some code? Here's how: #### Setting Up Development Environment ```bash theme={null} # 1. Fork and clone the repository git clone https://github.com/YOUR_USERNAME/mcphub.git cd mcphub # 2. Add upstream remote git remote add upstream https://github.com/samanhappy/mcphub.git # 3. Install dependencies pnpm install # 4. Set up environment (MCPHub reads .env in the project root) cp .env.example .env # 5. Optional: use PostgreSQL instead of the default file storage. # Start only the database service, then add to .env: # DB_URL=postgresql://mcphub:mcphub_password@localhost:5432/mcphub # The schema is created when MCPHub starts. docker compose -f docker-compose.db.yml up -d postgres # 6. Start development server pnpm run dev ``` #### Branch Naming Convention Use descriptive branch names with prefixes: ```bash theme={null} # Features git checkout -b feature/smart-routing-improvements git checkout -b feature/user-authentication # Bug fixes git checkout -b fix/server-startup-error git checkout -b fix/memory-leak-in-cache # Documentation git checkout -b docs/api-reference-update git checkout -b docs/deployment-guide # Refactoring git checkout -b refactor/auth-service-cleanup git checkout -b refactor/database-queries ``` ### 📚 Documentation Help improve our documentation: * **Fix typos and grammar** * **Improve existing guides** * **Add missing documentation** * **Create tutorials and examples** * **Translate documentation** ## Development Guidelines ### Code Style We use ESLint and Prettier to maintain code quality: ```bash theme={null} # Check code style pnpm run lint # Fix automatically fixable issues pnpm run lint --fix # Format code pnpm run format # Type check (backend and frontend) pnpm run backend:build pnpm run frontend:typecheck ``` #### TypeScript Best Practices ```typescript theme={null} // ✅ Good: Use proper types interface MCPServerConfig { name: string; command: string; args: string[]; env?: Record; } // ✅ Good: Use async/await async function startServer(config: MCPServerConfig): Promise { try { await mcpService.start(config); } catch (error) { logger.error('Failed to start server', { error, config }); throw error; } } // ❌ Bad: Using any type function processData(data: any): any { return data.something(); } // ❌ Bad: Not handling errors async function riskyOperation(): Promise { await dangerousFunction(); // Could throw } ``` #### React/Frontend Guidelines ```tsx theme={null} // ✅ Good: Functional components with proper typing interface ServerCardProps { server: MCPServer; onStart: (serverId: string) => void; onStop: (serverId: string) => void; } const ServerCard: React.FC = ({ server, onStart, onStop }) => { const handleStart = useCallback(() => { onStart(server.id); }, [server.id, onStart]); return ( {server.name} {server.status}

{server.description}

{server.status === 'stopped' ? ( ) : ( )}
); }; ``` ### Testing Requirements All contributions must include appropriate tests: #### Unit Tests ```typescript theme={null} // src/services/__tests__/mcpService.test.ts import { MCPService } from '../mcpService'; import { mockLogger, mockDatabase } from '../../__mocks__'; describe('MCPService', () => { let service: MCPService; beforeEach(() => { service = new MCPService(mockLogger, mockDatabase); }); describe('startServer', () => { it('should start a server successfully', async () => { const config = { name: 'test-server', command: 'node', args: ['server.js'], }; await service.startServer(config); expect(service.getServerStatus('test-server')).toBe('running'); }); it('should handle server startup failures', async () => { const invalidConfig = { name: 'invalid-server', command: 'invalid-command', args: [], }; await expect(service.startServer(invalidConfig)).rejects.toThrow( 'Failed to start server: Command not found', ); }); }); }); ``` #### Integration Tests ```typescript theme={null} // src/__tests__/integration/server-api.test.ts import request from 'supertest'; import { app } from '../../app'; import { setupTestDatabase, teardownTestDatabase } from '../helpers/database'; describe('Server API Integration', () => { beforeAll(async () => { await setupTestDatabase(); }); afterAll(async () => { await teardownTestDatabase(); }); describe('POST /api/servers', () => { it('should create a new server', async () => { const serverData = { name: 'test-server', command: 'node', args: ['server.js'], group: 'development', }; const response = await request(app).post('/api/servers').send(serverData).expect(201); expect(response.body).toMatchObject({ name: 'test-server', status: 'stopped', group: 'development', }); }); }); }); ``` #### End-to-End Tests ```typescript theme={null} // tests/e2e/server-management.spec.ts import { test, expect } from '@playwright/test'; test.describe('Server Management', () => { test('should create and manage MCP servers', async ({ page }) => { await page.goto('/dashboard'); // Create new server await page.click('[data-testid="add-server-button"]'); await page.fill('[data-testid="server-name-input"]', 'test-server'); await page.fill('[data-testid="server-command-input"]', 'node server.js'); await page.click('[data-testid="save-server-button"]'); // Verify server appears in list await expect(page.locator('[data-testid="server-list"]')).toContainText('test-server'); // Start the server await page.click('[data-testid="start-server-test-server"]'); // Verify server is running await expect(page.locator('[data-testid="server-status-test-server"]')).toContainText( 'running', ); }); }); ``` ### Commit Guidelines We follow [Conventional Commits](https://www.conventionalcommits.org/): ```bash theme={null} # Format: [optional scope]: # Features git commit -m "feat(auth): add JWT token refresh functionality" git commit -m "feat(ui): implement server status dashboard" # Bug fixes git commit -m "fix(api): resolve memory leak in server manager" git commit -m "fix(db): handle connection timeout gracefully" # Documentation git commit -m "docs(api): add examples for server endpoints" git commit -m "docs(readme): update installation instructions" # Refactoring git commit -m "refactor(services): extract auth logic into separate module" # Tests git commit -m "test(api): add integration tests for server management" # Chores git commit -m "chore(deps): update dependencies to latest versions" ``` #### Commit Types * **feat**: New feature * **fix**: Bug fix * **docs**: Documentation changes * **style**: Code style changes (formatting, etc.) * **refactor**: Code refactoring * **test**: Adding or updating tests * **chore**: Maintenance tasks * **perf**: Performance improvements * **ci**: CI/CD changes ### Pull Request Process #### Before Submitting ```bash theme={null} # 1. Sync with upstream git fetch upstream git checkout main git merge upstream/main # 2. Rebase your feature branch git checkout feature/your-feature git rebase main # 3. Run all checks pnpm run lint pnpm run frontend:typecheck pnpm run test pnpm run build pnpm run docs:validate # if you changed docs/ # 4. Update documentation if needed # 5. Add/update tests for your changes ``` #### Pull Request Template ```markdown theme={null} ## Description Brief description of the changes and motivation. ## Type of Change - [ ] Bug fix (non-breaking change which fixes an issue) - [ ] New feature (non-breaking change which adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) - [ ] Documentation update ## Testing - [ ] Unit tests pass - [ ] Integration tests pass - [ ] E2E tests pass (if applicable) - [ ] Manual testing completed ## Documentation - [ ] Code is self-documenting - [ ] API documentation updated - [ ] User documentation updated - [ ] README updated (if needed) ## Checklist - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes ## Screenshots (if applicable) Add screenshots to help explain your changes. ## Additional Notes Any additional information that reviewers should know. ``` ### Code Review Process #### For Contributors * **Be patient**: Reviews take time, and reviewers may have questions * **Be responsive**: Address feedback promptly and clearly * **Be open**: Accept constructive criticism and suggestions * **Ask questions**: If feedback is unclear, ask for clarification #### For Reviewers * **Be constructive**: Provide helpful suggestions, not just criticism * **Be specific**: Point out exact issues and suggest solutions * **Be timely**: Review PRs within a reasonable timeframe * **Be encouraging**: Recognize good work and improvements ## Community Guidelines ### Code of Conduct We are committed to providing a welcoming and inspiring community for all: * **Be respectful**: Treat everyone with respect and kindness * **Be inclusive**: Welcome people of all backgrounds and skill levels * **Be collaborative**: Work together towards common goals * **Be patient**: Help others learn and grow * **Be professional**: Maintain professional communication ### Communication Channels * **GitHub Issues**: Bug reports and feature requests * **GitHub Discussions**: General questions and community chat * **Discord**: Real-time community chat (link in README) * **Email**: Security issues and private matters ## Getting Help ### Documentation * [Getting Started Guide](./getting-started.mdx) * [Architecture Overview](./architecture.mdx) * [API Reference](../api-reference/introduction.mdx) * [Configuration Guide](../configuration/mcp-settings.mdx) ### Common Issues **Build Failures** ```bash theme={null} # Clear and reinstall dependencies rm -rf node_modules pnpm-lock.yaml pnpm install # Clear build cache rm -rf dist/ pnpm run build ``` **Test Failures** ```bash theme={null} # Run tests with verbose output pnpm run test:verbose # Run specific test file pnpm test tests/services/mcpService-headers.test.ts # Debug tests (then attach a debugger, e.g. chrome://inspect) node --inspect-brk node_modules/jest/bin/jest.js --runInBand tests/services/mcpService-headers.test.ts ``` **Database Issues** The schema is created and updated automatically when MCPHub starts in database mode, so there is no separate migration step. ```bash theme={null} # Reset the local database started with docker-compose.db.yml (deletes its data) docker compose -f docker-compose.db.yml down -v docker compose -f docker-compose.db.yml up -d postgres # Import users, servers and groups from mcp_settings.json into the database # (MCPHub also does this on first start when the database has no users) pnpm exec tsx src/scripts/migrate-to-database.ts ``` ### Getting Support If you need help: 1. **Check the documentation** first 2. **Search existing issues** on GitHub 3. **Ask in GitHub Discussions** for general questions 4. **Create an issue** if you found a bug 5. **Join our Discord** for real-time help ## Recognition Contributors will be recognized in several ways: * **GitHub contributors**: Merged commits are credited on the repository's contributors page * **Release notes**: Significant contributions are mentioned in release notes * **GitHub badges**: Active contributors receive special recognition * **Community showcase**: Outstanding contributions are featured in our blog ## Advanced Topics ### Maintainer Guidelines For project maintainers: #### Release Process ```bash theme={null} # 1. Create release branch git checkout -b release/v1.2.0 # 2. Update version npm version 1.2.0 --no-git-tag-version # 3. Update changelog # Edit CHANGELOG.md # 4. Commit changes git add . git commit -m "chore(release): prepare v1.2.0" # 5. Create PR for review # 6. After merge, tag release git tag v1.2.0 git push origin v1.2.0 ``` #### Security Handling For security issues: 1. **Do not** create public issues 2. **Email** [security@mcphub.dev](mailto:security@mcphub.dev) 3. **Wait** for response before disclosure 4. **Coordinate** with maintainers on fixes ### Architectural Decisions When making significant changes: 1. **Create an RFC** (Request for Comments) issue 2. **Discuss** with the community 3. **Get approval** from maintainers 4. **Document** decisions in ADR (Architecture Decision Records) ## Thank You! 🙏 Thank you for taking the time to contribute to MCPHub! Every contribution, no matter how small, helps make the project better for everyone. We look forward to collaborating with you! # Embedding Lifecycle Source: https://docs.mcphub.app/development/embedding-lifecycle Complete indexing flow for tool embeddings, including triggers, queueing, pacing, retries, provider requests, and database persistence. ## Overview This page documents the full embedding lifecycle in MCPHub, from the moment indexing is triggered to the moment vectors are persisted in PostgreSQL. It also covers provider request execution, adaptive pacing, retry behavior, queue-based serialization, and the follow-up resynchronization logic used when vector dimensions change. The lifecycle applies to two major use cases: * Tool indexing, where MCPHub creates embeddings for MCP tools and stores them in the vector database. * Query embedding, where MCPHub creates an embedding for a user search query in order to run semantic similarity search. The database persistence path exists only for tool indexing. Query embeddings are generated on demand and used immediately for search. ## When Embedding Creation Starts Embedding creation can begin from several entry points: 1. When an MCP server connects successfully and MCPHub loads its tool list. 2. When an MCP server reconnects and its tools are reloaded. 3. When an OpenAPI-backed server is initialized successfully. 4. When a single tool is re-synced explicitly. 5. When Smart Routing configuration changes and MCPHub triggers a full sync of all connected servers. 6. When a full resync is scheduled because the configured embedding dimensions no longer match the database schema. 7. When Smart Routing handles a user query and needs an embedding for semantic search. ## End-to-End Flow ```mermaid theme={null} flowchart TD A[Embedding trigger] --> B{Trigger type} B -->|Tool indexing| C[Collect tool metadata] B -->|Search query| D[Prepare user query text] C --> E[Build searchable text from tool name, description, and schema fields] D --> F[Call searchToolsByVector] F --> G[Generate query embedding] E --> H[Generate embedding] G --> H H --> I[Normalize whitespace] I --> J{Provider selection} J -->|Azure OpenAI| K[Azure truncation using configured Azure embedding model] J -->|OpenAI-compatible| L[Token truncation with provider-aware safety margin] J -->|No usable provider key| M[Fallback embedding generation] K --> N[Submit provider request through shared queue] L --> O[Choose float or base64 encoding] O --> N M --> P{Call purpose} N --> Q[Apply adaptive pacing delay] Q --> R[Execute request with retry policy] R --> S{Request succeeded} S -->|No| T[Log curated error details and propagate failure] S -->|Yes| U[Decode provider response] U --> P P -->|Query embedding| V[Run vector similarity search] P -->|Tool indexing| W[Check database vector dimensions once per server sync] W --> X{Dimensions compatible} X -->|No| Y[Clear incompatible rows, alter vector column type, recreate index] X -->|Yes| Z[Persist tool embedding with metadata] Y --> Z Z --> AA{Dimensions changed during sync} AA -->|Yes| AB[Schedule full embeddings resync] AA -->|No| AC[Finish current sync] AB --> AC V --> AD[Return ranked matching tools] ``` ## Tool Indexing Flow Tool indexing starts in the server lifecycle layer after tool discovery succeeds. MCPHub builds a searchable text payload per tool by combining: * Tool name * Tool description * Top-level schema property names * Nested input schema property names That searchable text becomes the input to the embedding provider or to the fallback embedding generator. ### Important Property Every tool is processed individually, but provider calls are not executed independently. All embedding provider requests share one queue, which means parallel syncs do not bypass rate limiting. ## Query Embedding Flow Search queries use the same embedding generator as tool indexing. This is important because: * Query-time requests use the same provider selection rules. * Query-time requests use the same queue. * Query-time requests use the same pacing and retry logic. This keeps the request pattern consistent whether MCPHub is indexing tools or searching for them. The main difference is that query embeddings are not written to the database. They are generated, used for similarity search, and then discarded. ## Provider Selection and Request Construction MCPHub currently supports three main execution paths: ### 1. Azure OpenAI Path * Validates Azure endpoint, API key, API version, and deployment name. * Truncates text using the configured underlying Azure embedding model. * Sends a direct HTTP request with axios. * Wraps the provider request with the shared queue and retry logic. ### 2. OpenAI-Compatible Path * Loads Smart Routing provider settings. * Normalizes whitespace before tokenization. * Applies token truncation based on the configured embedding model. * Applies an extra token safety factor for providers such as SiliconFlow, where local and server-side token counts can differ slightly. * Determines whether to request embeddings in float or base64 format. * Uses the OpenAI SDK client for the provider call. * Wraps the provider request with the shared queue and retry logic. If token truncation fails in this path, MCPHub falls back to heuristic character-based truncation to avoid losing the entire embedding operation. ### 3. Fallback Embedding Path If a required provider key is missing, MCPHub generates a deterministic low-dimensional fallback vector locally. This fallback path: * Does not call any external embedding provider. * Does not use the shared provider queue. * Still produces a vector that can be stored in PostgreSQL. * May trigger a dimension mismatch if the database currently expects a different vector size. ## Shared Queue, Pacing, and Retries The most important protection mechanism in the embedding pipeline is the shared queue. Every provider-backed embedding request passes through one promise chain. This guarantees that parallel sync jobs cannot send requests concurrently and accidentally bypass pacing rules. ### Queue and Retry Subprocess ```mermaid theme={null} flowchart TD A[Provider-backed embedding request] --> B[Append operation to shared queue tail] B --> C[Read current adaptive pacing delay] C --> D[Compute elapsed time since last provider call] D --> E{Need additional wait} E -->|Yes| F[Sleep for remaining pacing interval] E -->|No| G[Continue immediately] F --> H[Update lastEmbeddingCallAt timestamp] G --> H H --> I[Run executeWithRetry] I --> J{Operation succeeded} J -->|Yes| K[Resolve queue item] J -->|No| L[Extract status code and headers] L --> M{Retryable status} M -->|No| N[Reject and propagate error] M -->|Yes| P{Status is 403 or 429} P -->|Yes| Q[Increase adaptive pacing delay] Q --> Q1{Retry-After header present?} Q1 -->|Yes| Q2["Honor Retry-After wait unconditionally"] Q1 -->|No| Q3{"63s wait within 5-min budget?"} Q3 -->|No| N Q3 -->|Yes| Q4[Wait 63s] Q2 --> U[Sleep and retry] Q4 --> U P -->|No| R{Exponential attempts < 7?} R -->|No| N R -->|Yes| R2["Use sequence delay (4s→8s→16s→30s→60s→120s→240s)"] R2 --> U U --> I K --> V[Advance queue tail] N --> V ``` ### Pacing Rules The pacing system is adaptive and precisely aligned with provider rate-limit windows (typically 1-minute RPM): * MCPHub starts with a **0ms base delay** between provider calls by default. No pre-emptive wait is introduced — the system reacts to rate-limit responses rather than guessing in advance. * When a 403 or 429 error is detected, the pacing delay jumps immediately to **63000ms**. * That delay remains in effect for subsequent queued provider calls while rate-limit responses continue. * After **63 seconds** without new rate-limit errors, the delay **immediately resets** to the configured base level. **Why these specific values?** * **63s step / 63s maximum**: A single 403/429 is treated as a signal that retrying earlier is not useful, so MCPHub enters full protection immediately. * **63s cooldown**: This tracks the 1-minute RPM reset window with a small safety margin before returning to normal throughput. * **Retry-After precedence**: If the provider returns `Retry-After`, MCPHub honors that value instead of applying the local 63-second fallback. This strategy treats provider throttling as a binary state instead of a gradual one. For example: * **Normal state**: `0ms` pacing delay * **After first 403/429**: `63000ms` pacing delay * **After 63s without new 403/429**: Immediate return to base level (`0ms` by default) The base delay can be set to a non-zero value in Smart Routing settings for providers that require a fixed minimum interval between requests. The default of 0ms maximizes throughput when the provider cooperates, while the adaptive mechanism handles throttling reactively. ### Retry Strategy MCPHub uses different retry strategies depending on the error type, reflecting that rate-limit errors and infrastructure errors have fundamentally different characteristics. **For rate-limit errors (403/429):** * If the server provides a `Retry-After` header, MCPHub honors it **unconditionally** — no attempt budget or time budget overrides the server's instruction. * If no `Retry-After` is present, MCPHub applies a 63-second cooldown per retry, bounded by a **5-minute total budget**. This prevents indefinite retries when the provider gives no guidance. **For infrastructure errors (503/504):** * MCPHub uses a **fixed exponential sequence** of exactly 7 attempts: 4s, 8s, 16s, 30s, 60s, 120s, 240s. * No time budget is imposed — all 7 attempts are always made regardless of elapsed time. * Random jitter is added to each delay to prevent thundering herd effects. This approach is especially practical for rate-limited providers like SiliconFlow (10 RPM), because: Important: in MCPHub testing, SiliconFlow has been observed to return HTTP 403 with the message "RPM limit exceeded. Please complete identity verification to lift the restriction." on the 11th request made within one minute or less. This behavior is treated here as an observed runtime characteristic, not as a documented contract, because SiliconFlow's official documentation does not currently document this specific limit or response pattern. 1. The first 403/429 is examined immediately for `Retry-After`. 2. If missing, the system waits 63 seconds (guaranteeing the 60-second hard window has passed). 3. If the next attempt is rate-limited again, MCPHub repeats the same rule: `Retry-After` if present, otherwise another 63-second wait. 4. Without `Retry-After`, the operation fails definitively once the 5-minute budget is exhausted. ### Retry Rules Retries apply only to specific status codes: * 403 (Forbidden / Rate Limit) * 429 (Too Many Requests) * 503 (Service Unavailable) * 504 (Gateway Timeout) For retryable failures, MCPHub uses status-specific retry strategies: #### 1. Rate Limit Errors (403/429) - Priority to Server Information When a 403/429 occurs, MCPHub: 1. **On every 403/429 failure**: Checks for `Retry-After` response header. * If present, honors the server's wait time **unconditionally** — no time budget applies. * If absent, applies a 63-second hardcoded cooldown as the local fallback. 2. Retries the same provider request after the wait. 3. Repeats the same rule for the next 403/429 failure. 4. When `Retry-After` is absent: stops when the next 63s wait would exceed the 5-minute retry budget. This strategy always respects server-side rate limit policies and protects against indefinite retries when the server gives no guidance. #### 2. Server Errors (503/504) - Exponential Backoff For transient server errors: 1. Uses a fixed exponential sequence: 4s, 8s, 16s, 30s, 60s, 120s, 240s — exactly 7 retry attempts. 2. No time budget is imposed. All 7 attempts are always made, regardless of total elapsed time (\~8 minutes maximum when the full sequence is exhausted). 3. Does not apply the 63-second rate-limit cooldown. #### Example Retry Sequence for SiliconFlow (10 RPM, no Retry-After header) This example reflects the observed SiliconFlow behavior described above, including the undocumented 11th-request-within-a-minute 403 response seen in practice. It should not be read as an official provider guarantee. ``` Attempt 1: 403/429 → No Retry-After found → Wait 63s → Retry Attempt 2: 403/429 → No Retry-After found → Wait 63s → Retry Attempt 3: 403/429 → No Retry-After found → Wait 63s → Retry Attempt 4: 403/429 → No Retry-After found → Wait 63s → Retry Attempt 5: next 63s wait would exceed 5-minute budget → fail Total: at most 4 waits of 63s within a 5-minute retry budget ``` When the next 63s wait would push the accumulated retry time past 5 minutes, MCPHub logs the failure with detailed diagnostics and rethrows the error. This provides a hard deadline while giving the operation multiple retry opportunities. ## Error Logging During Embedding Requests When a provider request fails after retries, MCPHub emits structured diagnostic logs rather than dumping the raw error object. The logged fields include: * Error name * Message * Extracted status * Provider code when available * Response status when available * Nested provider error message when available * Request ID when present in response headers This curated logging keeps the logs readable while preserving the most important debugging signals. ## Database Persistence Flow For tool indexing, each successful embedding is persisted together with: * Entity type * Composite tool identifier * Searchable text * Embedding vector * Tool metadata, including server name, tool name, description, and input schema * Embedding model identifier The persistence step happens after the embedding vector has been produced. The database write itself is not part of the provider queue. ### Two-Phase Memory-First Sync (Tool Indexing) To avoid database connection timeouts and improve UI progress reporting, tool indexing now runs as a two-phase process: * Phase 1 — Generate in memory: MCPHub sequentially generates embeddings for all tools for a given server and model, keeping results in memory. Progress events are emitted during this phase so the UI updates incrementally while long-running provider calls are in progress. * Phase 2 — Persist to DB: After all embeddings are generated, MCPHub probes the database connection, performs a single dimension compatibility check, and then writes embeddings in a tight loop. Batching writes after generation prevents idle DB connections from staling during long provider waits. Additionally, a skip-check optimization avoids unnecessary re-generation. Before Phase 1, MCPHub: * Counts existing embeddings for the exact server prefix and model as a cheap pre-check. * Only if that count matches the current number of tools, loads the stored tool identities and compares them against the expected content IDs. * Verifies a deterministic tool-set hash stored in embedding metadata for every retrieved row. The hash is derived from the current tool list (name, description, and input schema) so equal counts alone do not cause a false skip. Only when both conditions hold does MCPHub skip regeneration: * Exact content ID set match (same IDs, same cardinality). * Every retrieved row carries a `toolSetHash` equal to the expected hash for the current tool list. Progress reporting is also intentionally asymmetric across the two phases: * Phase 1 emits incremental `in_progress` updates for each generated tool. * Phase 2 does not emit per-tool persistence progress; it emits a single `completed` event after all embeddings have been saved. * If either phase fails, MCPHub emits an `error` progress event before propagating the failure so the UI does not remain stuck in an in-progress state. ### Dimension Validation and Schema Management Before persisting the first tool embedding of a server sync, MCPHub validates that the database vector column can store the generated vector length. ```mermaid theme={null} flowchart TD A[First successful tool embedding for current server sync] --> B[Inspect vector column dimensions] B --> C[Optionally inspect existing rows for recorded dimensions] C --> D{Dimensions match generated embedding} D -->|Yes| E[Continue with normal save] D -->|No| F{Database has no dimensions yet} F -->|Yes| G[Prepare fresh schema for current dimensions] F -->|No| H[Delete incompatible rows] G --> I[Drop existing vector index if present] H --> I I --> J{Dimensions are 2000 or less} J -->|Yes| K[Alter column type to vector with target dimensions] J -->|No| L[Alter column type to halfvec with target dimensions] K --> M[Create best available vector index] L --> M M --> N{Index creation succeeded} N -->|Yes| O[Mark dimensions reset for current sync] N -->|No| P[Continue without optimized index] O --> E P --> E ``` ### Index Strategy MCPHub chooses an index strategy based on vector size: * Up to 2000 dimensions: standard vector with HNSW, with IVFFlat as a fallback. * 2001 to 4000 dimensions: halfvec with HNSW when supported by pgvector. * More than 4000 dimensions: no optimized vector index is created. If the dimensions change during a server sync, MCPHub schedules a full resynchronization so that all existing tool embeddings are regenerated consistently under the new schema. When the database has no prior vector dimensions configured, MCPHub treats that as first-time setup. In that case it updates the schema but does not schedule a full resynchronization, because there is no incompatible historical data to regenerate. ## Full Resynchronization Behavior Full resync is intentionally guarded so that MCPHub does not schedule multiple identical jobs at once. The scheduler uses two flags: * One flag to indicate that a resync has already been scheduled. * One flag to indicate that a resync is currently running. When the scheduler fires, MCPHub: 1. Enumerates all known servers. 2. Selects only connected servers that currently expose tools. 3. Re-runs tool indexing for each eligible server. This makes dimension changes safe, even when the previous embeddings were created using a different provider, model, or fallback path. ## Practical Notes ### Why the Queue Exists Without the shared queue, two parallel sync operations could both wait for the same pacing interval and then send requests at the same time. That would defeat the purpose of adaptive pacing. The queue prevents this by ensuring only one provider-backed embedding request is in flight at a time. ### Why Retries and Pacing Are Separate Pacing controls the baseline rate of new calls. Retries handle transient failures after a call has already been attempted. MCPHub keeps both mechanisms because provider rate limits and upstream instability are related but not identical problems. Rate-limit-aware retry strategy is implemented on top of pacing: when a rate limit (403/429) is detected, the retry logic checks for `Retry-After` headers and applies them immediately, or uses a 63-second fallback on that retry. If a later retry is rate-limited again, MCPHub applies the same rule again. This allows retries to respect the exact window timing that the server demands, while pacing ensures that the baseline request rate stays sustainable across all operations. ### Why Different Retry Strategies per Error Type **Rate-limit errors (403/429 without Retry-After): time-based budget** A fixed attempt count is unreliable here because the number of retries needed depends on the provider's quota window. A 5-minute budget instead: * Enforces a hard deadline without requiring a separately tuned maximum count. * Scales naturally: a provider with a 2-minute window fits \~2 retries; one with a strict 60-second window fits \~4. * `Retry-After` headers bypass the budget entirely, so the server always has the final word. **Infrastructure errors (503/504): fixed 7 attempts** A time budget is inappropriate here because: * The exponential sequence (4s→8s→16s→30s→60s→120s→240s) already has a well-defined total cost (\~8 minutes for all 7 attempts). * Fixed counts are more predictable than "retry until N minutes" when the wait intervals are already known. * A time budget could cut the sequence short unpredictably depending on accumulated jitter. ### Why Query Embeddings Use the Same Queue Search traffic and indexing traffic both consume provider quota. Putting them behind the same queue avoids a situation where search requests starve indexing, or indexing floods the provider while user queries are also running. ### Why Fallback Embeddings Still Matter Fallback vectors are lower quality than provider-generated embeddings, but they keep Smart Routing operational when provider credentials are missing or unavailable. They also make it possible to keep a consistent database pipeline even when external embedding services are temporarily not in use. ## Summary The embedding lifecycle in MCPHub is built around four core ideas: 1. Multiple triggers can start indexing or query embedding generation. 2. All provider-backed embedding requests flow through one serialized queue. 3. Adaptive pacing and bounded retries protect the system from provider throttling and transient failures. 4. Database persistence includes dimension validation and index management so that stored vectors remain queryable and structurally consistent. Together, these mechanisms let MCPHub index tools continuously, search semantically, survive provider limits, and keep the vector store aligned with the active embedding model configuration. # Getting Started with Development Source: https://docs.mcphub.app/development/getting-started Learn how to set up your development environment for MCPHub # Getting Started with Development This guide will help you set up your development environment for contributing to MCPHub. ## Prerequisites Before you begin, ensure you have the following installed: * **Node.js** (version 20 or higher) * **pnpm** (recommended package manager) * **Git** * **Docker** (optional, for containerized development) ## Setting Up the Development Environment ### 1. Clone the Repository ```bash theme={null} git clone https://github.com/your-username/mcphub.git cd mcphub ``` ### 2. Install Dependencies ```bash theme={null} pnpm install ``` ### 3. Environment Configuration Create a `.env` file in the root directory: ```bash theme={null} cp .env.example .env ``` Configure the following environment variables: ```env theme={null} # Server Configuration PORT=3000 NODE_ENV=development # Database Configuration DB_URL=postgresql://username:password@localhost:5432/mcphub # JWT Configuration JWT_SECRET=your-secret-key # OpenAI Configuration (for smart routing) OPENAI_API_KEY=your-openai-api-key ``` ### 4. Database Setup If using PostgreSQL, create a database: ```bash theme={null} createdb mcphub ``` ### 5. MCP Server Configuration Create or modify `mcp_settings.json`: ```json theme={null} { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] } } } ``` ## Development Workflow ### Running the Development Server Start both backend and frontend in development mode: ```bash theme={null} pnpm dev ``` This will start: * Backend server on `http://localhost:3000` * Frontend development server on `http://localhost:5173` Local development uses `admin` / `admin123` and writes runtime changes to `data/mcp_settings.dev.json`. The repository `mcp_settings.json` remains credential-free. To develop with a sub-path, set `BASE_PATH=/mcphub` in the project-root `.env` file, restart `pnpm dev`, and open `http://localhost:5173/mcphub/`. The Vite server proxies the runtime configuration and dashboard API through that prefix. ### Running Backend Only ```bash theme={null} pnpm backend:dev ``` ### Running Frontend Only ```bash theme={null} pnpm frontend:dev ``` ### Building the Project Build both backend and frontend: ```bash theme={null} pnpm build ``` ## Project Structure ``` mcphub/ ├── src/ # Backend source code │ ├── controllers/ # Express controllers │ ├── routes/ # API routes │ ├── services/ # Business logic │ ├── models/ # Database models │ └── utils/ # Utility functions ├── frontend/ # Frontend React application │ ├── src/ │ │ ├── components/ # React components │ │ ├── pages/ # Page components │ │ ├── services/ # API services │ │ └── utils/ # Frontend utilities ├── docs/ # Documentation ├── bin/ # CLI scripts └── scripts/ # Build and utility scripts ``` ## Development Tools ### Linting and Formatting ```bash theme={null} # Run ESLint pnpm lint # Format code with Prettier pnpm format ``` ### Testing ```bash theme={null} # Run tests pnpm test # Run tests in watch mode pnpm test --watch ``` ### Debugging To debug the backend with Node.js inspector: ```bash theme={null} pnpm backend:debug ``` Then attach your debugger to `http://localhost:9229`. ## Making Changes ### Backend Development 1. **Controllers**: Handle HTTP requests and responses 2. **Services**: Implement business logic 3. **Models**: Define database schemas 4. **Routes**: Define API endpoints ### Frontend Development 1. **Components**: Reusable React components 2. **Pages**: Route-specific components 3. **Services**: API communication 4. **Hooks**: Custom React hooks ### Adding New MCP Servers 1. Update `mcp_settings.json` with the new server configuration 2. Test the server integration 3. Update documentation if needed ## Common Development Tasks ### Adding a New API Endpoint 1. Create a controller in `src/controllers/` 2. Define the route in `src/routes/` 3. Add any necessary middleware 4. Write tests for the new endpoint ### Adding a New Frontend Feature 1. Create components in `frontend/src/components/` 2. Add routes if needed 3. Implement API integration 4. Style with Tailwind CSS ### Database Migrations When modifying database schemas: 1. Update models in `src/models/` 2. Create migration scripts if using TypeORM 3. Test migrations locally ## Troubleshooting ### Common Issues **Port conflicts**: Ensure ports 3000 and 5173 are available **Database connection**: Verify PostgreSQL is running and credentials are correct **MCP server startup**: Check server configurations in `mcp_settings.json` **Permission issues**: Ensure MCP servers have necessary permissions ### Getting Help * Check the [Contributing Guide](/development/contributing) * Review [Architecture Documentation](/development/architecture) * Open an issue on GitHub for bugs * Join our community discussions ## Next Steps * Read the [Architecture Overview](/development/architecture) * Learn about [Contributing Guidelines](/development/contributing) * Explore [Configuration Options](/configuration/environment-variables) # Authentication & Security Source: https://docs.mcphub.app/features/authentication How clients authenticate against MCPHub ## Self-registration Administrators can enable **Allow self-registration** under **Settings → Security**. It is disabled by default. When enabled, the login page offers **Create account**. New accounts are always regular users; registration cannot grant administrator privileges. Usernames still referenced by resource ownership, sharing lists, authentication state, or user configuration cannot be claimed through self-registration, even after the original account is deleted. Passwords must contain at least eight characters, including a letter, a number, and a special character. The setting is stored as `systemConfig.auth.allowRegistration` in `mcp_settings.json` and works with both file and database storage. It takes effect without a restart. When password login is disabled by the effective SSO configuration, local registration is also refused and the registration entry is hidden. Existing accounts remain available. ## Overview The dashboard API supports the following authentication paths (configured forward auth is evaluated first): 1. **Bearer key** — a static token created from the dashboard or `/api/auth/keys`. 2. **Better Auth session cookie** — issued by GitHub / Google login (optional). 3. **JWT** — issued by `POST /api/auth/login`, sent via the `x-auth-token` header or `?token=` query. 4. **`skipAuth` guest** — only when `systemConfig.routing.skipAuth` is `true`, the dashboard API treats unauthenticated calls as a synthetic admin `guest` user. All persistent user records are stored as `{ username, password, isAdmin }`. There are no `manager` or `viewer` roles; permissions are binary (admin or not). Social accounts that log in via Better Auth live in a separate `mcphub_better_auth_*` table but are mapped to the same `username` / `isAdmin` shape at request time. ### Per-server visibility Each server has a `visibility` setting (`private` / `group` / `public`) that controls which non-admin users can discover and call the server. Admins always have access. The same filter is applied to dashboard listings, direct tool and prompt calls, group routes, and `$smart` routing. * `'private'` (default): only the server's `owner` (and admins) can see it. Servers seeded from `mcp_settings.json` migrate in with `'private'` to preserve the historical admin-only behaviour. * `'public'`: every authenticated user can see and call the server. Use this for servers you want to share across all users of the same MCPHub instance — including social-login users created by Better Auth. * `'group'`: the owner and usernames listed in the server's `sharedWithUsers` array can see and call it. Sharing grants runtime access only; selected users cannot edit or delete the server. The visibility is editable per server from the dashboard (Server form → Advanced Options → Visibility). Edit an existing server to select local usernames for `'group'` sharing. For deployments where every user should see every server (single-user self-hosted, small trusted team), an operator can set the relevant servers to `'public'` from the dashboard. ## Login (JWT) The dashboard and any script that does not present a bearer key uses the JWT flow: ```bash theme={null} curl -X POST http://localhost:3000/api/auth/login \ -H "Content-Type: application/json" \ -d '{ "username": "admin", "password": "your-password" }' ``` The response includes a JWT. Send it on subsequent requests using the `x-auth-token` header: ```bash theme={null} curl http://localhost:3000/api/servers \ -H "x-auth-token: " ``` The JWT is signed with `JWT_SECRET`. If `JWT_SECRET` is not set, MCPHub generates an ephemeral random secret at startup; this is fine for development but means every restart invalidates outstanding tokens. **Set `JWT_SECRET` explicitly in production.** Issued JWTs expire after a fixed 24 hours (`TOKEN_EXPIRY` in `src/controllers/authController.ts`); the lifetime is not configurable via environment variables. JWTs and MCPHub OAuth grants are bound to the user's current password hash. Deleting the local user or changing their password invalidates those credentials; recreating the same username does not restore them. JWT authorization reads the user's current administrator status. User deletion also removes OAuth tokens, pending authorization codes on the handling instance, and entries in server/group sharing lists. Upgrading from credentials without a session version requires signing in again and reconnecting OAuth clients. Database deployments add a nullable `session_version` column to `oauth_tokens` through the normal startup schema synchronization. Restart every replica when upgrading. `POST /api/auth/register` follows the opt-in self-registration policy above and always creates a non-admin user. Use the administrator user-management API to assign roles. The first admin account is created at startup by `initializeDefaultUser()`. If no users exist, MCPHub creates `admin` and uses `ADMIN_PASSWORD` if set. When running through `pnpm dev`, it uses `admin123`; other starts generate a random password and print it to the server logs. ## Bearer keys Bearer keys are long-lived static tokens managed at `/api/auth/keys`. Tokens are generated by the backend and returned in plaintext only once, in the create response. Later list and update responses contain a masked value. | Field | Description | | - | - | | `name` | Human-readable label. | | `token` | The secret value sent in the `Authorization: Bearer ...` header. | | `kind` | `system` for operator-managed scoped keys, or `user` for keys that inherit an owner's visibility. | | `owner` | Required for user-level keys. | | `accessType` | `all` (dashboard + MCP), or a scoped value enforced inside `sseService.ts` for MCP-only access. | | `enabled` | Disabled keys are ignored without being deleted. | Admins can create and manage all keys. Regular users can create, rename, enable, disable and delete their own user-level keys from the settings page. User-level keys are MCP transport credentials only: they are never accepted by the dashboard / management API. When used on `/mcp`, `/mcp/$smart` or a group/server endpoint, MCPHub resolves the current owner record and applies that user's live server visibility. The optional `/:user/mcp/...` routes remain supported, but the URL username must match the key owner. System-level keys preserve the original scoped-key behavior. Only system-level keys with `accessType === 'all'` are accepted by the dashboard / management API middleware. Restricted system-level keys are enforced at the MCP transport layer. The header name can be customized via `systemConfig.routing.bearerAuthHeaderName` (default `Authorization`). The legacy `systemConfig.routing.bearerAuthKey` field is preserved only for one-time migration into the bearer key store. To globally turn off bearer auth on MCP endpoints, set `systemConfig.routing.enableBearerAuth` to `false`. ## MCPHub OAuth authorization server When `systemConfig.oauthServer.enabled` is `true`, MCPHub exposes an OAuth 2.0 / OIDC-compatible server (`@node-oauth/oauth2-server`). Endpoints: | Method & Path | Purpose | | - | - | | `GET /oauth/authorize`, `POST /oauth/authorize` | Authorization endpoint (PKCE supported). | | `POST /oauth/token` | Token endpoint. | | `GET /oauth/userinfo` | OIDC-style userinfo (also validates an access token). | | `GET /.well-known/oauth-authorization-server` | RFC 8414 metadata. | | `GET /.well-known/oauth-protected-resource` | Protected resource metadata. | | `POST /oauth/register`, `GET/PUT/DELETE /oauth/register/:clientId` | RFC 7591 dynamic client registration. | | `GET/POST/PUT/DELETE /api/oauth/clients[...]` | Admin-only client CRUD. | OAuth access tokens issued here authenticate MCP transport requests and `/oauth/userinfo`. They are not dashboard API credentials and are rejected on `/api/*`; use a dashboard JWT, Better Auth session, configured forward auth, or a system all-access bearer key for management. ## Better Auth (GitHub / Google / local OIDC) Better Auth is optional and initialized once at process startup. That means any change to either `BETTER_AUTH_*` environment variables or stored `systemConfig.auth.betterAuth` values still requires a restart. Non-secret Better Auth settings can now live either in environment variables or in `systemConfig.auth.betterAuth` (from `mcp_settings.json` or the database-backed system config). The priority order is: 1. `BETTER_AUTH_*` environment variables 2. `systemConfig.auth.betterAuth` 3. Built-in defaults Provider credentials remain environment-variable only. Required variables (see `.env.example`): * `BETTER_AUTH_ENABLED` — master switch for Better Auth. * `BETTER_AUTH_URL` — public base URL of MCPHub (used to build redirect URIs). * `BETTER_AUTH_BASE_PATH` — optional mount path override for the Better Auth handler. * `BETTER_AUTH_TRUSTED_ORIGINS` — optional extra trusted origins (comma-separated, whitespace-separated, or JSON array). * `BETTER_AUTH_DISABLE_PASSWORD_LOGIN` — turn off username/password login while an SSO provider is enabled (see [SSO-only sign-in](#sso-only-sign-in)). * `BETTER_AUTH_GOOGLE_ENABLED` — enable / disable Google login when credentials are present. * `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` — for Google login. * `BETTER_AUTH_GITHUB_ENABLED` — enable / disable GitHub login when credentials are present. * `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` — for GitHub login. * `BETTER_AUTH_OIDC_ENABLED` — enable / disable the generic OIDC provider. * `BETTER_AUTH_OIDC_PROVIDER_ID` — OIDC provider identifier (defaults to `oidc`). * `BETTER_AUTH_OIDC_DISCOVERY_URL` — discovery URL for a local issuer. * `BETTER_AUTH_OIDC_SCOPES` — optional requested scopes for local OIDC login. * `BETTER_AUTH_OIDC_PKCE` — optional PKCE toggle for local OIDC login. * `BETTER_AUTH_OIDC_PROMPT` — optional `prompt` parameter for local OIDC login. * `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` — for a local OIDC provider. For a local issuer (for example Keycloak, Authentik, or Dex), you can either configure the metadata entirely through environment variables, or keep the non-secret values in `systemConfig.auth.betterAuth.providers.oidc` with: * `enabled` * `providerId` * `discoveryUrl` * optional `scopes`, `pkce`, and `prompt` The `discoveryUrl` should point at the provider's `/.well-known/openid-configuration` endpoint. `OIDC_DISCOVERY_URL` remains supported as a legacy alias for `BETTER_AUTH_OIDC_DISCOVERY_URL`, and it can still be referenced from config files via `${OIDC_DISCOVERY_URL}`. The login page shows a generic **Continue with OIDC** button when this provider is enabled. If the dashboard runs behind a different public origin, set `BETTER_AUTH_TRUSTED_ORIGINS` (or `systemConfig.auth.betterAuth.trustedOrigins`) to allow that origin to start the login flow. If no explicit trusted origins are configured, MCPHub automatically trusts the origins from `BETTER_AUTH_URL` and `systemConfig.install.baseUrl` when they are set. > [!NOTE] > > Better Auth in MCPHub currently requires PostgreSQL-backed storage (`DB_URL`). File-only mode does not support Better Auth sessions, including local OIDC login. When enabled, Better Auth mounts at `${BASE_PATH}${betterAuthConfig.basePath}` (default `/api/auth/better`). `BETTER_AUTH_BASE_PATH` overrides the stored `betterAuth.basePath` value at startup. A valid Better Auth session cookie counts as authentication for the dashboard API. The mapped MCPHub user is admin only if a local user with the same username exists and is marked admin. Local OIDC login follows the same mapping rule as GitHub / Google login: MCPHub resolves a local username from the Better Auth session (`email ?? name ?? id`). If that username does not exist yet, MCPHub auto-creates a **non-admin** local user. OIDC login never auto-promotes a user to admin. ### SSO-only sign-in To allow sign-in only through the SSO providers, turn on **Disable password login** in Settings → Better Auth, or set `systemConfig.auth.betterAuth.disablePasswordLogin: true`. The login page then shows only the provider buttons, and `POST /api/auth/login` answers `403`. Unlike the other Better Auth settings, it takes effect without a restart. * **No lockout:** it applies only while at least one provider is actually enabled (Better Auth on, database mode, credentials present) and was enabled when MCPHub started, since a provider's sign-in route is set up only at startup. Otherwise password login stays available: after enabling a new provider, restart MCPHub before the switch applies. * **Recovery:** `BETTER_AUTH_DISABLE_PASSWORD_LOGIN` overrides the stored setting in both directions. If the identity provider is unavailable, set it to `false` and restart to sign in with a local password again. Keep a strong password on the local admin for that case. * **Scripts and the CLI** cannot use the password login while it is disabled. Use a system-level bearer key with access type `all`, which authenticates the management API (`mcphub config set-token --bearer` for the CLI). * Tokens issued by earlier password logins stay valid until they expire. Changing a local password still works, so the recovery password can be rotated. ## Forward auth (JWKS-verified gateway identity) When MCPHub runs behind an API gateway or identity-aware proxy that already authenticates users, forward auth lets MCPHub trust the gateway's JWT instead of asking users to log in again. MCPHub verifies the token itself against the identity provider's JWKS, so it remains the security boundary. Forward auth is **off by default** and applies to both the dashboard API and the MCP transport routes (`/mcp/*`, `/sse/*`, and their user-scoped variants). ```json theme={null} "systemConfig": { "auth": { "forwardAuth": { "enabled": true, "mode": "jwks", "jwks": { "uri": "https://idp.example.com/.well-known/jwks.json", "issuer": "https://idp.example.com/", "audience": "mcphub", "algorithms": ["RS256", "ES256"] }, "usernameClaim": "preferred_username", "emailClaim": "email", "autoCreate": true } } } ``` The JWT is read from the configured bearer header (`Authorization: Bearer ` by default). **Verification** * The token is verified against `jwks.uri`. Keys are cached and refetched when a token carries an unknown `kid`, so key rotation at the identity provider needs no restart. * `iss`, `aud`, and `exp` are always validated; `sub` and `exp` are required. A 30-second clock tolerance applies. * Only asymmetric algorithms are accepted (`RS*`, `PS*`, `ES*`, `EdDSA`; default `RS256` and `ES256`). Listing `HS*` or `none` disables forward auth. * `jwks.uri` must use `https` (plain `http` is allowed only for `localhost`). `issuer` and `audience` are required. If the configuration is incomplete or invalid, forward auth stays disabled and MCPHub logs an error. **Request handling** * Forward auth is checked before bearer keys, OAuth tokens, Better Auth sessions, and dashboard JWTs. * A token that is not a JWT, or whose `iss` is not the configured issuer, is ignored and the other methods run as usual. * A token from the configured issuer that fails verification (bad signature, expired, wrong audience, disallowed algorithm) or cannot be mapped to a user is rejected with `401`. MCPHub **never falls back** to another authentication method for it. **Identity mapping** * The identity is the pair (`iss`, `sub`). It is stored in the user's `ssoUserId` under a `forward-auth:` namespace, separate from Better Auth identities. A Better Auth login never adopts a forward-auth user, and forward auth never adopts a Better Auth user. * On first sight, MCPHub creates a **non-admin** local user. Its username comes from `usernameClaim`, falling back to `emailClaim`, then `sub`. * Existing accounts are **never linked** by username or email. If the username or email already belongs to another account, including `admin`, the request is rejected with `401`. * Once bound, the local username does not change when the user is renamed at the identity provider, because servers, groups, and keys reference the username. * Admin status always comes from the local user record. Role claims in the JWT are ignored. * With `autoCreate: false`, only identities already bound to a local user are accepted. Because existing accounts are never linked, this effectively rejects every new identity. Forward-auth settings are read on each request, so changes do not require a restart. Each field can be overridden by an environment variable; see [Environment variables](/configuration/environment-variables#forward-auth). ## Read-only mode Set `READONLY=true` to prevent any mutating request from succeeding. The middleware permits: * All `GET` requests. * All paths starting with `${basePath}/tools/` (so OpenAPI tool execution still works). Other methods receive `403`. ## Skip-auth mode (local development only) > [!WARNING] > > **`skipAuth` disables dashboard authentication entirely.** When `systemConfig.routing.skipAuth = true`, every unauthenticated request to `${basePath}/api/*` is automatically treated as a synthetic admin user (`{ username: 'guest', isAdmin: true }`). Anyone who can reach the API — including a misconfigured reverse proxy, a forgotten port-forward, or a public cloud IP — gets full administrative access: they can read every server's secrets, create / delete users, mint bearer keys, and exfiltrate the entire `mcp_settings.json`. **Never enable this in staging, production, demo, or any deployment that is reachable beyond `localhost`.** Bind MCPHub to `127.0.0.1` while developing, and double-check that `skipAuth` is `false` before deploying. MCP transport routes (`/mcp/*`, `/sse/*`) are not affected by `skipAuth` and continue to require their own credentials. The flag exists purely to make local iteration painless: you can hit the dashboard API from `curl` or a frontend dev server without pasting a JWT. If you need API access from automated tooling instead, prefer a scoped bearer key (`/api/auth/keys`) over `skipAuth`. ## Password policy Local user passwords are validated by `src/utils/passwordValidation.ts` (length and character-class rules). Failed validations on `POST /api/users` or `PUT /api/users/:username` return `400` with the failed checks under `errors`. ## See also * [Users API](/api-reference/users) — local user CRUD. * [OAuth API](/api-reference/auth) — OAuth client management endpoints. * [MCP Settings](/configuration/mcp-settings) — where `routing`, `oauth`, and `auth.betterAuth` configuration lives. # CLI Source: https://docs.mcphub.app/features/cli Manage a running mcphub instance and call MCP tools from the terminal ## Overview The `mcphub` binary doubles as a CLI for the running hub. Invoke `mcphub` with no arguments to start the server (the original behavior); pass a subcommand to drive a remote (or local) hub over its HTTP API. ```bash theme={null} mcphub --help # list every subcommand mcphub [options] # run a CLI command mcphub # legacy: start the server (unchanged) ``` The CLI ships with the same npm package as the server — there's nothing extra to install. ## Logging in ```bash theme={null} mcphub login --url http://localhost:3000 --username admin --password ******** ``` A JWT is cached in an XDG-compliant credentials file with `0600` permissions: * macOS/Linux: `$XDG_DATA_HOME/mcphub/credentials.json` (default `~/.local/share/mcphub/credentials.json`) * Legacy: `~/.mcphub/credentials.json` is still honored when it already exists Multiple **profiles** are supported, so a single shell can target `staging`, `prod`, etc. side-by-side: ```bash theme={null} mcphub login --profile staging --url https://staging.hub.example mcphub config use prod mcphub config list ``` You can also bypass the credentials file entirely: | Override | Where it comes from | | - | - | | `--url`, `--token`, `--bearer` flags | per-command | | `MCPHUB_URL`, `MCPHUB_TOKEN`, `MCPHUB_TOKEN_KIND` env | shell session / CI | | Active profile in `credentials.json` | default | Resolution order: flag → environment variable → active profile. ## Global options | Option | Purpose | | - | - | | `--url ` | Override the active profile's base URL | | `--token ` | Override the active profile's token | | `--bearer` | Treat `--token` as a bearer key (default is JWT via `x-auth-token`) | | `--profile ` | Pick a saved profile other than `current` | | `--json` | Print JSON instead of human-friendly tables | | `--debug` | Print stack traces on error | ## Commands ### `servers` ```bash theme={null} mcphub servers list mcphub servers get # Add from a JSON file: mcphub servers add fetch --from-file ./fetch.json # Or inline (--arg / --env can repeat): mcphub servers add fetch \ --type stdio --command uvx \ --arg mcp-server-fetch \ --env USER_AGENT=mcphub mcphub servers remove fetch mcphub servers toggle fetch --on mcphub servers reload fetch ``` ### `groups` ```bash theme={null} mcphub groups list mcphub groups add dev --description "developer tools" mcphub groups add-server dev fetch mcphub groups remove-server dev fetch mcphub groups remove dev ``` `` accepts either the UUID or the human-readable name; the CLI resolves the name to an id by listing groups first. ### `keys` Manage bearer keys. Admins manage system-level and user-level keys; ordinary users manage their own user-level keys. ```bash theme={null} mcphub keys list mcphub keys create --name ci --access-type all mcphub keys create --name dev-only --access-type groups --groups dev,staging mcphub keys delete ``` The hub-generated token is printed once on `create` — copy it before navigating away. ### `tools` — discover what to call `tools` is the agent-friendly index for `call`: it answers **what tools exist, what params each one takes, which server hosts it** without having to hand-parse `servers list` JSON. ```bash theme={null} # Flat list of every tool across every connected server. mcphub tools list mcphub tools list --json # machine-readable, no schemas mcphub tools list --schema --json # include each tool's inputSchema mcphub tools list --server fetch # scope to one server mcphub tools list --enabled-only # Inspect one tool: description, parameter table, full input schema, sample command. mcphub tools get fetch_url mcphub tools get fetch_url --server fetch # disambiguate when two servers expose the same name mcphub tools schema fetch_url # alias for `get` ``` The `tools get` text view prints a parameter table marking which keys are `required`, followed by the full JSON schema and a ready-to-paste `mcphub call ...` example with the required params filled in. ### `call` Call an MCP tool against the active profile's hub. By default `/mcp/$smart` is used so smart routing picks the right tool. The recommended agent workflow is **`tools list` → `tools get` → `call`**. ```bash theme={null} # 1. Find a tool mcphub tools list --json | jq '.[] | select(.name | contains("fetch"))' # 2. Check its schema mcphub tools get fetch_url --json | jq '.inputSchema' # 3. Call it mcphub call fetch_url url=https://example.com timeout=30 mcphub call fetch_url url=https://example.com --server fetch # pin to a specific server mcphub call my_tool --group dev nested='{"a":1}' mcphub call my_tool --params-json '{"deep":{"v":1}}' ``` Routing precedence inside `call`: `--smart` > `--server ` > `--group ` > default (`$smart`). All three resolve to the same `/mcp/` endpoint; `--server` is the natural pair for `tools list`/`tools get` output. `key=value` argument coercion: | Form | Result | | - | - | | `n=42`, `f=1.5` | number | | `b=true`, `b=false` | boolean | | `x=null` | `null` | | `obj={"a":1}`, `arr=[1,2]` | parsed JSON | | `payload=@./body.json` | JSON loaded from file | | `phone=0123` | preserved as the string `"0123"` (leading-zero protection) | | anything else | string | Pass `--no-coerce` to treat every value as a string. ### `export` Download `mcp_settings.json` from the running hub. ```bash theme={null} mcphub export --out backup.json mcphub export > backup.json ``` ### `discover` / `install` (marketplace) When the hub has `systemConfig.discovery.enabled = true`, the CLI can browse the public marketplace and install servers into your own hub or directly into a client-side config file. ```bash theme={null} # Browse the active profile's hub mcphub discover --search github --limit 10 mcphub discover info amap-maps mcphub discover categories mcphub discover tags # Install into the active hub (POST /api/servers) mcphub install amap-maps --env AMAP_MAPS_API_KEY=... # Install from a different hub into the active one mcphub install some-server --remote https://hub.example.com --yes # Dry-run: just print the mcpServers snippet mcphub install amap-maps --dry-run # Install into a Claude Desktop / OpenClaw-style config file mcphub install amap-maps --to file --out ~/Library/Application\ Support/Claude/claude_desktop_config.json ``` Notes: * `--type` picks the installation method (`npm`, `docker`, `uvx`, `pip`, `binary`). If omitted, the hub picks the first available; if the requested type doesn't exist, the CLI lists the alternatives. * `--env KEY=VAL` (repeatable) injects environment values — both predeclared and additional keys are merged into the resulting `env` block. * `--to file` writes atomically (temp file + rename), so a crashed write can't corrupt your client config. `mcphub call` initializes an MCP session before invoking the tool, accepts JSON or SSE responses, and closes the session after the call. This supports both older sessionful hubs and current hubs through their compatible Streamable HTTP endpoint. ## Exit codes | Code | Meaning | | - | - | | `0` | Success | | `1` | Usage error (bad flag, missing argument, unauthorized state) | | `2` | API error (non-2xx response from the hub) | ## Autonomous-agent recipe Every command supports `--json` and reads `MCPHUB_URL` / `MCPHUB_TOKEN`, so an agent can drive the entire discover → call loop from JSON: ```bash theme={null} export MCPHUB_URL=http://localhost:3000 export MCPHUB_TOKEN=$JWT # 1. enumerate tools mcphub tools list --json > tools.json # 2. pick one and read its schema mcphub tools get fetch_url --json > schema.json # 3. build a payload from the schema and invoke mcphub call fetch_url --params-json "$(jq -n '{url:"https://example.com"}')" --json ``` The `tools list --json` response is intentionally flat (`{server, serverStatus, name, description, enabled}`) so an agent can filter in one pass without traversing nested `servers[*].tools[*]`. ## Scripting and CI The CLI is JSON-friendly: `--json` is honored by every command, and `MCPHUB_URL` / `MCPHUB_TOKEN` make it easy to skip the credentials file in CI. ```bash theme={null} export MCPHUB_URL=https://hub.example.com export MCPHUB_TOKEN=$CI_HUB_TOKEN mcphub servers list --json | jq '.[] | select(.status != "connected") | .name' ``` For bearer-key auth in CI, add `MCPHUB_TOKEN_KIND=bearer` (or `--bearer`). Note that only system-level bearer keys with `accessType=all` authorize the `/api/*` management surface. Scoped system keys and user-level keys are for MCP transports only. # Group Management Source: https://docs.mcphub.app/features/group-management Organize servers into logical groups for streamlined access control ## Overview Group Management in MCPHub allows you to organize your MCP servers into logical collections based on functionality, use case, or access requirements. This enables fine-grained control over which tools are available to different AI clients and users. ## Core Concepts ### What are Groups? Groups are named collections of MCP servers that can be accessed through dedicated endpoints. Instead of connecting to all servers at once, AI clients can connect to specific groups to access only relevant tools. ### Benefits of Groups * **Focused Tool Access**: AI clients see only relevant tools for their use case * **Better Performance**: Reduced tool discovery overhead * **Enhanced Security**: Limit access to sensitive tools * **Improved Organization**: Logical separation of functionality * **Simplified Management**: Easier to manage related servers together ## Creating Groups ### Via Dashboard 1. **Navigate to Groups Section**: Click "Groups" in the main navigation 2. **Click "Create Group"**: Start the group creation process 3. **Fill Group Details**: * **Name**: Unique identifier for the group 4. **Add Servers**: Select servers to include in the group ### Bulk Capability Selection Expand a server in the group editor to search tools, prompts, or resources by name (resource URI) or description. Search is a case-insensitive substring match, not a glob or regular expression. **Select visible**, **Deselect visible**, and **Invert visible** affect only the filtered items; other selections stay unchanged. Tools also support explicit `readOnlyHint`, `destructiveHint`, and `idempotentHint` filters. Switch between the flat list and risk groups (Read/Create/Update/Delete/Other) for per-group counters and batch selection. Classification uses upstream hints and name heuristics as an editing aid, not a safety guarantee. Group actions also respect the current filters. These actions modify this group's selection only, not the server's global enabled state. Deselecting tools clears their Smart Routing pins. Editing an explicit subset stores a snapshot of tool names; it does not automatically select future tools. The existing whole-section **Select All** retains the dynamic `all` selection behavior. ### Via API Create groups programmatically: ```bash theme={null} curl -X POST http://localhost:3000/api/groups \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{ "name": "web-automation", "servers": ["playwright", "fetch"] }' ``` ## Group Types and Use Cases > **Web Automation Group** > > **Purpose**: Browser automation and web scraping > > **Servers**: > > * `playwright`: Browser automation > * `fetch`: HTTP requests and web scraping > * `selenium`: Alternative browser automation > > **Use Cases**: > > * Automated testing > * Data collection > * Web monitoring > * Content analysis > > **Endpoint**: `http://localhost:3000/mcp/web-automation` > **Data Processing Group** > > **Purpose**: Data manipulation and analysis > > **Servers**: > > * `sqlite`: Database operations > * `filesystem`: File operations > * `spreadsheet`: Excel/CSV processing > > **Use Cases**: > > * Data analysis > * Report generation > * File processing > * Database queries > > **Endpoint**: `http://localhost:3000/mcp/data-processing` > **Communication Group** > > **Purpose**: Messaging and collaboration > > **Servers**: > > * `slack`: Slack integration > * `discord`: Discord bot > * `email`: Email sending > * `sms`: SMS notifications > > **Use Cases**: > > * Team notifications > * Customer communication > * Alert systems > * Social media management > > **Endpoint**: `http://localhost:3000/mcp/communication` > **Development Group** > > **Purpose**: Software development tools > > **Servers**: > > * `github`: GitHub operations > * `gitlab`: GitLab integration > * `docker`: Container management > * `kubernetes`: K8s operations > > **Use Cases**: > > * Code deployment > * Repository management > * CI/CD operations > * Infrastructure management > > **Endpoint**: `http://localhost:3000/mcp/development` > **AI/ML Group** > > **Purpose**: Machine learning and AI tools > > **Servers**: > > * `openai`: OpenAI API integration > * `huggingface`: Hugging Face models > * `vector-db`: Vector database operations > > **Use Cases**: > > * Model inference > * Data embeddings > * Natural language processing > * Computer vision > > **Endpoint**: `http://localhost:3000/mcp/ai-ml` ## Group Endpoints ### Accessing Groups Each group gets its own MCP endpoint: **HTTP MCP** ``` http://localhost:3000/mcp/{group-name} ``` Examples: * `http://localhost:3000/mcp/web-tools` * `http://localhost:3000/mcp/data-processing` * `http://localhost:3000/mcp/communication` **SSE (Legacy)** ``` http://localhost:3000/sse/{group-name} ``` Examples: * `http://localhost:3000/sse/web-tools` * `http://localhost:3000/sse/data-processing` * `http://localhost:3000/sse/communication` ### Group Tool Discovery When connecting to a group endpoint, AI clients will only see tools from servers within that group: ```bash theme={null} # List tools in web-tools group curl -X POST http://localhost:3000/mcp/web-tools \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }' ``` Response will only include tools from `fetch` and `playwright` servers. ### Group instructions MCP clients receive an optional `instructions` string from the `initialize` response, and most of them add it to the model's system prompt. It is the place to tell the model when to use a connection, for example to prefer the group's fetch tool over a built-in one, or which tool to try when another fails. Set it with the group's `instructions` field (dashboard, `POST`/`PUT /api/groups`, or the group entry in `mcp_settings.json`). It is sent on: * `/mcp/{group}`: for a group with several servers, the group's text alone. For a single-server group, the group's text followed by the server's own upstream instructions (a single-server group already forwarded those). * `/mcp/$smart/{group}`: the group's text. Bare `/mcp/$smart`, the global route and server-name routes have no group, so they send no group instructions. Clients read instructions once per session: reconnect after changing them. ```json theme={null} { "name": "web", "instructions": "For web pages use fetch first. If it fails (403, blocked, empty or JavaScript-only page), retry the same URL with firecrawl_scrape.", "servers": ["fetch", "firecrawl"] } ``` ## Dynamic Group Management ### Adding Servers to Groups **Dashboard** 1. Navigate to the group in the dashboard 2. Click "Manage Servers" 3. Select additional servers to add 4. Click "Save Changes" **API** ```bash theme={null} curl -X POST http://localhost:3000/api/groups/web-tools/servers \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{"serverId": "new-server"}' ``` ### Removing Servers from Groups **Dashboard** 1. Navigate to the group in the dashboard 2. Click "Manage Servers" 3. Unselect servers to remove 4. Click "Save Changes" **API** ```bash theme={null} curl -X DELETE http://localhost:3000/api/groups/web-tools/servers/server-name \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ## Best Practices ### Group Organization > [!TIP] > > **Organize by Use Case**: Group servers based on what users want to accomplish, not just technical > similarity. > [!TIP] > > **Keep Groups Focused**: Avoid creating groups with too many diverse tools. Smaller, focused > groups are more useful. > [!TIP] > > **Use Descriptive Names**: Choose names that clearly indicate the group's purpose and contents. ### Performance Optimization > [!NOTE] > > **Balance Group Size**: Very large groups may have slower tool discovery. Consider splitting into > smaller groups. > [!NOTE] > > **Monitor Usage**: Use analytics to identify which groups are heavily used and optimize > accordingly. ## Troubleshooting > **Group Not Accessible** > > **Check:** > > * User has proper permissions > * Group exists and is active > * Servers in group are running > * Network connectivity > > **Solutions:** > > 1. Verify user group membership > 2. Check group configuration > 3. Test individual server connections > 4. Review access logs > **Tools Missing from Group** > > **Possible causes:** > > * Server not properly added to group > * Server is not running > * Tool discovery failed > * Caching issues > > **Debug steps:** > > 1. Verify server is in group configuration > 2. Check server status > 3. Force refresh tool discovery > 4. Clear group cache > **Group Performance Issues** > > **Common issues:** > > * Too many servers in group > * Slow server responses > * Network latency > * Resource constraints > > **Optimizations:** > > 1. Split large groups > 2. Monitor server performance > 3. Implement request caching > 4. Use connection pooling ## Next Steps > **[Smart Routing](/features/smart-routing)** > > AI-powered tool discovery across groups > **[Authentication](/features/authentication)** > > User management and access control > **[API Reference](/api-reference/groups)** > > Complete group management API > **[Configuration](/configuration/mcp-settings)** > > Advanced group configuration options ## Visibility and sharing New groups default to `private`. Set `visibility` to `private` (owner and admins), `group` (owner, admins, and usernames in `sharedWithUsers`), or `public` (all users). The dashboard labels `group` as Shared. Save a group before choosing users in its edit form. Only the owner and admins can change or delete a group, including its server configuration. For user-authenticated MCP requests, access requires both group and server visibility, subject to the legacy group behavior described below. Sharing a group never grants access to a server the user cannot otherwise access. These checks also apply to SSE and `$smart/` routes, and group access is checked again for existing sessions. User bearer keys (`kind: 'user'`) inherit their owner's live permissions and do not support an independent key scope. The API creates them with `accessType: 'all'` and empty `allowedGroups`/`allowedServers`; these scope fields do not restrict user keys at runtime. Here, `all` means the owner's permitted resources, not unrestricted system access. System bearer keys (`kind: 'system'`) use their configured operator scope without applying user-based group or server visibility. A system key with `accessType: 'all'` can therefore access private groups and servers; scoped system keys remain limited to their configured scope. System keys do not establish a user identity or provide personal credentials. Group lists, details, and server configuration responses omit servers the caller cannot access, including their names, aliases, and capability selections. When an owner replaces the visible server selection, inaccessible existing entries are preserved; an admin can manage the complete group. ### Upgrading existing groups Groups without a visibility value retain legacy endpoint access: users who know the endpoint can use servers they are authorized to access. Their directory visibility remains owner/admin only. The dashboard flags these groups for an explicit visibility choice; saving unrelated fields does not silently make them private or public. Database mode adds a unique constraint on group names. Startup checks for duplicate names before schema synchronization and stops with the conflicting names/counts if any exist. Resolve duplicates manually before restarting; MCPHub does not rename or delete them. Group names remain globally reserved, including private groups. Contact an administrator for conflicts you cannot resolve from your directory. # MCP Apps Source: https://docs.mcphub.app/features/mcp-apps Proxy interactive MCP Apps through MCPHub routes, including multi-server groups ## Overview MCPHub supports [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) as a transparent proxy. It forwards UI-linked tool metadata, app-only tools, `ui://` resources, and upstream list-change notifications. MCPHub does not render app UIs in its dashboard. No MCPHub configuration switch is required. Support is enabled automatically when the downstream host advertises the `io.modelcontextprotocol/ui` extension with the `text/html;profile=mcp-app` MIME type. Legacy clients declare this during initialization; modern `2026-07-28` clients declare it on each request under `params._meta["io.modelcontextprotocol/clientCapabilities"]`. See the [protocol compatibility matrix](/api-reference/mcp-http#protocol-compatibility). ## Routing Requirements MCP Apps passthrough is enabled whenever a request route resolves to at least one connected MCP upstream server: * `/mcp/{server}` * `/mcp/{group}`, whether the group contains one visible MCP server or several * `/mcp` when at least one MCP server is visible to the caller `$smart` routes are the one exception and continue to work as ordinary MCP routes, since Smart Routing tools aren't addressed by name at all. On that route MCPHub: * hides app-only tools such as tools with `_meta.ui.visibility: ["app"]` * removes Apps-specific `_meta.ui` and legacy `_meta["ui/resourceUri"]` fields * rejects direct calls to app-only tools * rejects unlisted `ui://` resource reads Direct server routes and group or aggregate routes use different tool names. See below. ## Tool Names Direct server routes such as `/mcp/weather-station` expose upstream tool names unchanged, such as `forecast`, regardless of client Apps support or upstream idle state. MCPHub removes only its own prefix; prefixes in upstream tool names remain intact. Group and aggregate routes keep qualified names (`weather-station::forecast`), even with only one member. Group aliases and the configured name separator still apply. Apps widgets on these routes should use the qualified name from the tool listing or host context. Previously advertised raw and qualified names remain callable where the route resolves them unambiguously, subject to the same tool visibility and access restrictions. On direct routes, upstream raw names take precedence over colliding legacy qualified aliases, including names such as `call_tool`, `search_tools`, and `describe_tool`. Apps support still controls Apps metadata and app-only tool visibility. ## UI Resources MCP Apps servers may omit UI-only resources from `resources/list`. On an Apps-eligible route, MCPHub forwards direct `resources/read` calls for unlisted `ui://` URIs: * On a single-server route, the request goes straight to that server. * On a multi-server route, MCPHub has no prior way to know which upstream owns a given `ui://` URI, since it isn't listed. It probes each connected server in the group in turn and returns the first successful response. Ordinary listed resources remain readable, but Apps-specific metadata is removed outside Apps-eligible routes. ## Dynamic Lists MCPHub subscribes to upstream `tools/list_changed`, `resources/list_changed`, and `prompts/list_changed` notifications. It refreshes its cache, updates Smart Routing embeddings for model-visible tools, and notifies connected downstream clients. # Monitoring & Observability Source: https://docs.mcphub.app/features/monitoring What MCPHub exposes for health checks, logs, and activity tracking MCPHub exposes health checks, an optional Prometheus metrics endpoint, logs, and activity tracking. OpenTelemetry exporters and alerting pipelines are not bundled. ## Health endpoint `GET /health` — public, no authentication. ```bash theme={null} curl http://localhost:3000/health ``` Sample response: ```json theme={null} { "status": "healthy", "message": "All enabled MCP servers are ready", "servers": { "connected": 3, "connecting": 0, "disconnected": 0, "total": 3 }, "timestamp": "2026-05-18T12:34:56.789Z" } ``` `status` is one of: | Value | HTTP code | Meaning | | - | - | - | | `healthy` | `200` | All enabled MCP servers connected; DB (if enabled) is healthy. | | `degraded` | `200` | Some enabled MCP servers are not connected, but MCPHub itself is up. | | `unhealthy` | `503` | Database health check failed (in DB mode) or an internal error occurred. | This makes `/health` suitable for liveness/readiness probes. See `src/controllers/healthController.ts`. ## Prometheus metrics Set `ENABLE_METRICS=true` to enable `GET /metrics` at the root, independently of `BASE_PATH`. It returns 404 when disabled (the default). Set `METRICS_TOKEN` to require `Authorization: Bearer `; this is a dedicated scrape credential, not a dashboard or MCP bearer key. Without this variable the enabled endpoint is public. Restrict access to your monitoring network or configure the token, especially on multi-tenant deployments. ```yaml theme={null} scrape_configs: - job_name: mcphub static_configs: - targets: ['mcphub:3000'] authorization: credentials_file: /etc/prometheus/mcphub-token ``` | Metric | Labels | Meaning | | - | - | - | | `mcphub_servers_total` | `status`, `type` | Gauge of enabled shared server definitions. Includes `oauth_required` and OpenAPI. Personal credential runtimes are excluded. An idle on-demand server may be disconnected without being unhealthy. | | `mcphub_tool_calls_total` | `server`, `status` | Completed client tool calls (`success` or `error`), including Smart Routing meta-tools and failures before execution. | | `mcphub_tool_call_duration_seconds` | `server` | Histogram of complete call duration, including credential preparation, upstream connection, and retries. | | `mcphub_active_sessions` | none | Gauge of stateful client transport sessions, excluding stateless HTTP and synthetic REST session IDs. | Calls through MCP, Dashboard and the OpenAPI tool API share instrumentation. One client call counts once regardless of internal retries; both thrown errors and `isError` results count as failures. Calls without a resolved upstream (including search/describe meta-tools and credential preparation failures) use `server="unknown"`. These counters do not include HTTP authentication rejections before the tool handler. There are no group, user, session, tool-name or raw-error labels. Error categories and an OTel exporter are outside this initial implementation. Metrics work without a database or activity logging. Each process owns its counters, which reset on restart; scrape each replica separately. Server/session gauges refresh at scrape time without connecting to upstreams. Time series for removed server names remain until restart, so avoid continually creating new server names. ## In-process logs MCPHub keeps a rolling in-memory log buffer (`src/services/logService.ts`) that powers the dashboard's log viewer. | Method & Path | Description | | - | - | | `GET /api/logs` | Returns the buffered log entries as JSON. | | `DELETE /api/logs` | Clears the buffer. | | `GET /api/logs/stream` | Server-Sent Events stream of new log entries as they are produced. | The stream emits one `data:` line per event, including an initial `{ "type": "initial", "logs": [...] }` snapshot. All three endpoints require an authenticated admin (dashboard JWT, bearer key with `accessType: all`, OAuth token, or Better Auth session). The buffer is process-local. For multi-instance deployments, ship `stdout` to your log aggregator instead of relying on this endpoint. ## Activity log (database mode) When MCPHub runs against PostgreSQL (see [Database Configuration](/configuration/database-configuration)), every MCP request/response is persisted to the `mcphub_activity` table by `src/services/activityLoggingService.ts`. The dashboard exposes it through: | Method & Path | Description | | - | - | | `GET /api/activities/available` | Returns `{ available: true }` when activity logging is on (DB mode). | | `GET /api/activities` | Paginated activities. Query: `page`, `limit`, `server`, `tool`, `status` (`success`/`error`), `group`, `keyId`, `keyName`, `startDate`, `endDate`. | | `GET /api/activities/stats` | Aggregate stats with the same filters. | | `GET /api/activities/filters` | Distinct values for filter dropdowns. | | `GET /api/activities/:id` | Full record including request input and response output. | | `DELETE /api/activities/cleanup` | Delete activities older than a configured retention threshold. | In file mode these endpoints return `404` and the dashboard hides the Activity page. ## Process-level metrics The dashboard surfaces basic server connection statistics from `getServerConnectionStats()` (also embedded in `/health`). To export richer process metrics: * Use any Node.js APM agent that wraps `http`/`express` automatically. * Or scrape `/health` and `/api/activities/stats` from your monitoring platform. ## See also * [Database Configuration](/configuration/database-configuration) — enable PostgreSQL to unlock activity logging. * [Authentication & Security](/features/authentication) — how the management API and bearer-key authentication work. # Oauth Source: https://docs.mcphub.app/features/oauth # OAuth Support ## At a Glance * Covers end-to-end OAuth 2.0 Authorization Code with PKCE for upstream MCP servers. * Supports automatic discovery from `WWW-Authenticate` responses and RFC 8414 metadata. * Implements dynamic client registration (RFC 7591) and resource indicators (RFC 8707). * Persists client credentials and tokens to `mcp_settings.json` for reconnects. ## When MCPHub Switches to OAuth 1. MCPHub calls an MCP server that requires authorization and receives `401 Unauthorized`. 2. The response exposes a `WWW-Authenticate` header pointing to protected resource metadata (`authorization_server` or `as_uri`). 3. MCPHub discovers the authorization server metadata, registers (if needed), and opens the browser so the user can authorize once. 4. After the callback is handled, MCPHub reconnects with fresh tokens and resumes requests transparently. > MCPHub logs each stage (discovery, registration, authorization URL, token exchange) in the server detail view and the backend logs. ## Quick Start by Server Type ### Servers with Dynamic Registration Support Some servers expose complete OAuth metadata and allow dynamic client registration. For example, Vercel and Linear MCP servers only need their SSE endpoint configured: ```json theme={null} { "mcpServers": { "vercel": { "type": "sse", "url": "https://mcp.vercel.com" }, "linear": { "type": "sse", "url": "https://mcp.linear.app/mcp" } } } ``` * MCPHub discovers the authorization server, registers the client, and handles PKCE automatically. * Tokens are stored in `mcp_settings.json`; no additional dashboard configuration is needed. ### Servers Requiring Manual Client Provisioning Other providers do not support dynamic registration. GitHub’s MCP endpoint (`https://api.githubcopilot.com/mcp/`) is one example. To connect: 1. Create an OAuth App in the provider’s console (for GitHub, go to **Settings → Developer settings → OAuth Apps**). 2. Set the callback/redirect URL to `http://localhost:3000/oauth/callback` (or your deployed dashboard domain). Ensure **Settings → Installation → Base URL** matches this domain so MCP requests and OAuth callbacks resolve correctly. 3. Copy the issued client ID and client secret. 4. Supply the credentials through the MCPHub dashboard or by editing `mcp_settings.json` as shown below. ```json theme={null} { "mcpServers": { "github": { "type": "sse", "url": "https://api.githubcopilot.com/mcp/", "oauth": { "clientId": "${GITHUB_OAUTH_APP_ID}", "clientSecret": "${GITHUB_OAUTH_APP_SECRET}", "scopes": ["replace-with-provider-scope"], "resource": "https://api.githubcopilot.com" } } } } ``` * MCPHub skips dynamic registration and uses the credentials you provide to complete the OAuth exchange. * Update the dashboard or configuration file whenever you rotate secrets. * Replace `scopes` with the exact scope strings required by the provider. ## HTTP Token Endpoints in Private Networks Since v1.0.42, the MCP SDK rejects non-HTTPS OAuth token endpoints except loopback hosts (`localhost`, `127.0.0.1`, and `::1`). This affects both authorization-code exchange and token refresh; reconnecting or re-authorizing does not bypass the check. HTTPS is recommended. Administrators can explicitly allow HTTP token endpoints for one upstream in its dashboard **OAuth Configuration** section, or in the operator-managed configuration file: ```json theme={null} { "mcpServers": { "internal-gitlab": { "type": "streamable-http", "url": "http://gitlab.internal.example/api/v4/mcp", "oauth": { "allowInsecureTokenEndpoint": true } } } } ``` The option defaults to `false`. Credentials, authorization codes, and tokens can be exposed in transit when enabled; use it only on a trusted network. Each HTTP token request using the exception logs a warning. Non-admin users cannot enable the exception through creation, editing, batch import, or template import, and cannot edit a server while its exception is enabled. Disabling it also blocks pending callbacks and refreshes on existing providers. The exception does not disable SSRF protection, authorize other upstreams, or change MCPHub's own OAuth endpoints for downstream clients. The SDK client is pinned to 2.1.0 with a pnpm patch adding a per-provider exception hook. The patched client is bundled into the backend build so npm/npx installs also receive it. When upgrading it, port or replace the patch and run `tests/integration/oauth-insecure-token-endpoint.test.ts`. ## Configuration Options You can rely on auto-detection for most servers or declare OAuth settings explicitly in `mcp_settings.json`. Only populate the fields you need. ### Basic Auto Detection (Minimal Config) ```json theme={null} { "mcpServers": { "secured-sse": { "type": "sse", "url": "https://mcp.example.com/sse", "oauth": { "scopes": ["mcp.tools", "mcp.prompts"], "resource": "https://mcp.example.com" } } } } ``` * MCPHub will discover the authorization server from challenge headers and walk the user through authorization automatically. * Tokens (including refresh tokens) are stored on disk and reused on restart. ### Static Client Credentials (Bring Your Own Client) ```json theme={null} { "oauth": { "clientId": "mcphub-client", "clientSecret": "replace-me-if-required", "authorizationEndpoint": "https://auth.example.com/oauth/authorize", "tokenEndpoint": "https://auth.example.com/oauth/token", "redirectUri": "http://localhost:3000/oauth/callback" } } ``` * Use this when the authorization server requires manual client provisioning. * `redirectUri` overrides the callback derived from **Settings → Installation → Base URL** for that server. * If `redirectUri` is omitted, MCPHub falls back to `${Base URL}/oauth/callback`, then to `http://localhost:3000/oauth/callback`. * Keep **Settings → Installation → Base URL** aligned with your deployed domain so the default callback URL is correct. ### Dynamic Client Registration (RFC 7591) ```json theme={null} { "oauth": { "dynamicRegistration": { "enabled": true, "issuer": "https://auth.example.com", "metadata": { "client_name": "MCPHub", "redirect_uris": [ "http://localhost:3000/oauth/callback", "https://mcphub.example.com/oauth/callback" ], "scope": "mcp.tools mcp.prompts", "grant_types": ["authorization_code", "refresh_token"] }, "initialAccessToken": "optional-token-if-required" }, "scopes": ["mcp.tools", "mcp.prompts"], "resource": "https://mcp.example.com" } } ``` * MCPHub discovers endpoints via `issuer`, registers itself, and persists the issued `client_id`/`client_secret`. * If `oauth.redirectUri` is provided, MCPHub uses it as the preferred callback URI and includes it in the dynamically registered `redirect_uris`. * Provide `initialAccessToken` only when the registration endpoint is protected. ## Authorization Flow 1. **Initialization** – On startup MCPHub processes every server entry, discovers metadata, and registers the client if `dynamicRegistration.enabled` is true. 2. **User Authorization** – Initiating a connection launches the system browser to the server’s authorize page with PKCE parameters. 3. **Callback Handling** – The built-in route (`/oauth/callback`) verifies the `state`, completes the token exchange, and saves the tokens via the MCP SDK. 4. **Token Lifecycle** – Access and refresh tokens are cached in memory, refreshed automatically, and written back to `mcp_settings.json`. ## Tips & Troubleshooting * Confirm that the redirect URI used during authorization exactly matches one of the `redirect_uris` registered with the authorization server. * When running behind HTTPS, expose the callback URL publicly or configure a reverse proxy at `/oauth/callback`. * If discovery fails, supply `authorizationEndpoint` and `tokenEndpoint` explicitly to bypass metadata lookup. * Remove stale tokens from `mcp_settings.json` if an authorization server revokes access—MCPHub will prompt for a fresh login on the next request. # Per-user Credentials Source: https://docs.mcphub.app/features/per-user-credentials Use one shared MCP server definition with a separate credential binding for each user A shared server can declare required personal credential slots. Each authenticated user binds their own values in **Credentials** or from the server card. Server owners and administrators manage the declaration; users can only replace or delete their own binding. Shared users see slot names and labels without seeing the server's connection configuration. ## Deployment `MCPHUB_CREDENTIAL_ENCRYPTION_KEY` is optional for a single instance. When it is absent, the first binding save generates 32 random bytes and atomically creates `.credentials.key` next to the settings file (or next to the default settings file). The key file has mode `0600` and is reused after restart. It is excluded from configuration exports, Git, and the Docker build context. The settings directory must be writable; failure to persist the key stops the save. An explicitly set environment variable takes precedence and must contain 32 random bytes encoded as base64, for example generated with `openssl rand -base64 32`. Invalid environment values or key files fail instead of being silently replaced. If the key file is missing while encrypted bindings already exist, restore the original file or environment key; MCPHub will not generate a replacement that makes old bindings unreadable. Back up the key separately and persist the settings directory in Docker; mounting only the JSON settings file does not persist its sibling key file. Multiple instances sharing a database must use the same environment key or the same shared key file, rather than generating independent keys. Keep the key outside server definitions. The environment variable is removed from child-process environments. Bindings use AES-256-GCM with the server name and username authenticated as associated data. JSON mode stores ciphertext in `.credentials.json` (or next to the default settings file), with mode `0600` and atomic replacement. Database mode stores ciphertext in `credential_bindings`. The normal configuration export excludes bindings. File-to-database migration copies ciphertext; retain the same encryption key. JSON mode assumes a single writer process; use database mode for multiple hub processes. ## Declare slots Use `env` or `args` slots for stdio and `headers` slots for SSE, Streamable HTTP, and OpenAPI. Values are literal strings, without environment-variable expansion. Supply the entire header value, including `Bearer ` when required. Declared personal headers override static, passthrough, and OpenAPI tool-parameter headers. Templates contain `target`, `name`, an optional `label`, and an `allowed` list for URL slots (maximum 32 slots). ```json theme={null} { "mcpServers": { "tavily": { "type": "stdio", "command": "npx", "args": ["-y", "tavily-mcp"], "owner": "admin", "visibility": "public", "idleTimeoutMs": 300000, "credentialTemplate": [ { "target": "env", "name": "TAVILY_API_KEY", "label": "Tavily API key" } ] }, "context7": { "type": "stdio", "command": "npx", "args": ["-y", "@upstash/context7-mcp"], "owner": "admin", "visibility": "public", "credentialTemplate": [ { "target": "env", "name": "CONTEXT7_API_KEY", "label": "Context7 API key" } ] } } } ``` The environment variables follow the upstream [Tavily configuration](https://github.com/tavily-ai/tavily-mcp) and [Context7 developer guide](https://github.com/upstash/context7/blob/master/docs/resources/developer.mdx). Remove any credential-bearing CLI arguments when migrating: Context7's `--api-key` argument takes precedence over its environment variable. For an HTTP server, declare `{"target":"headers","name":"Authorization"}` instead. Shared upstream OAuth cannot be combined with a personal credential template. ## CLI-only credentials For a CLI that accepts credentials only as arguments, declare an `args` slot and reference it with `{{args.API_KEY}}` in an argument. Replace `API_KEY` with the slot name (case-sensitive; letters, digits and underscores, starting with a letter or underscore). In the dashboard, select **Arguments** as the slot target and enter each argument in its own input. ```json theme={null} { "type": "stdio", "command": "npx", "args": ["-y", "some-mcp", "--api-key", "{{args.API_KEY}}"], "credentialTemplate": [ { "target": "args", "name": "API_KEY", "label": "API key" } ] } ``` Bind the value under `args.API_KEY` through the credentials API. A placeholder may appear multiple times, including inside an argument such as `--api-key={{args.API_KEY}}`; each occurrence uses the same personal value. Every args slot must be referenced, and unknown or malformed references are rejected. Env and args slots may share a name because their targets identify separate fields. MCPHub substitutes the personal value at spawn time, after ordinary environment expansion and proxy argument cleaning. Spaces, quotes, `$` and placeholder-like text in a value remain literal; values are not expanded recursively. No shell wrapper is required. Do not place personal placeholders inside a shell script or other executable code: the interpreter would evaluate the substituted text. Credentials passed as arguments may be visible to operating-system process inspection tools even though hub logs and public configuration responses omit them. For personal upstream OAuth, use the separate identity mode described below. Existing POSIX shell wrappers remain supported. Their env slots must use bare lowercase references such as `"$my_api_key"`: `$MY_API_KEY` and `${my_api_key}` can be expanded and cleared before personal credentials are injected. For `sh -c`, enter `-c` and the complete script as two separate dashboard arguments. ## Manage a binding through the API Use dashboard authentication (JWT in `x-auth-token`, an OIDC/session login). User-level MCP bearer keys remain MCP-only credentials; system bearer keys and anonymous dashboard mode cannot manage personal bindings. No endpoint accepts a target username. | Method | Endpoint | Behavior | | - | - | - | | GET | `/api/credentials` | All visible templated servers, slot metadata, `configured`, `configuredSlots`, and `updatedAt`; no values | | PUT | `/api/credentials/:name` | Replace the current user's complete binding | | DELETE | `/api/credentials/:name` | Delete the current user's binding | Example PUT body: ```json theme={null} { "values": { "env.TAVILY_API_KEY": "your-personal-api-key" } } ``` All slots are required; undeclared slots are rejected. Inputs stay blank when revisiting the dashboard. Deleting a server or user removes the corresponding bindings. Renaming a server keeps the bindings: each one is re-encrypted under the new name. If the same edit also changes a connection field (URL, command, headers, credential slots, …), the bindings are removed and users must bind again. If a binding cannot be decrypted (for example, the encryption key is missing), the rename fails and nothing changes: restore the key, or have that user replace or delete the binding, then rename again. Adding a required slot also requires users to replace their binding with values for all slots. ## Runtime behavior * MCP session/OIDC users resolve their own binding; user-level bearer keys resolve their live owner user. A route name or caller-supplied username never selects a credential. * Every request checks the current binding. Missing values fail with an instruction to bind credentials in **Credentials**; there is no implicit organization fallback. * Templated servers start on first discovery or invocation. A stdio child is keyed by `(server, user)`, shared across that user's MCP sessions, and hidden from the server list. This takes precedence over `perSessionClient`. * Active requests hold their runtime. After the last request finishes, `idleTimeoutMs` (default five minutes) recycles it. Updates/deletes invalidate the local runtime immediately; other hub processes observe the new binding on their next request. * HTTP clients are isolated per user too and replaced when bindings change. Personal tools, prompts, and resources are not added to shared embedding caches; ordinary routing works, while vector-based smart search does not index personal catalogs. * Personal upstream connection errors and stderr are withheld because they may contain credential values. Normal server definitions retain their existing behavior. For staging, bind different Alice/Bob keys, call both services concurrently using each user's bearer key, replace/delete Alice's binding, and verify Bob's calls remain unaffected. Test idle recycling and email-style OIDC usernames as well. The automated integration suite covers real local stdio, HTTP, and OpenAPI servers; real Tavily/Context7 API calls require your staging credentials. ## Personal upstream OAuth For OAuth-protected HTTP MCP servers, set `oauth.identityMode` to `personal` in the server configuration (or select **Each user authorizes independently** in the dashboard). Existing configurations default to shared OAuth. Personal OAuth and manual credential slots are separate modes; do not supply shared tokens or pending authorization in a personal-mode declaration. ```json theme={null} { "type": "streamable-http", "url": "https://gitlab.example/api/v4/mcp", "owner": "admin", "visibility": "public", "oauth": { "identityMode": "personal", "scopes": ["mcp"] } } ``` Each user connects from their server card in **Servers**. MCP calls using a user-associated Bearer key use that key's owner, including `/mcp/` aggregate routes. Shared/system keys and anonymous callers cannot use personal OAuth. An unbound caller receives **Personal authorization required**; there is no shared-token fallback. The MCP SDK discovers the upstream OAuth endpoints and dynamically registers a client for the user's binding. When registration is disabled upstream, configure a pre-registered non-confidential `oauth.clientId` and the callback redirect URI instead. The existing administrator-controlled `allowInsecureTokenEndpoint` exception still applies to self-managed HTTP deployments; HTTPS remains the default. Tokens, client registration and pending PKCE/state are encrypted in the same private binding store used by credential slots. Back up its encryption key as described above. Pending flows expire after ten minutes, survive a restart within that period, and can be consumed only once. Starting another flow supersedes the previous one. Changes to the server definition invalidate its personal authorizations and require reconnecting. Disconnect takes effect locally immediately, invalidates only that user's runtime, and attempts upstream token revocation when an endpoint is known. Failed revocation does not restore the local binding. Refresh/callback writes from an obsolete binding are rejected. File storage assumes one hub process owns the settings directory; database storage uses conditional writes to reject stale revisions. Dashboard-authenticated APIs operate only on the current user: | Method | Path | Behavior | | - | - | - | | GET | `/api/servers/:name/oauth/personal` | Return own connection status and pending authorization URL. | | POST | `/api/servers/:name/oauth/personal` | Start/restart own authorization; return the authorization URL. | | DELETE | `/api/servers/:name/oauth/personal` | Disconnect own binding and attempt upstream revocation. | The shared `/oauth/callback` endpoint resolves personal flow ownership from stored opaque state; request parameters cannot select a different user. ## Personal HTTP endpoints SSE and Streamable HTTP servers can declare URL slots for regional or tenant endpoints: ```json theme={null} { "type": "streamable-http", "url": "https://mcp.{{url.region}}.example.com/mcp", "credentialTemplate": [ { "target": "url", "name": "region", "label": "Region", "allowed": ["us", "eu"] }, { "target": "headers", "name": "X-Api-Key" } ] } ``` Select **URL** in the server's credential slot editor and enter allowed values one per line. Users choose a value in **Credentials**, or bind `url.region` through the API alongside all other slots. URL slots cannot be combined with OAuth. Each reference must fill a complete hostname label or path segment. Host values use ASCII letters, digits and internal hyphens (maximum 63 characters); path values use ASCII letters, digits, `_`, `-`, `.`, `~`, excluding `.` and `..`. No URL encoding or free-form input is accepted. Slots cannot appear in schemes, ports, user information, queries or fragments. Every URL slot must be referenced. Allowed lists contain 1–256 unique values, each at most 256 characters. Bindings are checked on save and every runtime acquisition. Removing a choice makes old bindings unconfigured and rejects their next request, including when a user already has a cached client. Calls already in progress are not retroactively cancelled. URL-slot servers always use the SSRF blocklist, including administrator-owned servers; `network.allowedCidrs` still applies to connections and redirects. # Server Management Source: https://docs.mcphub.app/features/server-management Centrally manage multiple MCP servers with hot-swappable configuration ## Overview MCPHub's server management system allows you to centrally configure, monitor, and control multiple MCP (Model Context Protocol) servers from a single dashboard. All changes are applied in real-time without requiring server restarts. The server list displays the negotiated MCP protocol version below the transport type, separately from the server software version. Unconnected or disabled servers show "Not available"; OpenAPI servers do not display an MCP protocol version. ## Adding MCP Servers MCPHub supports four upstream transports. Pick the one that matches your server before filling in the dashboard form (or the JSON body). | Transport (`type`) | Description | Key fields | | - | - | - | | `stdio` (default) | MCPHub launches a child process and talks MCP over stdio. | `command`, `args`, `env` | | `sse` | Long-lived Server-Sent Events HTTP endpoint. | `url`, `headers`, `enableKeepAlive`, `keepAliveInterval` | | `streamable-http` | MCP Streamable HTTP transport. | `url`, `headers` | | `openapi` | Wraps an OpenAPI service so its operations appear as MCP tools. | `openapi.url` or `openapi.schema`, optional `headers` | ### Via Dashboard 1. **Access the Dashboard**: Navigate to `http://localhost:3000` and log in. 2. **Click "Add Server"** and choose a transport. 3. Fill in the fields that apply to that transport. Common optional fields available on every transport: `enabled`, `owner`, `description`, `passthroughHeaders`, `oauth`, `proxy`. See [MCP Settings](/configuration/mcp-settings) for the full schema. The server card menu also offers **Duplicate**, which opens the Add Server form prefilled with the source server's configuration under the name `-copy`; OAuth authorization state is not copied. ### Via Configuration File Edit your `mcp_settings.json` file. Examples for each transport: ```json theme={null} { "mcpServers": { "fetch": { "type": "stdio", "command": "uvx", "args": ["mcp-server-fetch"], "env": { "USER_AGENT": "MCPHub/1.0" } }, "amap": { "type": "sse", "url": "https://mcp.amap.com/sse?key=${AMAP_API_KEY}" }, "weather": { "type": "streamable-http", "url": "https://example.com/mcp", "headers": { "Authorization": "Bearer ${WEATHER_TOKEN}" } }, "stripe": { "type": "openapi", "openapi": { "url": "https://stripe.com/api/openapi.json" } } } } ``` ### Via API Use the REST API to add servers programmatically: ```bash theme={null} curl -X POST http://localhost:3000/api/servers \ -H "Content-Type: application/json" \ -H "x-auth-token: YOUR_JWT" \ -d '{ "name": "fetch-server", "config": { "type": "stdio", "command": "uvx", "args": ["mcp-server-fetch"] } }' ``` ## Popular MCP Server Examples > **Web Fetch Server** > > Provides web scraping and HTTP request capabilities: > > ```json theme={null} > { > "fetch": { > "command": "uvx", > "args": ["mcp-server-fetch"] > } > } > ``` > > **Available Tools:** > > * `fetch`: Make HTTP requests > * `fetch_html`: Scrape web pages > * `fetch_json`: Get JSON data from APIs > **Playwright Browser Automation** > > Browser automation for web interactions: > > ```json theme={null} > { > "playwright": { > "command": "npx", > "args": ["@playwright/mcp@latest", "--headless"] > } > } > ``` > > **Available Tools:** > > * `playwright_navigate`: Navigate to web pages > * `playwright_screenshot`: Take screenshots > * `playwright_click`: Click elements > * `playwright_fill`: Fill forms > **File System Operations** > > File and directory management: > > ```json theme={null} > { > "filesystem": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"] > } > } > ``` > > **Available Tools:** > > * `read_file`: Read file contents > * `write_file`: Write to files > * `create_directory`: Create directories > * `list_directory`: List directory contents > **SQLite Database** > > Database operations: > > ```json theme={null} > { > "sqlite": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"] > } > } > ``` > > **Available Tools:** > > * `execute_query`: Execute SQL queries > * `describe_tables`: Get table schemas > * `create_table`: Create new tables > **Slack Integration** > > Slack workspace integration: > > ```json theme={null} > { > "slack": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-slack"], > "env": { > "SLACK_BOT_TOKEN": "xoxb-your-bot-token", > "SLACK_TEAM_ID": "T1234567890" > } > } > } > ``` > > **Available Tools:** > > * `send_slack_message`: Send messages to channels > * `list_slack_channels`: List available channels > * `get_slack_thread`: Get thread messages > **GitHub Integration** > > GitHub repository operations: > > ```json theme={null} > { > "github": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-github"], > "env": { > "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token" > } > } > } > ``` > > **Available Tools:** > > * `create_or_update_file`: Create/update repository files > * `search_repositories`: Search GitHub repositories > * `create_issue`: Create issues > * `create_pull_request`: Create pull requests > **Google Drive** > > Google Drive file operations: > > ```json theme={null} > { > "gdrive": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-gdrive"], > "env": { > "GDRIVE_CLIENT_ID": "your-client-id", > "GDRIVE_CLIENT_SECRET": "your-client-secret", > "GDRIVE_REDIRECT_URI": "your-redirect-uri" > } > } > } > ``` > > **Available Tools:** > > * `gdrive_search`: Search files and folders > * `gdrive_read`: Read file contents > * `gdrive_create`: Create new files > **Amap Maps (China)** > > Chinese mapping and location services: > > ```json theme={null} > { > "amap": { > "command": "npx", > "args": ["-y", "@amap/amap-maps-mcp-server"], > "env": { > "AMAP_MAPS_API_KEY": "your-api-key" > } > } > } > ``` > > **Available Tools:** > > * `search_location`: Search for locations > * `get_directions`: Get route directions > * `reverse_geocode`: Convert coordinates to addresses ## Copying a client configuration Both server cards and group cards offer a copy action that opens a client preset dialog, so you can paste a ready-made configuration into your favorite MCP client: * **Copy MCP config** (group card menu): generates the configuration for the group's aggregated endpoint (`/mcp/`), which multiplexes every server in the group behind one URL. * **Copy** (server card menu): wraps that server's own stored configuration (its `command`/`args`/`env` for stdio servers, or its `url`/`headers` for HTTP/SSE servers) in the shape the selected client expects. Servers backed by the `openapi` transport have no client-side transport of their own, so the dialog shows a "no transport" notice instead of presets; this is expected, not an error. Servers using the `sse` transport keep their `"type": "sse"` marker in the generated JSON (and the Claude Code command switches to `--transport sse`) so clients do not misdial them as streamable HTTP. The dialog provides presets for the following clients: Generic (mcpServers), Generic HTTP, Claude Code, Cursor, VS Code / Copilot, Codex, OpenCode, Windsurf, Cherry Studio, CodeBuddy Code, Qoder, TRAE, ZCode, and WorkBuddy. The **Claude Code** tab additionally offers a one-liner CLI command: ```bash theme={null} claude mcp add --transport http --header 'Authorization: Bearer ' ``` Values containing characters outside a safe CLI set are wrapped in single quotes (as the header above is), so the command can be pasted verbatim into a POSIX shell; on Windows shells that do not parse single quotes, re-quote the affected values. A few clients use their own key names in the generated configuration: Codex uses `http_headers` (there is no `headers` key), VS Code uses `servers` plus a `type` field, and Cherry Studio uses `baseUrl` with a `type` of `streamableHttp` — or `sse` when the upstream is an SSE endpoint. ## Server Lifecycle Management ### Starting Servers Servers are automatically started when: * MCPHub boots up * A server is added via the dashboard or API * A server configuration is updated * A stopped server is manually restarted ### Stopping Servers You can stop servers: * **Via Dashboard**: Toggle the server status switch * **Via API**: Send a POST request to `/api/servers/{name}/toggle` * **Automatically**: Servers stop if they crash or encounter errors ### Restarting Servers Servers are automatically restarted: * When configuration changes are made * After environment variable updates * When manually triggered via dashboard or API ## Server Status Monitoring ### Status Indicators Each server displays a status indicator: * 🟢 **Running**: Server is active and responding * 🟡 **Starting**: Server is initializing * 🔴 **Stopped**: Server is not running * ⚠️ **Error**: Server encountered an error ### Real-time Logs View server logs in real-time: 1. **Dashboard Logs**: Click on a server to view its logs 2. **API Logs**: Access logs via `/api/logs` endpoint 3. **Streaming Logs**: Subscribe to log streams via WebSocket ### Health Checks MCPHub automatically performs health checks: * **Initialization Check**: Verifies server starts successfully * **Tool Discovery**: Confirms available tools are detected * **Response Check**: Tests server responsiveness * **Resource Monitoring**: Tracks CPU and memory usage ## Configuration Management ### Environment Variables Servers can use environment variables for configuration: ```json theme={null} { "server-name": { "command": "python", "args": ["server.py"], "env": { "API_KEY": "${YOUR_API_KEY}", "DEBUG": "true", "MAX_CONNECTIONS": "10" } } } ``` **Environment Variable Expansion:** * `${VAR_NAME}`: Expands to environment variable value * `${VAR_NAME:-default}`: Uses default if variable not set * `${VAR_NAME:+value}`: Uses value if variable is set ### Command Variations Different ways to specify server commands: **npm/npx** ```json theme={null} { "npm-server": { "command": "npx", "args": ["-y", "package-name", "--option", "value"] } } ``` **Python/uvx** ```json theme={null} { "python-server": { "command": "uvx", "args": ["package-name", "--config", "config.json"] } } ``` ## Advanced Features ### Hot Reloading MCPHub supports hot reloading of server configurations: 1. **Dashboard Updates**: Immediately applies changes made through the web interface 2. **API Updates**: Real-time updates via REST API calls 3. **Zero Downtime**: Graceful server restarts without affecting other servers ## Troubleshooting > **Server Won't Start** > > **Check the following:** > > * Command is available in PATH > * All required environment variables are set > * Working directory exists and is accessible > * Network ports are not blocked > * Dependencies are installed > > **Debug steps:** > > 1. Check server logs in the dashboard > 2. Test command manually in terminal > 3. Verify environment variable expansion > 4. Check file permissions > **Server Keeps Crashing** > > **Common causes:** > > * Invalid configuration parameters > * Missing API keys or credentials > * Resource limits exceeded > * Dependency conflicts > > **Solutions:** > > 1. Review server logs for error messages > 2. Test with minimal configuration > 3. Verify all credentials and API keys > 4. Check system resource availability > **Tools Not Appearing** > > **Possible issues:** > > * Server not fully initialized > * Tool discovery timeout > * Communication protocol mismatch > * Server reporting errors > > **Debug steps:** > > 1. Wait for server initialization to complete > 2. Check server logs for tool registration messages > 3. Test direct communication with server > 4. Verify MCP protocol compatibility ## Next Steps > **[Group Management](/features/group-management)** > > Organize servers into logical groups > **[Smart Routing](/features/smart-routing)** > > Set up AI-powered tool discovery > **[API Reference](/api-reference/servers)** > > Server management API documentation > **[Configuration Guide](/configuration/mcp-settings)** > > Detailed configuration options # Smart Routing Source: https://docs.mcphub.app/features/smart-routing AI-powered tool discovery using vector semantic search ## Overview Smart Routing is MCPHub's intelligent tool discovery system that uses vector semantic search to automatically find the most relevant tools for any given task. Instead of manually specifying which tools to use, AI clients can describe what they want to accomplish, and Smart Routing will identify and provide access to the most appropriate tools. ## How Smart Routing Works ### 1. Tool Indexing When servers start up, Smart Routing automatically: * Discovers all available tools from MCP servers * Extracts tool metadata (names, descriptions, parameters) * Converts tool information to vector embeddings * Stores embeddings in PostgreSQL with pgvector ### 2. Semantic Search When a query is made: * User queries are converted to vector embeddings * Similarity search finds matching tools using cosine similarity * Dynamic thresholds filter out irrelevant results * Results are ranked by relevance score ### 3. Intelligent Filtering Smart Routing applies several filters: * **Relevance Threshold**: Only returns tools above similarity threshold * **Context Awareness**: Considers conversation context * **Tool Availability**: Ensures tools are currently accessible * **Permission Filtering**: Respects user access permissions ### 4. Tool Execution Found tools can be directly executed: * Parameter validation ensures correct tool usage * Error handling provides helpful feedback * Response formatting maintains consistency * Logging tracks tool usage for analytics ## Prerequisites Smart Routing requires additional setup compared to basic MCPHub usage: ### Required Components 1. **PostgreSQL with pgvector**: Vector database for embeddings storage 2. **Embedding Service**: OpenAI API or compatible service 3. **Environment Configuration**: Proper configuration variables ## Using Smart Routing ### Smart Routing Endpoint Access Smart Routing through the special `$smart` endpoint: **HTTP MCP** ``` # Search across all servers http://localhost:3000/mcp/$smart # Search within a specific group http://localhost:3000/mcp/$smart/{group} ``` **SSE (Legacy)** ``` # Search across all servers http://localhost:3000/sse/$smart # Search within a specific group http://localhost:3000/sse/$smart/{group} ``` ### Group-Scoped Smart Routing Smart Routing now supports group-scoped searches, allowing you to limit tool discovery to servers within a specific group: > **Using Group-Scoped Smart Routing** > > Connect your AI client to a group-specific Smart Routing endpoint: > > ``` > http://localhost:3000/mcp/$smart/production > ``` > > This endpoint will only search for tools within servers that belong to the "production" group. > > **Benefits:** > > * **Focused Results**: Only tools from relevant servers are returned > * **Better Performance**: Reduced search space for faster queries > * **Environment Isolation**: Keep development, staging, and production tools separate > * **Access Control**: Limit tool discovery based on user permissions > **Example: Environment-Based Groups** > > Create groups for different environments: > > ```bash theme={null} > # Development environment > http://localhost:3000/mcp/$smart/development > > # Staging environment > http://localhost:3000/mcp/$smart/staging > > # Production environment > http://localhost:3000/mcp/$smart/production > ``` > > Each endpoint will only return tools from servers in that specific environment group. > **Example: Team-Based Groups** > > Organize tools by team or department: > > ```bash theme={null} > # Backend team tools > http://localhost:3000/mcp/$smart/backend-team > > # Frontend team tools > http://localhost:3000/mcp/$smart/frontend-team > > # DevOps team tools > http://localhost:3000/mcp/$smart/devops-team > ``` > > This enables teams to have focused access to their relevant toolsets. > **How It Works** > > When using `$smart/{group}`: > > 1. The system identifies the specified group > 2. Retrieves all servers belonging to that group > 3. Filters the tool search to only those servers > 4. Returns results scoped to the group's servers > > If the group doesn't exist or has no servers, the search will return no results. ### Pinned Tools A `$smart/{group}` endpoint lists only the meta-tools, so even a tool the model calls in nearly every session costs a `search_tools` round trip first. Pin such tools on the group's server entry and the endpoint lists their full definitions next to the meta-tools: ```json theme={null} { "name": "production", "servers": [{ "name": "time", "tools": "all", "pinnedTools": ["get_current_time"] }] } ``` In the dashboard, edit the group, expand the server and click the pin icon next to a selected tool. The icon is shown only while Smart Routing is enabled. * Pins use the bare tool name, like the `tools` selection. A pin only takes effect for a tool the group selects and that is enabled; other pins are ignored. * A pinned tool is listed exactly as on the direct `/mcp/{group}` endpoint — under the server's exposed name, with its description override — and is called directly by that name. It stays in the search index and keeps working through `search_tools`, `describe_tool` and `call_tool`. * The global `$smart` endpoint has no group and never lists pinned tools. A group without pins lists exactly the meta-tools, as before. * A pinned tool whose listed name equals a meta-tool name (`search_tools`, `describe_tool`, `call_tool`, e.g. alias `describe` + separator `_` + tool `tool`) is not listed, in any mode: calls to those names always go to the meta-tool. * The Smart Routing footprint shown for a group includes the definition cost of its pinned tools. * Deselecting a tool, in the dashboard or through `PUT /api/groups/:id/server-configs/:serverName/tools`, also removes its pin. * On `$smart/{group}`, a pinned tool keeps its MCP Apps metadata (`_meta.ui`) for a client that declares MCP Apps support, so its widget renders, and the server's `ui://` resources and app-only helper tools are reachable from that widget. A client without MCP Apps support gets the metadata stripped, as before. Only pinned tools carry a widget: a tool reached through `search_tools` and `call_tool` has no descriptor of its own for the host to attach a widget to, so pin the tools whose widgets you want. The global `$smart` endpoint is never an MCP Apps route. > [!WARNING] > > Behind `call_tool`, every tool shares a single client-side approval decision. A pinned tool is listed under its own name, so a client that approves tools one by one applies its per-tool rules to it — review that before pinning a tool that writes. ### Progressive Disclosure Mode Progressive Disclosure is an optimization feature that reduces token usage when working with Smart Routing. When enabled, the tool discovery workflow changes from a 2-step to a 3-step process. > **What is Progressive Disclosure?** > > By default, Smart Routing returns full tool information including complete parameter schemas in `search_tools` results. This can consume significant tokens when dealing with tools that have complex input schemas. > > **Progressive Disclosure** changes this behavior: > > * `search_tools` returns only tool names and descriptions (minimal info) > * A new `describe_tool` endpoint provides full parameter schema on demand > * `call_tool` executes the tool as before > > This approach is particularly useful when: > > * Working with many tools with complex schemas > * Token usage optimization is important > * AI clients need to browse many tools before selecting one > **Enabling Progressive Disclosure** > > Enable Progressive Disclosure through the Settings page or environment variable: > > **Via Settings UI:** > > 1. Navigate to Settings → Smart Routing > 2. Enable the "Progressive Disclosure" toggle > 3. The change takes effect immediately > > **Via Environment Variable:** > > ```bash theme={null} > SMART_ROUTING_PROGRESSIVE_DISCLOSURE=true > ``` > **Including Server Descriptions in search\_tools** > > By default, `search_tools` lists only server names in its tool description. To include each server's description (or upstream instructions) alongside the name, set: > > ```bash theme={null} > SMART_ROUTING_SERVER_DESCRIPTION_MODE=full > ``` > > Use `names` (default) to keep the legacy compact list. > > The list names the servers `search_tools` actually searches: enabled servers that are > connected or start on demand. An on-demand server stays listed while it is not connected, > whether it shut down after idle or failed to start, since `call_tool` starts it; any other > server that failed to connect is left out until it reconnects. Upstream instructions only > arrive on connect, so in `full` mode a server that has not connected yet shows its > configured description, or just its name. > **Embedding Task Prefixes** > > Asymmetric retrieval models are trained with different task prefixes for queries and > documents, and score noticeably worse on raw text. Set the prefixes your model expects > in Settings → Smart Routing or via environment variables: > > ```bash theme={null} > # EmbeddingGemma > SMART_ROUTING_EMBEDDING_QUERY_PREFIX="task: search result | query: " > SMART_ROUTING_EMBEDDING_DOCUMENT_PREFIX="title: none | text: " > ``` > > Both are empty by default, which embeds raw text as before. Prefixes are used verbatim, > so keep the trailing space. Changing the document prefix re-embeds every server's tools; > changing the query prefix takes effect on the next search. > > Prefixes shift the similarity scores (for EmbeddingGemma, weak matches drop more than > strong ones), so a threshold tuned on raw text may need recalibrating after you enable them (see > **Search Results** below; each hit reports its `score`). A prefix also counts toward the > embedding token limit (`EMBEDDING_MAX_TOKENS` or the model default): it is kept and the > tool text is truncated that many tokens earlier. > **Search Results: Scores, Threshold and Schema Budget** > > Every `search_tools` hit carries a `score`: the cosine similarity between the query and > the tool (0-1, two decimals), the same value the threshold is compared with. Results are > ordered by relevance, which also weighs how well the tool's server matches the query. > > ```bash theme={null} > # Minimum similarity for a hit (0-1). Unset: by query length — > # 0.2 for short queries, 0.4 for long or "specific"/"exact" ones, 0.3 otherwise. > SMART_ROUTING_SIMILARITY_THRESHOLD=0.45 > > # Only the first N hits carry their full definition (inputSchema etc.); the rest > # carry name, description and score, and describe_tool is listed to fetch them. > # Unset: every hit is returned in full. Ignored with Progressive Disclosure. > SMART_ROUTING_FULL_SCHEMA_TOP_N=3 > > # Optional tool fields a full hit (and describe_tool) carries next to name, > # description and inputSchema: title, annotations, outputSchema, execution, > # icons, _meta. "none" sends none of them. Default: title,annotations. > SMART_ROUTING_TOOL_DEFINITION_FIELDS=title,annotations > ``` > > All three can also be set in Settings → Smart Routing → Search Results, and none of > them re-embeds anything. The fields left out by default are meant for clients rather > than models and can be large: an upstream server may ship base64 `icons` or a long > `outputSchema` with every tool, and in a search result they would only cost tokens. > The `inputSchema` is always sent, with its `$schema` key removed. When no tool clears the threshold, the response says so, names the > threshold and lists the servers that were searched, so the model can tell the user the > capability is not available instead of calling the nearest unrelated tool. > **Standard Mode (Default)** > > When Progressive Disclosure is **disabled** (default), Smart Routing provides two tools: > > **Workflow:** `search_tools` → `call_tool` > > | Tool | Purpose | > | - | - | > | `search_tools` | Find tools by query, returns full tool info including `inputSchema` | > | `call_tool` | Execute a tool with the provided arguments | > > This mode is simpler but uses more tokens due to full schemas in search results. > `SMART_ROUTING_FULL_SCHEMA_TOP_N` caps that cost: only the top N hits carry a schema, > and `describe_tool` is listed for the rest. > **Progressive Disclosure Mode** > > When Progressive Disclosure is **enabled**, Smart Routing provides three tools: > > **Workflow:** `search_tools` → `describe_tool` → `call_tool` > > | Tool | Purpose | > | - | - | > | `search_tools` | Find tools by query, returns only name and description | > | `describe_tool` | Get full schema for a specific tool (new) | > | `call_tool` | Execute a tool with the provided arguments | > > **Example workflow:** > > 1. AI calls `search_tools` with query "file operations" > 2. Results show tool names and descriptions (minimal tokens) > 3. AI calls `describe_tool` for a specific tool to get full `inputSchema` > 4. AI calls `call_tool` with the correct arguments > > This mode reduces token usage by only fetching full schemas when needed. > **Response Format Comparison** > > **Standard Mode search\_tools response:** > > ```json theme={null} > { > "tools": [ > { > "name": "read_file", > "description": "Read contents of a file", > "inputSchema": { > "type": "object", > "properties": { > "path": { "type": "string", "description": "File path to read" }, > "encoding": { "type": "string", "default": "utf-8" } > }, > "required": ["path"] > }, > "score": 0.71 > } > ] > } > ``` > > **Progressive Disclosure Mode search\_tools response:** > > ```json theme={null} > { > "tools": [ > { > "name": "read_file", > "description": "Read contents of a file", > "score": 0.71 > } > ], > "metadata": { > "progressiveDisclosure": true, > "guideline": "Use describe_tool to get the full parameter schema before calling." > } > } > ``` > > **describe\_tool response:** > > ```json theme={null} > { > "tool": { > "name": "read_file", > "description": "Read contents of a file", > "inputSchema": { > "type": "object", > "properties": { > "path": { "type": "string", "description": "File path to read" }, > "encoding": { "type": "string", "default": "utf-8" } > }, > "required": ["path"] > } > } > } > ``` ## Troubleshooting > **Database Connection Issues** > > **Symptoms:** > > * Smart Routing not available > * Database connection errors > * Embedding storage failures > > **Solutions:** > > 1. Verify PostgreSQL is running > 2. Check DB\_URL format > 3. Ensure pgvector extension is installed > 4. Test connection manually: > > ```bash theme={null} > psql $DB_URL -c "SELECT 1;" > ``` > **Embedding Service Problems** > > **Symptoms:** > > * Tool indexing failures > * Query processing errors > * API rate limit errors > > **Solutions:** > > 1. Verify API key validity > 2. Check network connectivity > 3. Monitor rate limits > 4. Test embedding service: > > ```bash theme={null} > curl -X POST https://api.openai.com/v1/embeddings \ > -H "Authorization: Bearer $OPENAI_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{"input": "test", "model": "text-embedding-3-small"}' > ``` > **Poor Search Results** > > **Symptoms:** > > * Irrelevant tools returned > * Low relevance scores > * Missing expected tools > > **Solutions:** > > 1. Adjust the similarity threshold (`SMART_ROUTING_SIMILARITY_THRESHOLD`), using the > `score` of each hit to pick a value > 2. Re-index tools with better descriptions > 3. Use more specific queries > 4. Check tool metadata quality > > ```bash theme={null} > # Re-index all tools (full rebuild: clears vector_embeddings first, then > # regenerates embeddings for every enabled server; servers that require > # personal credentials are reached through the users holding a binding, > # indexing the union of the tools those users can see; runs synchronously > # and streams per-server progress through SSE embedding-sync-progress events) > curl -X POST http://localhost:3000/api/smart-routing/reindex \ > -H "Authorization: Bearer YOUR_JWT_TOKEN" > ``` > **Performance Issues** > > **Symptoms:** > > * Slow query responses > * High database load > * Memory usage spikes > > **Solutions:** > > 1. Optimize database configuration > 2. Increase cache sizes > 3. Reduce batch sizes > 4. Monitor system resources > > ```bash theme={null} > # Check vector store status and performance (config, database connection, > # row counts, per-model/per-server breakdown, sync freshness, coverage) > curl http://localhost:3000/api/smart-routing/performance \ > -H "Authorization: Bearer YOUR_JWT_TOKEN" > ``` > **Authentication Errors Despite a Valid Dashboard Key** > > **Symptoms:** > > * Tool indexing fails with an authentication (401/403) error > * A valid key is saved in the dashboard, yet requests keep being rejected > > **Cause:** an environment variable outranks the dashboard value, so the key you saved is never used (issue #642). Recent versions show a warning under the affected field naming the variable in use. > > **Solutions:** > > 1. Look for `OPENAI_API_KEY` / `AZURE_OPENAI_API_KEY` in `.env*` files, your shell profile, or the container/service definition > 2. Remove the stale variable and restart, then re-index if the provider or model also changed > 3. Confirm the key actually in use with a manual request: > > ```bash theme={null} > curl -X POST "${OPENAI_API_BASE_URL:-https://api.openai.com/v1}/embeddings" \ > -H "Authorization: Bearer $OPENAI_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{"input": "test", "model": "text-embedding-3-small"}' > ``` ## Best Practices ### Query Writing > [!TIP] > > **Be Descriptive**: Use specific, descriptive language in queries for better tool matching. > [!TIP] > > **Include Context**: Provide relevant context about your task or domain for more accurate results. > [!TIP] > > **Use Natural Language**: Write queries as you would describe the task to a human. ### Tool Descriptions > [!WARNING] > > **Quality Metadata**: Ensure MCP servers provide high-quality tool descriptions and metadata. > [!WARNING] > > **Regular Updates**: Keep tool descriptions current as functionality evolves. > [!WARNING] > > **Consistent Naming**: Use consistent naming conventions across tools and servers. ### System Maintenance > [!NOTE] > > **Regular Re-indexing**: Periodically re-index tools to ensure embedding quality. > [!NOTE] > > **Monitor Performance**: Track query patterns and optimize based on usage. > [!NOTE] > > **Update Models**: Consider updating to newer embedding models as they become available. ## Next Steps > **[Authentication](/features/authentication)** > > User management and access control > **[Monitoring](/features/monitoring)** > > System monitoring and analytics > **[API Reference](/api-reference/smart-routing)** > > Complete Smart Routing API documentation > **[Configuration](/configuration/environment-variables)** > > Advanced configuration options # MCPHub Source: https://docs.mcphub.app/index Open-source, self-hosted MCP gateway and control plane for connecting, controlling, and operating MCP servers. # Welcome to MCPHub MCPHub is an open-source, self-hosted MCP gateway and control plane that helps you connect, control, route, and operate multiple MCP servers through unified endpoints. ## Key Features > **[Unified Management](/features/server-management)** > > Centrally manage multiple MCP servers with hot-swappable configuration > **[Group Management](/features/group-management)** > > Organize servers into logical groups for streamlined access control > **[Smart Routing](/features/smart-routing)** > > AI-powered tool discovery using vector semantic search > **[Tool Result Compression](/configuration/mcp-settings#tool-result-compression)** > > Transparently reduce large text tool outputs before they reach clients > **[Real-time Monitoring](/features/monitoring)** > > Monitor server status and performance from a unified dashboard ## Quick Start Get MCPHub running in minutes with Docker: ```bash theme={null} docker run -p 3000:3000 samanhappy/mcphub ``` Or with custom configuration: ```bash theme={null} docker run -p 3000:3000 -v $(pwd)/mcp_settings.json:/app/mcp_settings.json samanhappy/mcphub ``` Access the dashboard at `http://localhost:3000` with username `admin`. On first launch, a random password is generated and printed to the server logs. Set `ADMIN_PASSWORD` env var to use a custom password. ## Core Concepts ### MCP Endpoints MCPHub provides multiple ways to access your MCP servers: * **Unified Access**: `http://localhost:3000/mcp` - Access all servers * **Group Access**: `http://localhost:3000/mcp/{group}` - Access specific groups * **Server Access**: `http://localhost:3000/mcp/{server}` - Access individual servers * **Smart Routing**: `http://localhost:3000/mcp/$smart` - AI-powered tool discovery ### Protocol Support * **HTTP MCP**: Modern streamable HTTP interface (recommended) * **SSE**: Server-Sent Events for legacy compatibility * **stdio**: Native MCP protocol for server communication ## Getting Started > **[Quick Start Guide](/quickstart)** > > Get MCPHub running in 5 minutes > **[Installation Guide](/installation)** > > Detailed installation instructions for all platforms > **[Configuration](/configuration/mcp-settings)** > > Learn how to configure your MCP servers > **[API Reference](/api-reference/introduction)** > > Complete API documentation ## Community & Support > **[GitHub](https://github.com/samanhappy/mcphub)** > > Source code and issue tracking > **[Discord](https://discord.gg/2BJehJZVH5)** > > Join our community discussions > **[Sponsor](https://ko-fi.com/samanhappy)** > > Support the project development # Installation Guide Source: https://docs.mcphub.app/installation Detailed installation instructions for all platforms ## Prerequisites Before installing MCPHub, ensure you have the following prerequisites: * **Node.js** 20+ (for local development) * **Docker** (recommended for production) * **pnpm** (for local development) Optional for Smart Routing: * **PostgreSQL** with pgvector extension * **LLM Provider (OpenAI or compatible) API Key** ## Installation Methods **Docker (Recommended)** ### Docker Installation Docker is the recommended way to deploy MCPHub in production. #### 1. Basic Installation ```bash theme={null} # Pull the latest image docker pull samanhappy/mcphub:latest # Run with default settings docker run -d \ --name mcphub \ -p 3000:3000 \ samanhappy/mcphub:latest ``` #### 2. With Custom Configuration ```bash theme={null} # Create your configuration file cat > mcp_settings.json << 'EOF' { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] } } } EOF # Run with mounted config docker run -d \ --name mcphub \ -p 3000:3000 \ -v $(pwd)/mcp_settings.json:/app/mcp_settings.json \ samanhappy/mcphub:latest ``` #### 3. With Environment Variables ```bash theme={null} docker run -d \ --name mcphub \ -p 3000:3000 \ -e PORT=3000 \ -e BASE_PATH="" \ samanhappy/mcphub:latest ``` #### 4. Docker Compose Create a `docker-compose.yml` file: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest ports: - "3000:3000" volumes: - ./mcp_settings.json:/app/mcp_settings.json environment: - PORT=3000 - BASE_PATH= - INIT_TIMEOUT=300000 restart: unless-stopped # Optional: PostgreSQL for Smart Routing postgres: image: pgvector/pgvector:pg17 environment: POSTGRES_DB: mcphub POSTGRES_USER: mcphub POSTGRES_PASSWORD: mcphub_password volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" volumes: postgres_data: ``` Run with: ```bash theme={null} docker-compose up -d ``` **npm Package** ### npm Package Installation Install MCPHub as a global npm package: #### 1. Global Installation ```bash theme={null} # Install globally npm install -g @samanhappy/mcphub # Or with yarn yarn global add @samanhappy/mcphub # Or with pnpm pnpm add -g @samanhappy/mcphub ``` #### 2. Running MCPHub ```bash theme={null} # Run with default settings mcphub # Run with custom port PORT=8080 mcphub ``` **Local Development** ### Local Development Setup For development, customization, or contribution: #### 1. Clone Repository ```bash theme={null} # Clone the repository git clone https://github.com/samanhappy/mcphub.git cd mcphub ``` #### 2. Install Dependencies ```bash theme={null} # Install dependencies with pnpm (recommended) pnpm install # Or with npm npm install # Or with yarn yarn install ``` #### 3. Development Mode ```bash theme={null} # Start both backend and frontend in development mode pnpm dev # This will start: # - Backend on http://localhost:3000 # - Frontend on http://localhost:5173 # - Frontend proxies API calls to backend ``` #### 4. Build for Production ```bash theme={null} # Build both backend and frontend pnpm build # Start production server pnpm start ``` #### 5. Development Scripts ```bash theme={null} # Backend only (for API development) pnpm backend:dev # Frontend only (when backend is running separately) pnpm frontend:dev # Run tests pnpm test # Lint code pnpm lint # Format code pnpm format ``` > [!NOTE] > > On Windows, you may need to run backend and frontend separately: > > ```bash theme={null} > # Terminal 1: Backend > pnpm backend:dev > > # Terminal 2: Frontend > pnpm frontend:dev > ``` **Kubernetes** ### Kubernetes Deployment Deploy MCPHub on Kubernetes with these manifests: #### 1. ConfigMap for Settings ```yaml theme={null} apiVersion: v1 kind: ConfigMap metadata: name: mcphub-config data: mcp_settings.json: | { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] } } } ``` #### 2. Deployment ```yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: mcphub spec: replicas: 1 selector: matchLabels: app: mcphub template: metadata: labels: app: mcphub spec: initContainers: - name: prepare-config image: busybox:1.28 command: [ "sh", "-c", "cp /config-ro/mcp_settings.json /etc/mcphub/mcp_settings.json", ] volumeMounts: - name: config mountPath: /config-ro readOnly: true - name: app-storage mountPath: /etc/mcphub containers: - name: mcphub image: samanhappy/mcphub:latest ports: - containerPort: 3000 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi env: - name: PORT value: "3000" - name: MCPHUB_SETTING_PATH value: /etc/mcphub/mcp_settings.json volumeMounts: - name: app-storage mountPath: /etc/mcphub volumes: - name: config configMap: name: mcphub-config - name: app-storage emptyDir: {} ``` #### 3. Service ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: mcphub-service spec: selector: app: mcphub ports: - port: 80 targetPort: 3000 type: ClusterIP ``` #### 4. Ingress (Optional) ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: mcphub-ingress annotations: nginx.ingress.kubernetes.io/proxy-buffering: "off" spec: rules: - host: mcphub.yourdomain.com http: paths: - path: / pathType: Prefix backend: service: name: mcphub-service port: number: 80 ``` Deploy with: ```bash theme={null} kubectl apply -f mcphub-configmap.yaml kubectl apply -f mcphub-deployment.yaml kubectl apply -f mcphub-service.yaml kubectl apply -f mcphub-ingress.yaml ``` ## Smart Routing Setup (Optional) Smart Routing provides AI-powered tool discovery using vector semantic search. ### Prerequisites 1. **PostgreSQL with pgvector** 2. **LLM Provider (OpenAI or compatible) API Key** ### Database Setup **Docker PostgreSQL** ```bash theme={null} # Run PostgreSQL with pgvector docker run -d \ --name mcphub-postgres \ -e POSTGRES_DB=mcphub \ -e POSTGRES_USER=mcphub \ -e POSTGRES_PASSWORD=your_password \ -p 5432:5432 \ pgvector/pgvector:pg17 ``` **Existing PostgreSQL** If you have an existing PostgreSQL instance: ```sql theme={null} -- Connect to your PostgreSQL instance -- Create database CREATE DATABASE mcphub; -- Connect to the mcphub database \c mcphub; -- Enable pgvector extension CREATE EXTENSION IF NOT EXISTS vector; ``` **Cloud PostgreSQL** For cloud providers (AWS RDS, Google Cloud SQL, etc.): 1. Enable the pgvector extension in your cloud provider's console 2. Create a database named `mcphub` 3. Note down the connection details ## Verification After installation, verify MCPHub is working: ### Dashboard Access Open your browser and navigate to: ``` http://localhost:3000 ``` Log in with username `admin`. On first launch, if no `ADMIN_PASSWORD` environment variable is set, a random password is generated and printed to the server logs. ## Troubleshooting > **Docker Issues** > > **Port already in use:** > > ```bash theme={null} > # Check what's using port 3000 > lsof -i :3000 > > # Use a different port > docker run -p 8080:3000 samanhappy/mcphub > ``` > > **Container won't start:** > > ```bash theme={null} > # Check container logs > docker logs mcphub > > # Run interactively for debugging > docker run -it --rm samanhappy/mcphub /bin/bash > ``` > **npm Installation Issues** > > **Permission errors:** > > ```bash theme={null} > # Use npx instead of global install > npx @samanhappy/mcphub > > # Or fix npm permissions > npm config set prefix ~/.npm-global > export PATH=~/.npm-global/bin:$PATH > ``` > > **Node version issues:** > > ```bash theme={null} > # Check Node version > node --version > > # Install Node 20+ using nvm > nvm install 20 > nvm use 18 > ``` > **Network Issues** > > **Can't access dashboard:** > > * Check if MCPHub is running: `ps aux | grep mcphub` > * Verify port binding: `netstat -tlnp | grep 3000` > * Check firewall settings > * Try accessing via `127.0.0.1:3000` instead of `localhost:3000` > > **AI clients can't connect:** > > * Ensure the endpoint URL is correct > * Check if MCPHub is behind a proxy > * Verify network policies in Kubernetes/Docker environments > **Smart Routing Issues** > > **Database connection failed:** > > ```bash theme={null} > # Test database connection > psql $DB_URL -c "SELECT 1;" > > # Check if pgvector is installed > psql $DB_URL -c "CREATE EXTENSION IF NOT EXISTS vector;" > ``` > > **Embedding service errors:** > > * Verify LLM provider API key is valid > * Check internet connectivity > * Monitor rate limits ## Next Steps > **[Configuration](/configuration/mcp-settings)** > > Configure your MCP servers and settings > **[Quick Start](/quickstart)** > > Get up and running in 5 minutes > **[Server Management](/features/server-management)** > > Learn how to manage your MCP servers > **[API Reference](/api-reference/introduction)** > > Explore the complete API documentation # Quick Start Guide Source: https://docs.mcphub.app/quickstart Get MCPHub running in 5 minutes ## Installation **Docker (Recommended)** The fastest way to get started with MCPHub is using Docker: ```bash theme={null} # Run with default configuration docker run -p 3000:3000 samanhappy/mcphub ``` Or mount your custom configuration: ```bash theme={null} # Run with custom MCP settings docker run -p 3000:3000 \ -v $(pwd)/mcp_settings.json:/app/mcp_settings.json \ samanhappy/mcphub ``` **Local Development** For development or customization: ```bash theme={null} # Clone the repository git clone https://github.com/samanhappy/mcphub.git cd mcphub # Install dependencies pnpm install # Start development servers pnpm dev ``` This starts both backend (port 3000) and frontend (port 5173) in development mode. The Vite dev server proxies API and MCP traffic to the backend. Local development uses `admin` / `admin123` and stores writable settings in `data/mcp_settings.dev.json`. **npm Package** Install MCPHub as a global package: ```bash theme={null} # Install globally npm install -g @samanhappy/mcphub # Run MCPHub mcphub ``` ## Initial Setup ### 1. Access the Dashboard Open your browser and navigate to: ``` http://localhost:3000 ``` ### 2. Login The default username is `admin`. For local development with `pnpm dev`, use `admin123`. Docker, npm package, and production starts do not include a default user; if no `ADMIN_PASSWORD` environment variable is set on first launch, a random password is generated and printed to the server logs. To pre-set the admin password: ```bash theme={null} # Via environment variable ADMIN_PASSWORD=your-secure-password docker run -p 3000:3000 samanhappy/mcphub ``` > [!WARNING] > > Change the admin password after first login for security. > [!NOTE] > > `ADMIN_PASSWORD` and the random password printed to the logs apply only the **first** time MCPHub starts with an empty users list. Once any user exists, the variable is ignored — manage passwords through the dashboard or `PUT /api/users/:username` afterwards. To force a fresh bootstrap, remove the users from `mcp_settings.json` (file mode) or the `mcphub_user` table (database mode) before restarting. ### 3. Configure Your First MCP Server Click **"Add Server"** in the dashboard. The form supports four transport types — pick the one that matches the upstream server: | Type | When to use | Required fields | | - | - | - | | `stdio` (default) | The server runs as a local process you launch (`uvx`, `npx`, etc.). | `command`, optional `args` and `env`. | | `sse` | The server is reachable over HTTP/SSE. | `url`, optional `headers`, `enableKeepAlive`, `keepAliveInterval`. | | `streamable-http` | The server speaks MCP Streamable HTTP. | `url`, optional `headers`. | | `openapi` | You want to expose an OpenAPI service as MCP tools. | `openapi.url` or `openapi.schema`. | Example stdio configuration: ```json theme={null} { "name": "fetch", "config": { "type": "stdio", "command": "uvx", "args": ["mcp-server-fetch"] } } ``` Example SSE configuration: ```json theme={null} { "name": "amap", "config": { "type": "sse", "url": "https://mcp.amap.com/sse?key=YOUR_KEY" } } ``` See [MCP Settings](/configuration/mcp-settings) for the complete field reference. ## Basic Usage ### Connecting AI Clients Once your servers are configured, connect your AI clients using MCPHub endpoints: **All Servers** Access all configured MCP servers: `http://localhost:3000/mcp` **Specific Group** Access servers in a specific group: `http://localhost:3000/mcp/{groupName}` **Individual Server** Access a single server: `http://localhost:3000/mcp/{serverName}` **Smart Routing** Use AI-powered tool discovery: `http://localhost:3000/mcp/$smart` \[!NOTE] Smart routing requires PostgreSQL with pgvector and an LLM provider (OpenAI or compatible) API key. ### Example: Adding Popular MCP Servers Here are some popular MCP servers you can add: > **Web Fetch Server** > > ```json theme={null} > { > "name": "fetch", > "command": "uvx", > "args": ["mcp-server-fetch"] > } > ``` > **Playwright Browser Automation** > > ```json theme={null} > { > "name": "playwright", > "command": "npx", > "args": ["@playwright/mcp@latest", "--headless"] > } > ``` > **Amap Maps (with API key)** > > ```json theme={null} > { > "name": "amap", > "command": "npx", > "args": ["-y", "@amap/amap-maps-mcp-server"], > "env": { > "AMAP_MAPS_API_KEY": "your-api-key-here" > } > } > ``` > **Slack Integration** > > ```json theme={null} > { > "name": "slack", > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-slack"], > "env": { > "SLACK_BOT_TOKEN": "your-bot-token", > "SLACK_TEAM_ID": "your-team-id" > } > } > ``` ## Next Steps > **[Server Management](/features/server-management)** > > Learn advanced server configuration and management > **[Group Management](/features/group-management)** > > Organize servers into logical groups > **[Smart Routing](/features/smart-routing)** > > Set up AI-powered tool discovery > **[API Reference](/api-reference/introduction)** > > Explore the complete API documentation ## Troubleshooting > **Server won't start** > > * Check if the MCP server command is accessible in your PATH - Verify environment variables are > correctly set - Check MCPHub logs for detailed error messages > **Can't connect from AI client** > > * Ensure MCPHub is running on the correct port - Check firewall settings - Verify the endpoint > URL format > **Authentication issues** > > * Verify credentials are correct - Check if JWT token is valid - Try clearing browser cache and > cookies Need more help? Join our [Discord community](https://discord.gg/2BJehJZVH5) for support! # 身份验证 Source: https://docs.mcphub.app/zh/api-reference/auth 管理用户和身份验证。 > **[POST /api/auth/login](#login)** > > 登录以获取 JWT 令牌。 > **[POST /api/auth/register](#register)** > > 注册一个新用户。 > **[GET /api/auth/user](#get-current-user)** > > 获取当前已验证的用户。 > **[POST /api/auth/change-password](#change-password)** > > 更改当前用户的密码。 *** ### 登录 验证用户身份并返回 JWT 令牌及用户详细信息。 * **端点**: `/api/auth/login` * **方法**: `POST` * **正文**: * `username` (string, 必填): 用户名。 * `password` (string, 必填): 用户密码。 * **请求示例**: ```json theme={null} { "username": "admin", "password": "your-admin-password" } ``` * **成功响应**: ```json theme={null} { "success": true, "message": "登录成功", "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "username": "admin", "isAdmin": true, "permissions": { ... } } } ``` * **已禁用密码登录**:当 Better Auth 的 `disablePasswordLogin` 生效时,该端点返回 `403`,正文为 `{"success": false, "message": "..."}`。参见 [仅允许 SSO 登录](/zh/features/authentication)。 *** ### 注册 注册一个普通用户并返回 JWT 令牌。 本地自助注册要求 `systemConfig.auth.allowRegistration: true`(默认关闭)且密码登录可用,否则返回 HTTP 403。新账号始终为普通用户;`isAdmin` 参数不生效。密码至少八位,包含字母、数字和特殊字符。 * **端点**: `/api/auth/register` * **方法**: `POST` * **正文**: * `username` (string, 必填): 新的用户名。 * `password` (string, 必填): 新的用户密码(至少八位,包含字母、数字和特殊字符)。 * 不能申请管理员权限;`isAdmin` 参数会被忽略。 * **请求示例**: ```json theme={null} { "username": "newuser", "password": "Password123!" } ``` * **成功响应**: ```json theme={null} { "success": true, "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "user": { "username": "newuser", "isAdmin": false, "permissions": { ... } } } ``` *** ### 获取当前用户 检索当前通过身份验证的用户的个人资料。 * **端点**: `/api/auth/user` * **方法**: `GET` * **身份验证**: 需要承载令牌 (Bearer Token)。 * **成功响应**: ```json theme={null} { "success": true, "user": { "username": "admin", "isAdmin": true, "permissions": { ... } } } ``` *** ### 更改密码 允许通过身份验证的用户更改其密码。 * **端点**: `/api/auth/change-password` * **方法**: `POST` * **身份验证**: 需要承载令牌 (Bearer Token)。 * **正文**: * `currentPassword` (string, 必填): 用户的当前密码。 * `newPassword` (string, 必填): 新的密码 (至少6个字符)。 * **请求示例**: ```json theme={null} { "currentPassword": "oldpassword", "newPassword": "newpassword123" } ``` * **成功响应**: ```json theme={null} { "success": true, "message": "密码更新成功" } ``` *** ### 社交登录 (Better Auth) MCPHub 通过集成 [Better Auth](https://www.better-auth.com/) 支持社交登录功能(GitHub, Google 等)。 **前提条件**: 1. **数据库模式**: 必须配置 `DB_URL` 环境变量(Better Auth 需要 PostgreSQL 进行数据持久化)。 2. **启动配置**: Better Auth 相关设置只会在启动时读取一次,因此改完后需要重启 MCPHub。 3. **环境变量**: * **全局**: `BETTER_AUTH_ENABLED`、`BETTER_AUTH_URL`、可选 `BETTER_AUTH_BASE_PATH`、可选 `BETTER_AUTH_TRUSTED_ORIGINS`。 * **GitHub**: `BETTER_AUTH_GITHUB_ENABLED`、`GITHUB_CLIENT_ID`、`GITHUB_CLIENT_SECRET`。 * **Google**: `BETTER_AUTH_GOOGLE_ENABLED`、`GOOGLE_CLIENT_ID`、`GOOGLE_CLIENT_SECRET`。 * **本地 OIDC**: `BETTER_AUTH_OIDC_ENABLED`、可选 `BETTER_AUTH_OIDC_PROVIDER_ID`、`BETTER_AUTH_OIDC_DISCOVERY_URL`(或兼容别名 `OIDC_DISCOVERY_URL`)、可选 `BETTER_AUTH_OIDC_SCOPES`、可选 `BETTER_AUTH_OIDC_PKCE`、可选 `BETTER_AUTH_OIDC_PROMPT`,以及 `OIDC_CLIENT_ID`、`OIDC_CLIENT_SECRET`。 非敏感的 Better Auth 配置也可以保存在 `systemConfig.auth.betterAuth` 中,但 `BETTER_AUTH_*` 环境变量会优先覆盖它们。 **端点**: Better Auth API 挂载在 `${BASE_PATH}${betterAuthConfig.basePath}`。默认路径是 `/api/auth/better`;可以用 `BETTER_AUTH_BASE_PATH` 在启动时覆盖,也可以把 `systemConfig.auth.betterAuth.basePath` 作为后备值。构建回调 URL 时,`BETTER_AUTH_URL` 会优先于 `systemConfig.install.baseUrl` 生效。主要端点包括: * **发起登录**: * `GET /api/auth/better/signIn/social?provider=github` * `GET /api/auth/better/signIn/social?provider=google` * **会话管理**: * `GET /api/auth/better/session` (获取当前会话) * `POST /api/auth/better/signOut` (登出) 如需完整的端点列表和使用详情,请参阅 [Better Auth API 文档](https://www.better-auth.com/docs)。 **禁用 Better Auth**: 如果未配置 `DB_URL`、设置了 `BETTER_AUTH_ENABLED=false`,或者没有任何提供商在满足凭据条件后真正启用,Better Auth 都会自动禁用。 # 配置 Source: https://docs.mcphub.app/zh/api-reference/config 管理和检索系统级配置。 > **[PUT /api/system-config](#update-system-config)** > > 更新主系统配置。 > **[GET /api/settings](#get-all-settings)** > > 获取所有系统设置,包括服务器和群组。 > **[GET /config](#get-runtime-config)** > > 获取前端的公共运行时配置。 > **[GET /public-config](#get-public-config)** > > 获取公共配置以检查是否跳过身份验证。 *** ### 更新系统配置 更新系统配置的各个部分。您只需提供要更新部分的键。 * **端点**: `/api/system-config` * **方法**: `PUT` * **正文**: 一个 JSON 对象,包含以下一个或多个顶级键:`routing`、`install`、`smartRouting`、`toolResultCompression`、`mcpRouter`。 #### 路由配置 (`routing`) * `enableGlobalRoute` (boolean): 启用或禁用全局 `/api/mcp` 路由。 * `enableGroupNameRoute` (boolean): 启用或禁用基于群组的路由 (例如 `/api/mcp/group/:groupName`)。 * `enableBearerAuth` (boolean): 为 MCP 端点启用 Bearer 令牌身份验证(默认:`true`)。 * `bearerAuthKey` (string): 旧版 Bearer 密钥(仅用于一次性迁移)。 * `skipAuth` (boolean): 如果为 true,则跳过仪表盘 / Web API 的登录要求,前端可在没有会话令牌的情况下访问 `/api` 路由。未登录的仪表盘用户会被视为内置 `guest` 管理员,因此仅应在受信任环境中启用,但**不会**关闭 MCP 端点认证。 #### 安装配置 (`install`) * `pythonIndexUrl` (string): 用于安装的 Python 包索引 (PyPI) 的基础 URL。 * `npmRegistry` (string): 用于安装的 npm 注册表 URL。 * `baseUrl` (string): 此 MCPHub 实例的公共基础 URL,用于 MCP 请求与 OAuth 回调地址。 #### 智能路由配置 (`smartRouting`) * `enabled` (boolean): 启用或禁用智能路由功能。 * `dbUrl` (string): 用于存储嵌入的数据库连接 URL。 * `llmProviderBaseUrl` (string): 用于生成嵌入的 OpenAI 兼容 API 的基础 URL。 * `llmProviderApiKey` (string): 嵌入服务的 API 密钥。 * `embeddingModel` (string): 要使用的嵌入模型的名称。 * `embeddingDimensions` (number,可选):对于支持自定义维度的提供方,指定嵌入输出维度;不设置时使用模型默认值。该值同时声明了预期的存储维度,但仅对已知支持 Matryoshka(MRL)表示的模型、或开启 `embeddingDimensionsApiPassthrough` 时,才会作为 `dimensions` 请求参数转发给提供方(issue #1131)。 * `embeddingDimensionsApiPassthrough` (boolean,可选,默认 `false`):设为 `true` 时,始终将 `embeddingDimensions` 作为 `dimensions` 请求参数转发给提供方。非 MRL 模型/后端(Qwen3-Embedding、BGE、vLLM/sglang 等)会直接拒绝该参数;仅对其它支持 MRL 的模型开启。参见 `EMBEDDING_DIMENSIONS_API_PASSTHROUGH`。 #### 工具结果压缩配置 (`toolResultCompression`) * `enabled` (boolean): 是否对成功的工具结果启用透明压缩。 * `minTokens` (number): 单个文本块达到或超过该估算 token 数时才会压缩。默认值:`2000`。 * `maxOutputTokens` (number): 每个压缩后文本块的目标 token 预算。默认值:`1200`。 * `strategy` (string): 压缩策略。允许值:`auto`、`json`、`log`、`search`、`diff`、`text`。默认值:`auto`。 压缩只作用于成功的工具响应。`isError: true` 的响应和非文本内容块会原样返回。活动日志会在压缩前记录上游原始输出。 #### MCP 路由器配置 (`mcpRouter`) * `apiKey` (string): MCP 路由器服务的 API 密钥。 * `referer` (string): 用于 MCP 路由器请求的 referer 头。 * `title` (string): 在 MCP 路由器上显示的此实例的标题。 * `baseUrl` (string): MCP 路由器 API 的基础 URL。 * **请求示例**: ```json theme={null} { "routing": { "enableBearerAuth": true, "skipAuth": false }, "smartRouting": { "enabled": true, "dbUrl": "postgresql://user:pass@host:port/db" }, "toolResultCompression": { "enabled": true, "minTokens": 2000, "maxOutputTokens": 1200, "strategy": "auto" } } ``` *** ### 获取所有设置 检索实例的整个设置对象,包括所有服务器配置、群组和系统设置。这是 `mcp_settings.json` 文件的完整转储。 * **端点**: `/api/settings` * **方法**: `GET` *** ### 获取运行时配置 检索前端应用程序所需的基本运行时配置。此端点不需要身份验证。 * **端点**: `/config` * **方法**: `GET` * **成功响应**: ```json theme={null} { "success": true, "data": { "basePath": "", "version": "1.0.0", "name": "MCPHub" } } ``` *** ### 获取公共配置 检索公共配置,主要用于检查是否跳过身份验证。这允许前端在用户登录前相应地调整其行为。此端点不需要身份验证。 * **端点**: `/public-config` * **方法**: `GET` * **成功响应**: ```json theme={null} { "success": true, "data": { "skipAuth": false, "permissions": {} } } ``` # 群组 Source: https://docs.mcphub.app/zh/api-reference/groups 管理服务器群组以组织和路由请求。 > **[GET /api/groups](#get-all-groups)** > > 获取所有群组的列表。 > **[POST /api/groups](#create-a-new-group)** > > 创建一个新群组。 > **[GET /api/groups/:id](#get-a-group)** > > 获取特定群组的详细信息。 > **[PUT /api/groups/:id](#update-a-group)** > > 更新现有群组。 > **[DELETE /api/groups/:id](#delete-a-group)** > > 删除一个群组。 > **[POST /api/groups/:id/servers](#add-server-to-group)** > > 将服务器添加到群组。 > **[DELETE /api/groups/:id/servers/:serverName](#remove-server-from-group)** > > 从群组中删除服务器。 > **[PUT /api/groups/:id/servers/batch](#batch-update-group-servers)** > > 批量更新群组中的服务器。 > **[GET /api/groups/:id/server-configs](#get-group-server-configs)** > > 获取群组中详细的服务器配置。 > **[PUT /api/groups/:id/server-configs/:serverName/tools](#update-group-server-tools)** > > 更新群组中服务器的工具选择。 *** ### 获取所有群组 检索所有服务器群组的列表。 * **端点**: `/api/groups` * **方法**: `GET` * **成功响应**: ```json theme={null} { "success": true, "data": [ { "id": "group-1", "name": "我的群组", "description": "服务器的集合。", "servers": ["server1", "server2"], "owner": "admin" } ] } ``` *** ### 创建一个新群组 创建一个新的服务器群组。 * **端点**: `/api/groups` * **方法**: `POST` * **正文**: * `name` (string, 必填): 群组的名称。 * `description` (string, 可选): 群组的描述。 * `instructions` (string, 可选): 在该群组路由上作为 MCP `initialize` 的 instructions 发送。参见[分组说明](/zh/features/group-management#分组说明)。 * `servers` (array of strings, 可选): 要包含在群组中的服务器名称列表。 * **请求示例**: ```json theme={null} { "name": "我的新群组", "description": "新群组的描述", "servers": ["server1", "server2"] } ``` *** ### 获取一个群组 通过 ID 或名称检索特定群组的详细信息。 * **端点**: `/api/groups/:id` * **方法**: `GET` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 *** ### 更新一个群组 更新现有群组的名称、描述或服务器列表。 * **端点**: `/api/groups/:id` * **方法**: `PUT` * **参数**: * `:id` (string, 必填): 要更新的群组的 ID 或名称。 * **正文**: * `name` (string, 可选): 群组的新名称。 * `description` (string, 可选): 群组的新描述。 * `instructions` (string, 可选): 群组的新说明;空字符串表示移除。 * `servers` (array, 可选): 群组的新服务器列表。格式请参阅 [批量更新群组服务器](#batch-update-group-servers)。 * **请求示例**: ```json theme={null} { "name": "更新后的群组名称", "description": "更新后的描述" } ``` *** ### 删除一个群组 通过 ID 或名称删除一个群组。 * **端点**: `/api/groups/:id` * **方法**: `DELETE` * **参数**: * `:id` (string, 必填): 要删除的群组的 ID 或名称。 *** ### 将服务器添加到群组 将单个服务器添加到群组。 * **端点**: `/api/groups/:id/servers` * **方法**: `POST` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 * **正文**: * `serverName` (string, 必填): 要添加的服务器的名称。 * **请求示例**: ```json theme={null} { "serverName": "my-server" } ``` *** ### 从群组中删除服务器 从群组中删除单个服务器。 * **端点**: `/api/groups/:id/servers/:serverName` * **方法**: `DELETE` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 * `:serverName` (string, 必填): 要删除的服务器的名称。 *** ### 批量更新群组服务器 用新的列表替换群组中的所有服务器。该列表可以是简单的字符串或详细的配置对象。 * **端点**: `/api/groups/:id/servers/batch` * **方法**: `PUT` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 * **正文**: * `servers` (array, 必填): 服务器名称(字符串)或服务器配置对象的数组。 * **请求示例 (简单)**: ```json theme={null} { "servers": ["server1", "server2"] } ``` * **请求示例 (详细)**: ```json theme={null} { "servers": [ { "name": "server1", "tools": "all" }, { "name": "server2", "tools": ["toolA", "toolB"] } ] } ``` 服务器配置对象还可以包含 `pinnedTools`:不带前缀的工具名数组,这些工具会在分组的 `$smart/{group}` 端点中与元工具一同列出(参见 [固定工具](/zh/features/smart-routing#固定工具))。只有同时在 `tools` 中选中的固定工具才会生效。 *** ### 获取群组服务器配置 检索群组内所有服务器的详细配置,包括启用了哪些工具。 * **端点**: `/api/groups/:id/server-configs` * **方法**: `GET` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 * **成功响应**: ```json theme={null} { "success": true, "data": [ { "name": "server1", "tools": "all" }, { "name": "server2", "tools": ["toolA", "toolB"] } ] } ``` *** ### 更新群组服务器工具 更新群组内特定服务器的工具选择。 * **端点**: `/api/groups/:id/server-configs/:serverName/tools` * **方法**: `PUT` * **参数**: * `:id` (string, 必填): 群组的 ID 或名称。 * `:serverName` (string, 必填): 要更新的服务器的名称。 * **正文**: * `tools` (string or array of strings, 必填): 字符串 `"all"` 表示启用所有工具,或一个工具名称数组以指定启用哪些工具。未包含在数组中的工具会被移除固定(`pinnedTools`)。 * **请求示例**: ```json theme={null} { "tools": ["toolA", "toolC"] } ``` ## 可见性字段 `POST /api/groups`、`POST /api/groups/batch` 中的各组以及 `PUT /api/groups/:id` 支持 `visibility`(`private | group | public`)和 `sharedWithUsers`(非空用户名数组)。 新组默认私有,更新时省略字段则保持原值。`shared` 和 `null` 不是合法的可见性值。 共享用户为空时仅所有者和管理员有权限。 `GET /api/groups/:id/share-candidates` 返回按名称排序的用户名数组(不包含所有者), 仅所有者和管理员可调用。共享允许发现和使用,不赋予管理权限。 组读取响应会隐藏无权访问的服务器;替换服务器列表时保留已有的隐藏条目。 历史组兼容和数据库升级规则见[组管理](/zh/features/group-management)。 # 介绍 Source: https://docs.mcphub.app/zh/api-reference/introduction 欢迎来到 MCPHub API 文档。 MCPHub API 提供了一整套端点来管理您的 MCP 服务器、群组、用户等。该 API 分为两个主要类别: * **MCP 端点**: 这些是与您的 MCP 服务器交互的主要端点。它们提供了一个统一的界面,用于向您的服务器发送请求并实时接收响应。 * **管理 API**: 这些端点用于管理 MCPHub 实例本身。这包括管理服务器、群组、用户和系统设置。 所有 API 端点都在 `/api` 路径下可用。例如,获取所有服务器的端点是 `/api/servers`。 大多数管理 API 端点都需要身份验证。有关更多详细信息,请参阅[身份验证](/api-reference/auth)部分。 # 日志 Source: https://docs.mcphub.app/zh/api-reference/logs 访问和管理服务器日志。 > **[GET /api/logs](#get-all-logs)** > > 获取所有日志。 > **[DELETE /api/logs](#clear-logs)** > > 清除所有日志。 > **[GET /api/logs/stream](#stream-logs)** > > 实时流式传输日志。 *** ### 获取所有日志 检索所有存储的日志。 * **端点**: `/api/logs` * **方法**: `GET` * **成功响应**: ```json theme={null} { "success": true, "data": [ { "timestamp": "2023-10-27T10:00:00.000Z", "level": "info", "message": "服务器成功启动。", "service": "system" } ] } ``` *** ### 清除日志 删除所有存储的日志。 * **端点**: `/api/logs` * **方法**: `DELETE` * **成功响应**: ```json theme={null} { "success": true, "message": "日志清除成功" } ``` *** ### 流式传输日志 使用服务器发送事件 (SSE) 实时流式传输日志。连接将保持打开状态,新的日志条目将在发生时发送。 * **端点**: `/api/logs/stream` * **方法**: `GET` * **响应格式**: 该流发送带有包含 JSON 对象的 `data` 字段的事件。第一个事件的 `type` 为 `initial`,包含所有历史日志。后续事件的 `type` 为 `log`,包含单个新日志条目。 * **事件示例**: ``` data: {"type":"log","log":{"timestamp":"2023-10-27T10:00:05.000Z","level":"debug","message":"正在处理 /api/some-endpoint 的请求","service":"mcp-server"}} ``` # MCP HTTP 端点 Source: https://docs.mcphub.app/zh/api-reference/mcp-http 使用统一的 HTTP 端点连接到您的 MCP 服务器。 MCPHub 为您的所有 MCP 服务器提供统一的可流式 HTTP 接口。这使您可以向任何配置的 MCP 服务器发送请求并实时接收响应。 **安全提示**:MCP 端点默认需要身份验证,以避免暴露已连接的工具和数据。若需对 MCP 端点开放匿名访问,请在密钥设置中关闭 **启用 Bearer 认证**。**免登录开关**仅影响仪表盘登录。 ### 统一端点 此端点提供对所有已启用的 MCP 服务器的访问。 * **端点**: `http://localhost:3000/mcp` * **方法**: `POST` ### 特定群组的端点 要定向访问特定的服务器群组,请使用基于群组的 HTTP 端点。 * **端点**: `http://localhost:3000/mcp/{group}` * **方法**: `POST` * **参数**: * `{group}`: 群组的 ID 或名称。 ### 特定服务器的端点 要直接访问单个服务器,请使用特定于服务器的 HTTP 端点。 * **端点**: `http://localhost:3000/mcp/{server}` * **方法**: `POST` * **参数**: * `{server}`: 服务器的名称。 > **注意**: 如果服务器名称和群组名称相同,则群组将优先。 ## 协议兼容矩阵 同一个 `/mcp` URL 同时服务旧版 `2025-11-25` 和新版 `2026-07-28` 客户端。 群组和服务器端点使用相同的协议处理方式。仅将 URL 从 `/sse` 改成 `/mcp`, 不会自动升级客户端的协议版本。 | 行为 | 旧版 HTTP+SSE(`/sse`) | HTTP `2025-11-25`(`/mcp`) | HTTP `2026-07-28`(`/mcp`) | | - | - | - | - | | 支持状态 | 已弃用,仍受支持 | 保留兼容支持 | 已支持,迁移工作仍在继续 | | 启动 | 通过 SSE 传输执行 `initialize` | 通过 Streamable HTTP 执行 `initialize` | 无需 `initialize` 握手,使用 SDK 发现机制 | | 下游会话 | SSE 连接及会话消息端点 | `Mcp-Session-Id` | 不创建下游传输会话 | | 路由请求头 | 使用旧版传输行为 | 使用旧版传输行为 | `Mcp-Method`;具名调用和资源读取还需 `Mcp-Name` | | 有状态工具 | 使用已有会话生命周期 | 使用已有会话生命周期 | Bearer 认证及显式状态 ID 或 OpenAI 会话元数据 | | MCP Apps 能力 | 初始化时声明 | 初始化时声明 | 每次请求在 `params._meta` 中声明 | | 列表缓存提示 | 保留已有旧版响应结构 | 保留已有旧版响应结构 | 上游剩余 TTL,最长 5 秒,私有;否则为零 | | 资源读取缓存提示 | 保留已有上游提示 | 保留已有上游提示 | 有效的上游剩余 TTL,范围收紧为 `private`;其他情况为零 | MCP Apps 透传还取决于请求路由和可见的上游服务器;`$smart` 路由仍按普通 MCP 路由处理。参见 [MCP Apps](/zh/features/mcp-apps)。上游 Streamable HTTP 默认自动协商:先探测 `server/discover`,支持时使用 `2026-07-28` 无状态 HTTP,否则按 SDK 规则回退旧版握手。 上下游协议互相独立。服务器配置中的 `protocolNegotiation: "legacy"` 可跳过探测。 stdio 和旧 SSE 上游继续使用旧版协商。 | 上游行为 | 新版 Streamable HTTP | 旧版上游 | | - | - | - | | 建立连接 | `server/discover`,无传输 session 和 GET 流 | `initialize`,保留现有传输生命周期 | | 列表更新 | 原始上游 TTL 到期后按需刷新,合并并发刷新 | 保留列表变更通知 | | TTL 缺失、为零或列表分页未完成 | 下一次相关请求重新获取 | 保留现有发现行为 | | 健康检查(需启用) | 发起网络 `server/discover`,不依赖工具能力 | `ping`,保留方法不存在时的回退 | | 启用 elicitation 的隔离连接 | relay 仍依赖旧版请求,因此继续使用旧版协商 | 保留现有转发支持 | 工具调用、prompt 获取和资源读取之前也会重验证对应列表,使新增和删除项及时影响路由。 资源模板按需获取。刷新失败只在当前请求中排除该上游,健康上游和内置内容仍可用; 部分列表返回零 TTL,后续请求会重试失败的上游。 不增加后台轮询;没有相关请求时不会刷新列表。共享列表刷新后若发生变化,会通知已连接的 旧版下游客户端;个人列表仍限定在各自运行时中。上游发现继续保留单页行为,带 `nextCursor` 的响应不提供正 TTL。 配置 `passthroughHeaders` 的新版上游使用请求内独立客户端和快照,即使 TTL 为正或请求头 缺失,也不跨请求共享列表或刷新任务。每次相关请求增加一次 discovery 往返;个性化快照 不更新全局描述、向量索引或列表变更广播。 自动协商不意味着所有错误都回退:HTTP 探测超时、网络故障和认证错误可能导致连接失败。 详见[上游协议协商配置](/zh/configuration/mcp-settings#上游协议协商)。 ## Elicitation 转发 旧协议客户端调用工具时,若上游服务器启用了 `perSessionClient: true`,MCPHub 会转发 form 和 URL 模式的 `elicitation/create` 请求。独立连接只声明下游客户端支持的模式; 旧式空能力 `elicitation: {}` 仅表示支持 form。accept、decline 和 cancel 响应原样返回 上游,URL 完成通知仅发送给所属下游会话。普通工具调用和智能路由的 `call_tool` 均支持。 旧协议没有可移植的父调用标识,因此同一会话内、启用 elicitation 的上游连接会串行执行 工具调用;不同会话使用不同连接。取消调用、结束会话或超时会中止等待中的交互。请求 抛出异常时会丢弃上游连接,不自动重放调用;下次调用重新连接,上游会话状态需要重新 建立。正常返回的 `isError` 工具响应仍按原样交付。 首期不支持共享上游连接、个人凭据/OAuth 运行时、提示词或资源操作,也不支持新版 `2026-07-28` 多轮 elicitation;这些路径不会声明 elicitation 能力。 新版客户端即使携带 `X-MCPHub-State-Id` 也不会启用 elicitation。 form 模式用于普通结构化输入。密码、API Key 和访问令牌必须使用 URL 模式,在上游 服务页面收集,不能通过 MCP 表单传递。交互界面由连接的 MCP 客户端提供,MCPHub 不会新增控制台输入弹窗。 ## 客户端迁移 1. 将旧 SSE 客户端传输改为 HTTP 传输,将 `/sse` 改为 `/mcp`,保留原有群组、 服务器或用户路由范围,以及 Bearer 凭据。旧版 HTTP 客户端可以先完成传输迁移, 继续使用原有初始化和会话生命周期。 2. 要采用 `2026-07-28`,需使用支持新版 HTTP 发现机制及逐请求元数据的客户端。 SDK v2 HTTP 客户端会自动设置协议和路由请求头。自定义客户端需发送常规 JSON-RPC 请求体,将 `params._meta["io.modelcontextprotocol/protocolVersion"]` 设为 `2026-07-28`,并在 `params._meta["io.modelcontextprotocol/clientCapabilities"]` 中声明客户端能力。 同时发送一致的 `MCP-Protocol-Version` 和 `Mcp-Method` 请求头;`tools/call` 和 `prompts/get` 需要与 `params.name` 一致的 `Mcp-Name`,`resources/read` 则需要与 `params.uri` 一致。工具名称应使用该路由列表响应中的名称。 3. 使用 `perSessionClient` 或 OpenAPI `cookieSession` 工具时,为每个独立工作流 生成 UUID v4,并通过工具调用的 `X-MCPHub-State-Id` 请求头发送。无法设置该 扩展的已认证 OpenAI/ChatGPT 工具调用,在显式请求头缺失时也可复用客户端声明的 `_meta["openai/session"]` 会话提示。MCPHub 会先对该提示做哈希;它只用于状态 关联,不用于认证。显式状态请求头一旦存在但非法,将直接失败而不会回退。无状态 工具无需这两个值。只有相同 Bearer 凭据、用户和路由才能复用状态。状态仅保存在 当前进程中,闲置 30 分钟后过期,多副本部署需要粘性路由。进程重启、状态过期或 凭据、路由变化后需要重新建立应用状态。参见 [显式状态配置](/zh/configuration/mcp-settings#mcp-2026-07-28-的有状态工具)。 4. 使用实际权限验证工具列表和调用,再验证所用的资源读取及 MCP Apps 功能。 新版 Apps 宿主需在每次请求中声明 UI 能力。新版调用不依赖 `Mcp-Session-Id` 或 `enableSessionRebuild`,这两者仍属于旧版机制。 ## 缓存行为 现代列表把上游发现快照与实时配置、权限和每次请求的客户端能力组合起来。 正向提示始终为私有,取所有上游剩余新鲜时间的最小值,并受最长 5 秒的投影 窗口限制。保留发现快照的年龄,重复网关列表请求不会续期;空上游列表也参与 计算。缺失/无效 TTL、未完整分页、失败或不完整的发现、网关生成的智能路由 条目、内置提示词/资源,以及无法确定新鲜度的托管权限均使用零 TTL。 每个请求重新生成投影。投影期间检测到配置、成员、权限或运行时变更时归零。 列表刷新在等待新响应之前撤销旧快照的新鲜度;失败、断连或替换不能建立新的 新鲜度。资源模板列表使用该请求实际获取的响应。不增加网关响应缓存。 本地配置/权限改变后,客户端可能继续保留已获得的列表最多 5 秒,服务端失效 不能撤回客户端缓存。缓存必须按端点、认证身份和每次请求的客户端能力隔离。 缓存列表不代表调用权限:MCPHub 在每个请求上重新检查访问权。上游无法提供 可信新鲜度时使用零 TTL。 现代资源读取仍绕过 SDK 响应缓存,从有效上游 TTL 扣除处理耗时,并限制为 请求认证上下文内的私有共享。缺失/无效 TTL、内置资源和网关生成的读取错误 均使用零。旧版响应形状和会话行为保持不变。 ## 旧版 SSE 弃用计划 公开通知随包含本计划的下一次版本发布。发布说明必须记录实际通知日期 **N** 和最早可移除日期 **N 加 12 个日历月**。本页面和 ADR 不构成更早的 通知日期。在该发布说明发布之前,通知日期和移除日期均未公布。 计划在达到上述日期且满足下方迁移就绪条件后的第一个主版本中移除 SSE。 条件未满足时延后移除并公布调整后的目标,不缩短通知窗口。至少提前 90 天 公布确切的目标版本和日期,提前 30 天再次提醒,每次公告附迁移说明。 实际移除必须通过独立代码评审。 ### 保留路由范围和认证 部署配置了基础路径时,为下表所有 URL 加上该前缀。建议使用分组 ID; 保留原来的路由标识以及 bearer 请求头名称和值。分组名与服务器名相同时, 分组优先。 | 原 SSE 连接 | Streamable HTTP 替代地址 | 将退役的 SSE 消息端点 | | - | - | - | | `/sse` | `/mcp` | `/messages` | | `/sse/{group}` | `/mcp/{group}` | `/messages` | | `/sse/{server}` | `/mcp/{server}` | `/messages` | | `/{user}/sse` | `/{user}/mcp` | `/{user}/messages` | | `/{user}/sse/{group}` | `/{user}/mcp/{group}` | `/{user}/messages` | | `/{user}/sse/{server}` | `/{user}/mcp/{server}` | `/{user}/messages` | 同时保留 `$smart/{group}` 等智能路由后缀。SSE 客户端使用服务端公布的 消息 URL 和会话查询参数;不要把消息 URL 转换成 HTTP 新端点。替换连接 URL,让 HTTP transport 管理后续请求。 先独立迁移传输方式,再考虑升级协议。例如使用旧版 TypeScript SDK (`@modelcontextprotocol/sdk` 1.29.0,与 MCPHub 独立兼容性测试一致): ```typescript theme={null} import { Client } from '@modelcontextprotocol/sdk/client/index.js'; import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js'; const client = new Client({ name: 'my-client', version: '1.0.0' }); await client.connect( new StreamableHTTPClientTransport(new URL('https://hub.example.com/alice/mcp/my-group-id'), { requestInit: { headers: { Authorization: 'Bearer YOUR_EXISTING_KEY' } }, }), ); const tools = await client.listTools(); // 使用返回的工具名;在整个工作流中复用此客户端。 await client.close(); ``` 此迁移保留旧版初始化和 `Mcp-Session-Id` 生命周期。有状态工具仍按会话 隔离,单纯迁移传输方式不需要 `X-MCPHub-State-Id`。现代协议发现和显式 状态是上文描述的独立升级。SSE 会话 ID 不能跨传输方式复用:建立新的 HTTP 工作流,重新执行登录或工具初始化。 ### 就绪条件和移除边界 公布确切移除目标之前,维护者必须记录: * 包含公开通知的版本、实际发布日期,以及移除时已满 12 个日历月的依据。 * 整个窗口内旧版 SSE 和 Streamable HTTP 兼容性测试持续通过,覆盖认证后的 全局、分组、服务器和用户路由,会话续接/重建及有状态工具隔离。 * 每个文档列出的客户端集成的迁移清单:HTTP transport、保留路由/认证、 列表和调用成功,以及实际使用的资源、Apps 或有状态工作流。记录测试版本 和未解决阻碍;阻碍发布的迁移缺陷必须为零。可补充部署者确认,不能把 缺少遥测当作无人使用 SSE 的证据。 * 中英双语发布说明和文档、提前 90 天和 30 天的提醒,以及仍使用 SSE 的 部署的支持渠道。 移除清单仅包括 SSE GET 路由、对应 POST 消息路由、`handleSseConnection`、 `handleSseMessage`、SSE transport 创建以及 SSE 专用测试/导入。删除共享 辅助函数前必须检查所有调用方。保留 `/mcp` POST/GET/DELETE、旧版 HTTP 初始化、会话映射与清理、创建/重建锁、bearer/分组认证、`perSessionClient` 隔离、OpenAPI cookie jar 和现代显式状态。`server-legacy` 还提供共享的 旧版 HTTP/OAuth 功能,不能仅因 SSE 退役就删除该依赖。 本计划由 [#1272](https://github.com/samanhappy/mcphub/issues/1272) 跟踪。 发布通知的版本必须建立独立移除任务,记录实际日期、客户端清单和删除清单。 旧版 `2025-11-25` Streamable HTTP 支持独立保留。 # MCP SSE 端点 (已弃用) Source: https://docs.mcphub.app/zh/api-reference/mcp-sse 使用 SSE 端点连接到您的 MCP 服务器。 旧版 HTTP+SSE 端点已弃用,但迁移期间仍受支持。新接入请使用 [MCP HTTP 端点与迁移指南](/zh/api-reference/mcp-http)。目前尚未公布移除版本或日期。 分阶段升级 ADR 要求至少 12 个月的弃用窗口。移除前需要公布时间计划并验证迁移路径。这不代表弃用旧版 `2025-11-25` HTTP 客户端的会话生命周期:旧版和新版 HTTP 继续受支持。 **安全提示**:MCP 端点默认需要身份验证,以避免暴露已连接的工具和数据。若需对 MCP 端点开放匿名访问,请在密钥设置中关闭 **启用 Bearer 认证**。**免登录开关**仅影响仪表盘登录。 ### 统一端点 * **端点**: `http://localhost:3000/sse` * **方法**: `GET` ### 特定群组的端点 * **端点**: `http://localhost:3000/sse/{group}` * **方法**: `GET` * **参数**: * `{group}`: 群组的 ID 或名称。 ### 特定服务器的端点 * **端点**: `http://localhost:3000/sse/{server}` * **方法**: `GET` * **参数**: * `{server}`: 服务器的名称。 # OpenAPI 集成 Source: https://docs.mcphub.app/zh/api-reference/openapi 从 MCP 工具生成 OpenAPI 规范,与 OpenWebUI 和其他系统无缝集成 # OpenWebUI 集成的 OpenAPI 生成 MCPHub 现在支持从 MCP 工具生成 OpenAPI 3.0.3 规范,实现与 OpenWebUI 和其他 OpenAPI 兼容系统的无缝集成,无需 MCPO 作为中间代理。 ## 功能特性 * ✅ **自动 OpenAPI 生成**:将 MCP 工具转换为 OpenAPI 3.0.3 规范 * ✅ **OpenWebUI 兼容**:无需 MCPO 代理的直接集成 * ✅ **实时工具发现**:动态包含已连接 MCP 服务器的工具 * ✅ **双参数支持**:支持 GET(查询参数)和 POST(JSON 正文)进行工具执行 * ✅ **无需身份验证**:OpenAPI 端点公开,便于集成 * ✅ **完整元数据**:具有适当模式和文档的完整 OpenAPI 规范 ## API 端点 ### OpenAPI 规范 ```bash GET /api/openapi.json theme={null} curl "http://localhost:3000/api/openapi.json" ``` ```bash GET /api/openapi.yaml theme={null} curl "http://localhost:3000/api/openapi.yaml" ``` ```bash 带参数 theme={null} curl "http://localhost:3000/api/openapi.json?title=我的 MCP API&version=2.0.0" ``` 生成并返回所有已连接 MCP 工具的完整 OpenAPI 3.0.3 规范。`.json` 和 `.yaml` 版本接受相同的查询参数。 **查询参数:** **title** `string` 自定义 API 标题 **description** `string` 自定义 API 描述 **version** `string` 自定义 API 版本 **serverUrl** `string` 自定义服务器 URL **includeDisabled** `boolean` (default: false) 包含禁用的工具 **servers** `string` 要包含的服务器名称列表(逗号分隔) ### 组/服务器特定的 OpenAPI 规范 ```bash GET /api/:name/openapi.json theme={null} curl "http://localhost:3000/api/mygroup/openapi.json" ``` ```bash GET /api/:name/openapi.yaml theme={null} curl "http://localhost:3000/api/mygroup/openapi.yaml" ``` ```bash 带参数 theme={null} curl "http://localhost:3000/api/myserver/openapi.json?title=我的服务器 API&version=1.0.0" ``` 为特定组或服务器生成并返回 JSON 或 YAML 格式的 OpenAPI 3.0.3 规范。如果存在具有给定名称的组,则返回该组中所有服务器的规范。否则,将名称视为服务器名称并仅返回该服务器的规范。 **路径参数:** **name** `string` *required* 组 ID/名称或服务器名称 **查询参数:** 与主 OpenAPI 规范端点相同(title、description、version、serverUrl、includeDisabled)。 ### 可用服务器 ```bash GET /api/openapi/servers theme={null} curl "http://localhost:3000/api/openapi/servers" ``` 返回已连接的 MCP 服务器名称列表。 ```json 示例响应 theme={null} { "success": true, "data": ["amap", "playwright", "slack"] } ``` ### 工具统计 ```bash GET /api/openapi/stats theme={null} curl "http://localhost:3000/api/openapi/stats" ``` 返回有关可用工具和服务器的统计信息。 ```json 示例响应 theme={null} { "success": true, "data": { "totalServers": 3, "totalTools": 41, "serverBreakdown": [ {"name": "amap", "toolCount": 12, "status": "connected"}, {"name": "playwright", "toolCount": 21, "status": "connected"}, {"name": "slack", "toolCount": 8, "status": "connected"} ] } } ``` ### 工具执行 ```bash GET /api/tools/{serverName}/{toolName} theme={null} curl "http://localhost:3000/api/tools/amap/amap-maps_weather?city=Beijing" ``` ```bash POST /api/tools/{serverName}/{toolName} theme={null} curl -X POST "http://localhost:3000/api/tools/playwright/playwright-browser_navigate" \ -H "Content-Type: application/json" \ -d '{"url": "https://example.com"}' ``` 通过 OpenAPI 兼容端点执行 MCP 工具。 **路径参数:** **serverName** `string` *required* MCP 服务器的名称 **toolName** `string` *required* 要执行的工具名称 ## OpenWebUI 集成 要将 MCPHub 与 OpenWebUI 集成: **1. 启动 MCPHub** 确保 MCPHub 正在运行,并且已配置 MCP 服务器 **2. 获取 OpenAPI 规范** ```bash theme={null} curl http://localhost:3000/api/openapi.json > mcphub-api.json curl http://localhost:3000/api/openapi.yaml > mcphub-api.yaml ``` **3. 添加到 OpenWebUI** 在 OpenWebUI 中导入 OpenAPI 规范文件或直接指向 URL ### 配置示例 在 OpenWebUI 中,您可以通过以下方式将 MCPHub 添加为 OpenAPI 工具: > **OpenAPI URL** > > `http://localhost:3000/api/openapi.json` 或 `http://localhost:3000/api/openapi.yaml` > **基础 URL** > > `http://localhost:3000/api` ## 生成的 OpenAPI 结构 生成的 OpenAPI 规范包括: ### 工具转换逻辑 * **简单工具**(≤10 个原始参数)→ 带查询参数的 GET 端点 * **复杂工具**(对象、数组或 >10 个参数)→ 带 JSON 请求正文的 POST 端点 * **所有工具**都包含完整的响应模式和错误处理 ### 生成操作示例 ```yaml theme={null} /tools/amap/amap-maps_weather: get: summary: "根据城市名称或者标准adcode查询指定城市的天气" operationId: "amap_amap-maps_weather" tags: ["amap"] parameters: - name: city in: query required: true description: "城市名称或者adcode" schema: type: string responses: '200': description: "Successful tool execution" content: application/json: schema: $ref: '#/components/schemas/ToolResponse' ``` ### 安全性 * 定义了 Bearer 身份验证但不对工具执行端点强制执行 * 支持与各种 OpenAPI 兼容系统的灵活集成 ## 相比 MCPO 的优势 > **直接集成** > > 无需中间代理 > **实时更新** > > OpenAPI 规范随着 MCP 服务器连接/断开自动更新 > **更好的性能** > > 直接工具执行,无代理开销 > **简化架构** > > 减少一个需要管理的组件 ## 故障排除 > **OpenAPI 规范显示没有工具** > > 确保 MCP 服务器已连接。检查 `/api/openapi/stats` 查看服务器状态。 > **工具执行失败** > > 验证工具名称和参数是否与 OpenAPI 规范匹配。检查服务器日志以获取详细信息。 > **OpenWebUI 无法连接** > > 确保 MCPHub 可从 OpenWebUI 访问,并且 OpenAPI URL 正确。 > **规范中缺少工具** > > 检查您的 MCP 服务器配置中是否启用了工具。使用 `includeDisabled=true` 查看所有工具。 # 提示词 Source: https://docs.mcphub.app/zh/api-reference/prompts 管理和执行 MCP 提示词。 > **[POST /api/mcp/:serverName/prompts/:promptName](#get-a-prompt)** > > 在 MCP 服务器上执行提示词。 > **[POST /api/servers/:serverName/prompts/:promptName/toggle](#toggle-a-prompt)** > > 启用或禁用提示词。 > **[PUT /api/servers/:serverName/prompts/:promptName/description](#update-prompt-description)** > > 更新提示词的描述。 *** ### 获取提示词 在 MCP 服务器上执行提示词并获取结果。 * **端点**: `/api/mcp/:serverName/prompts/:promptName` * **方法**: `POST` * **身份验证**: 必需 * **参数**: * `:serverName` (字符串, 必需): MCP 服务器的名称。 * `:promptName` (字符串, 必需): 提示词的名称。 * **请求正文**: ```json theme={null} { "arguments": { "arg1": "value1", "arg2": "value2" } } ``` * `arguments` (对象, 可选): 传递给提示词的参数。 * **响应**: ```json theme={null} { "success": true, "data": { "messages": [ { "role": "user", "content": { "type": "text", "text": "提示词内容" } } ] } } ``` **请求示例:** ```bash theme={null} curl -X POST "http://localhost:3000/api/mcp/myserver/prompts/code-review" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "arguments": { "language": "typescript", "code": "const x = 1;" } }' ``` *** ### 切换提示词 启用或禁用服务器上的特定提示词。 * **端点**: `/api/servers/:serverName/prompts/:promptName/toggle` * **方法**: `POST` * **身份验证**: 必需 * **参数**: * `:serverName` (字符串, 必需): 服务器的名称。 * `:promptName` (字符串, 必需): 提示词的名称。 * **请求正文**: ```json theme={null} { "enabled": true } ``` * `enabled` (布尔值, 必需): `true` 启用提示词, `false` 禁用提示词。 **请求示例:** ```bash theme={null} curl -X POST "http://localhost:3000/api/servers/myserver/prompts/code-review/toggle" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"enabled": false}' ``` *** ### 更新提示词描述 更新特定提示词的描述。 * **端点**: `/api/servers/:serverName/prompts/:promptName/description` * **方法**: `PUT` * **身份验证**: 必需 * **参数**: * `:serverName` (字符串, 必需): 服务器的名称。 * `:promptName` (字符串, 必需): 提示词的名称。 * **请求正文**: ```json theme={null} { "description": "新的提示词描述" } ``` * `description` (字符串, 必需): 提示词的新描述。 **请求示例:** ```bash theme={null} curl -X PUT "http://localhost:3000/api/servers/myserver/prompts/code-review/description" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{"description": "审查代码的最佳实践和潜在问题"}' ``` **注意**: 提示词是可用于生成标准化请求到 MCP 服务器的模板。它们由 MCP 服务器定义,并且可以具有在执行提示词时填充的参数。 # 服务器 Source: https://docs.mcphub.app/zh/api-reference/servers 管理您的 MCP 服务器。 > **[GET /api/servers](#get-all-servers)** > > 获取所有 MCP 服务器的列表。 > **[POST /api/servers](#create-a-new-server)** > > 创建一个新的 MCP 服务器。 > **[PUT /api/servers/:name](#update-a-server)** > > 更新现有的 MCP 服务器。 > **[DELETE /api/servers/:name](#delete-a-server)** > > 删除一个 MCP 服务器。 > **[POST /api/servers/:name/toggle](#toggle-a-server)** > > 切换服务器的启用状态。 > **[POST /api/servers/:serverName/tools/:toolName/toggle](#toggle-a-tool)** > > 切换工具的启用状态。 > **[PUT /api/servers/:serverName/tools/:toolName/description](#update-tool-description)** > > 更新工具的描述。 > **[PUT /api/system-config](#update-system-config)** > > 更新系统配置设置。 > **[GET /api/settings](#get-settings)** > > 获取所有服务器设置和配置。 *** ### 获取所有服务器 已连接且已启用的 MCP 服务器会返回可选的 `protocolVersion` 字段,表示当前连接实际协商的 MCP 协议版本。断开、禁用、OpenAPI 服务器或版本不可用时省略该字段;此字段与服务器软件版本 `version` 不同。 检索所有已配置的 MCP 服务器的列表,包括其状态和可用工具。 * **端点**: `/api/servers` * **方法**: `GET` * **响应**: ```json theme={null} { "success": true, "data": [ { "name": "example-server", "status": "connected", "tools": [ { "name": "tool1", "description": "工具1的描述" } ], "config": { "type": "stdio", "command": "node", "args": ["server.js"] } } ] } ``` *** ### 创建一个新服务器 将一个新的 MCP 服务器添加到配置中。 * **端点**: `/api/servers` * **方法**: `POST` * **正文**: ```json theme={null} { "name": "my-new-server", "config": { "type": "stdio", "command": "python", "args": ["-u", "my_script.py"], "owner": "admin" } } ``` * `name` (string, 必填): 服务器的唯一名称。 * `config` (object, 必填): 服务器配置对象。 * `type` (string): `stdio`、`sse`、`streamable-http` 或 `openapi`。 * `command` (string): `stdio` 类型要执行的命令。 * `args` (array of strings): 命令的参数。 * `url` (string): `sse`、`streamable-http` 或 `openapi` 类型的 URL。 * `openapi` (object): OpenAPI 配置。 * `url` (string): OpenAPI 模式的 URL。 * `schema` (object): OpenAPI 模式对象本身。 * `headers` (object): `sse`、`streamable-http` 和 `openapi` 类型请求要发送的标头。 * `keepAliveInterval` (number): 远端服务健康检查的间隔(毫秒)。默认为 60000。 * `enableKeepAlive` (boolean): 是否为远端服务启用周期性健康检查。已断连的远端服务始终会通过有上限的指数退避和随机抖动自动重连。 * `owner` (string): 服务器的所有者。默认为当前用户或“admin”。 *** ### 更新一个服务器 更新现有 MCP 服务器的配置。 * **端点**: `/api/servers/:name` * **方法**: `PUT` * **参数**: * `:name` (string, 必填): 要更新的服务器的名称。 * **正文**: ```json theme={null} { "config": { "type": "stdio", "command": "node", "args": ["new_server.js"] } } ``` * `config` (object, 必填): 更新后的服务器配置对象。详情请参阅“创建一个新服务器”。 *** ### 删除一个服务器 从配置中删除一个 MCP 服务器。 * **端点**: `/api/servers/:name` * **方法**: `DELETE` * **参数**: * `:name` (string, 必填): 要删除的服务器的名称。 *** ### 切换一个服务器 启用或禁用一个 MCP 服务器。 * **端点**: `/api/servers/:name/toggle` * **方法**: `POST` * **参数**: * `:name` (string, 必填): 要切换的服务器的名称。 * **正文**: ```json theme={null} { "enabled": true } ``` * `enabled` (boolean, 必填): `true` 启用服务器,`false` 禁用服务器。 *** ### 批量设置工具状态 `POST /api/servers/:serverName/tools/toggle` 在单个请求中启用或禁用当前已发现的工具。需要仪表板身份认证,以及与单工具接口相同的服务器配置管理权限。 ```json theme={null} { "enabled": false, "tools": ["create_*", "delete_*", "specific_tool"] } ``` * `enabled` 必须是布尔值;重复请求设置相同状态。 * `tools` 包含 1–100 个非空名称或模式,每项最多 256 个字符。模式区分大小写并匹配完整名称:`*` 匹配零个或多个字符,`?` 匹配一个字符,其他字符均按字面匹配。 * 精确名称优先解析带服务器前缀的运行时名称,其次解析上游原始名称。模式可匹配这两种形式,前缀使用当前配置的名称分隔符。 * 匹配项去重后一次保存,保留描述和隐藏参数。变更影响所有分组,无需重新连接即对工具发现和后续调用生效。 * 个人 OAuth 或个人凭据绑定服务器使用当前认证用户的工具清单,不会借用其他用户的凭据。无法获取该清单时返回 `409`,不修改工具状态。 * 模式只针对当前清单展开一次,不会保存为约束未来工具的策略。工具清单不可用时返回 `409`;没有匹配项时成功返回且不写入配置。 响应包含匹配的运行时工具名: ```json theme={null} { "success": true, "data": { "matchedTools": ["example-create_item", "example-delete_item"], "enabled": false } } ``` 现有单工具接口继续可用。 *** ### 切换一个工具 启用或禁用服务器上的特定工具。 * **端点**: `/api/servers/:serverName/tools/:toolName/toggle` * **方法**: `POST` * **参数**: * `:serverName` (string, 必填): 服务器的名称。 * `:toolName` (string, 必填): 工具的名称。 * **正文**: ```json theme={null} { "enabled": true } ``` * `enabled` (boolean, 必填): `true` 启用工具,`false` 禁用工具。 *** ### 更新工具描述 更新特定工具的描述。 * **端点**: `/api/servers/:serverName/tools/:toolName/description` * **方法**: `PUT` * **参数**: * `:serverName` (string, 必填): 服务器的名称。 * `:toolName` (string, 必填): 工具的名称。 * **正文**: ```json theme={null} { "description": "新的工具描述" } ``` * `description` (string, 必填): 工具的新描述。 *** ### 隐藏工具参数 设置模型看不到的工具顶层输入参数。这些参数会从 `tools/list`、`search_tools` 结果、`describe_tool`、智能路由的嵌入文本、生成的 OpenAPI 规范和上下文成本统计中的输入 schema 里移除,并在转发调用前从参数中丢弃,由上游默认值生效。适用于部署无法使用的选项,例如自托管后端的云端专属功能。控制台仍显示完整 schema。 * **端点**: `/api/servers/:serverName/tools/:toolName/hidden-params` * **方法**: `PUT` * **参数**: * `:serverName` (string, 必填): 服务器的名称。 * `:toolName` (string, 必填): 工具的名称,可带或不带服务器前缀。 * **正文**: ```json theme={null} { "hiddenParams": ["profile", "zeroDataRetention"] } ``` * `hiddenParams` (string\[], 必填): 顶层参数名。空数组表示移除该设置。 * **错误**: 参数名不属于该工具或列在其 `required` 中时返回 `400`。这些检查在服务器已连接时进行;否则按原样保存,且必填参数始终不会被隐藏。 *** ### 更新系统配置 更新系统范围的配置设置。 * **端点**: `/api/system-config` * **方法**: `PUT` * **正文**: ```json theme={null} { "openaiApiKey": "sk-...", "openaiBaseUrl": "https://api.openai.com/v1", "modelName": "gpt-4", "temperature": 0.7, "maxTokens": 2048 } ``` * 所有字段都是可选的。只有提供的字段会被更新。 *** ### 获取设置 检索所有服务器设置和配置。 * **端点**: `/api/settings` * **方法**: `GET` * **响应**: ```json theme={null} { "success": true, "data": { "servers": [...], "groups": [...], "systemConfig": {...} } } ``` **注意**: 有关详细的提示词管理,请参阅 [提示词 API](/zh/api-reference/prompts) 文档。 # 智能路由 Source: https://docs.mcphub.app/zh/api-reference/smart-routing 使用向量语义搜索进行智能工具发现。 智能路由是 MCPHub 的智能工具发现系统,它使用向量语义搜索来自动为任何给定任务找到最相关的工具。 ### HTTP 端点 * **端点**: `http://localhost:3000/mcp/$smart` * **方法**: `POST` ### SSE 端点 (已弃用) * **端点**: `http://localhost:3000/sse/$smart` * **方法**: `GET` ### 工作原理 1. **工具索引**: 所有 MCP 工具都会自动转换为向量嵌入并存储在带有 pgvector 的 PostgreSQL 中。 2. **语义搜索**: 用户查询被转换为向量,并使用余弦相似度与工具嵌入进行匹配。 3. **智能过滤**: 动态阈值可确保相关结果而无噪音。 4. **精确执行**: 找到的工具可以通过适当的参数验证直接执行。 ### 设置要求 * 带有 pgvector 扩展的 PostgreSQL * OpenAI API 密钥(或兼容的嵌入服务) * 在 MCPHub 设置中启用智能路由 ## 诊断与维护 API 以下管理端点位于已认证的 Dashboard API 下,与其它 `/api/*` 路由一样,需要有效的 Dashboard 会话(JWT)或系统级 All-Access Bearer Key(`Authorization: Bearer mcphub_...`)。两个端点还额外**仅限管理员**:已认证但非管理员的身份会收到 `403 Admin privileges required`。 ### 索引状态与性能 返回智能路由配置、数据库连接状态、向量库规模(tool/server 行数、去重服务器数、维度)、按模型/按服务器的统计、同步新鲜度以及索引覆盖率。 ```bash theme={null} curl http://localhost:3000/api/smart-routing/performance \ -H "Authorization: Bearer YOUR_JWT_OR_BEARER_KEY" ``` ### 重新索引 全量强制重建向量索引:先清空 `vector_embeddings`,再对全部已启用服务器重新生成嵌入。同步执行并返回汇总;重建进度会通过 SSE `embedding-sync-progress` 事件实时推送。 需要**个人凭据**(`credentialTemplate`)的服务器不会参与全局连接,因此会以已绑定凭据的用户身份(服务器所有者优先)建立连接,并把这些用户可见工具的**并集**写入索引;没有任何用户绑定凭据的服务器会被跳过,并在响应的 `results` 中给出原因。 同一时间只允许一次重建:若已有重建在执行,新的调用会收到 `409 A reindex pass is already running`。`GET /api/smart-routing/performance` 的返回中通过 `reindexing` 暴露同一状态,前端可据此在执行期间禁用按钮。 ```bash theme={null} curl -X POST http://localhost:3000/api/smart-routing/reindex \ -H "Authorization: Bearer YOUR_JWT_OR_BEARER_KEY" ``` # 系统 Source: https://docs.mcphub.app/zh/api-reference/system 系统和实用程序端点。 > **[GET /health](#health-check)** > > 检查 MCPHub 服务器的健康状态。 > **[GET /oauth/callback](#oauth-callback)** > > 用于身份验证流程的 OAuth 回调端点。 > **[POST /api/mcpb/upload](#upload-mcpb-file)** > > 上传 MCPB 文件。 > **[GET /api/mcp-settings/export](#export-mcp-settings)** > > 将 MCP 设置导出为 JSON。 *** ### 健康检查 检查 MCPHub 服务器的健康状态。 * **端点**: `/health` * **方法**: `GET` * **身份验证**: 不需要 * **响应**: ```json theme={null} { "status": "ok", "timestamp": "2024-11-12T01:30:00.000Z", "uptime": 12345 } ``` **请求示例:** ```bash theme={null} curl "http://localhost:3000/health" ``` *** ### OAuth 回调 用于处理 OAuth 身份验证流程的 OAuth 回调端点。此端点在用户授权后由 OAuth 提供商自动调用。 * **端点**: `/oauth/callback` * **方法**: `GET` * **身份验证**: 不需要(公共回调 URL) * **查询参数**: 因 OAuth 提供商而异(通常包括 `code`、`state` 等) **注意**: 此端点由 MCPHub 的 OAuth 集成内部使用,客户端不应直接调用。 *** ### 上传 MCPB 文件 上传 MCPB(MCP Bundle)文件以导入服务器配置。 * **端点**: `/api/mcpb/upload` * **方法**: `POST` * **身份验证**: 必需(仅限管理员) * **Content-Type**: `multipart/form-data` * **正文**: * `mcpbFile` (文件, 必需): 要上传的 MCPB 文件。 **安全说明:** * **仅限管理员**:上传的 bundle 代码会被解压到确定性的 `data/uploads/mcpb/server-` 路径,现有的 stdio 服务器配置会从该路径执行代码,因此非管理员上传会在请求体写入磁盘之前被 `403` 拒绝。 * `manifest.json` 中的 `name` 只能包含字母、数字、`.`、`_` 和 `-`。 * 如果清单名称包含路径分隔符、`..` 或其他不安全的路径字符,上传会被拒绝。 **请求示例:** ```bash theme={null} curl -X POST "http://localhost:3000/api/mcpb/upload" \ -H "Authorization: Bearer YOUR_TOKEN" \ -F "mcpbFile=@bundle.mcpb" ``` *** ### 导出 MCP 设置 将当前 MCP 设置配置导出为 JSON 文件。 * **端点**: `/api/mcp-settings/export` * **方法**: `GET` * **身份验证**: 必需 * **响应**: 返回 `mcp_settings.json` 配置文件。 **请求示例:** ```bash theme={null} curl "http://localhost:3000/api/mcp-settings/export" \ -H "Authorization: Bearer YOUR_TOKEN" \ -o mcp_settings.json ``` **注意**: 此端点允许您下载 MCP 设置的备份,可用于恢复或迁移您的配置。 # 工具 Source: https://docs.mcphub.app/zh/api-reference/tools 以编程方式执行 MCP 工具。 > **[POST /api/tools/call/:server](#call-a-tool)** > > 在 MCP 服务器上调用特定工具。 *** ### 调用工具 使用给定参数在 MCP 服务器上执行特定工具。 * **端点**: `/api/tools/call/:server` * **方法**: `POST` * **参数**: * `:server` (字符串, 必需): MCP 服务器的名称。 * **请求正文**: ```json theme={null} { "toolName": "tool-name", "arguments": { "param1": "value1", "param2": "value2" } } ``` * `toolName` (字符串, 必需): 要执行的工具名称。 * `arguments` (对象, 可选): 传递给工具的参数。默认为空对象。 * **响应**: ```json theme={null} { "success": true, "data": { "content": [ { "type": "text", "text": "工具执行结果" } ], "toolName": "tool-name", "arguments": { "param1": "value1", "param2": "value2" } } } ``` 响应保留 `success` / `data` 包装。`data` 保留工具结果中的字段,包括存在时的 `structuredContent`、`isError` 和 `_meta`,并附带 `toolName` 与类型转换后的 `arguments`。缺少 `content` 时默认返回 `[]`。工具执行失败由 `data.isError: true` 表示;外层的 `success: true` 表示 API 返回了工具结果,并不代表工具执行成功。 **请求示例:** ```bash theme={null} curl -X POST "http://localhost:3000/api/tools/call/amap" \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_TOKEN" \ -d '{ "toolName": "amap-maps_weather", "arguments": { "city": "Beijing" } }' ``` **注意事项:** * 工具参数会根据工具的输入模式自动转换为适当的类型。 * 如果需要,可以使用 `x-session-id` 请求头在多个工具调用之间维护会话状态。 * 此端点需要身份验证。 *** ### 替代方案:OpenAPI 工具执行 有关无需身份验证的 OpenAPI 兼容工具执行,请参阅 [OpenAPI 集成](/api-reference/openapi#tool-execution) 文档。OpenAPI 端点提供: * **GET** `/api/tools/:serverName/:toolName` - 用于带查询参数的简单工具 * **POST** `/api/tools/:serverName/:toolName` - 用于带 JSON 正文的复杂工具 这些端点专为与 OpenWebUI 和其他 OpenAPI 兼容系统集成而设计。 # 用户 Source: https://docs.mcphub.app/zh/api-reference/users 在 MCPHub 中管理用户。 > **[GET /api/users](#get-all-users)** > > 获取所有用户的列表。 > **[GET /api/users/:username](#get-a-user)** > > 获取特定用户的详细信息。 > **[POST /api/users](#create-a-user)** > > 创建新用户。 > **[PUT /api/users/:username](#update-a-user)** > > 更新现有用户。 > **[DELETE /api/users/:username](#delete-a-user)** > > 删除用户。 > **[GET /api/users-stats](#get-user-statistics)** > > 获取有关用户及其服务器访问权限的统计信息。 *** ### 获取所有用户 检索系统中所有用户的列表。 * **端点**: `/api/users` * **方法**: `GET` * **身份验证**: 必需(仅管理员) * **响应**: ```json theme={null} { "success": true, "data": [ { "username": "admin", "role": "admin", "servers": ["server1", "server2"], "groups": ["group1"] }, { "username": "user1", "role": "user", "servers": ["server1"], "groups": [] } ] } ``` *** ### 获取用户 检索特定用户的详细信息。 * **端点**: `/api/users/:username` * **方法**: `GET` * **身份验证**: 必需(仅管理员) * **参数**: * `:username` (字符串, 必需): 用户的用户名。 * **响应**: ```json theme={null} { "success": true, "data": { "username": "user1", "role": "user", "servers": ["server1", "server2"], "groups": ["group1"] } } ``` *** ### 创建用户 在系统中创建新用户。 * **端点**: `/api/users` * **方法**: `POST` * **身份验证**: 必需(仅管理员) * **请求正文**: ```json theme={null} { "username": "newuser", "password": "securepassword", "role": "user", "servers": ["server1"], "groups": ["group1"] } ``` * `username` (字符串, 必需): 新用户的用户名。 * `password` (字符串, 必需): 新用户的密码。至少 6 个字符。 * `role` (字符串, 可选): 用户的角色。可以是 `"admin"` 或 `"user"`。默认为 `"user"`。 * `servers` (字符串数组, 可选): 用户可以访问的服务器名称列表。 * `groups` (字符串数组, 可选): 用户所属的组 ID 列表。 *** ### 更新用户 更新现有用户的信息。 * **端点**: `/api/users/:username` * **方法**: `PUT` * **身份验证**: 必需(仅管理员) * **参数**: * `:username` (字符串, 必需): 要更新的用户的用户名。 * **请求正文**: ```json theme={null} { "password": "newpassword", "role": "admin", "servers": ["server1", "server2", "server3"], "groups": ["group1", "group2"] } ``` * `password` (字符串, 可选): 用户的新密码。 * `role` (字符串, 可选): 用户的新角色。 * `servers` (字符串数组, 可选): 更新的可访问服务器列表。 * `groups` (字符串数组, 可选): 更新的组列表。 *** ### 删除用户 从系统中删除用户。 * **端点**: `/api/users/:username` * **方法**: `DELETE` * **身份验证**: 必需(仅管理员) * **参数**: * `:username` (字符串, 必需): 要删除的用户的用户名。 *** ### 获取用户统计信息 检索有关用户及其对服务器和组的访问权限的统计信息。 * **端点**: `/api/users-stats` * **方法**: `GET` * **身份验证**: 必需(仅管理员) * **响应**: ```json theme={null} { "success": true, "data": { "totalUsers": 5, "adminUsers": 1, "regularUsers": 4, "usersPerServer": { "server1": 3, "server2": 2 }, "usersPerGroup": { "group1": 2, "group2": 1 } } } ``` **注意**: 所有用户管理端点都需要管理员身份验证。 # 数据库配置 Source: https://docs.mcphub.app/zh/configuration/database-configuration 使用 PostgreSQL 数据库配置 MCPHub 作为 mcp_settings.json 文件的替代方案。 # MCPHub 数据库配置 ## 概述 MCPHub 支持将配置数据存储在 PostgreSQL 数据库中,作为 `mcp_settings.json` 文件配置的替代方案。数据库模式为生产环境和企业级部署提供了更强大的持久化和扩展能力。 ## 为什么使用数据库配置? **核心优势:** * ✅ **更好的持久化** - 配置数据存储在专业数据库中,支持事务和数据完整性 * ✅ **高可用性** - 利用数据库复制和故障转移能力 * ✅ **企业级支持** - 符合企业数据管理和合规要求 * ✅ **备份恢复** - 使用成熟的数据库备份工具和策略 ## 环境变量 ### 数据库模式必需变量 ```bash theme={null} # 数据库连接 URL(PostgreSQL) # 只需设置 DB_URL 即可自动启用数据库模式 DB_URL=postgresql://user:password@localhost:5432/mcphub # 或显式控制 USE_DB(覆盖自动检测) # USE_DB=true ``` > [!NOTE] > > **简化配置**:您只需设置 `DB_URL` 即可启用数据库模式。MCPHub 会自动检测 `DB_URL` 是否存在并启用数据库模式。如果需要在设置了 `DB_URL` 的情况下禁用数据库模式,可以显式设置 `USE_DB=false`。 ## 设置说明 ### 1. 使用 Docker #### 方案 A:使用外部数据库 如果您已有 PostgreSQL 数据库: ```bash theme={null} docker run -d \ -p 3000:3000 \ -v ./mcp_settings.json:/app/mcp_settings.json \ -e DB_URL="postgresql://user:password@your-db-host:5432/mcphub" \ samanhappy/mcphub ``` #### 方案 B:将 PostgreSQL 作为独立服务 创建 `docker-compose.yml` 文件: ```yaml theme={null} version: '3.8' services: postgres: image: pgvector/pgvector:pg17 environment: POSTGRES_DB: mcphub POSTGRES_USER: mcphub POSTGRES_PASSWORD: your_secure_password volumes: - pgdata:/var/lib/postgresql/data ports: - "5432:5432" mcphub: image: samanhappy/mcphub:latest environment: USE_DB: "true" DB_URL: "postgresql://mcphub:your_secure_password@postgres:5432/mcphub" PORT: 3000 ports: - "3000:3000" depends_on: - postgres volumes: pgdata: ``` 运行: ```bash theme={null} docker-compose up -d ``` ### 2. 手动设置 #### 步骤 1:设置 PostgreSQL 数据库 ```bash theme={null} # 安装 PostgreSQL(如果尚未安装) sudo apt-get install postgresql postgresql-contrib # 创建数据库和用户 sudo -u postgres psql < mcphub_backup.sql # 或使用 Docker docker exec postgres pg_dump -U mcphub mcphub > mcphub_backup.sql ``` ### 恢复 ```bash theme={null} # PostgreSQL 恢复 psql -U mcphub mcphub < mcphub_backup.sql # 或使用 Docker docker exec -i postgres psql -U mcphub mcphub < mcphub_backup.sql ``` ## 切换回基于文件的配置 如果您需要切换回基于文件的配置: 1. 设置 `USE_DB=false` 或删除 `DB_URL` 和 `USE_DB` 环境变量 2. 重启 MCPHub 3. MCPHub 将再次使用 `mcp_settings.json` 注意:在数据库模式下所做的更改不会反映到文件中,除非您手动导出。 ## 故障排除 ### 连接被拒绝 ``` Error: connect ECONNREFUSED 127.0.0.1:5432 ``` **解决方案:** 检查 PostgreSQL 是否正在运行并可访问: ```bash theme={null} # 检查 PostgreSQL 状态 sudo systemctl status postgresql # 或对于 Docker docker ps | grep postgres ``` ### 认证失败 ``` Error: password authentication failed for user "mcphub" ``` **解决方案:** 验证 `DB_URL` 环境变量中的数据库凭据。 ### 迁移失败 ``` ❌ Migration failed: ... ``` **解决方案:** 1. 检查 `mcp_settings.json` 是否存在且为有效的 JSON 2. 验证数据库连接 3. 检查日志获取具体错误信息 4. 确保数据库用户具有 CREATE TABLE 权限 ### 表已存在 如果数据库表不存在,会自动创建。如果遇到关于已存在表的错误,请检查: 1. 之前的迁移是否部分完成 2. 手动创建表的冲突 3. 如果需要,在数据库配置中使用 `synchronize: false` 运行 ## 环境变量参考 | 变量 | 必需 | 默认值 | 描述 | | - | - | - | - | | `DB_URL` | 是\* | - | 完整的 PostgreSQL 连接 URL。设置此变量会自动启用数据库模式 | | `USE_DB` | 否 | 自动 | 显式启用/禁用数据库模式。如果未设置,根据 `DB_URL` 是否存在自动检测 | | `MCPHUB_SETTING_PATH` | 否 | - | mcp\_settings.json 的路径(用于迁移) | \*数据库模式必需。只需设置 `DB_URL` 即可自动启用数据库模式 ## 安全注意事项 1. **数据库凭据:** 安全存储数据库凭据,使用环境变量或密钥管理 2. **网络访问:** 仅限 MCPHub 实例访问数据库 3. **加密:** 在生产环境中使用 SSL/TLS 进行数据库连接: ```bash theme={null} DB_URL=postgresql://user:password@host:5432/mcphub?sslmode=require ``` 4. **备份:** 定期备份您的数据库 5. **访问控制:** 使用强密码并限制用户权限 ## 性能 数据库模式在以下场景提供更好的性能: * 多个并发用户 * 频繁的配置更改 * 大量服务器/分组 文件模式可能更快的场景: * 单用户设置 * 读取密集型工作负载且更改不频繁 * 开发/测试环境 # Docker 部署 Source: https://docs.mcphub.app/zh/configuration/docker-setup 使用 Docker 和 Docker Compose 部署 MCPHub # Docker 部署 本指南介绍使用 Docker 部署 MCPHub,包括开发和生产配置。 ## Docker 快速开始 ### 使用预构建镜像 标准 MCPHub 镜像内置 Node.js/pnpm、Python、uv/uvx、Git 和构建工具。如果需要为 Rust MCP server 使用 Cargo 和稳定版 Rust 工具链,请使用 `latest-full` 镜像。 ```bash theme={null} # 拉取最新镜像 docker pull samanhappy/mcphub:latest # 使用默认配置运行 docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ samanhappy/mcphub:latest ``` 镜像默认使用 `/app/data/mcp_settings.json`。挂载整个数据目录可持久化配置、用户、凭据绑定及其加密密钥。如需自定义服务器,可在首次启动前创建 `data/mcp_settings.json`,或在启动后编辑现有文件。后台运行(`-d`)时,通过 `docker logs mcphub` 查看首次生成的管理员密码。 启动时会自动识别旧方式挂载的 `/app/mcp_settings.json`,并优先于数据目录使用,因此原有 Docker 命令无需修改。显式设置的 `MCPHUB_SETTING_PATH` 始终拥有最高优先级。为完整持久化数据,应将配置文件及已有的 `.credentials.json`、`.credentials.key` 伴随文件一起迁入挂载的数据目录。仍支持显式覆盖 `MCPHUB_SETTING_PATH`。 ### 从源码构建 ```bash theme={null} # 克隆仓库 git clone https://github.com/your-username/mcphub.git cd mcphub # 构建 Docker 镜像 docker build -t mcphub:local . # 运行容器 docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ mcphub:local ``` ### 构建扩展功能版本 Docker 镜像支持 `INSTALL_EXT` 构建参数以包含额外工具。发布的 `-full` 镜像会启用此参数: ```bash theme={null} # 构建扩展功能版本(包含稳定版 Rust/Cargo、Docker 引擎,以及供 Playwright 使用的 Chrome 和 Firefox) docker build --build-arg INSTALL_EXT=true -t mcphub:extended . # 方式 1: 使用自动 Docker-in-Docker(需要特权模式) docker run -d \ --name mcphub \ --privileged \ -p 3000:3000 \ -v ./data:/app/data \ mcphub:extended # 方式 2: 挂载 Docker socket(使用宿主机的 Docker 守护进程) docker run -d \ --name mcphub \ -p 3000:3000 \ -v ./data:/app/data \ -v /var/run/docker.sock:/var/run/docker.sock \ mcphub:extended # 验证 Docker 可用 docker exec mcphub docker --version docker exec mcphub docker ps ``` > [!NOTE] > > **INSTALL\_EXT=true 包含的功能:** > > * **稳定版 Rust + Cargo**:通过 rustup 安装,用于基于 Rust 的 MCP server。 > * **Docker 引擎**:完整的 Docker 守护进程和 CLI,用于容器管理。在特权模式下运行时,守护进程会自动启动。 > * **Chrome + Firefox(供 Playwright 使用,仅 amd64)**:用于浏览器自动化任务 > > 扩展镜像较大,但为高级用例提供了额外功能。 > [!WARNING] > > **Docker-in-Docker 安全注意事项:** > > * **特权模式**(`--privileged`):容器内启动 Docker 守护进程需要此权限。这会授予容器在宿主机上的提升权限。 > * **Docker socket 挂载**(`/var/run/docker.sock`):使容器可以访问宿主机的 Docker 守护进程。两种方式都应仅在可信环境中使用。 > * 生产环境建议使用 Docker socket 挂载而非特权模式,以提高安全性。 ## Docker Compose 设置 ### 基本配置 创建 `docker-compose.yml` 文件: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest # 本地开发时使用: # build: . container_name: mcphub ports: - '3000:3000' environment: - NODE_ENV=production - PORT=3000 - JWT_SECRET=${JWT_SECRET:-your-jwt-secret} - DB_URL=postgresql://mcphub:password@postgres:5432/mcphub volumes: - ./servers.json:/app/servers.json:ro - ./data:/app/data # 让已安装的 stdio 服务器包在容器重建后依然保留。 # 参见下方"持久化 stdio 包缓存"。 - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv depends_on: postgres: condition: service_healthy restart: unless-stopped networks: - mcphub-network postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=password volumes: - postgres_data:/var/lib/postgresql/data - ./scripts/init-db.sql:/docker-entrypoint-initdb.d/init-db.sql:ro ports: - '5432:5432' healthcheck: test: ['CMD-SHELL', 'pg_isready -U mcphub -d mcphub'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network volumes: postgres_data: mcphub_npm_cache: mcphub_uv_cache: networks: mcphub-network: driver: bridge ``` ### 持久化 stdio 包缓存 `stdio` 服务器在首次使用时会下载并安装对应的包:`npx` 使用 npm 的缓存,`uvx` 以及其他基于 `uv` 的命令使用 uv 的缓存。镜像未设置 `USER`,因此以 `root` 运行,这些安装内容位于: | 路径 | 存放内容 | | - | - | | `/root/.npm/_npx` | `npx` 安装的包 | | `/root/.npm/_cacache` | npm 下载的 tarball | | `/root/.cache/uv` | uv 的下载缓存,以及 `uvx` 创建的环境 | `uv tool install` 创建的环境位于 `~/.local/share/uv/tools`,它们烘焙在镜像层中,不受这里的 讨论影响。如果你以非 root 用户运行镜像,`~` 就不是 `/root`:上表中的路径与下文的挂载目标都需 要改成该用户的家目录。 这些路径默认都不是卷,因此只存在于容器的可写层。而可写层会在容器被**重建**时丢弃——升级镜像、 执行 `docker compose down && up -d`,或修改任何服务配置都会触发重建。下次启动时,所有 `stdio` 服务器会同时从零重新安装,之后才能开始各自的 MCP 握手。 > [!WARNING] > > `docker restart` 保留可写层,因此不会丢失缓存;而 `docker compose down` 后再 `up -d` > 会删除并重建容器,缓存随之消失。如果你发现"重启后似乎重装了所有东西",通常就是这个区别。 把这两个路径挂载出来,安装内容就会保存在宿主机上: ```yaml theme={null} services: mcphub: volumes: - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv volumes: mcphub_npm_cache: mcphub_uv_cache: ``` 具名卷可以在 `docker compose down` 后保留,只有 `down -v` 才会删除。绑定挂载 (`./data/npm-cache:/root/.npm`)同样可行,也更方便手动查看或清理,但它不会像具名卷那样用镜像 中该路径的内容初始化——而是直接遮蔽掉镜像里已有的东西。在 amd64 上,`-full` 镜像会在 `/root/.npm/_npx` 中预装 Playwright,因此在该路径使用绑定挂载意味着要重新下载一次。 几点值得注意: * **不需要锁定版本。** 未指定版本的 `npx -y ` 会直接复用缓存中已安装的包,而不会重新 安装。但 npm 每次运行仍会向 registry 重新校验包的元数据,因此 registry(或你的代理)必须保持 可达——缓存省下的是安装成本,不是这一次网络往返。 * **挂载后的第一次启动仍然是冷的。** 卷刚创建时是空的,这一次需要付出完整的安装成本;此后每次 重建都会复用它。 * **预留足够空间。** 约 90 个 `stdio` 服务器大致占用 2 GB。 * **安装内容损坏时**,请使用该服务器的「重新安装」操作——仅 `npx` 与 `uvx` 服务器支持。对 `uvx` 而言,它只刷新那一个包(在下次启动时注入 `--refresh` 参数);对 `npx` 而言,它会删除 整个共用的 `/root/.npm/_npx` 目录,因此下次启动时**所有** npx 服务器都会重新安装,而不只是 你指定的那一个。`_cacache` 卷的作用就是让这次重装的代价变小。 规模越大影响越明显。在一个约 95 个 `stdio` 服务器的部署中,冷启动重建后只有 19 个连接成功, 74 个因 `MCP error -32001: Request timed out` 连接失败,因为所有服务器都在同时安装、互相争抢 资源。挂载缓存后,同一次重启达到 73 个连接成功且没有任何超时——但那次运行同时把 `INIT_TIMEOUT` 从默认的 300000 提高到了 900000,所以这两项改动必须分开看。按每分钟的连接曲线粗略拆分:缓存在 原本 300 秒的预算内约值 +45 台,更长的超时约值 +9 台。主要成本是重新安装包,而不是等待。 ### 生产配置(包含 Nginx) ```yaml theme={null} version: '3.8' services: nginx: image: nginx:alpine container_name: mcphub-nginx ports: - '80:80' - '443:443' volumes: - ./nginx.conf:/etc/nginx/conf.d/default.conf:ro - ./ssl:/etc/nginx/ssl:ro - nginx_logs:/var/log/nginx depends_on: - mcphub restart: unless-stopped networks: - mcphub-network mcphub: image: samanhappy/mcphub:latest container_name: mcphub-app expose: - '3000' environment: - NODE_ENV=production - PORT=3000 - JWT_SECRET=${JWT_SECRET} - DB_URL=postgresql://mcphub:${POSTGRES_PASSWORD}@postgres:5432/mcphub - OPENAI_API_KEY=${OPENAI_API_KEY} - REDIS_URL=redis://redis:6379 volumes: - ./servers.json:/app/servers.json:ro - ./data:/app/data - mcphub_logs:/app/logs - mcphub_npm_cache:/root/.npm - mcphub_uv_cache:/root/.cache/uv depends_on: postgres: condition: service_healthy redis: condition: service_healthy restart: unless-stopped networks: - mcphub-network healthcheck: test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://localhost:3000/health'] interval: 30s timeout: 10s retries: 3 postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} volumes: - postgres_data:/var/lib/postgresql/data - ./backups:/backups healthcheck: test: ['CMD-SHELL', 'pg_isready -U mcphub -d mcphub'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network redis: image: redis:7-alpine container_name: mcphub-redis command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD} volumes: - redis_data:/data healthcheck: test: ['CMD', 'redis-cli', 'ping'] interval: 10s timeout: 5s retries: 5 restart: unless-stopped networks: - mcphub-network volumes: postgres_data: redis_data: mcphub_logs: mcphub_npm_cache: mcphub_uv_cache: nginx_logs: networks: mcphub-network: driver: bridge ``` ### 环境变量 为 Docker Compose 创建 `.env` 文件: ```env theme={null} # 应用程序 NODE_ENV=production JWT_SECRET=your-super-secret-jwt-key-change-this # 数据库 POSTGRES_PASSWORD=your-secure-database-password # Redis REDIS_PASSWORD=your-secure-redis-password # 外部 API OPENAI_API_KEY=your-openai-api-key # 可选:自定义端口 # PORT=3000 ``` ## 开发设置 ### 开发 Docker Compose 创建 `docker-compose.dev.yml`: ```yaml theme={null} version: '3.8' services: mcphub-dev: build: context: . dockerfile: Dockerfile.dev container_name: mcphub-dev ports: - '3000:3000' - '5173:5173' # 前端开发服务器 - '9229:9229' # 调试端口 environment: - NODE_ENV=development - PORT=3000 - DB_URL=postgresql://mcphub:password@postgres:5432/mcphub volumes: - .:/app - /app/node_modules - /app/frontend/node_modules depends_on: - postgres command: pnpm dev networks: - mcphub-dev postgres: image: pgvector/pgvector:pg17 container_name: mcphub-postgres-dev environment: - POSTGRES_DB=mcphub - POSTGRES_USER=mcphub - POSTGRES_PASSWORD=password ports: - '5432:5432' volumes: - postgres_dev_data:/var/lib/postgresql/data networks: - mcphub-dev volumes: postgres_dev_data: networks: mcphub-dev: driver: bridge ``` ### 开发 Dockerfile 创建 `Dockerfile.dev`: ```dockerfile theme={null} FROM node:20-alpine # 安装 pnpm RUN npm install -g pnpm # 设置工作目录 WORKDIR /app # 复制包文件 COPY package.json pnpm-lock.yaml ./ COPY frontend/package.json ./frontend/ # 安装依赖 RUN pnpm install # 复制源代码 COPY . . # 暴露端口 EXPOSE 3000 5173 9229 # 启动开发服务器 CMD ["pnpm", "dev"] ``` ## 运行应用程序 ### 开发模式 ```bash theme={null} # 启动开发环境 docker-compose -f docker-compose.dev.yml up -d # 查看日志 docker-compose -f docker-compose.dev.yml logs -f mcphub-dev # 停止开发环境 docker-compose -f docker-compose.dev.yml down ``` ### 生产模式 ```bash theme={null} # 启动生产环境 docker-compose up -d # 查看日志 docker-compose logs -f mcphub # 停止生产环境 docker-compose down ``` ## 配置管理 ### MCP 设置卷挂载 首次启动前,创建 `data/mcp_settings.json`: ```json theme={null} { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] }, "amap": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "your-api-key" } } } } ``` ### 密钥管理 对于生产环境,使用 Docker 密钥: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest environment: - JWT_SECRET_FILE=/run/secrets/jwt_secret - DATABASE_PASSWORD_FILE=/run/secrets/db_password secrets: - jwt_secret - db_password secrets: jwt_secret: file: ./secrets/jwt_secret.txt db_password: file: ./secrets/db_password.txt ``` ## 数据持久化 ### 数据库备份 在 `docker-compose.yml` 中添加备份服务: ```yaml theme={null} services: backup: image: pgvector/pgvector:pg17 container_name: mcphub-backup environment: - PGPASSWORD=${POSTGRES_PASSWORD} volumes: - ./backups:/backups - ./scripts/backup.sh:/backup.sh:ro command: /bin/sh -c "chmod +x /backup.sh && /backup.sh" depends_on: - postgres profiles: - backup networks: - mcphub-network ``` 创建 `scripts/backup.sh`: ```bash theme={null} #!/bin/sh BACKUP_FILE="/backups/mcphub_$(date +%Y%m%d_%H%M%S).sql" pg_dump -h postgres -U mcphub -d mcphub > "$BACKUP_FILE" echo "备份已创建:$BACKUP_FILE" # 只保留最近 7 天的备份 find /backups -name "mcphub_*.sql" -mtime +7 -delete ``` 运行备份: ```bash theme={null} docker-compose --profile backup run --rm backup ``` ## 监控和健康检查 ### 健康检查端点 在您的应用程序中添加: ```javascript theme={null} // 在您的 Express 应用中 app.get('/health', (req, res) => { res.json({ status: 'healthy', timestamp: new Date().toISOString(), uptime: process.uptime(), memory: process.memoryUsage(), version: process.env.npm_package_version, }); }); ``` ### Docker 健康检查 ```yaml theme={null} services: mcphub: # ... 其他配置 healthcheck: test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://localhost:3000/health'] interval: 30s timeout: 10s retries: 3 start_period: 60s ``` ### 使用 Watchtower 监控 添加自动更新: ```yaml theme={null} services: watchtower: image: containrrr/watchtower container_name: mcphub-watchtower volumes: - /var/run/docker.sock:/var/run/docker.sock environment: - WATCHTOWER_CLEANUP=true - WATCHTOWER_POLL_INTERVAL=3600 - WATCHTOWER_INCLUDE_STOPPED=true restart: unless-stopped ``` ## 故障排除 ### 常见问题 **容器启动失败**:使用 `docker-compose logs mcphub` 检查日志 **数据库连接错误**:确保 PostgreSQL 健康且可访问 **端口冲突**:检查端口 3000/5432 是否已被占用 **卷挂载问题**:验证文件路径和权限 ### 调试命令 ```bash theme={null} # 检查容器状态 docker-compose ps # 查看日志 docker-compose logs -f [service_name] # 在容器中执行命令 docker-compose exec mcphub sh # 检查数据库连接 docker-compose exec postgres psql -U mcphub -d mcphub # 重启特定服务 docker-compose restart mcphub # 重新构建并重启 docker-compose up --build -d ``` ### 性能优化 ```yaml theme={null} services: mcphub: # ... 其他配置 deploy: resources: limits: memory: 512M cpus: '0.5' reservations: memory: 256M cpus: '0.25' ``` 此 Docker 设置为 MCPHub 提供了完整的容器化环境,包含开发和生产配置。 # 环境变量 Source: https://docs.mcphub.app/zh/configuration/environment-variables 使用环境变量配置 MCPHub # 环境变量 MCPHub 使用环境变量进行配置。本指南涵盖了所有可用的变量及其用法。 ## 核心应用设置 ### 服务器配置 | 变量 | 默认值 | 描述 | | - | - | - | | `PORT` | `3000` | HTTP 服务器的端口号 | | `DEFAULT_REQUEST_TIMEOUT` | `60000` | 当服务器未设置 `options.timeout` 时,上游 MCP 服务器请求使用的默认超时时间(毫秒) | | `INIT_TIMEOUT` | `300000` | 应用程序的初始超时时间 | | `STARTUP_CONNECT_CONCURRENCY` | `0` | 启动时最多允许多少个上游服务器同时连接。`0`(或不设置)表示全部并行连接。当部署了大量 `stdio` 服务器时,设置上限可以避免它们争抢 CPU、网络和磁盘,进而耗尽 `INIT_TIMEOUT`。排队等待的时间不计入服务器自身的超时预算。 | | `BASE_PATH` | `''` | 应用程序的基本路径 | | `SMART_ROUTING_PATH` | `$smart` | 智能路由路径段,例如 `my_smart` 对应 `/mcp/my_smart`。允许 1–64 位英文字母、数字、连字符和下划线;修改后重启生效。旧 `$smart` 地址继续可用,服务器名、分组名及分组 ID 不得与配置值冲突。 | | `READONLY` | `false` | 设置为 `true` 以启用只读模式 | | `MCPHUB_SETTING_PATH` | | MCPHub 设置文件的路径 | | `INSTALL_BASE_URL` | `http://localhost:3000` | 当 `systemConfig.install.baseUrl` 未设置时使用的公网安装地址。Dashboard/API 中显式保存的 Base URL 优先于该环境变量,适合 Docker/Kubernetes 等声明式部署和 OAuth 回调地址。 | | `NODE_ENV` | `development` | 应用程序环境 (`development`, `production`, `test`) | ```env theme={null} PORT=3000 DEFAULT_REQUEST_TIMEOUT=60000 INIT_TIMEOUT=300000 STARTUP_CONNECT_CONCURRENCY=0 BASE_PATH=/api READONLY=true MCPHUB_SETTING_PATH=/path/to/settings INSTALL_BASE_URL=https://mcphub.example.com NODE_ENV=production ``` ## 认证与安全 ### JWT 配置 | 变量 | 默认值 | 描述 | | - | - | - | | `ADMIN_PASSWORD` | `pnpm dev` 中为 `admin123`,其他启动随机生成 | 首次启动且没有用户时用于创建 `admin` 用户的密码。未设置时,`pnpm dev` 使用 `admin123`;其他启动会生成随机密码并输出到日志。 | | `JWT_SECRET` | - | 用于 JWT 令牌签名的密钥 (必需) | ```env theme={null} JWT_SECRET=your-super-secret-key-change-this-in-production ``` ### Better Auth(可选:GitHub / Google / 本地 OIDC 登录) Better Auth 会在 `src/betterAuth.ts` 中于启动时初始化一次,因此无论是改环境变量还是改 `systemConfig.auth.betterAuth` / `systemConfig.install.baseUrl`,都需要重启后才会生效。 对于非敏感的 Better Auth 配置,MCPHub 现在按以下优先级解析: 1. `BETTER_AUTH_*` 环境变量 2. `systemConfig.auth.betterAuth`(来自 `mcp_settings.json` 或数据库系统配置) 3. 内置默认值 Provider 的客户端凭据仍然只支持通过环境变量提供。 MCPHub 里的 Better Auth 目前依赖 PostgreSQL 会话存储,因此需要配置 `DB_URL`;纯文件模式不支持 Better Auth 登录。 | 变量 | 描述 | | - | - | | `BETTER_AUTH_ENABLED` | Better Auth 总开关。即使打开了它,如果 MCPHub 不在数据库模式下,或者没有任何提供商具备所需凭据,Better Auth 仍会自动禁用。 | | `BETTER_AUTH_URL` | 用于生成 Better Auth 回调 URL 的公网访问地址。优先级高于 `systemConfig.auth.betterAuth.baseUrl`(可在 设置 → Better Auth 中配置)和 `systemConfig.install.baseUrl`,其来源也会自动加入可信来源列表。 | | `BETTER_AUTH_BASE_PATH` | Better Auth 处理器挂载路径,默认 `/api/auth/better`。 | | `BETTER_AUTH_TRUSTED_ORIGINS` | 允许发起 Better Auth 登录流程的额外来源。支持逗号分隔、空白分隔或 JSON 数组。未设置时,MCPHub 仍会自动信任 `BETTER_AUTH_URL` 和 `systemConfig.install.baseUrl` 的来源。 | | `BETTER_AUTH_DISABLE_PASSWORD_LOGIN` | 在至少启用一个 SSO 提供商(且启动时已启用)时关闭用户名/密码登录(`POST /api/auth/login` 和登录表单)。在两个方向上都优先于已保存的 `disablePasswordLogin` 设置,因此当身份提供商不可用时,设为 `false` 即可恢复。默认 `false`。 | | `BETTER_AUTH_GOOGLE_ENABLED` | 在提供了凭据后启用或禁用 Google 登录,默认 `true`。 | | `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` | Google 登录凭据。 | | `BETTER_AUTH_GITHUB_ENABLED` | 在提供了凭据后启用或禁用 GitHub 登录,默认 `true`。 | | `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` | GitHub 登录凭据。 | | `BETTER_AUTH_OIDC_ENABLED` | 启用或禁用通用 OIDC Provider,默认 `false`。 | | `BETTER_AUTH_OIDC_PROVIDER_ID` | 传给 Better Auth generic OAuth 插件的 Provider 标识,默认 `oidc`。 | | `BETTER_AUTH_OIDC_DISCOVERY_URL` | 本地 OIDC Issuer 的 discovery 地址。 | | `OIDC_DISCOVERY_URL` | `BETTER_AUTH_OIDC_DISCOVERY_URL` 的兼容别名;如果仍在配置文件里使用 `${OIDC_DISCOVERY_URL}` 插值,也可以继续保留。 | | `BETTER_AUTH_OIDC_SCOPES` | 本地 OIDC 登录请求的 scopes。支持逗号分隔、空白分隔或 JSON 数组,默认 `openid profile email`。 | | `BETTER_AUTH_OIDC_PKCE` | 是否启用本地 OIDC 登录的 PKCE,默认 `true`。 | | `BETTER_AUTH_OIDC_PROMPT` | 可选的 OIDC `prompt` 参数(如 `login`、`consent`、`select_account`)。 | | `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` | 本地 OIDC Provider(例如 Keycloak、Authentik、Dex)的客户端凭据。 | 非敏感的 Better Auth 配置仍然可以放在 `systemConfig.auth.betterAuth` 中,但现在推荐优先使用 `BETTER_AUTH_*` 环境变量。一个纯环境变量驱动的本地 OIDC 配置示例如下: ```env theme={null} BETTER_AUTH_ENABLED=true BETTER_AUTH_URL=https://mcphub.example.com BETTER_AUTH_BASE_PATH=/api/auth/better BETTER_AUTH_TRUSTED_ORIGINS=https://dashboard.example.com,https://admin.example.com BETTER_AUTH_OIDC_ENABLED=true BETTER_AUTH_OIDC_PROVIDER_ID=local-oidc BETTER_AUTH_OIDC_DISCOVERY_URL=https://auth.example.com/.well-known/openid-configuration BETTER_AUTH_OIDC_SCOPES=openid,profile,email BETTER_AUTH_OIDC_PKCE=true BETTER_AUTH_OIDC_PROMPT=login OIDC_CLIENT_ID=your-oidc-client-id OIDC_CLIENT_SECRET=your-oidc-client-secret ``` ### 转发认证 转发认证信任上游网关签发给用户的 JWT,并使用身份提供方的 JWKS 进行验证。行为说明参见[认证 → 转发认证](/zh/features/authentication)。取值优先级:`FORWARD_AUTH_*` 环境变量,其次为 `systemConfig.auth.forwardAuth`,最后为内置默认值。配置在每次请求时读取,无需重启。 | 变量 | 说明 | | - | - | | `FORWARD_AUTH_ENABLED` | 启用转发认证。默认 `false`。 | | `FORWARD_AUTH_MODE` | 验证模式,目前仅支持 `jwks`。 | | `FORWARD_AUTH_JWKS_URI` | 身份提供方的 JWKS 端点。必须为 `https`(仅 `localhost` 允许 `http`)。必填。 | | `FORWARD_AUTH_ISSUER` | 期望的 `iss` 声明。必填。 | | `FORWARD_AUTH_AUDIENCE` | 接受的 `aud` 值,支持逗号分隔或 JSON 数组;值中可以包含空格。必填。 | | `FORWARD_AUTH_ALGORITHMS` | 允许的非对称算法,默认 `RS256,ES256`。永远不接受 `HS*` 和 `none`。 | | `FORWARD_AUTH_USERNAME_CLAIM` | 新建用户的用户名所用声明,默认 `preferred_username`。 | | `FORWARD_AUTH_EMAIL_CLAIM` | 新建用户的邮箱所用声明,默认 `email`。 | | `FORWARD_AUTH_AUTO_CREATE` | 为新身份创建非管理员本地用户,默认 `true`。 | ## 智能路由与嵌入 智能路由使用 PostgreSQL + pgvector 和嵌入服务,对上游工具执行向量搜索。启用智能路由至少需要配置 `DB_URL`、OpenAI 兼容嵌入服务和开关变量。 | 变量 | 默认值 | 描述 | | - | - | - | | `SMART_ROUTING_ENABLED` | `false` | 智能路由的规范总开关。`ENABLE_SMART_ROUTING` 仍作为旧版兼容别名;两者同时设置时,以本变量为准。 | | `SMART_ROUTING_EMBEDDING_PROVIDER` | `openai` | 嵌入提供商,目前支持 `openai` 和 `azure_openai`。 | | `SMART_ROUTING_EMBEDDING_ENCODING_FORMAT` | `auto` | 覆盖发送给嵌入 API 的 `encoding_format`。 | | `SMART_ROUTING_BASE_PACING_DELAY_MS` | `0` | 嵌入请求之间的基础等待时间(毫秒)。 | | `SMART_ROUTING_PROGRESSIVE_DISCLOSURE` | `false` | 是否启用渐进式工具信息披露。 | | `SMART_ROUTING_SERVER_DESCRIPTION_MODE` | `names` | `search_tools` 中服务器信息的展示方式:`names` 或 `full`。 | | `EMBEDDING_MODEL` | `text-embedding-3-small` | 嵌入模型名称。`OPENAI_API_EMBEDDING_MODEL` 仍作为旧版兼容别名;两者同时设置时,以本变量为准。 | | `EMBEDDING_DIMENSIONS` | 未设置 | 支持该参数的提供方可使用的嵌入输出维度;未设置时使用提供方的模型默认值。 | | `EMBEDDING_DIMENSIONS_API_PASSTHROUGH` | `false` | 设为 `true` 时,始终将 `embeddingDimensions` 作为 `dimensions` 请求参数转发给提供方。默认情况下 MCPHub 仅对已知支持 Matryoshka(MRL)表示的模型(`text-embedding-3-*`、`gemini-embedding-*`)转发 `dimensions`;许多开源模型/后端(Qwen3-Embedding、BGE、vLLM/sglang 等)会直接拒绝该参数(issue #1131)。仅对其它支持 MRL 的模型开启。 | | `EMBEDDING_MAX_TOKENS` | 按模型决定 | 生成嵌入前的最大 token 数。 | | `SMART_ROUTING_EMBEDDING_QUERY_PREFIX` | 空 | 嵌入前添加到 `search_tools` 查询开头的任务前缀(如 EmbeddingGemma 的 `task: search result \| query: `),按原样使用,包括末尾空格。 | | `SMART_ROUTING_EMBEDDING_DOCUMENT_PREFIX` | 空 | 嵌入前添加到工具和服务器文档开头的任务前缀(如 EmbeddingGemma 的 `title: none \| text: `)。修改后会重新生成所有服务器的嵌入。 | | `SMART_ROUTING_SIMILARITY_THRESHOLD` | 未设置 | `search_tools` 结果的最低查询-工具相似度(0-1)。未设置时按查询长度启发式决定(0.2 / 0.3 / 0.4)。 | | `SMART_ROUTING_FULL_SCHEMA_TOP_N` | 未设置 | 只有前 N 个 `search_tools` 结果带完整定义;其余结果只有名称、描述和分数,并列出 `describe_tool` 以获取完整定义。未设置时返回全部完整结果。启用渐进式披露时忽略。 | | `SMART_ROUTING_TOOL_DEFINITION_FIELDS` | `title,annotations` | 以逗号分隔的可选工具字段,完整的 `search_tools` 结果和 `describe_tool` 会在名称、描述和 `inputSchema` 之外附带:`title`、`annotations`、`outputSchema`、`execution`、`icons`、`_meta`。`none` 表示都不附带。 | ### OpenAI 或兼容服务 | 变量 | 描述 | | - | - | | `DB_URL` | PostgreSQL 连接 URL;数据库需要启用 `pgvector` 扩展。 | | `OPENAI_API_KEY` | OpenAI 或兼容嵌入服务的 API 密钥。 | | `OPENAI_API_BASE_URL` | OpenAI 兼容 API 的 Base URL,例如 LocalAI 或其他兼容服务。 | ### Azure OpenAI | 变量 | 描述 | | - | - | | `AZURE_OPENAI_API_KEY` | Azure OpenAI API 密钥。 | | `AZURE_OPENAI_ENDPOINT` | Azure OpenAI 资源地址。 | | `AZURE_OPENAI_API_VERSION` | Azure OpenAI API 版本。 | | `AZURE_OPENAI_EMBEDDING_DEPLOYMENT` | Azure 中的嵌入 deployment 名称。 | | `AZURE_OPENAI_EMBEDDING_MODEL` | deployment 背后的实际 OpenAI 模型名称,用于 tokenizer 和 token 上限选择。 | > [!NOTE] > > **环境变量优先于面板配置。** 上表中的每个变量都会覆盖 Dashboard 中保存的同名字段。解析顺序为 **环境变量 → 面板配置 → 内置默认值**。因此 `.env` 里遗留的旧值会静默覆盖你刚在面板里填入的密钥:嵌入请求会以鉴权失败告终,而界面上仍然显示着新密钥(issue #642)。 > > 面板现在会提示这一点:受影响字段下方会显示警告,并指出实际生效的环境变量名。若要让面板配置生效,请从环境中移除该变量并重启。 > > 「智能路由 → 启用」与「数据库 URL」同样遵循该顺序,但面板会直接反映环境变量的结果,而不是显示警告:开关会跟随环境变量,URL 字段会显示 `${DB_URL}` 占位符。 ## 配置示例 ### 开发环境 ```env theme={null} # .env(开发环境) NODE_ENV=development PORT=3000 # 认证 JWT_SECRET=dev-secret-key ``` ### 生产环境 ```env theme={null} # .env(生产环境) NODE_ENV=production PORT=3000 # 安全 JWT_SECRET=your-super-secure-production-secret ``` ### Docker 环境 ```env theme={null} # .env(Docker 环境) NODE_ENV=production PORT=3000 # 安全 JWT_SECRET_FILE=/run/secrets/jwt_secret ``` ## 环境变量加载 MCPHub 启动时只读取工作目录下的 `.env` 一个文件,不会读取 `.env.local` 或 `.env.{NODE_ENV}`(例如 `.env.production`)。 进程环境中已经存在的变量(shell、Docker 的 `-e` / `env_file`、Kubernetes 清单等)优先,`.env` 不会覆盖它们。 `.env` 中的值按原样使用,其中的 `${VAR}` 引用不会被展开。`mcp_settings.json` 支持 `${VAR}` 展开,参见 [MCP 设置](/zh/configuration/mcp-settings)。 ## 安全最佳实践 1. **永远不要将密钥提交**到版本控制 2. **为生产环境使用强大、独特的密钥** 3. **定期轮换密钥** 4. **使用特定于环境的文件** 5. **在启动时验证所有环境变量** 6. **为容器部署使用 Docker 密钥** ### 自定义智能路由地址 例如配置 `PORT=6001`、`BASE_PATH=/mcp_manage` 和 `SMART_ROUTING_PATH=my_smart` 后,Streamable HTTP 连接地址为 `http://localhost:6001/mcp_manage/mcp/my_smart`。追加 `/` 可限定到分组;在 `/mcp` 前加 `/` 可使用用户范围路由。旧版客户端使用的 `/sse` 同样支持该别名。 配置的路径段将保留给智能路由。启动时若存在同名服务器、分组或分组 ID,将明确报错;后续配置写入也拒绝新增冲突。请先修改路径段或重命名冲突目标。修改路径段后需要重启,并更新使用旧自定义地址的客户端;`$smart` 始终保留兼容,CLI 也继续使用该入口。此配置不改变鉴权和权限范围。 # MCP 设置配置 Source: https://docs.mcphub.app/zh/configuration/mcp-settings 配置 MCPHub 的 MCP 服务器及其设置 # MCP 设置配置 本指南说明如何使用 `mcp_settings.json` 文件和相关配置在 MCPHub 中配置 MCP 服务器。 ## 配置文件概述 MCPHub 使用几个配置文件: * **`mcp_settings.json`**:主要的 MCP 服务器配置 * **`servers.json`**:服务器元数据和分组 * **`.env`**:环境变量和密钥 ## 基本 MCP 设置结构 ### mcp\_settings.json ```json theme={null} { "mcpServers": { "server-name": { "command": "command-to-run", "args": ["arg1", "arg2"], "env": { "ENV_VAR": "value" }, "cwd": "/working/directory", "timeout": 30000, "restart": true } } } ``` ### 示例配置 ```json theme={null} { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "env": { "USER_AGENT": "MCPHub/1.0" } }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"], "timeout": 60000 }, "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}", "SLACK_TEAM_ID": "${SLACK_TEAM_ID}" } } } } ``` ## 服务器配置选项 ### 必需字段 | 字段 | 类型 | 描述 | | - | - | - | | `command` | string | 可执行命令或路径 | | `args` | array | 命令行参数 | ### 可选字段 | 字段 | 类型 | 默认值 | 描述 | | - | - | - | - | | `env` | object | `{}` | 环境变量 | | `cwd` | string | `process.cwd()` | 工作目录 | | `timeout` | number | `30000` | 启动超时(毫秒) | | `restart` | boolean | `true` | 失败时自动重启 | | `maxRestarts` | number | `5` | 最大重启次数 | | `restartDelay` | number | `5000` | 重启间延迟(毫秒) | | `stdio` | string | `pipe` | stdio 配置 | | `perSessionClient` | boolean | `false` | 为每个下游 MCP 会话创建独立的上游客户端,而不是在所有会话间共享同一个连接。适用于 Playwright 等有状态服务器。参见[会话级客户端隔离](#会话级客户端隔离有状态服务器)。 | ### `type: "openapi"` | 字段 | 类型 | 必需 | 描述 | | - | - | - | - | | `openapi.url` | string | `url`/`schema` 二选一 | MCPHub 将下载的 OpenAPI 文档 URL。 | | `openapi.schema` | object | `url`/`schema` 二选一 | 内联的 OpenAPI 文档。 | | `headers` | object | 否 | 应用于所有出站请求的静态请求头,包括文档下载本身。 | | `openapi.passthroughHeaders` | string\[] | 否 | 从调用方转发到上游的请求头名称。 | 每个字段的权威定义见 [src/types/index.ts](src/types/index.ts) 中的 `ServerConfig` 类型;如需此处未描述的行为,请查阅该类型。 > [!NOTE] > > 受保护的 `openapi.url` 会通过 MCPHub 的已认证客户端下载,因此配置的 `headers` 与 OpenAPI `security` 凭证(HTTP Basic/Bearer、API Key、OAuth2 令牌)会用于该次拉取的认证。凭证仅作用于文档所在源,不会转发给外部 `$ref` 解析或跨源重定向。若受保护的文档引用了同样需要认证的外部 `$ref`,请改用 `openapi.schema` 内联文档。 ## 常见 MCP 服务器示例 ### Web 和 API 服务器 #### Fetch 服务器 ```json theme={null} { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"], "env": { "USER_AGENT": "MCPHub/1.0", "MAX_REDIRECTS": "10" } } } ``` #### 使用 Playwright 进行网页抓取 ```json theme={null} { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"], "timeout": 60000, "env": { "PLAYWRIGHT_BROWSERS_PATH": "/tmp/browsers" } } } ``` ### 会话级客户端隔离(有状态服务器) 默认情况下,MCPHub 为每个服务器仅保持**一个**上游连接,并在所有下游 MCP 会话间共享。 对于有状态服务器来说,这意味着所有客户端共享同一份状态——以 Playwright 为例,每个会话都会 操作同一个浏览器实例及其标签页。 设置 `perSessionClient: true` 可以让 MCPHub 改为**为每个下游会话创建独立的上游客户端**。 每个会话拥有各自的连接(对于 stdio 服务器,还会拥有各自的子进程),在首次工具调用时惰性创建, 并在会话结束时自动销毁。 > [!NOTE] > > `perSessionClient` 是 **MCPHub 侧**的设置:它控制 MCPHub 打开多少个上游连接。它与 Playwright > MCP 自身的 `--isolated` 标志不同,后者控制 Playwright 侧的浏览器配置隔离。二者天然互补——同时 > 启用即可获得完全隔离的浏览器会话。 ```json theme={null} { "mcpServers": { "playwright": { "type": "streamable-http", "url": "http://playwright-mcp:3000/mcp", "perSessionClient": true } } } ``` 或者使用本地启动并带有 `--isolated` 标志的 Playwright MCP: ```json theme={null} { "mcpServers": { "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless", "--isolated"], "perSessionClient": true } } } ``` > [!TIP] > > 对持有会话级状态或需要[旧协议工具 elicitation 转发](/zh/api-reference/mcp-http#elicitation-转发) > 的服务器启用 `perSessionClient`。它会让上游连接(以及 stdio 子进程) > 数量随并发会话数成倍增长,不需要以上功能时请保持关闭。 ### 上游协议协商 Streamable HTTP 上游默认使用 `protocolNegotiation: "auto"`,先探测 `server/discover`,支持时使用新版无状态 HTTP,否则按 SDK 规则回退旧版握手。 HTTP 探测超时、网络故障和认证失败可能使连接失败,而不是回退。 已知只支持旧版的端点可跳过探测: ```json theme={null} { "mcpServers": { "legacy-gateway": { "type": "streamable-http", "url": "https://gateway.example.com/mcp", "protocolNegotiation": "legacy" } } } ``` 该可选字段接受 `"auto"` 和 `"legacy"`。`"auto"` 要求 `type: "streamable-http"`;stdio 和旧 SSE 保持旧版协商。 可通过配置 JSON、JSON 导入或服务器 API 设置;表单编辑和复制会保留该字段。 文件、数据库、模板及声明式配置同步均支持,修改会重连服务器。 共享、隔离和个人连接使用相同策略,但转发旧版 elicitation 的隔离连接在实现 modern `input_required` 桥接前仍使用旧版协议。 新版上游没有服务端推送流。相关请求会在上游 TTL 到期后刷新列表,并合并并发刷新。 TTL 缺失、为零、无效或列表分页未完成时,下一次相关请求重新获取。 健康检查仍需设置 `enableKeepAlive`;新版连接通过网络 `server/discover` 探测, 不调用 `ping`。详见[兼容矩阵](/zh/api-reference/mcp-http#协议兼容矩阵)。 共享远端 SSE 和 Streamable HTTP 服务在连接失败或连接关闭后会在后台自动重试,无需启用健康检查。重试使用指数退避和随机抖动,首次等待 5–10 秒,最高等待 150–300 秒,不限制重试次数;连接成功后重置。已禁用或等待 OAuth 授权的服务不会重试。关闭健康检查时,故障会在请求失败或连接关闭时被发现。控制台展示下次重试时间及上次重试的时间和次数,对应运行时 `recovery` 状态中的 `attempts`、`lastAttemptAt` 和 `nextAttemptAt`(时间戳单位为毫秒)。 配置 `passthroughHeaders` 的新版上游按请求隔离 discovery 客户端和快照,不跨透传身份共享。 每次相关请求增加一次 discovery 往返,个性化描述不写入全局缓存或向量索引。 刷新失败只在当前请求中排除该上游,保留健康结果;部分列表返回零 TTL,后续请求会重试。 ### MCP 2026-07-28 的路由请求头 新版请求使用 `MCP-Protocol-Version: 2026-07-28`,并携带与 JSON-RPC 方法一致的 `Mcp-Method`。`tools/call` 和 `prompts/get` 还需要与 `params.name` 一致的 `Mcp-Name`;`resources/read` 则需要与 `params.uri` 一致。列表方法无需 `Mcp-Name`。SDK v2 HTTP 客户端会自动设置这些请求头。 例如,向 `/mcp` 调用 `params.name` 为 `my-server-my-tool` 的工具时发送: ```http theme={null} MCP-Protocol-Version: 2026-07-28 Mcp-Method: tools/call Mcp-Name: my-server-my-tool ``` 这些请求头与正常的 JSON-RPC 请求体一起发送,请求体仍需包含新版协议的 `params._meta` 协议版本与客户端能力信息。请求头不选择分组,也不授予访问权限; 可访问的服务器仍由 URL 和 bearer 权限决定。使用服务器专属 URL 时,请求体与 请求头都应使用该 URL 下暴露的工具名称。非 ASCII 名称在 `Mcp-Name` 中使用 SDK 的 `=?base64??=` 编码。 缺失必需请求头、请求头与请求体不一致或名称编码非法时,在执行前返回 HTTP 400 和 JSON-RPC 错误 `-32020`。旧版 `2025-11-25` 请求继续使用原有握手与会话机制, 无需这些路由请求头。 ### MCP 2026-07-28 的有状态工具 新版 MCP 请求不创建下游传输会话。调用启用 `perSessionClient: true` 或 OpenAPI `cookieSession: true` 的工具时,请开启 bearer 认证,并在同一应用工作流的每个请求中 携带客户端生成的 UUID v4: ```http theme={null} Authorization: Bearer X-MCPHub-State-Id: 65b523af-e4d0-4d99-8e06-461a62a14967 ``` `X-MCPHub-State-Id` 是 MCPHub 扩展,与 `Mcp-Session-Id` 和 MCP 握手独立。 只有 bearer 凭据、用户和路由均相同的请求才会复用同一状态标识对应的独立上游客户端 和 cookie jar。不同工作流应使用不同 UUID。 无法设置该扩展的已认证 OpenAI/ChatGPT 工具调用可以省略它,并使用 ChatGPT 工具调用中 客户端声明的 `_meta["openai/session"]` 会话提示。MCPHub 会先对该值做哈希,再仅用于状态 关联;它不会参与认证或授权,复用范围仍由已验证的 bearer 凭据、用户和路由限制。如果 显式 `X-MCPHub-State-Id` 已提供但非法,则直接失败而不会回退。如果两个可用关联值都缺失, 有状态工具返回工具错误;其他工具无需状态标识。 状态只保存在单个 MCPHub 进程的内存中,空闲 30 分钟后回收,执行中的调用会保留状态 直到完成。回收和关闭进程时会关闭独立上游客户端并清空 cookie jar。每个进程最多容纳 1,000 个新版状态作用域,超出时返回工具错误,等待容量释放后可重试。进程重启、状态 过期、bearer 更换或路由变化后需要重新建立应用状态。多副本部署需要粘性路由。 旧版 `2025-11-25` 客户端继续使用已有的下游会话生命周期。 ### 文件和系统服务器 #### 文件系统服务器 ```json theme={null} { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"], "env": { "ALLOWED_OPERATIONS": "read,write,list" } } } ``` #### SQLite 服务器 ```json theme={null} { "sqlite": { "command": "uvx", "args": ["mcp-server-sqlite", "--db-path", "/path/to/database.db"], "env": { "SQLITE_READONLY": "false" } } } ``` ### 通信服务器 #### Slack 服务器 ```json theme={null} { "slack": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-slack"], "env": { "SLACK_BOT_TOKEN": "${SLACK_BOT_TOKEN}", "SLACK_TEAM_ID": "${SLACK_TEAM_ID}", "SLACK_APP_TOKEN": "${SLACK_APP_TOKEN}" } } } ``` #### 邮件服务器 ```json theme={null} { "email": { "command": "python", "args": ["-m", "mcp_server_email"], "env": { "SMTP_HOST": "smtp.gmail.com", "SMTP_PORT": "587", "EMAIL_USER": "${EMAIL_USER}", "EMAIL_PASSWORD": "${EMAIL_PASSWORD}" } } } ``` ### 开发和 API 服务器 #### GitHub 服务器 ```json theme={null} { "github": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-github"], "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" } } } ``` #### Google Drive 服务器 ```json theme={null} { "gdrive": { "command": "npx", "args": ["-y", "@google/mcp-server-gdrive"], "env": { "GOOGLE_CLIENT_ID": "${GOOGLE_CLIENT_ID}", "GOOGLE_CLIENT_SECRET": "${GOOGLE_CLIENT_SECRET}", "GOOGLE_REFRESH_TOKEN": "${GOOGLE_REFRESH_TOKEN}" } } } ``` ### 地图和位置服务 #### 高德地图服务器 ```json theme={null} { "amap": { "command": "npx", "args": ["-y", "@amap/amap-maps-mcp-server"], "env": { "AMAP_MAPS_API_KEY": "${AMAP_API_KEY}", "AMAP_LANGUAGE": "zh-cn" } } } ``` #### OpenStreetMap 服务器 ```json theme={null} { "osm": { "command": "python", "args": ["-m", "mcp_server_osm"], "env": { "OSM_USER_AGENT": "MCPHub/1.0" } } } ``` ## 高级配置 ### 环境变量替换 MCPHub 支持使用 `${VAR_NAME}` 语法进行环境变量替换: ```json theme={null} { "mcpServers": { "api-server": { "command": "python", "args": ["-m", "api_server"], "env": { "API_KEY": "${API_KEY}", "API_URL": "${API_BASE_URL}/v1", "DEBUG": "${NODE_ENV:development}" } } } } ``` 可以使用 `${VAR_NAME:default}` 指定默认值: ```json theme={null} { "timeout": "${MCP_TIMEOUT:30000}", "maxRestarts": "${MCP_MAX_RESTARTS:5}" } ``` ### 条件配置 根据环境使用不同配置: ```json theme={null} { "mcpServers": { "database": { "command": "python", "args": ["-m", "db_server"], "env": { "DB_URL": "${NODE_ENV:development == 'production' ? DB_URL : DEV_DB_URL}" } } } } ``` ### 自定义服务器脚本 #### 本地 Python 服务器 ```json theme={null} { "custom-python": { "command": "python", "args": ["./servers/custom_server.py"], "cwd": "/app/custom-servers", "env": { "PYTHONPATH": "/app/custom-servers", "CONFIG_FILE": "./config.json" } } } ``` #### 本地 Node.js 服务器 ```json theme={null} { "custom-node": { "command": "node", "args": ["./servers/custom-server.js"], "cwd": "/app/custom-servers", "env": { "NODE_ENV": "production" } } } ``` ## 服务器元数据配置 ### servers.json 使用服务器元数据补充 `mcp_settings.json`: ```json theme={null} { "servers": { "fetch": { "name": "Fetch 服务器", "description": "用于网络请求的 HTTP 客户端", "category": "web", "tags": ["http", "api", "web"], "version": "1.0.0", "author": "MCPHub 团队", "documentation": "https://docs.mcphub.com/servers/fetch", "enabled": true }, "playwright": { "name": "Playwright 浏览器", "description": "网页自动化和抓取", "category": "automation", "tags": ["browser", "scraping", "automation"], "version": "2.0.0", "enabled": true } }, "groups": { "web-tools": { "name": "网页工具", "description": "用于网页交互的工具", "servers": ["fetch", "playwright"], "access": "public" }, "admin-tools": { "name": "管理工具", "description": "管理实用程序", "servers": ["filesystem", "database"], "access": "admin" } } } ``` ## 组管理 ### 组配置 ```json theme={null} { "groups": { "production": { "name": "生产工具", "description": "稳定的生产服务器", "servers": ["fetch", "slack", "github"], "access": "authenticated", "rateLimit": { "requestsPerMinute": 100, "burstLimit": 20 } }, "experimental": { "name": "实验功能", "description": "测试版和实验性服务器", "servers": ["experimental-ai", "beta-search"], "access": "admin", "enabled": false } } } ``` ### 访问控制 | 访问级别 | 描述 | | - | - | | `public` | 无需认证 | | `authenticated` | 需要有效的 JWT 令牌 | | `admin` | 需要管理员角色 | | `custom` | 自定义权限逻辑 | ## 动态配置 ### 热重载 MCPHub 支持配置热重载: ```bash theme={null} # 不重启重新加载配置 curl -X POST http://localhost:3000/api/admin/reload-config \ -H "Authorization: Bearer your-admin-token" ``` ### 配置验证 MCPHub 在启动和重新加载时验证配置: ```json theme={null} { "validation": { "strict": true, "allowUnknownServers": false, "requireDocumentation": true } } ``` ## 工具结果压缩 MCPHub 可以在工具结果返回 MCP 客户端前,透明压缩较大的文本结果。它适合处理大型日志、diff、搜索输出、JSON 数组或长文本,避免这些内容占用过多模型上下文。 工具结果压缩默认关闭。可以在 **设置 → 工具结果压缩** 中实时开关,也可以在 `systemConfig.toolResultCompression` 下配置: ```json theme={null} { "systemConfig": { "toolResultCompression": { "enabled": false, "minTokens": 2000, "maxOutputTokens": 1200, "strategy": "auto" } } } ``` | 字段 | 类型 | 默认值 | 描述 | | - | - | - | - | | `enabled` | boolean | `false` | 是否对成功的工具结果启用透明压缩。 | | `minTokens` | number | `2000` | 单个文本块的估算 token 数达到或超过该阈值时才会压缩。 | | `maxOutputTokens` | number | `1200` | 每个压缩后文本块的目标 token 预算。 | | `strategy` | `"auto" \| "json" \| "log" \| "search" \| "diff" \| "text"` | `"auto"` | 压缩策略。`auto` 会自动识别常见结构化输出。 | 压缩只作用于成功的工具响应。带有 `isError: true` 的响应会原样返回,非文本内容块会被保留,每个文本块会独立判断是否需要压缩。MCPHub 压缩结果时会在文本开头加入类似下面的标记: ```text theme={null} [mcphub:compressed-tool-result strategy=log original_tokens=9000 compressed_tokens=1200 omitted_lines=240] ``` 活动日志仍记录上游工具的原始输出,压缩只发生在返回 MCP 客户端之前,因此运维人员仍可在 MCPHub 中查看完整结果。 ## 最佳实践 ### 安全 1. **对敏感数据使用环境变量**: ```json theme={null} { "env": { "API_KEY": "${API_KEY}", "DATABASE_PASSWORD": "${DB_PASSWORD}" } } ``` 2. **限制服务器权限**: ```json theme={null} { "filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "/restricted/path"], "env": { "READONLY": "true" } } } ``` ### 性能 1. **设置适当的超时**: ```json theme={null} { "timeout": 30000, "maxRestarts": 3, "restartDelay": 5000 } ``` 2. **资源限制**: ```json theme={null} { "env": { "NODE_OPTIONS": "--max-old-space-size=512", "MEMORY_LIMIT": "512MB" } } ``` ### 监控 1. **启用健康检查**: ```json theme={null} { "healthCheck": { "enabled": true, "interval": 30000, "timeout": 5000 } } ``` 2. **日志配置**: ```json theme={null} { "env": { "LOG_LEVEL": "info", "LOG_FORMAT": "json" } } ``` ## 故障排除 ### 常见问题 **服务器无法启动**:检查命令和参数 ```bash theme={null} # 手动测试命令 uvx mcp-server-fetch ``` **找不到环境变量**:验证 `.env` 文件 ```bash theme={null} # 检查环境 printenv | grep API_KEY ``` **权限错误**:检查文件权限和路径 ```bash theme={null} # 验证可执行权限 ls -la /path/to/server ``` ### 调试配置 启用调试模式进行详细日志记录: ```json theme={null} { "debug": { "enabled": true, "logLevel": "debug", "includeEnv": false, "logStartup": true } } ``` ### 验证错误 常见验证错误和解决方案: 1. **缺少必需字段**:添加 `command` 和 `args` 2. **无效超时**:使用数字,不是字符串 3. **找不到环境变量**:检查 `.env` 文件 4. **找不到命令**:验证安装和 PATH 这个全面的指南涵盖了在 MCPHub 中为各种用例和环境配置 MCP 服务器的所有方面。 ### Stdio 响应缓冲区 将 `options.maxBufferSize` 设置为以字节为单位的正安全整数,可接收更大的 stdio 响应: ```json theme={null} { "type": "stdio", "command": "node", "args": ["server.js"], "options": { "maxBufferSize": 268435456 } } ``` 省略该选项时保留 SDK 默认值 10 MiB(10485760 字节)。上限用于读取包含 MCP 消息封装的序列化数据的缓冲区。响应超过上限会关闭传输连接。提高上限会允许使用更多内存,请按服务器需求设置有限上限。零、负数、小数和非有限值均被拒绝。该选项不会改变 HTTP 传输或 SDK 处理大消息的性能。 ## 放行内网上游网络 管理员可在设置页维护**允许访问的上游 CIDR 范围**,JSON 存储模式也可配置 `systemConfig.network.allowedCidrs`: ```json theme={null} { "systemConfig": { "network": { "allowedCidrs": ["192.168.1.0/24", "fd00:1234::/64"] } } } ``` 默认为空列表。配置后,所有用户的上游 MCP(SSE 和 Streamable HTTP)、OpenAPI 及上游 OAuth 请求均可访问这些范围。公网地址仍可访问,管理员拥有的服务器保留已有的不受限内网访问权限。服务器可见性与共享权限不受影响。 每项必须是带前缀长度的 IPv4 或 IPv6 地址,例如 `/32` 表示单个 IPv4 地址,`/128` 表示单个 IPv6 地址。系统检查全部 DNS 结果、每次重定向目标及建立连接时的 DNS 结果。删除范围会影响后续请求,但不会关闭已有连接或清除缓存。匿名 OAuth 客户端元数据(CIMD)URL 仍仅允许公网地址。 只有管理员可调用 `PUT /api/system-config`,请求体为 `{"network":{"allowedCidrs":["192.168.1.0/24"]}}`。传入空数组可清空白名单,非法列表返回 HTTP 400 且不会保存。数据库模式请通过管理界面或 API 配置,该字段持久化到系统配置表。 请仅放行必要的最小范围。`0.0.0.0/0` 或 `::/0` 等过大的范围也会放行回环地址、云元数据端点等敏感内网目标。 ## 按需启动的工具快照 对于 stdio 服务器,`startOnDemand: true` 让子进程在需要时才启动。MCPHub 会持久化最近一次成功发现的原始工具目录,重启后可直接恢复 `tools/list`,无需逐个启动所有按需服务器。成功发现的空工具列表也是有效快照。首次工具调用仍连接真实上游并刷新目录,上游已删除的工具不能仅凭快照执行。失败调用会触发目录刷新,但不会重放调用。 文件模式将快照保存在设置文件旁的 `.capabilities.json`,容器替换后仍要保留快照时,请将该目录挂载到持久化存储。数据库模式使用 `capability_snapshots`表。快照不进入设置导出,也不包含启动配置或凭据;但工具定义可能包含上游私有元数据,请按运行时数据的要求保护其存储。 服务器列表和详情 API 提供 `capabilityCache.state`(`fresh`、`stale`、`missing`)及可用时的 `capabilityCache.capturedAt`。`fresh` 表示配置匹配的历史发现结果,不保证上游此刻未发生变化。启动配置、子进程实际环境或个人凭据绑定变更会让快照失效。每次列举工具仍重新应用当前权限及服务器、分组的工具配置。个人快照按认证用户隔离,读取时仍要求有效的凭据绑定和当前服务器访问权限。 快照缺失或不兼容时沿用实时发现流程:共享 stdio 服务器在启动时预发现,个人服务器在首次认证使用时发现。不增加全量后台刷新。配置不变时,上游包更新或外部权限变化需要等下次真实连接才能被发现。本阶段不持久化提示词和资源;个人服务器的提示词、资源请求仍连接真实上游。 # Nginx 配置 Source: https://docs.mcphub.app/zh/configuration/nginx 配置 Nginx 作为 MCPHub 的反向代理 # Nginx 配置 本指南说明如何配置 Nginx 作为 MCPHub 的反向代理,包括 SSL 终止、负载均衡和缓存策略。 ## 基本反向代理设置 ### 配置文件 创建或更新您的 Nginx 配置文件(`/etc/nginx/sites-available/mcphub`): ```nginx theme={null} server { listen 80; server_name your-domain.com; # 将 HTTP 重定向到 HTTPS return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name your-domain.com; # SSL 配置 ssl_certificate /path/to/your/certificate.crt; ssl_certificate_key /path/to/your/private.key; ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384; ssl_prefer_server_ciphers off; # 安全头 add_header X-Frame-Options DENY; add_header X-Content-Type-Options nosniff; add_header X-XSS-Protection "1; mode=block"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; # Gzip 压缩 gzip on; gzip_vary on; gzip_min_length 1024; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/atom+xml image/svg+xml; # 主应用程序 location / { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 86400; } # API 端点,为 MCP 操作设置更长的超时 location /api/ { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_cache_bypass $http_upgrade; proxy_read_timeout 300; proxy_connect_timeout 60; proxy_send_timeout 60; } # 静态资源缓存 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://127.0.0.1:3000; proxy_cache_valid 200 1d; proxy_cache_valid 404 1m; add_header Cache-Control "public, immutable"; expires 1y; } } ``` ### 启用配置 ```bash theme={null} # 创建符号链接启用站点 sudo ln -s /etc/nginx/sites-available/mcphub /etc/nginx/sites-enabled/ # 测试配置 sudo nginx -t # 重新加载 Nginx sudo systemctl reload nginx ``` ## 负载均衡配置 对于具有多个 MCPHub 实例的高可用性设置: ```nginx theme={null} upstream mcphub_backend { least_conn; server 127.0.0.1:3000 weight=1 max_fails=3 fail_timeout=30s; server 127.0.0.1:3001 weight=1 max_fails=3 fail_timeout=30s; server 127.0.0.1:3002 weight=1 max_fails=3 fail_timeout=30s; # 健康检查(Nginx Plus 功能) # health_check interval=5s fails=3 passes=2; } server { listen 443 ssl http2; server_name your-domain.com; # SSL 和其他配置... location / { proxy_pass http://mcphub_backend; # 其他代理设置... } } ``` ## 缓存配置 ### 浏览器缓存 ```nginx theme={null} # 缓存静态资源 location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ { proxy_pass http://127.0.0.1:3000; expires 1y; add_header Cache-Control "public, immutable"; } # 缓存 API 响应(小心动态内容) location /api/public/ { proxy_pass http://127.0.0.1:3000; proxy_cache mcphub_cache; proxy_cache_valid 200 5m; proxy_cache_key "$scheme$request_method$host$request_uri"; add_header X-Cache-Status $upstream_cache_status; } ``` ### Nginx 代理缓存 在 `nginx.conf` 的 `http` 块中添加: ```nginx theme={null} http { # 代理缓存配置 proxy_cache_path /var/cache/nginx/mcphub levels=1:2 keys_zone=mcphub_cache:10m max_size=1g inactive=60m use_temp_path=off; # 其他配置... } ``` ## WebSocket 支持 对于实时功能和 SSE(服务器发送事件): ```nginx theme={null} location /api/stream { proxy_pass http://127.0.0.1:3000; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 禁用实时响应的缓冲 proxy_buffering off; proxy_cache off; # 长连接超时 proxy_read_timeout 24h; proxy_send_timeout 24h; } ``` ## 安全配置 ### 速率限制 ```nginx theme={null} http { # 定义速率限制区域 limit_req_zone $binary_remote_addr zone=api:10m rate=10r/s; limit_req_zone $binary_remote_addr zone=login:10m rate=1r/s; server { # 对 API 端点应用速率限制 location /api/ { limit_req zone=api burst=20 nodelay; # 其他配置... } # 登录端点的严格速率限制 location /api/auth/login { limit_req zone=login burst=5; # 其他配置... } } } ``` ### IP 白名单 ```nginx theme={null} # 为管理端点允许特定 IP location /api/admin/ { allow 192.168.1.0/24; allow 10.0.0.0/8; deny all; proxy_pass http://127.0.0.1:3000; # 其他代理设置... } ``` ## 监控和日志 ### 访问日志 ```nginx theme={null} http { # 自定义日志格式 log_format mcphub_format '$remote_addr - $remote_user [$time_local] ' '"$request" $status $body_bytes_sent ' '"$http_referer" "$http_user_agent" ' '$request_time $upstream_response_time'; server { # 启用访问日志 access_log /var/log/nginx/mcphub_access.log mcphub_format; error_log /var/log/nginx/mcphub_error.log; # 其他配置... } } ``` ### 状态页面 ```nginx theme={null} location /nginx_status { stub_status; allow 127.0.0.1; deny all; } ``` ## Docker 集成 当在 Docker 中运行 MCPHub 时,更新代理配置: ```nginx theme={null} upstream mcphub_docker { server mcphub:3000; # Docker 服务名 } server { location / { proxy_pass http://mcphub_docker; # 其他代理设置... } } ``` ## 完整示例配置 使用提供的 `nginx.conf.example` 的生产就绪示例: ```bash theme={null} # 复制示例配置 cp nginx.conf.example /etc/nginx/sites-available/mcphub # 使用您的域名和路径更新配置 sudo nano /etc/nginx/sites-available/mcphub # 启用站点 sudo ln -s /etc/nginx/sites-available/mcphub /etc/nginx/sites-enabled/ # 测试并重新加载 sudo nginx -t && sudo systemctl reload nginx ``` ## 故障排除 ### 常见问题 **502 Bad Gateway**:检查 MCPHub 是否正在运行且可访问 **504 Gateway Timeout**:为长时间运行的操作增加 `proxy_read_timeout` **WebSocket 连接失败**:确保正确的 `Upgrade` 和 `Connection` 头 **缓存问题**:清除代理缓存或在开发中禁用 ### 调试命令 ```bash theme={null} # 测试 Nginx 配置 sudo nginx -t # 检查 Nginx 状态 sudo systemctl status nginx # 查看错误日志 sudo tail -f /var/log/nginx/error.log # 检查 MCPHub 是否响应 curl -I http://localhost:3000 ``` ## 性能优化 ### 工作进程 ```nginx theme={null} # 在 nginx.conf 中 worker_processes auto; worker_connections 1024; ``` ### 缓冲区大小 ```nginx theme={null} proxy_buffering on; proxy_buffer_size 128k; proxy_buffers 4 256k; proxy_busy_buffers_size 256k; ``` ### Keep-Alive ```nginx theme={null} upstream mcphub_backend { server 127.0.0.1:3000; keepalive 32; } location / { proxy_pass http://mcphub_backend; proxy_http_version 1.1; proxy_set_header Connection ""; } ``` 此配置为在 Nginx 后运行 MCPHub 提供了坚实的基础,具有适当的安全性、性能和可靠性功能。 # 身份认证与安全 Source: https://docs.mcphub.app/zh/features/authentication 为 MCPHub 配置身份认证和安全设置 ## 自助注册 管理员可在「设置 → 安全」中开启「允许自助注册」,默认关闭。开启后,登录页显示「创建账号」入口。 自助注册始终创建普通用户,不能授予管理员权限。即使原账号已删除,仍被资源所有权、共享名单、认证状态或用户配置引用的用户名也不能通过自助注册领取。密码至少八位,且包含字母、数字和特殊字符。 该配置保存在 `mcp_settings.json` 的 `systemConfig.auth.allowRegistration`,支持文件和数据库存储,无需重启。 当生效的 SSO 配置禁用密码登录时,后端同时拒绝本地注册,登录页隐藏注册入口。已有账号仍然保留。 ## 概述 MCPHub 提供灵活的身份认证机制来保护您的 MCP 服务器管理平台。系统支持多种身份认证方法和基于角色的访问控制。 ## 本地用户与凭据失效 Dashboard JWT 和 MCPHub 内置 OAuth 授权绑定签发时的用户密码哈希。删除本地用户或修改密码后,旧凭据失效;重建同名用户不会恢复旧凭据。JWT 请求使用用户当前的管理员权限。删除用户还会清理 OAuth token、处理实例内待兑换的授权码,以及服务器和组的共享名单。 从没有会话版本的旧凭据升级后,需要重新登录并重新连接 OAuth 客户端。数据库模式会通过正常的启动 schema 同步为 `oauth_tokens` 添加可空的 `session_version` 列。升级时应重启所有副本。 `POST /api/auth/register` 遵循上文默认关闭的自助注册策略,并始终创建普通用户。角色分配请使用管理员用户管理 API。 内置 OAuth token 可用于 MCP 传输接口及 `/oauth/userinfo`,不能访问 `/api/*` 管理接口。管理操作请使用 Dashboard JWT、Better Auth session、已配置的 forward auth 或系统级全访问 bearer key。 ## Better Auth(GitHub / Google / 本地 OIDC) MCPHub 可以通过 Better Auth 为 Dashboard 提供第三方登录。当前支持: * GitHub * Google * 本地 OIDC Provider(例如 Keycloak、Authentik、Dex) 启用条件: 1. 配置 `DB_URL`(Better Auth 当前依赖 PostgreSQL,会话不支持纯文件模式) 2. 配置 Better Auth 启动参数并重启 MCPHub 3. 为对应登录方式提供客户端凭据 Better Auth 的非敏感配置现在既可以来自环境变量,也可以来自 `systemConfig.auth.betterAuth`(`mcp_settings.json` 或数据库系统配置)。优先级如下: 1. `BETTER_AUTH_*` 环境变量 2. `systemConfig.auth.betterAuth` 3. 默认值 常用环境变量包括: * `BETTER_AUTH_ENABLED` * `BETTER_AUTH_URL` * `BETTER_AUTH_BASE_PATH` * `BETTER_AUTH_TRUSTED_ORIGINS` * `BETTER_AUTH_DISABLE_PASSWORD_LOGIN`(参见下文“仅允许 SSO 登录”) * `BETTER_AUTH_GOOGLE_ENABLED` + `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` * `BETTER_AUTH_GITHUB_ENABLED` + `GITHUB_CLIENT_ID` / `GITHUB_CLIENT_SECRET` * `BETTER_AUTH_OIDC_ENABLED` * `BETTER_AUTH_OIDC_PROVIDER_ID` * `BETTER_AUTH_OIDC_DISCOVERY_URL`(兼容别名:`OIDC_DISCOVERY_URL`) * `BETTER_AUTH_OIDC_SCOPES` * `BETTER_AUTH_OIDC_PKCE` * `BETTER_AUTH_OIDC_PROMPT` * `OIDC_CLIENT_ID` / `OIDC_CLIENT_SECRET` 本地 OIDC 登录也可以继续在 `systemConfig.auth.betterAuth.providers.oidc` 中配置: * `enabled` * `providerId` * `discoveryUrl` * 可选的 `scopes`、`pkce`、`prompt` `discoveryUrl` 应指向 OIDC Provider 的 `/.well-known/openid-configuration`。如果仍然使用配置文件插值,`OIDC_DISCOVERY_URL` 也会继续生效。 如果 Dashboard 通过不同的公网来源发起登录,请设置 `BETTER_AUTH_TRUSTED_ORIGINS`(或 `systemConfig.auth.betterAuth.trustedOrigins`)来允许该来源。如果没有显式配置 `trustedOrigins`,MCPHub 会自动信任 `BETTER_AUTH_URL` 和 `systemConfig.install.baseUrl` 的来源。 当 OIDC Provider 可用时,登录页会显示“使用 OIDC 登录”按钮。 默认挂载路径是 `${BASE_PATH}/api/auth/better`;如果需要改路径,可以使用 `BETTER_AUTH_BASE_PATH`,或者在 `systemConfig.auth.betterAuth.basePath` 中提供后备值。 ### 本地用户映射规则 Better Auth 登录成功后,MCPHub 会把会话映射回本地用户: * 优先使用 `email` * 其次使用 `name` * 最后回退到 provider 返回的 `id` 如果本地不存在对应 `username`,MCPHub 会自动创建一个**非管理员**用户。 > OIDC / GitHub / Google 登录都不会自动授予管理员权限。只有本地已存在同名且 `isAdmin=true` 的用户,映射后才会拥有管理员权限。 ### 仅允许 SSO 登录 如需只允许通过 SSO 提供商登录,可在 设置 → Better Auth 中开启 **禁用密码登录**,或设置 `systemConfig.auth.betterAuth.disablePasswordLogin: true`。此时登录页只显示提供商按钮,`POST /api/auth/login` 返回 `403`。与其他 Better Auth 设置不同,它无需重启即可生效。 * **不会锁死:** 仅在至少有一个提供商真正可用(Better Auth 已开启、数据库模式、凭据齐全),且在 MCPHub 启动时已启用时生效,因为提供商的登录路由只在启动时创建;否则密码登录保持可用:新启用提供商后,需要重启 MCPHub 才会生效。 * **恢复:** `BETTER_AUTH_DISABLE_PASSWORD_LOGIN` 在两个方向上都优先于已保存的设置。如果身份提供商不可用,将其设为 `false` 并重启,即可重新使用本地密码登录。为此请给本地管理员保留一个强密码。 * **脚本和 CLI** 在禁用期间无法使用密码登录。请使用访问类型为 `all` 的系统级 Bearer 密钥,它可以访问管理 API(CLI 使用 `mcphub config set-token --bearer`)。 * 之前通过密码登录签发的令牌在过期前仍然有效。修改本地密码仍然可用,因此可以轮换用于恢复的密码。 ## 转发认证(基于 JWKS 验证的网关身份) 当 MCPHub 部署在已完成用户认证的 API 网关或身份感知代理之后时,转发认证允许 MCPHub 信任网关传来的 JWT,用户无需再次登录。MCPHub 会使用身份提供方的 JWKS 自行验证令牌,因此 MCPHub 仍然是安全边界。转发认证**默认关闭**,同时作用于仪表盘 API 和 MCP 传输路由(`/mcp/*`、`/sse/*` 及其用户级路由)。 ```json theme={null} "systemConfig": { "auth": { "forwardAuth": { "enabled": true, "mode": "jwks", "jwks": { "uri": "https://idp.example.com/.well-known/jwks.json", "issuer": "https://idp.example.com/", "audience": "mcphub", "algorithms": ["RS256", "ES256"] }, "usernameClaim": "preferred_username", "emailClaim": "email", "autoCreate": true } } } ``` JWT 从配置的 Bearer 请求头中读取(默认为 `Authorization: Bearer `)。 **验证** * 令牌使用 `jwks.uri` 进行验证。密钥会被缓存,遇到未知 `kid` 时会重新获取,因此身份提供方轮换密钥无需重启。 * 始终校验 `iss`、`aud` 和 `exp`;`sub` 与 `exp` 为必需声明。允许 30 秒的时钟偏差。 * 仅接受非对称算法(`RS*`、`PS*`、`ES*`、`EdDSA`;默认 `RS256` 和 `ES256`)。配置 `HS*` 或 `none` 会使转发认证被禁用。 * `jwks.uri` 必须使用 `https`(仅 `localhost` 允许 `http`)。`issuer` 和 `audience` 为必填项。配置不完整或无效时,转发认证保持禁用并记录错误日志。 **请求处理** * 转发认证先于 Bearer 密钥、OAuth 令牌、Better Auth 会话和仪表盘 JWT 进行检查。 * 非 JWT 令牌,或 `iss` 不是所配置签发方的令牌会被忽略,其他认证方式照常执行。 * 来自所配置签发方但验证失败(签名错误、已过期、受众错误、算法不允许)或无法映射到用户的令牌会返回 `401`,MCPHub **不会回退**到其他认证方式。 **身份映射** * 身份由(`iss`、`sub`)确定,以 `forward-auth:` 命名空间存储在用户的 `ssoUserId` 中,与 Better Auth 身份相互隔离。Better Auth 登录不会接管转发认证用户,反之亦然。 * 首次出现时,MCPHub 会创建一个**非管理员**本地用户。用户名取自 `usernameClaim`,其次为 `emailClaim`,最后为 `sub`。 * **不会**通过用户名或邮箱关联已有账户。如果用户名或邮箱已属于其他账户(包括 `admin`),请求会返回 `401`。 * 绑定后,即使用户在身份提供方处改名,本地用户名也不会改变,因为服务器、分组和密钥都引用用户名。 * 管理员身份始终取自本地用户记录,JWT 中的角色声明会被忽略。 * 设置 `autoCreate: false` 时,只接受已绑定本地用户的身份。由于不会关联已有账户,这实际上会拒绝所有新身份。 转发认证配置在每次请求时读取,修改后无需重启。每个字段都可以通过环境变量覆盖,参见[环境变量](/zh/configuration/environment-variables)。 ## 身份认证方法 ### 基于环境变量的认证 使用环境变量配置基础认证: ```bash theme={null} # 基础认证凭据 AUTH_USERNAME=admin AUTH_PASSWORD=your-secure-password # JWT 设置 JWT_SECRET=your-jwt-secret-key ``` ### 数据库认证 对于生产环境部署,启用基于数据库的用户管理: ```json theme={null} { "auth": { "provider": "database", "database": { "url": "postgresql://user:pass@localhost:5432/mcphub", "userTable": "users" } } } ``` ## 用户管理 ### 创建用户 通过管理界面或 API 创建用户: ```bash theme={null} # 通过 API curl -X POST http://localhost:3000/api/auth/users \ -H "Content-Type: application/json" \ -H "Authorization: Bearer $ADMIN_TOKEN" \ -d '{ "username": "newuser", "email": "user@example.com", "password": "securepassword", "role": "user" }' ``` ### 用户角色 MCPHub 支持基于角色的访问控制: * **管理员**: 完整系统访问权限、用户管理、服务器配置 * **管理者**: 服务器管理、组管理、监控 * **用户**: 在分配组内的基本服务器访问权限 * **查看者**: 对分配资源的只读访问权限 ## 基于组的访问控制 ### 将用户分配到组 ```bash theme={null} # 添加用户到组 curl -X POST http://localhost:3000/api/groups/{groupId}/users \ -H "Authorization: Bearer $TOKEN" \ -d '{"userId": "user123"}' ``` ### 组权限 配置组级别权限: ```json theme={null} { "groupId": "dev-team", "permissions": { "servers": ["read", "write", "execute"], "tools": ["read", "execute"], "logs": ["read"], "config": ["read"] } } ``` ## API 认证 ### JWT 令牌认证 ```javascript theme={null} // 获取认证令牌 const response = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: 'your-username', password: 'your-password', }), }); const { token } = await response.json(); // 在后续请求中使用令牌 const protectedResponse = await fetch('/api/servers', { headers: { Authorization: `Bearer ${token}`, }, }); ``` ### API 密钥认证 为系统集成生成 API 密钥: ```bash theme={null} # 生成新的 API 密钥 curl -X POST http://localhost:3000/api/auth/api-keys \ -H "Authorization: Bearer $TOKEN" \ -d '{ "name": "Integration Key", "permissions": ["servers:read", "servers:write"], "expiresAt": "2024-12-31T23:59:59.000Z" }' ``` ## 安全设置 ### HTTPS 配置 为生产环境启用 HTTPS: ```nginx theme={null} server { listen 443 ssl http2; server_name mcphub.example.com; ssl_certificate /path/to/certificate.crt; ssl_certificate_key /path/to/private.key; location / { proxy_pass http://localhost:3000; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; } } ``` ### 会话安全 配置安全的会话设置: ```javascript theme={null} // 会话配置 { "session": { "secret": "your-session-secret", "secure": true, // 生产环境中需要 HTTPS "httpOnly": true, "maxAge": 86400000, // 24 小时 "sameSite": "strict" } } ``` ### 速率限制 实施 API 速率限制: ```javascript theme={null} { "rateLimit": { "windowMs": 900000, // 15 分钟 "max": 100, // 每个 IP 限制 100 个请求 "message": "请求过于频繁,请稍后再试", "standardHeaders": true, "legacyHeaders": false } } ``` ## 多因素认证 (MFA) ### 启用 TOTP 为管理员帐户启用基于时间的一次性密码: ```bash theme={null} # 启用 MFA curl -X POST http://localhost:3000/api/auth/mfa/enable \ -H "Authorization: Bearer $TOKEN" \ -d '{ "type": "totp", "appName": "MCPHub" }' ``` ### 验证 MFA 代码 ```javascript theme={null} // 登录时验证 MFA const loginResponse = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ username: 'admin', password: 'password', mfaCode: '123456', // 来自认证器应用的 6 位数字 }), }); ``` ## 审计日志 ### 启用审计日志 跟踪所有认证和授权事件: ```json theme={null} { "audit": { "enabled": true, "logLevel": "info", "events": [ "login", "logout", "password_change", "role_change", "permission_change", "server_access", "config_change" ], "storage": { "type": "database", "retention": "90d" } } } ``` ### 查看审计日志 ```bash theme={null} # 获取审计日志 curl -X GET "http://localhost:3000/api/audit/logs?startDate=2024-01-01&endDate=2024-01-31" \ -H "Authorization: Bearer $TOKEN" ``` ## 密码策略 ### 配置密码要求 ```json theme={null} { "passwordPolicy": { "minLength": 12, "requireUppercase": true, "requireLowercase": true, "requireNumbers": true, "requireSpecialChars": true, "preventCommonPasswords": true, "preventReuse": 5, // 防止重复使用最近 5 个密码 "maxAge": 7776000 // 90 天后过期 } } ``` ## 故障排除 ### 常见认证问题 1. **JWT 令牌过期** ```bash theme={null} # 检查令牌有效期 curl -X GET http://localhost:3000/api/auth/verify \ -H "Authorization: Bearer $TOKEN" ``` 2. **权限被拒绝** ```bash theme={null} # 检查用户权限 curl -X GET http://localhost:3000/api/auth/permissions \ -H "Authorization: Bearer $TOKEN" ``` 3. **会话问题** * 清除浏览器 cookies * 检查会话配置 * 验证服务器时间同步 ### 调试认证流程 启用调试日志: ```bash theme={null} # 设置环境变量 export DEBUG=auth:* export LOG_LEVEL=debug # 启动服务器 npm start ``` ## 安全最佳实践 1. **定期更新凭据**: 定期轮换 JWT 密钥和 API 密钥 2. **最小权限原则**: 只授予用户执行其任务所需的最小权限 3. **监控异常活动**: 设置警报以检测可疑的登录模式 4. **备份配置**: 定期备份认证配置和用户数据 5. **安全更新**: 保持 MCPHub 和依赖项的最新状态 更多安全配置选项,请参阅 [环境变量配置](/zh/configuration/environment-variables) 和 [Docker 设置](/zh/configuration/docker-setup) 文档。 # 命令行 CLI Source: https://docs.mcphub.app/zh/features/cli 在终端中管理运行中的 mcphub 实例,并调用 MCP 工具 ## 概述 `mcphub` 二进制同时是 CLI 入口。**不带参数**调用 `mcphub` 启动服务器(保持原有行为);**带子命令**则通过 HTTP API 操作本地或远端 hub。 ```bash theme={null} mcphub --help # 列出所有子命令 mcphub [options] # 执行子命令 mcphub # 旧行为:启动服务器(不变) ``` CLI 与服务器共用同一个 npm 包,无需额外安装。 ## 登录 ```bash theme={null} mcphub login --url http://localhost:3000 --username admin --password ******** ``` JWT 会缓存到符合 XDG 规范的凭据文件中,权限 `0600`: * macOS/Linux: `$XDG_DATA_HOME/mcphub/credentials.json`(默认 `~/.local/share/mcphub/credentials.json`) * 兼容旧路径:若 `~/.mcphub/credentials.json` 已存在则继续使用 支持**多 profile**,可以在同一终端中并行操作 `staging`、`prod` 等: ```bash theme={null} mcphub login --profile staging --url https://staging.hub.example mcphub config use prod mcphub config list ``` 也可以完全绕过凭据文件: | 覆盖方式 | 来源 | | - | - | | `--url`、`--token`、`--bearer` 参数 | 单条命令 | | `MCPHUB_URL`、`MCPHUB_TOKEN`、`MCPHUB_TOKEN_KIND` 环境变量 | shell / CI | | `credentials.json` 中的 active profile | 默认 | 解析顺序:命令行参数 → 环境变量 → active profile。 ## 全局参数 | 参数 | 用途 | | - | - | | `--url ` | 覆盖 active profile 的 URL | | `--token ` | 覆盖 active profile 的 token | | `--bearer` | 将 `--token` 当作 bearer key 使用(默认是 JWT,走 `x-auth-token`) | | `--profile ` | 选择非 current 的 profile | | `--json` | 输出 JSON 而非人类可读表格 | | `--debug` | 出错时打印堆栈 | ## 子命令 ### `servers` ```bash theme={null} mcphub servers list mcphub servers get # 从 JSON 文件添加: mcphub servers add fetch --from-file ./fetch.json # 或者内联(--arg / --env 可重复): mcphub servers add fetch \ --type stdio --command uvx \ --arg mcp-server-fetch \ --env USER_AGENT=mcphub mcphub servers remove fetch mcphub servers toggle fetch --on mcphub servers reload fetch ``` ### `groups` ```bash theme={null} mcphub groups list mcphub groups add dev --description "开发工具组" mcphub groups add-server dev fetch mcphub groups remove-server dev fetch mcphub groups remove dev ``` `` 可填 UUID 或人类可读名称,CLI 会先列表再解析成 id。 ### `keys` 管理 bearer 密钥。管理员可管理系统级和用户级密钥;普通用户可管理自己的用户级密钥。 ```bash theme={null} mcphub keys list mcphub keys create --name ci --access-type all mcphub keys create --name dev-only --access-type groups --groups dev,staging mcphub keys delete ``` `create` 创建后服务端生成的 token 只会显示一次——记得当场复制。 ### `tools` —— 发现可调用的工具 `tools` 是 `call` 的发现索引:回答**有哪些 tool、每个 tool 接受什么参数、归属哪个 server**——不用手工解析 `servers list` 的嵌套 JSON。 ```bash theme={null} # 列出所有连接中 server 的全部 tool mcphub tools list mcphub tools list --json # 机器可读,默认不带 schema mcphub tools list --schema --json # 带上每个 tool 的 inputSchema mcphub tools list --server fetch # 限定到一个 server mcphub tools list --enabled-only # 查看单个 tool 的描述、参数表、完整 schema 与样例命令 mcphub tools get fetch_url mcphub tools get fetch_url --server fetch # 同名 tool 在多个 server 上时用 --server 消歧 mcphub tools schema fetch_url # `get` 的别名 ``` `tools get` 的文本视图会先列参数表(带 `required` 标识),再打印完整 JSON schema,最后给一条可直接复制的 `mcphub call ...` 样例(必填参数已占位)。 ### `call` 调用 active profile 对应 hub 上的 MCP 工具。默认走 `/mcp/$smart`,由智能路由挑选工具。**推荐的 agent 工作流:`tools list` → `tools get` → `call`**。 ```bash theme={null} # 1. 找到一个 tool mcphub tools list --json | jq '.[] | select(.name | contains("fetch"))' # 2. 查 schema mcphub tools get fetch_url --json | jq '.inputSchema' # 3. 调用 mcphub call fetch_url url=https://example.com timeout=30 mcphub call fetch_url url=https://example.com --server fetch # 指定到具体 server mcphub call my_tool --group dev nested='{"a":1}' mcphub call my_tool --params-json '{"deep":{"v":1}}' ``` `call` 的路由优先级:`--smart` > `--server ` > `--group ` > 默认(`$smart`)。三种最终都走 `/mcp/` 同一个端点;`--server` 是和 `tools list`/`tools get` 输出天然对齐的写法。 `key=value` 强转规则: | 形式 | 结果 | | - | - | | `n=42`、`f=1.5` | 数字 | | `b=true`、`b=false` | 布尔 | | `x=null` | `null` | | `obj={"a":1}`、`arr=[1,2]` | JSON 解析 | | `payload=@./body.json` | 从文件加载 JSON | | `phone=0123` | 保留为字符串 `"0123"`(保护前导零) | | 其他 | 字符串 | `--no-coerce` 强制所有值按字符串处理。 ### `export` 下载 hub 当前的 `mcp_settings.json`。 ```bash theme={null} mcphub export --out backup.json mcphub export > backup.json ``` ### `discover` / `install`(市场) 当 hub 开启了 `systemConfig.discovery.enabled = true`,CLI 即可浏览公共市场并把 server 装到自己的 hub 或客户端配置文件里。 ```bash theme={null} # 浏览 active profile 对应的 hub mcphub discover --search github --limit 10 mcphub discover info amap-maps mcphub discover categories mcphub discover tags # 装到 active hub(POST /api/servers) mcphub install amap-maps --env AMAP_MAPS_API_KEY=... # 从另一个 hub 装到 active hub mcphub install some-server --remote https://hub.example.com --yes # 仅打印片段(dry-run) mcphub install amap-maps --dry-run # 装到 Claude Desktop / OpenClaw 风格的客户端配置文件 mcphub install amap-maps --to file --out ~/Library/Application\ Support/Claude/claude_desktop_config.json ``` 要点: * `--type` 选择安装方式(`npm`、`docker`、`uvx`、`pip`、`binary`)。不指定则由 hub 选第一个可用;指定但不存在时 CLI 会列出可用类型。 * `--env KEY=VAL`(可重复)注入环境变量——预声明的键和新增的键都会合并进最终 `env`。 * `--to file` 采用原子写(临时文件 + rename),中断不会破坏客户端配置。 ## 退出码 | 退出码 | 含义 | | - | - | | `0` | 成功 | | `1` | 用法错误(参数缺失、未登录等) | | `2` | API 错误(hub 返回非 2xx) | ## Agent 自动化范式 所有命令都支持 `--json`,并读取 `MCPHUB_URL` / `MCPHUB_TOKEN`,因此 agent 可以纯 JSON 驱动整个"发现 → 调用"流程: ```bash theme={null} export MCPHUB_URL=http://localhost:3000 export MCPHUB_TOKEN=$JWT # 1. 枚举所有 tool mcphub tools list --json > tools.json # 2. 选定一个并读 schema mcphub tools get fetch_url --json > schema.json # 3. 根据 schema 构造参数并调用 mcphub call fetch_url --params-json "$(jq -n '{url:"https://example.com"}')" --json ``` `tools list --json` 的响应是**扁平的**(`{server, serverStatus, name, description, enabled}`),agent 一次过滤就能定位目标,不必再下钻 `servers[*].tools[*]`。 ## 脚本与 CI CLI 对 JSON 友好:所有命令都支持 `--json`;`MCPHUB_URL` / `MCPHUB_TOKEN` 在 CI 中可跳过凭据文件。 ```bash theme={null} export MCPHUB_URL=https://hub.example.com export MCPHUB_TOKEN=$CI_HUB_TOKEN mcphub servers list --json | jq '.[] | select(.status != "connected") | .name' ``` 如果在 CI 中使用 bearer key,再追加 `MCPHUB_TOKEN_KIND=bearer`(或加 `--bearer`)。注意:只有 `accessType=all` 的系统级 bearer key 才能调用 `/api/*` 管理接口;受限 scope 的系统级密钥和用户级密钥仅用于 MCP transport。 # 分组管理 Source: https://docs.mcphub.app/zh/features/group-management 将服务器组织成逻辑组以简化访问控制 ## 概述 MCPHub 的分组管理功能允许您根据功能、用例或访问要求将 MCP 服务器组织成逻辑集合。这使您能够对不同的 AI 客户端和用户可用的工具进行精细控制。 ## 核心概念 ### 什么是分组? 分组是可通过专用端点访问的 MCP 服务器的命名集合。AI 客户端可以连接到特定分组以仅访问相关工具,而不是一次性连接到所有服务器。 ### 分组的优势 * **聚焦工具访问**: AI 客户端只看到与其用例相关的工具 * **更好的性能**: 减少工具发现开销 * **增强安全性**: 限制对敏感工具的访问 * **改进组织**: 功能的逻辑分离 * **简化管理**: 更容易一起管理相关服务器 ## 创建分组 ### 通过仪表板 1. **导航到分组部分**: 在主导航中点击"分组" 2. **点击"创建分组"**: 开始分组创建流程 3. **填写分组详细信息**: * **名称**: 分组的唯一标识符 4. **添加服务器**: 选择要包含在分组中的服务器 ### 批量选择能力 在分组编辑器中展开服务器,可按名称(资源使用 URI)或描述搜索工具、提示和资源。搜索采用不区分大小写的子串匹配,不支持 glob 或正则表达式。**选择可见项**、**取消可见项**和**反选可见项**仅影响当前筛选结果,保留其他选择。 工具还支持按明确的 `readOnlyHint`、`destructiveHint` 和 `idempotentHint` 注解筛选。可切换平铺列表与风险分组(读取/创建/更新/删除/其他),查看组内计数并批量选择。分类依据上游提示和名称推测,仅辅助编辑,不保证安全性。分组操作同样遵守当前筛选条件。 这些操作仅修改当前分组的选择,不改变服务器的全局启用状态。取消选择工具会清除其 Smart Routing 置顶状态。编辑具体子集时保存的是名称快照,不会自动选中未来新增工具。原有的整个能力区**全选**仍保留动态 `all` 行为。 ### 通过 API 以编程方式创建分组: ```bash theme={null} curl -X POST http://localhost:3000/api/groups \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{ "name": "web-automation", "servers": ["playwright", "fetch"] }' ``` ## 分组类型和用例 > **Web 自动化分组** > > **用途**: 浏览器自动化和网页抓取 > > **服务器**: > > * `playwright`: 浏览器自动化 > * `fetch`: HTTP 请求和网页抓取 > * `selenium`: 替代浏览器自动化 > > **用例**: > > * 自动化测试 > * 数据收集 > * 网页监控 > * 内容分析 > > **端点**: `http://localhost:3000/mcp/web-automation` > **数据处理分组** > > **用途**: 数据操作和分析 > > **服务器**: > > * `sqlite`: 数据库操作 > * `filesystem`: 文件操作 > * `spreadsheet`: Excel/CSV 处理 > > **用例**: > > * 数据分析 > * 报告生成 > * 文件处理 > * 数据库查询 > > **端点**: `http://localhost:3000/mcp/data-processing` > **通信分组** > > **用途**: 消息传递和协作 > > **服务器**: > > * `slack`: Slack 集成 > * `discord`: Discord 机器人 > * `email`: 邮件发送 > * `sms`: 短信通知 > > **用例**: > > * 团队通知 > * 客户沟通 > * 警报系统 > * 社交媒体管理 > > **端点**: `http://localhost:3000/mcp/communication` > **开发分组** > > **用途**: 软件开发工具 > > **服务器**: > > * `github`: GitHub 操作 > * `gitlab`: GitLab 集成 > * `docker`: 容器管理 > * `kubernetes`: K8s 操作 > > **用例**: > > * 代码部署 > * 仓库管理 > * CI/CD 操作 > * 基础设施管理 > > **端点**: `http://localhost:3000/mcp/development` > **AI/ML 分组** > > **用途**: 机器学习和 AI 工具 > > **服务器**: > > * `openai`: OpenAI API 集成 > * `huggingface`: Hugging Face 模型 > * `vector-db`: 向量数据库操作 > > **用例**: > > * 模型推理 > * 数据嵌入 > * 自然语言处理 > * 计算机视觉 > > **端点**: `http://localhost:3000/mcp/ai-ml` ## 分组端点 ### 访问分组 每个分组都有自己的 MCP 端点: **HTTP MCP** ``` http://localhost:3000/mcp/{group-name} ``` 示例: * `http://localhost:3000/mcp/web-tools` * `http://localhost:3000/mcp/data-processing` * `http://localhost:3000/mcp/communication` **SSE (旧版)** ``` http://localhost:3000/sse/{group-name} ``` 示例: * `http://localhost:3000/sse/web-tools` * `http://localhost:3000/sse/data-processing` * `http://localhost:3000/sse/communication` ### 分组工具发现 当连接到分组端点时,AI 客户端将只看到该分组内服务器的工具: ```bash theme={null} # 列出 web-tools 分组中的工具 curl -X POST http://localhost:3000/mcp/web-tools \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "id": 1, "method": "tools/list", "params": {} }' ``` 响应将只包含来自 `fetch` 和 `playwright` 服务器的工具。 ### 分组说明 MCP 客户端会从 `initialize` 响应中收到可选的 `instructions` 字符串,大多数客户端会把它加入模型的系统提示。这里适合告诉模型何时使用该连接,例如优先使用分组中的 fetch 工具而不是客户端内置的工具,或者某个工具失败时改用哪个工具。 通过分组的 `instructions` 字段设置(控制台、`POST`/`PUT /api/groups`,或 `mcp_settings.json` 中的分组条目)。发送规则: * `/mcp/{group}`:包含多个服务器的分组只发送分组的说明;单服务器分组先发送分组的说明,再附上该服务器自身的上游 instructions(单服务器分组原本就会转发它们)。 * `/mcp/$smart/{group}`:发送分组的说明。 单独的 `/mcp/$smart`、全局路由和按服务器名的路由不对应任何分组,因此不发送分组说明。客户端每个会话只读取一次 instructions:修改后需重新连接。 ```json theme={null} { "name": "web", "instructions": "For web pages use fetch first. If it fails (403, blocked, empty or JavaScript-only page), retry the same URL with firecrawl_scrape.", "servers": ["fetch", "firecrawl"] } ``` ## 动态分组管理 ### 向分组添加服务器 **仪表板** 1. 在仪表板中导航到分组 2. 点击"管理服务器" 3. 选择要添加的其他服务器 4. 点击"保存更改" **API** ```bash theme={null} curl -X POST http://localhost:3000/api/groups/web-tools/servers \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{"serverId": "new-server"}' ``` ### 从分组中移除服务器 **仪表板** 1. 在仪表板中导航到分组 2. 点击"管理服务器" 3. 取消选择要移除的服务器 4. 点击"保存更改" **API** ```bash theme={null} curl -X DELETE http://localhost:3000/api/groups/web-tools/servers/server-name \ -H "Authorization: Bearer YOUR_JWT_TOKEN" ``` ## 最佳实践 ### 分组组织 > [!TIP] > > **按用例组织**: 根据用户想要完成的任务来组织服务器分组,而不仅仅是技术相似性。 > [!TIP] > > **保持分组聚焦**: 避免创建包含太多不同工具的分组。更小、更聚焦的分组更有用。 > [!TIP] > > **使用描述性名称**: 选择能清楚表明分组目的和内容的名称。 ### 性能优化 > [!NOTE] > > **平衡分组大小**: 非常大的分组可能导致工具发现较慢。考虑拆分为更小的分组。 > [!NOTE] > > **监控使用情况**: 使用分析来识别哪些分组被大量使用并相应优化。 ## 故障排除 > **分组无法访问** > > **检查:** > > * 用户具有适当权限 > * 分组存在且处于活跃状态 > * 分组中的服务器正在运行 > * 网络连接 > > **解决方案:** > > 1. 验证用户分组成员身份 > 2. 检查分组配置 > 3. 测试单个服务器连接 > 4. 查看访问日志 > **分组中缺少工具** > > **可能原因:** > > * 服务器未正确添加到分组 > * 服务器未运行 > * 工具发现失败 > * 缓存问题 > > **调试步骤:** > > 1. 验证服务器在分组配置中 > 2. 检查服务器状态 > 3. 强制刷新工具发现 > 4. 清除分组缓存 > **分组性能问题** > > **常见问题:** > > * 分组中服务器过多 > * 服务器响应慢 > * 网络延迟 > * 资源约束 > > **优化方案:** > > 1. 拆分大型分组 > 2. 监控服务器性能 > 3. 实施请求缓存 > 4. 使用连接池 ## 下一步 > **[智能路由](/zh/features/smart-routing)** > > 跨分组的 AI 驱动工具发现 > **[身份认证](/zh/features/authentication)** > > 用户管理和访问控制 > **[API 参考](/zh/api-reference/groups)** > > 完整的分组管理 API > **[配置](/zh/configuration/mcp-settings)** > > 高级分组配置选项 ## 可见性与共享 新建组默认使用 `private`。`visibility` 支持 `private`(所有者和管理员)、 `group`(所有者、管理员及 `sharedWithUsers` 中指定的用户名)和 `public`(所有用户)。 界面将 `group` 显示为共享;请先保存组,再在编辑界面选择用户。 只有所有者和管理员可以修改、删除组及其服务器配置。 以用户身份访问 MCP 时,必须同时满足组和服务器的可见性要求,历史组按下文的兼容规则处理。 共享组不会赋予用户原本无权访问的服务器的访问权。SSE 和 `$smart/` 路由也遵循 这些检查,已有会话的后续请求会重新检查组权限。 用户密钥(`kind: 'user'`)继承所属用户的实时权限,不支持独立的密钥范围。 API 创建用户密钥时固定使用 `accessType: 'all'`,并清空 `allowedGroups`/`allowedServers`; 运行时这些范围字段不会限制用户密钥。这里的 `all` 指用户有权访问的资源,不代表系统级无限制访问。 系统密钥(`kind: 'system'`)按配置的操作员权限范围访问,不应用基于用户的组或服务器可见性限制。 因此,`accessType: 'all'` 的系统密钥可以访问私有组和私有服务器;限定范围的系统密钥仍受其 配置范围约束。系统密钥不会建立用户身份或提供个人凭证。 组列表、详情和服务器配置响应会隐藏调用者无权访问的服务器,包括名称、别名及能力选择。 所有者保存可见服务器列表时,已有的隐藏条目会保留;管理员可以管理完整配置。 ### 历史组升级 未设置可见性的组保留旧端点规则:知道端点的用户可以使用其中有权限的服务器, 目录仍仅对所有者和管理员可见。界面会提示设置可见性;仅保存其他字段不会将历史组 自动改为私有或公开。 数据库模式为组名添加唯一约束。启动时会在同步结构前检查重复组名,发现重复时停止升级, 报告名称和数量。请手动处理冲突后重启;系统不会自动重命名或删除组。 私有组同样占用全局组名,无法自行解决的冲突需联系管理员。 # MCP Apps Source: https://docs.mcphub.app/zh/features/mcp-apps 通过 MCPHub 路由透明代理交互式 MCP Apps,包括多服务器分组 ## 概述 MCPHub 支持将 [MCP Apps](https://github.com/modelcontextprotocol/ext-apps) 作为透明代理转发。它会转发关联 UI 的工具元数据、仅 App 可用的工具、`ui://` 资源和上游列表变更通知。MCPHub 仪表板本身不负责渲染 App UI。 无需增加 MCPHub 配置开关。下游 Host 声明 `io.modelcontextprotocol/ui` 扩展,并支持 `text/html;profile=mcp-app` MIME 类型时,该能力会自动启用。旧版客户端在初始化时声明,新版 `2026-07-28` 客户端在每次请求的 `params._meta["io.modelcontextprotocol/clientCapabilities"]` 中声明。参见 [协议兼容矩阵](/zh/api-reference/mcp-http#协议兼容矩阵)。 ## 路由要求 请求路由解析到至少一个已连接的 MCP 上游服务器时,MCP Apps 透明代理才会启用: * `/mcp/{server}` * `/mcp/{group}`,分组内可以有一个或多个对调用方可见的 MCP 服务器 * `/mcp`,且调用方至少能看到一个 MCP 服务器 `$smart` 路由仍按普通 MCP 路由工作。在该路由上,MCPHub 会: * 隐藏 `_meta.ui.visibility: ["app"]` 等仅 App 可用的工具 * 移除 Apps 专属的 `_meta.ui` 和旧版 `_meta["ui/resourceUri"]` 字段 * 拒绝直接调用仅 App 可用的工具 * 拒绝读取未列出的 `ui://` 资源 ## 工具名称 直接服务器路由(如 `/mcp/weather-station`)保留上游工具原名(如 `forecast`),不受客户端 Apps 支持情况或上游休眠状态影响。只移除 MCPHub 添加的前缀,上游工具原名自带的前缀会保留。 分组和聚合路由始终使用限定名(如 `weather-station::forecast`),即使只有一个成员。分组别名和配置的名称分隔符仍然生效。这些路由上的组件应使用工具列表或宿主上下文中的限定名。 先前公布的原始名和限定名,在路由能够无歧义解析时仍可调用,并遵守相同的工具可见性和访问限制。直接路由优先匹配上游原名;与旧限定名冲突时,以原名为准,`call_tool`、`search_tools` 和 `describe_tool` 等名称也适用。Apps 支持情况仍决定 Apps 元数据和仅 App 可用工具的可见性。 ## UI 资源 MCP Apps 服务器可以不在 `resources/list` 中列出仅供 UI 使用的资源。在符合条件的 Apps 路由上,MCPHub 支持读取未列出的 `ui://` URI: * 单服务器路由直接将请求转发给该上游。 * 多服务器路由无法预先确定未列出的 URI 属于哪个上游,因此依次探测路由内已连接的候选服务器,返回首个成功响应。 普通已列出资源仍然可以读取,但在不符合条件的路由上会移除 Apps 专属元数据。 ## 动态列表 MCPHub 会订阅上游的 `tools/list_changed`、`resources/list_changed` 和 `prompts/list_changed` 通知。收到通知后,它会刷新缓存、为模型可见工具更新 Smart Routing 向量索引,并通知已连接的下游客户端。 # 监控与可观测性 Source: https://docs.mcphub.app/zh/features/monitoring MCPHub 提供的健康检查、日志和活动追踪能力 MCPHub 提供健康检查、可选的 Prometheus 指标端点、日志和活动记录。未内置 OpenTelemetry exporter 或告警管线。 ## 健康检查端点 `GET /health` —— 公开访问,无需认证。 ```bash theme={null} curl http://localhost:3000/health ``` 响应示例: ```json theme={null} { "status": "healthy", "message": "All enabled MCP servers are ready", "servers": { "connected": 3, "connecting": 0, "disconnected": 0, "total": 3 }, "timestamp": "2026-05-18T12:34:56.789Z" } ``` `status` 的取值如下: | 值 | HTTP 状态码 | 含义 | | - | - | - | | `healthy` | `200` | 所有启用的 MCP 服务器均已连接;数据库(如果启用)状态正常。 | | `degraded` | `200` | 部分启用的 MCP 服务器尚未连接,但 MCPHub 本身正常运行。 | | `unhealthy` | `503` | 数据库模式下的数据库健康检查失败,或发生内部错误。 | 因此,`/health` 适合用于存活和就绪探针。详见 `src/controllers/healthController.ts`。 ## 进程内日志 MCPHub 维护一个滚动的进程内存日志缓冲区(`src/services/logService.ts`),供控制台的日志查看器使用。 | 方法与路径 | 说明 | | - | - | | `GET /api/logs` | 以 JSON 返回缓冲区中的日志条目。 | | `DELETE /api/logs` | 清空日志缓冲区。 | | `GET /api/logs/stream` | 以 Server-Sent Events 的方式推送新产生的日志条目。 | 日志流会为每个事件发送一行 `data:`,并包含初始的 `{ "type": "initial", "logs": [...] }` 快照。以上三个端点都要求已认证的管理员权限(控制台 JWT、`accessType: all` 的 bearer key、OAuth token 或 Better Auth session)。 该缓冲区只存在于当前进程。在多实例部署中,请将 `stdout` 发送到日志聚合器,不要依赖此端点。 ## 活动日志(数据库模式) 当 MCPHub 使用 PostgreSQL 运行时(参见[数据库配置](/zh/configuration/database-configuration)),每个 MCP 请求和响应都会由 `src/services/activityLoggingService.ts` 持久化到 `mcphub_activity` 表。控制台通过以下端点提供访问: | 方法与路径 | 说明 | | - | - | | `GET /api/activities/available` | 活动日志启用时(数据库模式)返回 `{ available: true }`。 | | `GET /api/activities` | 分页查询活动记录。查询参数:`page`、`limit`、`server`、`tool`、`status`(`success`/`error`)、`group`、`keyId`、`keyName`、`startDate`、`endDate`。 | | `GET /api/activities/stats` | 使用相同筛选条件返回聚合统计数据。 | | `GET /api/activities/filters` | 返回筛选下拉框所需的去重值。 | | `GET /api/activities/:id` | 返回包含请求输入和响应输出的完整记录。 | | `DELETE /api/activities/cleanup` | 删除早于配置保留期限的活动记录。 | 在文件模式下,这些端点返回 `404`,控制台也会隐藏活动页面。 ## 进程级指标 控制台通过 `getServerConnectionStats()` 展示基础的服务器连接统计信息,该信息也嵌入在 `/health` 响应中。如需导出更丰富的进程指标,可以: * 使用能够自动包装 `http`/`express` 的 Node.js APM agent。 * 或从监控平台抓取 `/health` 和 `/api/activities/stats`。 ## 另请参阅 * [数据库配置](/zh/configuration/database-configuration) —— 启用 PostgreSQL 以使用活动日志。 * [认证与安全](/zh/features/authentication) —— 了解管理 API 和 bearer key 认证。 ## Prometheus 指标 设置 `ENABLE_METRICS=true` 启用根路径 `GET /metrics`,不受 `BASE_PATH` 影响;默认关闭并返回 404。设置 `METRICS_TOKEN` 后,抓取请求必须携带 `Authorization: Bearer `。这是独立的监控凭据,不使用 Dashboard 或 MCP bearer key。启用后如未设置 token,端点可公开访问;多租户部署应设置 token 或限制监控网络访问。 | 指标 | 标签 | 含义 | | - | - | - | | `mcphub_servers_total` | `status`, `type` | 已启用的共享服务器定义数量(Gauge),包含 `oauth_required` 和 `openapi`。不包含个人凭据运行实例;按需服务器空闲时断开不代表故障。 | | `mcphub_tool_calls_total` | `server`, `status` | 已完成的客户端调用数,包含 Smart Routing 元工具和执行前失败。状态为 `success` 或 `error`。 | | `mcphub_tool_call_duration_seconds` | `server` | 完整调用耗时直方图,包含凭据准备、连接和重试。 | | `mcphub_active_sessions` | 无 | 有状态客户端 transport 会话数,不包含无状态 HTTP 请求或 REST 合成会话 ID。 | MCP、Dashboard 和 OpenAPI 工具 API 共用埋点。内部重试不会重复计数;异常和 `isError` 返回均视为失败。未解析到上游的调用(包括搜索、描述元工具及凭据准备失败)使用 `server="unknown"`。工具处理器执行前的 HTTP 鉴权拒绝不计入工具调用。首版不增加 group、用户、会话、工具名、原始错误标签或错误分类,也不提供 OTel exporter。 指标不依赖数据库或活动日志。每个进程独立计数,重启归零;多副本部署应分别抓取。服务器和会话 Gauge 在抓取时刷新,不触发上游连接。已删除服务器的历史时间序列保留到重启,避免持续创建不同的服务器名称。Prometheus 抓取配置示例见[英文页面](/features/monitoring)。 # Oauth Source: https://docs.mcphub.app/zh/features/oauth # OAuth 支持 ## 核心亮点 * 覆盖上游 MCP 服务器的 OAuth 2.0 授权码(PKCE)全流程。 * 支持从 `WWW-Authenticate` 响应和 RFC 8414 元数据自动发现。 * 实现动态客户端注册(RFC 7591)以及资源指示(RFC 8707)。 * 会将客户端凭据与令牌持久化到 `mcp_settings.json`,重启后直接复用。 ## MCPHub 何时启用 OAuth 1. MCPHub 调用需要授权的 MCP 服务器并收到 `401 Unauthorized`。 2. 响应通过 `WWW-Authenticate` header 暴露受保护资源的元数据(`authorization_server` 或 `as_uri`)。 3. MCPHub 自动发现授权服务器、按需注册客户端,并引导用户完成一次授权。 4. 回调处理完成后,MCPHub 使用新令牌重新连接并继续请求。 > MCPHub 会在服务器详情视图和后端日志中记录发现、注册、授权链接、换取令牌等关键步骤。 ## 按服务器类型快速上手 ### 支持动态注册的服务器 部分服务器会公开完整的 OAuth 元数据,并允许客户端动态注册。例如 Vercel 与 Linear 的 MCP 服务器只需配置 SSE 地址即可: ```json theme={null} { "mcpServers": { "vercel": { "type": "sse", "url": "https://mcp.vercel.com" }, "linear": { "type": "sse", "url": "https://mcp.linear.app/mcp" } } } ``` * MCPHub 会自动发现授权服务器、完成注册,并处理整个 PKCE 流程。 * 所有凭据与令牌会写入 `mcp_settings.json`,无须在控制台额外配置。 ### 需要手动配置客户端的服务器 另有一些服务端并不支持动态注册。GitHub 的 MCP 端点(`https://api.githubcopilot.com/mcp/`)就是典型例子,接入步骤如下: 1. 在服务提供商控制台创建 OAuth 应用(GitHub 路径为 **Settings → Developer settings → OAuth Apps**)。 2. 将回调/重定向地址设置为 `http://localhost:3000/oauth/callback`(或你的部署域名)。同时请确保 **设置 → 安装 → 基础地址** 与该域名一致,用于 MCP 请求与 OAuth 回调。 3. 复制生成的 Client ID 与 Client Secret。 4. 通过 MCPHub 控制台或直接编辑 `mcp_settings.json` 写入如下配置。 ```json theme={null} { "mcpServers": { "github": { "type": "sse", "url": "https://api.githubcopilot.com/mcp/", "oauth": { "clientId": "${GITHUB_OAUTH_APP_ID}", "clientSecret": "${GITHUB_OAUTH_APP_SECRET}", "scopes": ["replace-with-provider-scope"], "resource": "https://api.githubcopilot.com" } } } } ``` * MCPHub 会跳过动态注册,直接使用你提供的凭据完成授权流程。 * 凭据轮换时需要同步更新控制台或配置文件。 * 将 `scopes` 替换为服务端要求的具体 Scope。 ## 私有网络中的 HTTP 令牌端点 从 v1.0.42 开始,MCP SDK 会拒绝非 HTTPS 的 OAuth 令牌端点,loopback 主机(`localhost`、`127.0.0.1` 和 `::1`)除外。此检查同时作用于授权码兑换和令牌刷新,重连或重新授权无法绕过。 建议使用 HTTPS。管理员可以在上游服务器的 **OAuth 配置** 中显式启用 HTTP 令牌端点,也可以通过运维管理的配置文件设置: ```json theme={null} { "mcpServers": { "internal-gitlab": { "type": "streamable-http", "url": "http://gitlab.internal.example/api/v4/mcp", "oauth": { "allowInsecureTokenEndpoint": true } } } } ``` 此选项默认 `false`。启用后,凭据、授权码和令牌可能在传输过程中泄露,仅应在可信网络中使用。每次使用例外发送 HTTP 令牌请求时都会记录警告。普通用户不能通过创建、编辑、批量导入或模板导入启用此选项,也不能编辑已启用例外的服务器。关闭选项后,待完成的回调和已有 provider 的刷新也会被阻止。此例外不会关闭 SSRF 防护、放行其他上游,也不会改变 MCPHub 向下游客户端公布的 OAuth 端点。 SDK 客户端固定为 2.1.0,通过 pnpm 补丁增加按 provider 判断的例外接口。修补后的客户端会打包进后端构建产物,npm/npx 安装也会获得修复。升级 SDK 时需要迁移或替换补丁,并运行 `tests/integration/oauth-insecure-token-endpoint.test.ts`。 ## 配置方式 大多数场景可依赖自动检测,也可以在 `mcp_settings.json` 中显式声明 OAuth 配置。只填写确实需要的字段。 ### 自动检测(最小配置) ```json theme={null} { "mcpServers": { "secured-sse": { "type": "sse", "url": "https://mcp.example.com/sse", "oauth": { "scopes": ["mcp.tools", "mcp.prompts"], "resource": "https://mcp.example.com" } } } } ``` * MCPHub 会根据挑战头自动发现授权服务器,并引导用户完成授权。 * 令牌(包含刷新令牌)会写入磁盘,重启后自动复用。 ### 静态客户端凭据(自带 Client) ```json theme={null} { "oauth": { "clientId": "mcphub-client", "clientSecret": "replace-me-if-required", "authorizationEndpoint": "https://auth.example.com/oauth/authorize", "tokenEndpoint": "https://auth.example.com/oauth/token", "redirectUri": "http://localhost:3000/oauth/callback" } } ``` * 适用于需要手动注册客户端的授权服务器。 * `redirectUri` 会覆盖该服务器从 **设置 → 安装 → 基础地址** 推导出的回调地址。 * 如果未配置 `redirectUri`,MCPHub 会依次回退到 `${Base URL}/oauth/callback`,再回退到 `http://localhost:3000/oauth/callback`。 * 同时确保 **设置 → 安装 → 基础地址** 与部署域名一致,以保证默认回调地址正确。 ### 动态客户端注册(RFC 7591) ```json theme={null} { "oauth": { "dynamicRegistration": { "enabled": true, "issuer": "https://auth.example.com", "metadata": { "client_name": "MCPHub", "redirect_uris": [ "http://localhost:3000/oauth/callback", "https://mcphub.example.com/oauth/callback" ], "scope": "mcp.tools mcp.prompts", "grant_types": ["authorization_code", "refresh_token"] }, "initialAccessToken": "optional-token-if-required" }, "scopes": ["mcp.tools", "mcp.prompts"], "resource": "https://mcp.example.com" } } ``` * MCPHub 会通过 `issuer` 发现端点、完成注册,并持久化下发的 `client_id`/`client_secret`。 * 如果配置了 `oauth.redirectUri`,MCPHub 会优先使用它作为回调地址,并把它加入动态注册的 `redirect_uris`。 * 只有当注册端点受保护时才需要提供 `initialAccessToken`。 ## 授权流程 1. **初始化**:启动时遍历服务器配置,发现元数据并在启用 `dynamicRegistration` 时注册客户端。 2. **用户授权**:建立连接时自动打开系统浏览器,携带 PKCE 参数访问授权页。 3. **回调处理**:内置路径 `/oauth/callback` 校验 `state`、完成换取令牌,并通过 MCP SDK 保存结果。 4. **令牌生命周期**:访问令牌与刷新令牌会缓存于内存,自动刷新,并写回 `mcp_settings.json`。 ## 提示与排障 * 确保授权过程中使用的回调地址与已注册的 `redirect_uris` 完全一致。 * 若部署在 HTTPS 域名下,请对外暴露 `/oauth/callback` 或通过反向代理转发。 * 如无法完成自动发现,可显式提供 `authorizationEndpoint` 与 `tokenEndpoint`。 * 授权服务器吊销令牌后,可手动清理 `mcp_settings.json` 中的旧令牌,MCPHub 会在下一次请求时重新触发授权。 # 个人凭据 Source: https://docs.mcphub.app/zh/features/per-user-credentials 一个共享 MCP 服务器定义,为每位用户绑定独立凭据 管理员或服务器所有者声明个人凭据字段,用户在**凭据**页面或服务器卡片入口绑定自己的值。共享用户可以查看字段名和显示名称,无法读取服务器连接配置,也无法读取其他用户的绑定。保存后的值无法回读,只能替换或删除。 ## 部署配置 单实例部署可以不设置 `MCPHUB_CREDENTIAL_ENCRYPTION_KEY`。首次保存绑定时,MCPHub 会生成 32 字节随机密钥,以 `0600` 权限原子创建配置文件旁的 `.credentials.key`(未指定配置路径时使用默认配置文件旁的路径),重启后继续读取同一文件。密钥文件不进入配置导出、Git 或 Docker 构建上下文。配置目录必须可写;持久化失败时会拒绝保存,不使用临时密钥。 显式设置的环境变量优先,值必须是 32 字节随机数据的 Base64 编码,可用 `openssl rand -base64 32` 生成。环境变量或密钥文件格式无效时直接报错,不会自动覆盖。若已有加密绑定但密钥文件丢失,必须恢复原文件或原环境密钥,系统不会生成新密钥导致旧绑定无法解密。 请单独备份密钥;Docker 部署需持久化配置目录,仅挂载 JSON 配置文件不会保留旁边的密钥文件。共享数据库的多实例必须设置相同的环境密钥,或读取同一个共享密钥文件,不能各自独立生成。不要将密钥写入服务器定义;子进程不会继承该环境变量。 绑定采用 AES-256-GCM 加密,并将服务器名、用户名作为认证附加数据。JSON 模式保存到 `.credentials.json`(默认路径则位于默认配置文件旁),权限为 `0600`,写入采用原子替换;数据库模式保存到 `credential_bindings` 表。常规配置导出不包含绑定。从文件迁移到数据库时复制密文,需继续使用原密钥。JSON 模式适用于单进程写入,多进程部署应使用数据库模式。 ## 声明字段 stdio 使用 `env` 或 `args`;SSE、Streamable HTTP 和 OpenAPI 使用 `headers`。每个字段包含 `target`、`name` 和可选 `label`,URL 槽还需提供 `allowed` 列表,最多 32 个字段。所有字段均为必填,不会回退到组织凭据。凭据按字面值注入,不展开环境变量;HTTP 头需填写完整值,例如包含 `Bearer ` 前缀。个人头优先于静态头、透传头和 OpenAPI 工具参数头,不能与共享上游 OAuth 配置组合使用。 ```json theme={null} { "mcpServers": { "tavily": { "type": "stdio", "command": "npx", "args": ["-y", "tavily-mcp"], "owner": "admin", "visibility": "public", "idleTimeoutMs": 300000, "credentialTemplate": [ { "target": "env", "name": "TAVILY_API_KEY", "label": "Tavily API key" } ] }, "context7": { "type": "stdio", "command": "npx", "args": ["-y", "@upstash/context7-mcp"], "owner": "admin", "visibility": "public", "credentialTemplate": [ { "target": "env", "name": "CONTEXT7_API_KEY", "label": "Context7 API key" } ] } } } ``` 环境变量来自 [Tavily 官方说明](https://github.com/tavily-ai/tavily-mcp)和 [Context7 开发指南](https://github.com/upstash/context7/blob/master/docs/resources/developer.mdx)。迁移时移除命令行中的旧凭据:Context7 的 `--api-key` 参数优先于环境变量。 HTTP 服务器可声明 `{"target":"headers","name":"Authorization"}`。 ## 仅支持命令行参数的凭据 对于只接受命令行参数凭据的 CLI,声明 `args` 字段,并在参数中使用 `{{args.API_KEY}}`。将 `API_KEY` 替换为字段名(区分大小写,使用字母、数字或下划线,以字母或下划线开头)。在仪表板中选择“参数”作为凭据目标,每个参数使用一个独立输入框。 ```json theme={null} { "type": "stdio", "command": "npx", "args": ["-y", "some-mcp", "--api-key", "{{args.API_KEY}}"], "credentialTemplate": [ { "target": "args", "name": "API_KEY", "label": "API key" } ] } ``` 通过凭据 API 使用 `args.API_KEY` 绑定值。占位符可以重复使用,也可以嵌入 `--api-key={{args.API_KEY}}` 这样的参数;每次都使用同一个个人值。所有 args 字段必须被引用,未知或格式错误的引用会被拒绝。env 和 args 字段可使用同一个名称,目标类型会区分它们。 MCPHub 在启动子进程时、普通环境变量展开和代理参数清洗完成后替换个人值。值中的空格、引号、`$` 和类似占位符的文本按原样传入,不会递归展开,无需 shell 包装。不要把个人占位符放入 shell 脚本或其他可执行代码中,否则解释器会执行替换后的文本。即使 hub 日志和公开配置响应不含凭据,操作系统进程检查工具仍可能看到参数中的凭据。个人上游 OAuth 使用下文介绍的独立身份模式。 已有 POSIX shell 包装仍可使用。env 字段必须使用 `"$my_api_key"` 这样的无花括号小写引用:`$MY_API_KEY` 和 `${my_api_key}` 可能在注入个人凭据之前被展开并清空。使用 `sh -c` 时,在仪表板中将 `-c` 和完整脚本分别放入两个参数输入框。 ## 管理 API 使用仪表板 JWT(`x-auth-token`)、OIDC/session 登录。用户级 MCP bearer key 仅用于 MCP 调用;系统 bearer key 和免登录模式不能管理个人绑定。接口不接受目标用户名。 | 方法 | 路径 | 行为 | | - | - | - | | GET | `/api/credentials` | 返回可见服务器的字段元数据、`configured`、`configuredSlots`、`updatedAt`,不返回值 | | PUT | `/api/credentials/:name` | 替换当前用户的完整绑定 | | DELETE | `/api/credentials/:name` | 删除当前用户的绑定 | PUT 请求示例: ```json theme={null} { "values": { "env.TAVILY_API_KEY": "your-personal-api-key" } } ``` 替换时需要填写全部字段,未声明字段会被拒绝。重新打开页面时输入框保持空白。删除用户或服务器会删除对应绑定;重命名服务器会保留绑定:每个绑定都会以新名称重新加密。若同一次编辑还修改了连接相关字段(URL、命令、请求头、凭据字段等),则会删除绑定,用户需重新绑定。若某个绑定无法解密(例如加密密钥缺失),重命名会失败且不做任何更改:请恢复密钥,或由该用户替换或删除该绑定后再重命名。新增必填字段后也需重新填写完整绑定。 ## 运行方式与验证 每次请求读取最新绑定,身份来自当前登录用户或用户级 bearer key 的实际所有者,URL 或请求体中的用户名不参与凭据选择。 声明个人字段的服务器在首次发现或调用时启动。stdio 按 `(服务器, 用户)` 隔离子进程,同一用户跨 MCP 会话复用,优先于 `perSessionClient`。子进程不作为独立服务器展示。所有活动请求结束后开始空闲计时,`idleTimeoutMs` 默认为五分钟;长调用期间不会回收。修改或删除绑定立即使本机对应运行实例失效,多进程部署中的其他实例在下次请求时读取最新绑定。 HTTP 客户端也按用户隔离,绑定改变时重新创建。个人工具、提示词和资源仅保存在个人运行实例中,不进入共享向量索引;普通路由可直接调用,向量搜索不索引个人目录。个人上游连接错误详情和 stderr 不对外输出,避免其中包含凭据。未声明个人字段的服务器保留原有行为。 预发布验证应使用不同的 Alice/Bob 真实密钥,并发调用 Tavily/Context7,再替换、删除 Alice 的绑定,确认 Bob 不受影响,同时验证空闲回收和邮箱形式的 OIDC 用户名。自动集成测试使用真实本机 stdio、HTTP、OpenAPI 测试服务器;真实 Tavily/Context7 API 联调需要部署方的测试凭据。 ## 个人上游 OAuth 对于需要 OAuth 的 HTTP MCP 服务,配置 `oauth.identityMode: "personal"`,或在 Dashboard 中选择「每位用户独立授权」。未设置此字段时保留共享 OAuth 行为。个人 OAuth 与手动凭据 slots 分开使用;个人模式的服务声明不能携带共享 token 或待授权状态。 ```json theme={null} { "type": "streamable-http", "url": "https://gitlab.example/api/v4/mcp", "owner": "admin", "visibility": "public", "oauth": { "identityMode": "personal", "scopes": ["mcp"] } } ``` 用户在「服务器」卡片中连接自己的账号。用户关联的 Bearer key 使用其拥有者身份,包括 `/mcp/` 聚合入口;共享/系统 key 和匿名调用不支持个人 OAuth。未授权时返回「Personal authorization required」,不会回退到共享 token。 MCP SDK 自动发现 OAuth 端点,并为用户绑定动态注册客户端。上游禁用动态注册时,配置预注册的非机密 `oauth.clientId` 和回调 URI。自建 HTTP 环境继续使用管理员控制的 `allowInsecureTokenEndpoint` 例外;默认使用 HTTPS。 token、客户端注册和 PKCE/state 加密保存到现有个人绑定存储,需备份前文所述加密密钥。待授权流程十分钟过期,期间可在重启后恢复,仅可使用一次;重新发起会替代旧流程。修改服务器定义会使原有个人授权失效,需要重新连接。 断开后立即删除本地授权并使当前用户的运行实例失效,不影响其他用户;知道撤销端点时尝试撤销上游 token。撤销失败不会恢复本地绑定,旧刷新或回调结果也不能重新写回。文件模式要求同一设置目录由单个 hub 进程管理;数据库模式使用条件写入拒绝过期版本。 以下接口仅供 Dashboard 登录身份使用,始终操作当前用户: | 方法 | 路径 | 行为 | | - | - | - | | GET | `/api/servers/:name/oauth/personal` | 返回自己的授权状态和待授权 URL。 | | POST | `/api/servers/:name/oauth/personal` | 发起或重新发起个人授权,返回授权 URL。 | | DELETE | `/api/servers/:name/oauth/personal` | 断开个人绑定,并尝试撤销上游 token。 | 共用的 `/oauth/callback` 根据已保存的随机 state 确认流程归属,不能通过请求参数选择其他用户。 ## 个人 HTTP 端点 SSE 和 Streamable HTTP 支持 URL 凭据槽,用同一服务器定义连接不同区域或租户: ```json theme={null} { "type": "streamable-http", "url": "https://mcp.{{url.region}}.example.com/mcp", "credentialTemplate": [ { "target": "url", "name": "region", "label": "区域", "allowed": ["us", "eu"] }, { "target": "headers", "name": "X-Api-Key" } ] } ``` 管理员在凭据槽中选择 **URL**,每行填写一个允许值。用户在凭据页面选择,或通过 API 将 `url.region` 与其他槽一起绑定。URL 槽不能与 OAuth 同时使用。 占位符必须占满一个主机标签或路径段,不能用于协议、端口、用户信息、查询参数或片段。主机标签仅支持 ASCII 字母、数字和内部连字符,最多 63 个字符;路径段支持 ASCII 字母、数字、`_`、`-`、`.`、`~`,但不能是 `.` 或 `..`。不接受编码或自由输入。每个 URL 槽都必须被引用;允许列表包含 1–256 个不重复的值,每个值最多 256 个字符。 保存绑定及每次获取用户运行实例时都会校验当前允许列表。移除选项后,旧绑定显示为未配置,下次请求会被拒绝,即使已有缓存连接;已在执行中的调用不会被追溯取消。带 URL 槽的服务器始终启用 SSRF 防护,包括管理员拥有的服务器;连接和重定向仍遵守 `network.allowedCidrs`。 # 服务器管理 Source: https://docs.mcphub.app/zh/features/server-management 通过热插拔配置集中管理多个 MCP 服务器 ## 概述 MCPHub 的服务器管理系统允许您从单个仪表板集中配置、监控和控制多个 MCP(模型上下文协议)服务器。所有更改都会实时应用,无需重启服务器。 服务器列表在传输类型下方显示实际协商的 MCP 协议版本,并与服务器软件版本分开展示。尚未连接或已禁用的服务器显示“未获取”;OpenAPI 服务器不显示 MCP 协议版本。 ## 添加 MCP 服务器 ### 通过仪表板 1. **访问仪表板**: 导航到 `http://localhost:3000` 并登录 2. **点击"添加服务器"**: 位于服务器部分 3. **填写服务器详细信息**: * **名称**: 服务器的唯一标识符 * **命令**: 可执行命令(例如 `npx`、`uvx`、`python`) * **参数**: 命令参数数组 * **环境变量**: 环境设置的键值对 * **工作目录**: 命令的可选工作目录 服务器卡片菜单还提供 **Duplicate** 选项,会以 `-copy` 作为预填名称、携带源服务器的配置打开 Add Server 表单;OAuth 授权状态不会被复制。 ### 通过配置文件 编辑您的 `mcp_settings.json` 文件: ```json theme={null} { "mcpServers": { "server-name": { "command": "command-to-run", "args": ["arg1", "arg2"], "env": { "API_KEY": "your-api-key", "CONFIG_VALUE": "some-value" }, "cwd": "/optional/working/directory" } } } ``` ### 通过 API 使用 REST API 以编程方式添加服务器: ```bash theme={null} curl -X POST http://localhost:3000/api/servers \ -H "Content-Type: application/json" \ -H "Authorization: Bearer YOUR_JWT_TOKEN" \ -d '{ "name": "fetch-server", "command": "uvx", "args": ["mcp-server-fetch"], "env": {} }' ``` ## 流行的 MCP 服务器示例 > **Web 抓取服务器** > > 提供网页抓取和 HTTP 请求功能: > > ```json theme={null} > { > "fetch": { > "command": "uvx", > "args": ["mcp-server-fetch"] > } > } > ``` > > **可用工具:** > > * `fetch`: 发起 HTTP 请求 > * `fetch_html`: 抓取网页 > * `fetch_json`: 从 API 获取 JSON 数据 > **Playwright 浏览器自动化** > > 用于网页交互的浏览器自动化: > > ```json theme={null} > { > "playwright": { > "command": "npx", > "args": ["@playwright/mcp@latest", "--headless"] > } > } > ``` > > **可用工具:** > > * `playwright_navigate`: 导航到网页 > * `playwright_screenshot`: 截取屏幕截图 > * `playwright_click`: 点击元素 > * `playwright_fill`: 填写表单 > **文件系统操作** > > 文件和目录管理: > > ```json theme={null} > { > "filesystem": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-filesystem", "/allowed/path"] > } > } > ``` > > **可用工具:** > > * `read_file`: 读取文件内容 > * `write_file`: 写入文件 > * `create_directory`: 创建目录 > * `list_directory`: 列出目录内容 > **SQLite 数据库** > > 数据库操作: > > ```json theme={null} > { > "sqlite": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"] > } > } > ``` > > **可用工具:** > > * `execute_query`: 执行 SQL 查询 > * `describe_tables`: 获取表结构 > * `create_table`: 创建新表 > **Slack 集成** > > Slack 工作空间集成: > > ```json theme={null} > { > "slack": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-slack"], > "env": { > "SLACK_BOT_TOKEN": "xoxb-your-bot-token", > "SLACK_TEAM_ID": "T1234567890" > } > } > } > ``` > > **可用工具:** > > * `send_slack_message`: 发送消息到频道 > * `list_slack_channels`: 列出可用频道 > * `get_slack_thread`: 获取线程消息 > **GitHub 集成** > > GitHub 仓库操作: > > ```json theme={null} > { > "github": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-github"], > "env": { > "GITHUB_PERSONAL_ACCESS_TOKEN": "ghp_your_token" > } > } > } > ``` > > **可用工具:** > > * `create_or_update_file`: 创建/更新仓库文件 > * `search_repositories`: 搜索 GitHub 仓库 > * `create_issue`: 创建问题 > * `create_pull_request`: 创建拉取请求 > **Google Drive** > > Google Drive 文件操作: > > ```json theme={null} > { > "gdrive": { > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-gdrive"], > "env": { > "GDRIVE_CLIENT_ID": "your-client-id", > "GDRIVE_CLIENT_SECRET": "your-client-secret", > "GDRIVE_REDIRECT_URI": "your-redirect-uri" > } > } > } > ``` > > **可用工具:** > > * `gdrive_search`: 搜索文件和文件夹 > * `gdrive_read`: 读取文件内容 > * `gdrive_create`: 创建新文件 > **高德地图(中国)** > > 中国地图和位置服务: > > ```json theme={null} > { > "amap": { > "command": "npx", > "args": ["-y", "@amap/amap-maps-mcp-server"], > "env": { > "AMAP_MAPS_API_KEY": "your-api-key" > } > } > } > ``` > > **可用工具:** > > * `search_location`: 搜索位置 > * `get_directions`: 获取路线指引 > * `reverse_geocode`: 将坐标转换为地址 ## 复制客户端配置 服务器卡片和分组卡片都提供复制操作,会打开客户端预设弹窗,方便你将现成的配置粘贴到常用的 MCP 客户端中: * **Copy MCP config**(分组卡片菜单):为分组聚合端点(`/mcp/`)生成配置,分组下所有服务器共用一个 URL。 * **Copy**(服务器卡片菜单):将该服务器自身的存储配置(stdio 服务器为 `command`/`args`/`env`,HTTP/SSE 服务器为 `url`/`headers`)包装为所选客户端所需的形态。 使用 `openapi` 传输的服务器没有自身的客户端传输,弹窗会显示"无传输"提示而非预设,这是预期行为,并非出错。使用 `sse` 传输的服务器会在生成的 JSON 中保留 `"type": "sse"` 标记(Claude Code 命令也会切换为 `--transport sse`),避免客户端将其误当作 streamable HTTP。 弹窗为以下客户端提供预设:Generic (mcpServers)、Generic HTTP、Claude Code、Cursor、VS Code / Copilot、Codex、OpenCode、Windsurf、Cherry Studio、CodeBuddy Code、Qoder、TRAE、ZCode 和 WorkBuddy。 **Claude Code** 标签页还会额外提供一行 CLI 命令: ```bash theme={null} claude mcp add --transport http --header 'Authorization: Bearer ' ``` 取值中只要包含安全 CLI 字符集之外的字符就会被单引号包裹(如上例中的 header),因此可直接原样粘贴到 POSIX shell;在不识别单引号的 Windows shell 中,请自行调整引号。 部分客户端在生成的配置中使用自己的键名:Codex 使用 `http_headers`(没有 `headers` 键),VS Code 使用 `servers` 加 `type` 字段,Cherry Studio 使用 `baseUrl` 并将 `type` 设为 `streamableHttp`——上游为 SSE 端点时为 `sse`。 ## 服务器生命周期管理 ### 启动服务器 服务器会在以下情况下自动启动: * MCPHub 启动时 * 通过仪表板或 API 添加服务器时 * 服务器配置更新时 * 手动重启已停止的服务器时 ### 停止服务器 您可以通过以下方式停止服务器: * **通过仪表板**: 切换服务器状态开关 * **通过 API**: 发送 POST 请求到 `/api/servers/{name}/toggle` * **自动停止**: 服务器崩溃或遇到错误时会自动停止 ### 重启服务器 服务器会在以下情况下自动重启: * 配置更改时 * 环境变量更新后 * 通过仪表板或 API 手动触发时 ## 服务器状态监控 ### 状态指示器 每个服务器都显示状态指示器: * 🟢 **运行中**: 服务器处于活动状态并响应 * 🟡 **启动中**: 服务器正在初始化 * 🔴 **已停止**: 服务器未运行 * ⚠️ **错误**: 服务器遇到错误 ### 实时日志 实时查看服务器日志: 1. **仪表板日志**: 点击服务器查看其日志 2. **API 日志**: 通过 `/api/logs` 端点访问日志 3. **流式日志**: 通过 WebSocket 订阅日志流 ### 健康检查 MCPHub 自动执行健康检查: * **初始化检查**: 验证服务器成功启动 * **工具发现**: 确认检测到可用工具 * **响应检查**: 测试服务器响应性 * **资源监控**: 跟踪 CPU 和内存使用情况 ## 配置管理 ### 环境变量 服务器可以使用环境变量进行配置: ```json theme={null} { "server-name": { "command": "python", "args": ["server.py"], "env": { "API_KEY": "${YOUR_API_KEY}", "DEBUG": "true", "MAX_CONNECTIONS": "10" } } } ``` **环境变量展开:** * `${VAR_NAME}`: 展开为环境变量值 * `${VAR_NAME:-default}`: 如果变量未设置则使用默认值 * `${VAR_NAME:+value}`: 如果变量已设置则使用指定值 ### 命令变体 指定服务器命令的不同方式: **npm/npx** ```json theme={null} { "npm-server": { "command": "npx", "args": ["-y", "package-name", "--option", "value"] } } ``` **Python/uvx** ```json theme={null} { "python-server": { "command": "uvx", "args": ["package-name", "--config", "config.json"] } } ``` ## 高级功能 ### 热重载 MCPHub 支持服务器配置的热重载: 1. **仪表板更新**: 立即应用通过 Web 界面进行的更改 2. **API 更新**: 通过 REST API 调用进行实时更新 3. **零停机时间**: 优雅的服务器重启,不影响其他服务器 ## 故障排除 > **服务器无法启动** > > **检查以下项目:** > > * 命令在 PATH 中可用 > * 已设置所有必需的环境变量 > * 工作目录存在且可访问 > * 网络端口未被阻塞 > * 依赖项已安装 > > **调试步骤:** > > 1. 在仪表板中检查服务器日志 > 2. 在终端中手动测试命令 > 3. 验证环境变量展开 > 4. 检查文件权限 > **服务器持续崩溃** > > **常见原因:** > > * 无效的配置参数 > * 缺少 API 密钥或凭据 > * 超出资源限制 > * 依赖项冲突 > > **解决方案:** > > 1. 查看服务器日志中的错误消息 > 2. 使用最小配置进行测试 > 3. 验证所有凭据和 API 密钥 > 4. 检查系统资源可用性 > **工具未显示** > > **可能的问题:** > > * 服务器未完全初始化 > * 工具发现超时 > * 通信协议不匹配 > * 服务器报告错误 > > **调试步骤:** > > 1. 等待服务器初始化完成 > 2. 检查服务器日志中的工具注册消息 > 3. 测试与服务器的直接通信 > 4. 验证 MCP 协议兼容性 ## 下一步 > **[分组管理](/zh/features/group-management)** > > 将服务器组织成逻辑分组 > **[智能路由](/zh/features/smart-routing)** > > 设置 AI 驱动的工具发现 > **[API 参考](/zh/api-reference/servers)** > > 服务器管理 API 文档 > **[配置指南](/zh/configuration/mcp-settings)** > > 详细配置选项 # 智能路由 Source: https://docs.mcphub.app/zh/features/smart-routing 使用向量语义搜索的 AI 工具发现系统 ## 概述 智能路由是 MCPHub 的智能工具发现系统,它使用向量语义搜索来自动找到与任何给定任务最相关的工具。AI 客户端无需手动指定使用哪些工具,只需描述他们想要完成的任务,智能路由就会识别并提供对最合适工具的访问。 ## 智能路由的工作原理 ### 1. 工具索引 当服务器启动时,智能路由会自动: * 从 MCP 服务器发现所有可用工具 * 提取工具元数据(名称、描述、参数) * 将工具信息转换为向量嵌入 * 使用 pgvector 将嵌入存储在 PostgreSQL 中 ### 2. 语义搜索 当进行查询时: * 用户查询被转换为向量嵌入 * 相似性搜索使用余弦相似度找到匹配的工具 * 动态阈值过滤掉不相关的结果 * 结果按相关性得分排序 ### 3. 智能过滤 智能路由应用多个过滤器: * **相关性阈值**:只返回高于相似性阈值的工具 * **上下文感知**:考虑对话上下文 * **工具可用性**:确保工具当前可访问 * **权限过滤**:尊重用户访问权限 ### 4. 工具执行 找到的工具可以直接执行: * 参数验证确保正确的工具使用 * 错误处理提供有用的反馈 * 响应格式保持一致性 * 日志记录跟踪工具使用情况进行分析 ## 前置条件 智能路由需要比基础 MCPHub 使用更多的设置: ### 必需组件 1. **带有 pgvector 的 PostgreSQL**:用于嵌入存储的向量数据库 2. **嵌入服务**:OpenAI API 或兼容服务 3. **环境配置**:正确的配置变量 ## 使用智能路由 ### 智能路由端点 通过特殊的 `$smart` 端点访问智能路由: **HTTP MCP** ``` # 搜索所有服务器 http://localhost:3000/mcp/$smart # 在特定分组内搜索 http://localhost:3000/mcp/$smart/{group} ``` **SSE (Legacy)** ``` # 搜索所有服务器 http://localhost:3000/sse/$smart # 在特定分组内搜索 http://localhost:3000/sse/$smart/{group} ``` ### 分组范围的智能路由 智能路由支持分组范围的搜索,允许您将工具发现限制在特定分组内的服务器: > **使用分组范围的智能路由** > > 将您的 AI 客户端连接到特定分组的智能路由端点: > > ``` > http://localhost:3000/mcp/$smart/production > ``` > > 此端点只会搜索属于 "production" 分组的服务器中的工具。 > > **优势:** > > * **聚焦结果**:只返回相关服务器的工具 > * **更好的性能**:减少搜索空间以加快查询速度 > * **环境隔离**:将开发、预发布和生产工具分开 > * **访问控制**:根据用户权限限制工具发现 > **工作原理** > > 当使用 `$smart/{group}` 时: > > 1. 系统识别指定的分组 > 2. 获取属于该分组的所有服务器 > 3. 将工具搜索过滤到仅限那些服务器 > 4. 返回限定在该分组服务器范围内的结果 > > 如果分组不存在或没有服务器,搜索将不返回任何结果。 ### 固定工具 `$smart/{group}` 端点只列出元工具,因此即使是模型几乎每次会话都会调用的工具,也要先经过一次 `search_tools` 往返。在分组的服务器条目上固定这些工具后,端点会在元工具旁列出它们的完整定义: ```json theme={null} { "name": "production", "servers": [{ "name": "time", "tools": "all", "pinnedTools": ["get_current_time"] }] } ``` 在控制台中编辑分组,展开服务器,然后点击已选工具旁的图钉图标。仅在启用智能路由时显示该图标。 * 固定使用不带前缀的工具名,与 `tools` 选择相同。只有分组已选择且已启用的工具才会生效,其余固定会被忽略。 * 固定工具的列出方式与直接的 `/mcp/{group}` 端点完全相同(使用服务器的对外名称和自定义描述),并可直接按该名称调用。它仍保留在搜索索引中,`search_tools`、`describe_tool` 和 `call_tool` 照常可用。 * 全局 `$smart` 端点没有分组,从不列出固定工具。没有固定工具的分组与之前一样只列出元工具。 * 如果固定工具列出的名称与元工具名称(`search_tools`、`describe_tool`、`call_tool`,例如别名 `describe` + 分隔符 `_` + 工具 `tool`)相同,则在任何模式下都不会列出:对这些名称的调用始终交给元工具处理。 * 分组显示的智能路由占用包含其固定工具的定义成本。 * 在控制台中或通过 `PUT /api/groups/:id/server-configs/:serverName/tools` 取消选择某个工具时,也会移除它的固定。 * 在 `$smart/{group}` 上,对声明支持 MCP Apps 的客户端,固定工具会保留其 MCP Apps 元数据(`_meta.ui`),因此其小部件可以渲染,该服务器的 `ui://` 资源和仅限 Apps 的辅助工具也可从小部件访问;不支持 MCP Apps 的客户端仍会去除这些元数据。只有固定工具才带有小部件:通过 `search_tools` 和 `call_tool` 调用的工具没有可供宿主关联小部件的独立描述,因此请固定需要小部件的工具。全局 `$smart` 端点始终不是 MCP Apps 路由。 > [!WARNING] > > 通过 `call_tool` 调用时,所有工具共享客户端的同一个审批决定。固定工具以自己的名称列出,逐个审批工具的客户端会对其应用单独的规则——固定会写入数据的工具前请先确认这一点。 ### 渐进式披露模式 渐进式披露是一个优化功能,可以减少使用智能路由时的 Token 消耗。启用后,工具发现工作流从 2 步变为 3 步。 > **什么是渐进式披露?** > > 默认情况下,智能路由在 `search_tools` 结果中返回完整的工具信息,包括完整的参数模式。当处理具有复杂输入模式的工具时,这会消耗大量 Token。 > > **渐进式披露** 改变了这种行为: > > * `search_tools` 只返回工具名称和描述(最少信息) > * 新的 `describe_tool` 端点按需提供完整的参数模式 > * `call_tool` 像以前一样执行工具 > > 这种方法特别适用于: > > * 处理具有复杂模式的大量工具 > * Token 使用优化很重要的场景 > * AI 客户端需要浏览多个工具后再选择 > **启用渐进式披露** > > 通过设置页面或环境变量启用渐进式披露: > > **通过设置界面:** > > 1. 导航到 设置 → 智能路由 > 2. 启用"渐进式披露"开关 > 3. 更改立即生效 > > **通过环境变量:** > > ```bash theme={null} > SMART_ROUTING_PROGRESSIVE_DISCLOSURE=true > ``` > **在 search\_tools 中包含服务器描述** > > 默认情况下,`search_tools` 只会在工具描述中列出服务器名称。若要同时显示服务器描述(或上游返回的 instructions),请设置: > > ```bash theme={null} > SMART_ROUTING_SERVER_DESCRIPTION_MODE=full > ``` > > 使用 `names`(默认值)可保持原来的紧凑列表。 > > 该列表列出 `search_tools` 实际搜索的服务器:已启用且已连接或按需启动的服务器。按需启动的服务器未连接时仍会列出,无论是因空闲而关闭还是启动失败,因为 `call_tool` 会启动它;其他连接失败的服务器在重新连接之前不会列出。上游 instructions 只在连接时获得,因此在 `full` 模式下,尚未连接过的服务器只显示其配置的描述,或仅显示名称。 > **嵌入任务前缀** > > 非对称检索模型在训练时对查询和文档使用不同的任务前缀,直接嵌入原始文本时效果明显变差。可在 设置 → 智能路由 中或通过环境变量设置模型所需的前缀: > > ```bash theme={null} > # EmbeddingGemma > SMART_ROUTING_EMBEDDING_QUERY_PREFIX="task: search result | query: " > SMART_ROUTING_EMBEDDING_DOCUMENT_PREFIX="title: none | text: " > ``` > > 两者默认均为空,即与以前一样嵌入原始文本。前缀按原样使用,请保留末尾空格。修改文档前缀会重新生成所有服务器的工具嵌入;修改查询前缀在下一次搜索时生效。 > > 前缀会改变相似度分数(以 EmbeddingGemma 为例,弱匹配下降得比强匹配更多),因此基于原始文本调好的阈值在启用前缀后可能需要重新校准(参见下方的 **搜索结果**;每个结果都会返回 `score`)。前缀也计入嵌入的 Token 上限(`EMBEDDING_MAX_TOKENS` 或模型默认值):前缀会被保留,工具文本会相应提前截断。 > **搜索结果:分数、阈值与 Schema 预算** > > 每个 `search_tools` 结果都带有 `score`:查询与工具之间的余弦相似度(0-1,保留两位小数),也就是与阈值比较的值。结果按相关性排序,排序时还会考虑工具所在服务器与查询的匹配程度。 > > ```bash theme={null} > # 结果的最低相似度(0-1)。未设置时按查询长度决定: > # 短查询 0.2,长查询或包含 "specific"/"exact" 的查询 0.4,其余 0.3。 > SMART_ROUTING_SIMILARITY_THRESHOLD=0.45 > > # 只有前 N 个结果带完整定义(inputSchema 等);其余结果只有名称、描述和分数, > # 并会列出 describe_tool 以获取其 schema。未设置时所有结果都返回完整定义。启用渐进式披露时忽略。 > SMART_ROUTING_FULL_SCHEMA_TOP_N=3 > > # 完整结果(以及 describe_tool)在名称、描述和 inputSchema 之外附带的可选工具字段: > # title、annotations、outputSchema、execution、icons、_meta。"none" 表示都不附带。默认:title,annotations。 > SMART_ROUTING_TOOL_DEFINITION_FIELDS=title,annotations > ``` > > 三者也可以在 设置 → 智能路由 → 搜索结果 中设置,且都不会触发重新嵌入。默认不附带的字段主要供客户端而非模型使用,而且可能很大:上游服务器可能为每个工具附带 base64 格式的 `icons` 或很长的 `outputSchema`,放在搜索结果里只会消耗 Token。`inputSchema` 始终返回,并去掉其中的 `$schema` 键。当没有工具达到阈值时,响应会明确说明,给出阈值并列出已搜索的服务器,让模型可以告诉用户该能力不可用,而不是去调用最接近但无关的工具。 > **标准模式(默认)** > > 当渐进式披露**禁用**(默认)时,智能路由提供两个工具: > > **工作流:** `search_tools` → `call_tool` > > | 工具 | 用途 | > | - | - | > | `search_tools` | 按查询查找工具,返回包含 `inputSchema` 的完整工具信息 | > | `call_tool` | 使用提供的参数执行工具 | > > 这种模式更简单,但由于搜索结果中包含完整模式,会使用更多 Token。`SMART_ROUTING_FULL_SCHEMA_TOP_N` 可以限制这部分开销:只有前 N 个结果带 schema,其余结果通过列出的 `describe_tool` 获取。 > **渐进式披露模式** > > 当渐进式披露**启用**时,智能路由提供三个工具: > > **工作流:** `search_tools` → `describe_tool` → `call_tool` > > | 工具 | 用途 | > | - | - | > | `search_tools` | 按查询查找工具,只返回名称和描述 | > | `describe_tool` | 获取特定工具的完整模式(新增) | > | `call_tool` | 使用提供的参数执行工具 | > > **示例工作流:** > > 1. AI 使用查询 "文件操作" 调用 `search_tools` > 2. 结果显示工具名称和描述(最少 Token) > 3. AI 为特定工具调用 `describe_tool` 获取完整的 `inputSchema` > 4. AI 使用正确的参数调用 `call_tool` > > 这种模式通过仅在需要时获取完整模式来减少 Token 使用。 > **响应格式对比** > > **标准模式 search\_tools 响应:** > > ```json theme={null} > { > "tools": [ > { > "name": "read_file", > "description": "读取文件内容", > "inputSchema": { > "type": "object", > "properties": { > "path": { "type": "string", "description": "要读取的文件路径" }, > "encoding": { "type": "string", "default": "utf-8" } > }, > "required": ["path"] > }, > "score": 0.71 > } > ] > } > ``` > > **渐进式披露模式 search\_tools 响应:** > > ```json theme={null} > { > "tools": [ > { > "name": "read_file", > "description": "读取文件内容", > "score": 0.71 > } > ], > "metadata": { > "progressiveDisclosure": true, > "guideline": "使用 describe_tool 获取完整的参数模式后再调用。" > } > } > ``` > > **describe\_tool 响应:** > > ```json theme={null} > { > "tool": { > "name": "read_file", > "description": "读取文件内容", > "inputSchema": { > "type": "object", > "properties": { > "path": { "type": "string", "description": "要读取的文件路径" }, > "encoding": { "type": "string", "default": "utf-8" } > }, > "required": ["path"] > } > } > } > ``` ## 故障排除 > **数据库连接问题** > > **症状:** > > * 智能路由不可用 > * 数据库连接错误 > * 嵌入存储失败 > > **解决方案:** > > 1. 验证 PostgreSQL 是否正在运行 > 2. 检查 DB\_URL 格式 > 3. 确保安装了 pgvector 扩展 > 4. 手动测试连接: > > ```bash theme={null} > psql $DB_URL -c "SELECT 1;" > ``` > **嵌入服务问题** > > **症状:** > > * 工具索引失败 > * 查询处理错误 > * API 速率限制错误 > > **解决方案:** > > 1. 验证 API 密钥有效性 > 2. 检查网络连接 > 3. 监控速率限制 > 4. 测试嵌入服务: > > ```bash theme={null} > curl -X POST https://api.openai.com/v1/embeddings \ > -H "Authorization: Bearer $OPENAI_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{"input": "test", "model": "text-embedding-3-small"}' > ``` > **搜索结果不佳** > > **症状:** > > * 返回不相关的工具 > * 相关性得分低 > * 缺少预期的工具 > > **解决方案:** > > 1. 调整相似性阈值(`SMART_ROUTING_SIMILARITY_THRESHOLD`),可参考每个结果的 `score` 选择数值 > 2. 使用更好的描述重新索引工具 > 3. 使用更具体的查询 > 4. 检查工具元数据质量 > > ```bash theme={null} > # 重新索引所有工具(全量强制:先清空 vector_embeddings,再对全部已启用服务器重新生成嵌入; > # 需要个人凭据的服务器会以已绑定凭据的用户身份(所有者优先)重建,并索引其可见工具的并集; > # 同步执行,逐台进度通过 SSE embedding-sync-progress 事件推送) > curl -X POST http://localhost:3000/api/smart-routing/reindex \ > -H "Authorization: Bearer YOUR_JWT_TOKEN" > ``` > **性能问题** > > **症状:** > > * 查询响应缓慢 > * 数据库负载高 > * 内存使用激增 > > **解决方案:** > > 1. 优化数据库配置 > 2. 增加缓存大小 > 3. 减少批处理大小 > 4. 监控系统资源 > > ```bash theme={null} > # 查看向量库状态与性能指标(配置、数据库连接状态、行数、按模型/服务器统计、同步新鲜度、索引覆盖率) > curl http://localhost:3000/api/smart-routing/performance \ > -H "Authorization: Bearer YOUR_JWT_TOKEN" > ``` > **密钥已在面板填对,仍然报鉴权失败** > > **症状:** > > * 工具索引失败,返回鉴权错误(401/403) > * 面板里保存的密钥是有效的,但请求仍被拒绝 > > **原因:** 环境变量的优先级高于面板配置,因此你保存的密钥从未被使用(issue #642)。新版本会在对应字段下方显示警告,并指出实际生效的变量名。 > > **解决方案:** > > 1. 检查 `.env*` 文件、shell 配置以及容器/服务定义中的 `OPENAI_API_KEY` / `AZURE_OPENAI_API_KEY` > 2. 移除遗留变量并重启;若提供商或模型也发生变化,请重新索引 > 3. 确认实际生效的密钥是否可用: > > ```bash theme={null} > curl -X POST "${OPENAI_API_BASE_URL:-https://api.openai.com/v1}/embeddings" \ > -H "Authorization: Bearer $OPENAI_API_KEY" \ > -H "Content-Type: application/json" \ > -d '{"input": "test", "model": "text-embedding-3-small"}' > ``` ## 最佳实践 ### 查询编写 > [!TIP] > > **要具体描述**:在查询中使用具体、描述性的语言以获得更好的工具匹配。 > [!TIP] > > **包含上下文**:提供有关您的任务或领域的相关上下文以获得更准确的结果。 > [!TIP] > > **使用自然语言**:像向人类描述任务一样编写查询。 ### 工具描述 > [!WARNING] > > **质量元数据**:确保 MCP 服务器提供高质量的工具描述和元数据。 > [!WARNING] > > **定期更新**:随着功能的发展保持工具描述的最新状态。 > [!WARNING] > > **一致的命名**:在工具和服务器中使用一致的命名约定。 ### 系统维护 > [!NOTE] > > **定期重新索引**:定期重新索引工具以确保嵌入质量。 > [!NOTE] > > **监控性能**:跟踪查询模式并根据使用情况进行优化。 > [!NOTE] > > **更新模型**:随着新嵌入模型的出现,考虑更新到更新的模型。 ## 下一步 > **[身份验证](/zh/features/authentication)** > > 用户管理和访问控制 > **[监控](/zh/features/monitoring)** > > 系统监控和分析 > **[API 参考](/zh/api-reference/smart-routing)** > > 完整的智能路由 API 文档 > **[配置](/zh/configuration/environment-variables)** > > 高级配置选项 # 欢迎使用 Source: https://docs.mcphub.app/zh/index MCPHub 是一个开源、自托管的 MCP 网关与控制平面,用于连接、控制和运行 MCP 服务器。 ## 什么是 MCPHub? MCPHub 是一个开源、自托管的 MCP 网关与控制平面,帮助您通过统一端点连接、控制、路由和运行多个 MCP 服务器。 ### 核心功能 * **🏗️ 分组管理** - 灵活的服务器分组和配置管理 * **🚀 智能路由** - 基于语义检索的智能路由分发 * **🧩 工具结果压缩** - 在返回客户端前透明压缩大型文本工具输出 * **📊 实时监控** - 全面的性能指标和健康检查 * **🔐 安全认证** - 身份认证和访问控制 ## 快速开始 立即开始使用 MCPHub,只需几分钟即可部署您的第一个 MCP 服务器。 > **[5 分钟快速部署](/zh/quickstart)** > > 跟随我们的快速开始指南,5 分钟内部署 MCPHub 并连接您的第一个 MCP 服务器 > **[开发环境搭建](/zh/development/getting-started)** > > 设置本地开发环境,了解 MCPHub 的架构和开发工作流 ## 核心概念 了解 MCPHub 的核心概念,为深入使用做好准备。 > **[智能路由机制](/zh/features/smart-routing)** > > 学习 MCPHub 的智能路由算法和配置策略 > **[服务器管理](/zh/features/server-management)** > > 掌握 MCP 服务器的添加、配置和管理技巧 > **[监控与分析](/zh/features/monitoring)** > > 使用内置的监控工具跟踪性能和识别问题 ## 部署选项 MCPHub 支持多种部署方式,满足不同规模和场景的需求。 > **[Docker 部署](/zh/configuration/docker-setup)** > > 使用 Docker 容器快速部署,支持单机和集群模式 ## API 和集成 MCPHub 提供完整的 RESTful API 和多语言 SDK,方便与现有系统集成。 > **[API 参考文档](/zh/api-reference/introduction)** > > 完整的 API 接口文档,包含详细的请求示例和响应格式 ## 社区和支持 加入 MCPHub 社区,获取帮助和分享经验。 > **[GitHub 仓库](https://github.com/samanhappy/mcphub)** > > 查看源代码、提交问题和贡献代码 > **[Discord 社区](https://discord.gg/2BJehJZVH5)** > > 与其他开发者交流,获取实时帮助 > **[Sponsor](https://ko-fi.com/samanhappy)** > > 支持 MCPHub 的开发和维护,帮助我们持续改进 # 安装指南 Source: https://docs.mcphub.app/zh/installation 各种平台的详细安装说明 ## 先决条件 在安装 MCPHub 之前,请确保您具备以下先决条件: * **Node.js** 20+ (用于本地开发) * **Docker** (推荐用于生产环境) * **pnpm** (用于本地开发) 智能路由的可选要求: * **PostgreSQL** 带 pgvector 扩展 * **LLM 提供商(OpenAI 或兼容)API 密钥** ## 安装方法 **Docker (推荐)** ### Docker 安装 Docker 是在生产环境中部署 MCPHub 的推荐方式。 #### 1. 基础安装 ```bash theme={null} # 拉取最新镜像 docker pull samanhappy/mcphub:latest # 使用默认设置运行 docker run -d \ --name mcphub \ -p 3000:3000 \ samanhappy/mcphub:latest ``` #### 2. 使用自定义配置 ```bash theme={null} # 创建您的配置文件 cat > mcp_settings.json << 'EOF' { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] } } } EOF # 使用挂载的配置运行 docker run -d \ --name mcphub \ -p 3000:3000 \ -v $(pwd)/mcp_settings.json:/app/mcp_settings.json \ samanhappy/mcphub:latest ``` #### 3. 使用环境变量 ```bash theme={null} docker run -d \ --name mcphub \ -p 3000:3000 \ -e PORT=3000 \ -e BASE_PATH="" \ samanhappy/mcphub:latest ``` #### 4. Docker Compose 创建 `docker-compose.yml` 文件: ```yaml theme={null} version: '3.8' services: mcphub: image: samanhappy/mcphub:latest ports: - "3000:3000" volumes: - ./mcp_settings.json:/app/mcp_settings.json environment: - PORT=3000 - BASE_PATH= - INIT_TIMEOUT=300000 restart: unless-stopped # 可选:用于智能路由的 PostgreSQL postgres: image: pgvector/pgvector:pg17 environment: POSTGRES_DB: mcphub POSTGRES_USER: mcphub POSTGRES_PASSWORD: mcphub_password volumes: - postgres_data:/var/lib/postgresql/data ports: - "5432:5432" volumes: postgres_data: ``` 运行命令: ```bash theme={null} docker-compose up -d ``` **npm 包** ### npm 包安装 将 MCPHub 安装为全局 npm 包: #### 1. 全局安装 ```bash theme={null} # 全局安装 npm install -g @samanhappy/mcphub # 或使用 yarn yarn global add @samanhappy/mcphub # 或使用 pnpm pnpm add -g @samanhappy/mcphub ``` #### 2. 运行 MCPHub ```bash theme={null} # 使用默认设置运行 mcphub # 使用自定义端口运行 PORT=8080 mcphub ``` **本地开发** ### 本地开发环境设置 用于开发、自定义或贡献: #### 1. 克隆仓库 ```bash theme={null} # 克隆仓库 git clone https://github.com/samanhappy/mcphub.git cd mcphub ``` #### 2. 安装依赖 ```bash theme={null} # 使用 pnpm 安装依赖(推荐) pnpm install # 或使用 npm npm install # 或使用 yarn yarn install ``` #### 3. 开发模式 ```bash theme={null} # 在开发模式下同时启动后端和前端 pnpm dev # 这将启动: # - 后端在 http://localhost:3000 # - 前端在 http://localhost:5173 # - 前端代理 API 调用到后端 ``` #### 4. 生产构建 ```bash theme={null} # 构建后端和前端 pnpm build # 启动生产服务器 pnpm start ``` #### 5. 开发脚本 ```bash theme={null} # 仅后端(用于 API 开发) pnpm backend:dev # 仅前端(当后端单独运行时) pnpm frontend:dev # 运行测试 pnpm test # 代码检查 pnpm lint # 代码格式化 pnpm format ``` > [!NOTE] > > 在 Windows 上,您可能需要分别运行后端和前端: > > ```bash theme={null} > # 终端 1:后端 > pnpm backend:dev > > # 终端 2:前端 > pnpm frontend:dev > ``` **Kubernetes** ### Kubernetes 部署 使用这些清单在 Kubernetes 上部署 MCPHub: #### 1. 设置的 ConfigMap ```yaml theme={null} apiVersion: v1 kind: ConfigMap metadata: name: mcphub-config data: mcp_settings.json: | { "mcpServers": { "fetch": { "command": "uvx", "args": ["mcp-server-fetch"] }, "playwright": { "command": "npx", "args": ["@playwright/mcp@latest", "--headless"] } } } ``` #### 2. 部署 ```yaml theme={null} apiVersion: apps/v1 kind: Deployment metadata: name: mcphub spec: replicas: 1 selector: matchLabels: app: mcphub template: metadata: labels: app: mcphub spec: containers: - name: mcphub image: samanhappy/mcphub:latest ports: - containerPort: 3000 env: - name: PORT value: "3000" volumeMounts: - name: config mountPath: /app/mcp_settings.json subPath: mcp_settings.json volumes: - name: config configMap: name: mcphub-config ``` #### 3. 服务 ```yaml theme={null} apiVersion: v1 kind: Service metadata: name: mcphub-service spec: selector: app: mcphub ports: - port: 80 targetPort: 3000 type: ClusterIP ``` #### 4. Ingress (可选) ```yaml theme={null} apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: mcphub-ingress annotations: nginx.ingress.kubernetes.io/proxy-buffering: "off" spec: rules: - host: mcphub.yourdomain.com http: paths: - path: / pathType: Prefix backend: service: name: mcphub-service port: number: 80 ``` 部署命令: ```bash theme={null} kubectl apply -f mcphub-configmap.yaml kubectl apply -f mcphub-deployment.yaml kubectl apply -f mcphub-service.yaml kubectl apply -f mcphub-ingress.yaml ``` ## 智能路由设置 (可选) 智能路由使用向量语义搜索提供 AI 驱动的工具发现。 ### 先决条件 1. **PostgreSQL 带 pgvector 扩展** 2. **LLM 提供商(OpenAI 或兼容)API 密钥** ### 数据库设置 **Docker PostgreSQL** ```bash theme={null} # 运行带 pgvector 的 PostgreSQL docker run -d \ --name mcphub-postgres \ -e POSTGRES_DB=mcphub \ -e POSTGRES_USER=mcphub \ -e POSTGRES_PASSWORD=your_password \ -p 5432:5432 \ pgvector/pgvector:pg17 ``` **现有 PostgreSQL** 如果您有现有的 PostgreSQL 实例: ```sql theme={null} -- 连接到您的 PostgreSQL 实例 -- 创建数据库 CREATE DATABASE mcphub; -- 连接到 mcphub 数据库 \c mcphub; -- 启用 pgvector 扩展 CREATE EXTENSION IF NOT EXISTS vector; ``` **云 PostgreSQL** 对于云提供商(AWS RDS、Google Cloud SQL 等): 1. 在您的云提供商控制台中启用 pgvector 扩展 2. 创建名为 `mcphub` 的数据库 3. 记下连接详细信息 ## 验证 安装后,验证 MCPHub 是否正常工作: ### 控制台访问 打开浏览器并导航到: ``` http://localhost:3000 ``` ## 故障排除 > **Docker 问题** > > **端口已被使用:** > > ```bash theme={null} > # 检查是什么在使用端口 3000 > lsof -i :3000 > > # 使用不同的端口 > docker run -p 8080:3000 samanhappy/mcphub > ``` > > **容器无法启动:** > > ```bash theme={null} > # 检查容器日志 > docker logs mcphub > > # 交互式运行以进行调试 > docker run -it --rm samanhappy/mcphub /bin/bash > ``` > **npm 安装问题** > > **权限错误:** > > ```bash theme={null} > # 使用 npx 而不是全局安装 > npx @samanhappy/mcphub > > # 或修复 npm 权限 > npm config set prefix ~/.npm-global > export PATH=~/.npm-global/bin:$PATH > ``` > > **Node 版本问题:** > > ```bash theme={null} > # 检查 Node 版本 > node --version > > # 使用 nvm 安装 Node 20+ > nvm install 20 > nvm use 18 > ``` > **网络问题** > > **无法访问控制台:** > > * 检查 MCPHub 是否在运行:`ps aux | grep mcphub` > * 验证端口绑定:`netstat -tlnp | grep 3000` > * 检查防火墙设置 > * 尝试通过 `127.0.0.1:3000` 而不是 `localhost:3000` 访问 > > **AI 客户端无法连接:** > > * 确保端点 URL 正确 > * 检查 MCPHub 是否在代理后面 > * 验证 Kubernetes/Docker 环境中的网络策略 > **智能路由问题** > > **数据库连接失败:** > > ```bash theme={null} > # 测试数据库连接 > psql $DB_URL -c "SELECT 1;" > > # 检查是否安装了 pgvector > psql $DB_URL -c "CREATE EXTENSION IF NOT EXISTS vector;" > ``` > > **嵌入服务错误:** > > * 验证 OpenAI API 密钥是否有效 > * 检查互联网连接 > * 监控速率限制 ## 下一步 > **[配置](/zh/configuration/mcp-settings)** > > 配置您的 MCP 服务器和设置 > **[快速开始](/zh/quickstart)** > > 5分钟内启动并运行 > **[服务器管理](/zh/features/server-management)** > > 了解如何管理您的 MCP 服务器 > **[API 参考](/zh/api-reference/introduction)** > > 探索完整的 API 文档 # 快速开始指南 Source: https://docs.mcphub.app/zh/quickstart 5 分钟内运行 MCPHub ## 安装 **Docker(推荐)** 使用 Docker 是最快的开始方式: ```bash theme={null} # 使用默认配置运行 docker run -p 3000:3000 samanhappy/mcphub ``` 或者挂载自定义配置: ```bash theme={null} # 使用自定义 MCP 设置运行 docker run -p 3000:3000 \ -v $(pwd)/mcp_settings.json:/app/mcp_settings.json \ samanhappy/mcphub ``` **本地开发** 用于开发或自定义: ```bash theme={null} # 克隆仓库 git clone https://github.com/samanhappy/mcphub.git cd mcphub # 安装依赖 pnpm install # 启动开发服务器 pnpm dev ``` 这会同时启动后端(端口 3000)和前端(端口 5173)的开发模式,Vite 开发服务器会把 API 与 MCP 请求代理到后端。本地开发默认使用 `admin` / `admin123`,可写配置保存在 `data/mcp_settings.dev.json`。 **npm 包** 将 MCPHub 安装为全局包: ```bash theme={null} # 全局安装 npm install -g @samanhappy/mcphub # 运行 MCPHub mcphub ``` ## 初始设置 ### 1. 访问控制面板 打开浏览器并导航到: ``` http://localhost:3000 ``` ### 2. 登录 默认用户名为 `admin`。使用 `pnpm dev` 本地开发时,默认密码是 `admin123`。Docker、npm 包和生产启动不包含默认用户;首次启动时如果未设置 `ADMIN_PASSWORD` 环境变量,系统将自动生成随机密码并输出到服务器日志中。 可以通过环境变量预先设置管理员密码: ```bash theme={null} # 通过环境变量设置管理员密码 ADMIN_PASSWORD=your-secure-password docker run -p 3000:3000 samanhappy/mcphub ``` > [!WARNING] > > 首次登录后请及时修改管理员密码以确保安全。 ### 3. 配置您的第一个 MCP 服务器 1. 在控制面板中点击 **"添加服务器"** 2. 输入服务器详细信息: * **名称**: 唯一标识符(例如 `fetch`) * **命令**: 可执行命令(`uvx`) * **参数**: 命令参数(`["mcp-server-fetch"]`) * **环境**: 任何所需的环境变量 fetch 服务器的示例配置: ```json theme={null} { "name": "fetch", "command": "uvx", "args": ["mcp-server-fetch"], "env": {} } ``` ## 基本使用 ### 连接 AI 客户端 一旦配置了服务器,使用 MCPHub 端点连接您的 AI 客户端: **所有服务器** 访问所有已配置的 MCP 服务器:`http://localhost:3000/mcp` **特定组** 访问特定组中的服务器:`http://localhost:3000/mcp/{groupName}` **单个服务器** 访问单个服务器:`http://localhost:3000/mcp/{serverName}` **智能路由** 使用 AI 驱动的工具发现:`http://localhost:3000/mcp/$smart` \[!NOTE] 智能路由需要使用 pgvector 的 PostgreSQL 和 OpenAI API 密钥。 ### 示例:添加热门 MCP 服务器 以下是一些您可以添加的热门 MCP 服务器: > **Web Fetch 服务器** > > ```json theme={null} > { > "name": "fetch", > "command": "uvx", > "args": ["mcp-server-fetch"] > } > ``` > **Playwright 浏览器自动化** > > ```json theme={null} > { > "name": "playwright", > "command": "npx", > "args": ["@playwright/mcp@latest", "--headless"] > } > ``` > **高德地图(需要 API 密钥)** > > ```json theme={null} > { > "name": "amap", > "command": "npx", > "args": ["-y", "@amap/amap-maps-mcp-server"], > "env": { > "AMAP_MAPS_API_KEY": "your-api-key-here" > } > } > ``` > **Slack 集成** > > ```json theme={null} > { > "name": "slack", > "command": "npx", > "args": ["-y", "@modelcontextprotocol/server-slack"], > "env": { > "SLACK_BOT_TOKEN": "your-bot-token", > "SLACK_TEAM_ID": "your-team-id" > } > } > ``` ## 后续步骤 > **[服务器管理](/zh/features/server-management)** > > 学习高级服务器配置和管理 > **[组管理](/zh/features/group-management)** > > 将服务器组织成逻辑组 > **[智能路由](/zh/features/smart-routing)** > > 设置 AI 驱动的工具发现 > **[API 参考](/zh/api-reference/introduction)** > > 探索完整的 API 文档 ## 故障排除 > **服务器无法启动** > > * 检查 MCP 服务器命令是否在您的 PATH 中可访问 > * 验证环境变量是否正确设置 > * 检查 MCPHub 日志以获取详细错误信息 > **无法从 AI 客户端连接** > > * 确保 MCPHub 在正确的端口上运行 > * 检查防火墙设置 > * 验证端点 URL 格式 > **身份验证问题** > > * 验证凭据是否正确 > * 检查 JWT 令牌是否有效 > * 尝试清除浏览器缓存和 cookie 需要更多帮助?加入我们的 [Discord 社区](https://discord.gg/2BJehJZVH5) 获取支持!