# Authentication
Source: https://docs.mcphub.app/api-reference/auth
Manage users and authentication.
Log in to get a JWT token.
Register a new user.
Get the currently authenticated user.
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 new user and returns a JWT token.
* **Endpoint**: `/api/auth/register`
* **Method**: `POST`
* **Body**:
* `username` (string, required): The desired username.
* `password` (string, required): The desired password (must be at least 6 characters).
* `isAdmin` (boolean, optional): Whether the user should have admin privileges.
* **Request Example**:
```json theme={null}
{
"username": "newuser",
"password": "password123",
"isAdmin": false
}
```
* **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.
Update the main system configuration.Get all system settings, including servers and groups.Get public runtime configuration for the frontend.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 a list of all groups.Create a new group.Get details of a specific group.Update an existing group.Delete a group.Add a server to a group.Remove a server from a group.Batch update servers in a group.Get detailed server configurations in a group.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.
* `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, 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.
* `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 all logs.
Clear all 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 MCP connections currently retain legacy protocol negotiation; a modern
downstream request does not upgrade an upstream server.
## 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:**
Custom API title
Custom API description
Custom API version
Custom server URL
Include disabled tools
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:**
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.
**Path Parameters:**
The name of the MCP server
The name of the tool to execute
## OpenWebUI Integration
To integrate MCPHub with OpenWebUI:
Ensure MCPHub is running with your MCP servers configured
```bash theme={null}
curl http://localhost:3000/api/openapi.json > mcphub-api.json
curl http://localhost:3000/api/openapi.yaml > mcphub-api.yaml
```
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:
`http://localhost:3000/api/openapi.json` or `http://localhost:3000/api/openapi.yaml`
`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
No need for intermediate proxy
OpenAPI spec updates automatically as MCP servers connect/disconnect
Direct tool execution without proxy overhead
One less component to manage
## Troubleshooting
Ensure MCP servers are connected. Check `/api/openapi/stats` for server status.
Verify the tool name and parameters match the OpenAPI specification. Check server logs for details.
Ensure MCPHub is accessible from OpenWebUI and the OpenAPI URL is correct.
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.
Execute a prompt on an MCP server.
Enable or disable a prompt.
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.
List all MCP servers and their status.Read a single server's configuration.Add a new MCP server.Add multiple servers in one request.Update an existing server.Remove a server.Enable or disable a server.Reconnect to an upstream server.Enable or disable a tool.Override a tool description.Reset a tool description to upstream.Toggle / describe / reset prompts.Toggle / describe / reset resources.Read everything: servers, groups, system config.
***
### Get All Servers
Retrieves a list of all configured MCP servers, including their status and available tools.
* **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 and reconnect interval in milliseconds for remote servers. Defaults to 60000.
* `enableKeepAlive` (boolean): Toggle periodic health checks and automatic reconnect attempts for remote servers.
* `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.
***
### 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.
***
### 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`).
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.
Check the health status of the MCPHub server.
OAuth callback endpoint for authentication flows.
Upload a MCPB bundle file.
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.
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.
List all users.Get a single user.Create a user.Update a user.Delete a user.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.
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.
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
```
**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
```
**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.
**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.
`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`). |
| `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`. |
| `DEBUG` | `false` | Set to `true` to enable verbose startup logging for frontend-path discovery. |
## 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. |
**`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.
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. |
**`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. |
**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. |
## 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 and automatic reconnect attempts. |
| `keepAliveInterval` | number | no | Health check and reconnect 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": "..." } }`. 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.
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.
`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
}
}
}
```
Only enable `perSessionClient` for servers that actually hold per-session state.
It multiplies upstream connections (and stdio child processes) by the number of
concurrent sessions, so leave it off for stateless servers.
### 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}"
}
}
}
```
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).
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"
}
}
}
}
```
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; 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.
# 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/`.
Clone the repo, install dependencies, run the dev servers, and find your way around the code.
The actual moving parts: `AppServer`, the service layer, file vs. database storage, and request paths.
How smart-routing embeddings are generated, persisted, and refreshed.
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
## Overview
MCPHub supports several stacked authentication paths. The same middleware (`src/middlewares/auth.ts`) evaluates them in order on every authenticated request:
1. **Bearer key** — a static token created from the dashboard or `/api/auth/keys`.
2. **MCPHub OAuth access token** — issued by MCPHub's own authorization server (optional).
3. **Better Auth session cookie** — issued by GitHub / Google login (optional).
4. **JWT** — issued by `POST /api/auth/login`, sent via the `x-auth-token` header or `?token=` query.
5. **`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.
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 are accepted by `src/middlewares/auth.ts`.
## 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.
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)
**`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
### 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
**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`
**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`
**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`
**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`
**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://localhost:3000/mcp/{group-name}
```
Examples:
* `http://localhost:3000/mcp/web-tools`
* `http://localhost:3000/mcp/data-processing`
* `http://localhost:3000/mcp/communication`
```
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.
## Dynamic Group Management
### Adding Servers to Groups
1. Navigate to the group in the dashboard
2. Click "Manage Servers"
3. Select additional servers to add
4. Click "Save Changes"
```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
1. Navigate to the group in the dashboard
2. Click "Manage Servers"
3. Unselect servers to remove
4. Click "Save Changes"
```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
**Organize by Use Case**: Group servers based on what users want to accomplish, not just technical
similarity.
**Keep Groups Focused**: Avoid creating groups with too many diverse tools. Smaller, focused
groups are more useful.
**Use Descriptive Names**: Choose names that clearly indicate the group's purpose and contents.
### Performance Optimization
**Balance Group Size**: Very large groups may have slower tool discovery. Consider splitting into
smaller groups.
**Monitor Usage**: Use analytics to identify which groups are heavily used and optimize
accordingly.
## Troubleshooting
**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
**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
**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
AI-powered tool discovery across groups
User management and access control
Complete group management API
Advanced group configuration options
# 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
Routes with more than one connected server behave differently from single-server routes in one respect: tool names. See below.
## Tool Names
Ordinary (non-Apps) MCPHub routes expose qualified tool names such as `weather::forecast`, using your configured name separator.
A route eligible for Apps passthrough that resolves to a **single** connected server exposes the upstream tool names unchanged, such as `forecast`. This allows calls from the app iframe to pass through without rewriting, which is what most Apps widgets expect when they call back into a single-server host.
A route that resolves to **more than one** connected server keeps the qualified name (`weather-station::forecast`) instead, since more than one upstream server could otherwise expose a tool with the same bare name. Apps metadata (`_meta.ui`) and app-only tools are still fully surfaced on these multi-server routes — only the tool-name qualification differs from the single-server case. A widget on a multi-server route should read its own qualified name from the tool listing (or the host context passed to it) rather than assume the bare name.
## 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 ships three observability surfaces. There is **no** built-in Prometheus exporter, OpenTelemetry SDK, or alerting pipeline — wire up your own infrastructure tooling against the endpoints below.
## 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`.
## 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` 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 only `target`, `name`, and an optional `label` (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, use a POSIX shell wrapper with a lowercase environment slot. Use `$my_api_key`, not `$MY_API_KEY` or `${my_api_key}`: MCPHub expands the latter forms against its own environment before injecting personal credentials. Keep the shell reference double-quoted so spaces and shell symbols in the credential remain literal. In the dashboard, add two argument fields: `-c` and the complete script below. Do not add quotes around the entire script field.
```json theme={null}
{
"command": "sh",
"args": ["-c", "exec cli --api-key \"$my_api_key\""],
"credentialTemplate": [
{
"target": "env",
"name": "my_api_key",
"label": "API key"
}
]
}
```
Replace `cli` with your installed MCP server executable. This wrapper requires `sh`; native per-user OAuth and args credential slots are not currently supported. Validate through MCPHub with two user bindings, rather than only running the command in an interactive shell.
## Manage a binding through the API
Use dashboard authentication (JWT in `x-auth-token`, an OIDC/session login, or a user OAuth access token). 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.
# 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.
## 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
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
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 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
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 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 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 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
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:
```json theme={null}
{
"npm-server": {
"command": "npx",
"args": ["-y", "package-name", "--option", "value"]
}
}
```
```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
**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
**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
**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
Organize servers into logical groups
Set up AI-powered tool discovery
Server management API documentation
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:
```
# Search across all servers
http://localhost:3000/mcp/$smart
# Search within a specific group
http://localhost:3000/mcp/$smart/{group}
```
```
# 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:
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
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.
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.
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.
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.
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
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
```
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.
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.
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.
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.
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.
**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
**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;"
```
**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"}'
```
**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"
```
**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"
```
**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
**Be Descriptive**: Use specific, descriptive language in queries for better tool matching.
**Include Context**: Provide relevant context about your task or domain for more accurate results.
**Use Natural Language**: Write queries as you would describe the task to a human.
### Tool Descriptions
**Quality Metadata**: Ensure MCP servers provide high-quality tool descriptions and metadata.
**Regular Updates**: Keep tool descriptions current as functionality evolves.
**Consistent Naming**: Use consistent naming conventions across tools and servers.
### System Maintenance
**Regular Re-indexing**: Periodically re-index tools to ensure embedding quality.**Monitor Performance**: Track query patterns and optimize based on usage.
**Update Models**: Consider updating to newer embedding models as they become available.
## Next Steps
User management and access control
System monitoring and analytics
Complete Smart Routing API documentation
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
Centrally manage multiple MCP servers with hot-swappable configuration
Organize servers into logical groups for streamlined access control
AI-powered tool discovery using vector semantic search
Transparently reduce large text tool outputs before they reach clients
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
Get MCPHub running in 5 minutes
Detailed installation instructions for all platforms
Learn how to configure your MCP servers
Complete API documentation
## Community & Support
Source code and issue tracking
Join our community discussions
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 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 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 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
```
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 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
```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
```
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;
```
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
**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
```
**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
```
**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
**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
Configure your MCP servers and settings
Get up and running in 5 minutes
Learn how to manage your MCP servers
Explore the complete API documentation
# Quick Start Guide
Source: https://docs.mcphub.app/quickstart
Get MCPHub running in 5 minutes
## Installation
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
```
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`.
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
```
Change the admin password after first login for security.
`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:
Access all configured MCP servers: `http://localhost:3000/mcp`
Access servers in a specific group: `http://localhost:3000/mcp/{groupName}`
Access a single server: `http://localhost:3000/mcp/{serverName}`
Use AI-powered tool discovery: `http://localhost:3000/mcp/$smart`
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:
```json theme={null}
{
"name": "fetch",
"command": "uvx",
"args": ["mcp-server-fetch"]
}
```
```json theme={null}
{
"name": "playwright",
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless"]
}
```
```json theme={null}
{
"name": "amap",
"command": "npx",
"args": ["-y", "@amap/amap-maps-mcp-server"],
"env": {
"AMAP_MAPS_API_KEY": "your-api-key-here"
}
}
```
```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
Learn advanced server configuration and management
Organize servers into logical groups
Set up AI-powered tool discovery
Explore the complete API documentation
## Troubleshooting
* Check if the MCP server command is accessible in your PATH - Verify environment variables are
correctly set - Check MCPHub logs for detailed error messages
* Ensure MCPHub is running on the correct port - Check firewall settings - Verify the endpoint
URL format
* 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
管理用户和身份验证。
登录以获取 JWT 令牌。
注册一个新用户。
获取当前已验证的用户。
更改当前用户的密码。
***
### 登录
验证用户身份并返回 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 令牌。
* **端点**: `/api/auth/register`
* **方法**: `POST`
* **正文**:
* `username` (string, 必填): 新的用户名。
* `password` (string, 必填): 新的用户密码 (至少6个字符)。
* `isAdmin` (boolean, 可选): 用户是否应有管理员权限。
* **请求示例**:
```json theme={null}
{
"username": "newuser",
"password": "password123",
"isAdmin": false
}
```
* **成功响应**:
```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
管理和检索系统级配置。
更新主系统配置。获取所有系统设置,包括服务器和群组。获取前端的公共运行时配置。获取公共配置以检查是否跳过身份验证。
***
### 更新系统配置
更新系统配置的各个部分。您只需提供要更新部分的键。
* **端点**: `/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
管理服务器群组以组织和路由请求。
获取所有群组的列表。创建一个新群组。获取特定群组的详细信息。更新现有群组。删除一个群组。将服务器添加到群组。从群组中删除服务器。批量更新群组中的服务器。获取群组中详细的服务器配置。更新群组中服务器的工具选择。
***
### 获取所有群组
检索所有服务器群组的列表。
* **端点**: `/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, 可选): 群组的描述。
* `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, 可选): 群组的新描述。
* `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
访问和管理服务器日志。
获取所有日志。
清除所有日志。
实时流式传输日志。
***
### 获取所有日志
检索所有存储的日志。
* **端点**: `/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)。上游 MCP 连接目前继续使用
旧版协议协商;新版下游请求不会自动升级上游服务器。
## 客户端迁移
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` 版本接受相同的查询参数。
**查询参数:**
自定义 API 标题
自定义 API 描述
自定义 API 版本
自定义服务器 URL
包含禁用的工具
要包含的服务器名称列表(逗号分隔)
### 组/服务器特定的 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 规范。如果存在具有给定名称的组,则返回该组中所有服务器的规范。否则,将名称视为服务器名称并仅返回该服务器的规范。
**路径参数:**
组 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 工具。
**路径参数:**
MCP 服务器的名称
要执行的工具名称
## OpenWebUI 集成
要将 MCPHub 与 OpenWebUI 集成:
确保 MCPHub 正在运行,并且已配置 MCP 服务器
```bash theme={null}
curl http://localhost:3000/api/openapi.json > mcphub-api.json
curl http://localhost:3000/api/openapi.yaml > mcphub-api.yaml
```
在 OpenWebUI 中导入 OpenAPI 规范文件或直接指向 URL
### 配置示例
在 OpenWebUI 中,您可以通过以下方式将 MCPHub 添加为 OpenAPI 工具:
`http://localhost:3000/api/openapi.json` 或 `http://localhost:3000/api/openapi.yaml`
`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 服务器连接/断开自动更新
直接工具执行,无代理开销
减少一个需要管理的组件
## 故障排除
确保 MCP 服务器已连接。检查 `/api/openapi/stats` 查看服务器状态。
验证工具名称和参数是否与 OpenAPI 规范匹配。检查服务器日志以获取详细信息。
确保 MCPHub 可从 OpenWebUI 访问,并且 OpenAPI URL 正确。
检查您的 MCP 服务器配置中是否启用了工具。使用 `includeDisabled=true` 查看所有工具。
# 提示词
Source: https://docs.mcphub.app/zh/api-reference/prompts
管理和执行 MCP 提示词。
在 MCP 服务器上执行提示词。
启用或禁用提示词。
更新提示词的描述。
***
### 获取提示词
在 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 服务器。
获取所有 MCP 服务器的列表。
创建一个新的 MCP 服务器。
更新现有的 MCP 服务器。
删除一个 MCP 服务器。
切换服务器的启用状态。
切换工具的启用状态。
更新工具的描述。
更新系统配置设置。
获取所有服务器设置和配置。
***
### 获取所有服务器
检索所有已配置的 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` 禁用服务器。
***
### 切换一个工具
启用或禁用服务器上的特定工具。
* **端点**: `/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, 必填): 工具的新描述。
***
### 更新系统配置
更新系统范围的配置设置。
* **端点**: `/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
系统和实用程序端点。
检查 MCPHub 服务器的健康状态。
用于身份验证流程的 OAuth 回调端点。
上传 MCPB 文件。
将 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 工具。
在 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 中管理用户。
获取所有用户的列表。
获取特定用户的详细信息。
创建新用户。
更新现有用户。
删除用户。
获取有关用户及其服务器访问权限的统计信息。
***
### 获取所有用户
检索系统中所有用户的列表。
* **端点**: `/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
```
**简化配置**:您只需设置 `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
```
**INSTALL\_EXT=true 包含的功能:**
* **稳定版 Rust + Cargo**:通过 rustup 安装,用于基于 Rust 的 MCP server。
* **Docker 引擎**:完整的 Docker 守护进程和 CLI,用于容器管理。在特权模式下运行时,守护进程会自动启动。
* **Chrome + Firefox(供 Playwright 使用,仅 amd64)**:用于浏览器自动化任务
扩展镜像较大,但为高级用例提供了额外功能。
**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 握手。
`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` | `''` | 应用程序的基本路径 |
| `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 上限选择。 |
**环境变量优先于面板配置。** 上表中的每个变量都会覆盖 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 密钥**
# 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` 类型;如需此处未描述的行为,请查阅该类型。
受保护的 `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 服务器,还会拥有各自的子进程),在首次工具调用时惰性创建,
并在会话结束时自动销毁。
`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
}
}
}
```
仅对真正持有会话级状态的服务器启用 `perSessionClient`。它会让上游连接(以及 stdio 子进程)
数量随并发会话数成倍增长,因此对无状态服务器请保持关闭。
### 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` 等过大的范围也会放行回环地址、云元数据端点等敏感内网目标。
# 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 配置身份认证和安全设置
## 概述
MCPHub 提供灵活的身份认证机制来保护您的 MCP 服务器管理平台。系统支持多种身份认证方法和基于角色的访问控制。
## 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. **添加服务器**: 选择要包含在分组中的服务器
### 通过 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"]
}'
```
## 分组类型和用例
**用途**: 浏览器自动化和网页抓取
**服务器**:
* `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 工具
**服务器**:
* `openai`: OpenAI API 集成
* `huggingface`: Hugging Face 模型
* `vector-db`: 向量数据库操作
**用例**:
* 模型推理
* 数据嵌入
* 自然语言处理
* 计算机视觉
**端点**: `http://localhost:3000/mcp/ai-ml`
## 分组端点
### 访问分组
每个分组都有自己的 MCP 端点:
```
http://localhost:3000/mcp/{group-name}
```
示例:
* `http://localhost:3000/mcp/web-tools`
* `http://localhost:3000/mcp/data-processing`
* `http://localhost:3000/mcp/communication`
```
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` 服务器的工具。
## 动态分组管理
### 向分组添加服务器
1. 在仪表板中导航到分组
2. 点击"管理服务器"
3. 选择要添加的其他服务器
4. 点击"保存更改"
```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. 点击"保存更改"
```bash theme={null}
curl -X DELETE http://localhost:3000/api/groups/web-tools/servers/server-name \
-H "Authorization: Bearer YOUR_JWT_TOKEN"
```
## 最佳实践
### 分组组织
**按用例组织**: 根据用户想要完成的任务来组织服务器分组,而不仅仅是技术相似性。
**保持分组聚焦**: 避免创建包含太多不同工具的分组。更小、更聚焦的分组更有用。
**使用描述性名称**: 选择能清楚表明分组目的和内容的名称。
### 性能优化
**平衡分组大小**: 非常大的分组可能导致工具发现较慢。考虑拆分为更小的分组。
**监控使用情况**: 使用分析来识别哪些分组被大量使用并相应优化。
## 故障排除
**检查:**
* 用户具有适当权限
* 分组存在且处于活跃状态
* 分组中的服务器正在运行
* 网络连接
**解决方案:**
1. 验证用户分组成员身份
2. 检查分组配置
3. 测试单个服务器连接
4. 查看访问日志
**可能原因:**
* 服务器未正确添加到分组
* 服务器未运行
* 工具发现失败
* 缓存问题
**调试步骤:**
1. 验证服务器在分组配置中
2. 检查服务器状态
3. 强制刷新工具发现
4. 清除分组缓存
**常见问题:**
* 分组中服务器过多
* 服务器响应慢
* 网络延迟
* 资源约束
**优化方案:**
1. 拆分大型分组
2. 监控服务器性能
3. 实施请求缓存
4. 使用连接池
## 下一步
跨分组的 AI 驱动工具发现
用户管理和访问控制
完整的分组管理 API
高级分组配置选项
## 可见性与共享
新建组默认使用 `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://` 资源
## 工具名称
普通 MCPHub 路由使用带服务器前缀的工具名称,例如 `weather::forecast`,分隔符取决于您的配置。
符合条件且仅包含一个已连接服务器的 MCP Apps 路由会保留上游原始工具名,例如 `forecast`。这样 App iframe 发起的调用无需改写即可直接转发。
包含多个已连接服务器的 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 提供三个可观测性入口。MCPHub **没有**内置 Prometheus exporter、OpenTelemetry SDK 或告警管线;如需这些能力,请使用外部基础设施工具接入下面的端点。
## 健康检查端点
`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 认证。
# 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`;SSE、Streamable HTTP 和 OpenAPI 使用 `headers`。每个字段仅包含 `target`、`name` 和可选 `label`,最多 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 仅通过命令行参数接收凭据,可使用 POSIX shell 包装并声明小写环境变量字段。使用 `$my_api_key`,不要使用 `$MY_API_KEY` 或 `${my_api_key}`:后两种写法会在注入个人凭据前,按 MCPHub 自身的环境变量展开。对 shell 变量引用保留双引号,使凭据中的空格和 shell 符号按字面值传递。在控制台添加两个参数输入框,分别填写 `-c` 和下面的完整脚本;不要给整个脚本输入框的内容再加一层引号。
```json theme={null}
{
"command": "sh",
"args": ["-c", "exec cli --api-key \"$my_api_key\""],
"credentialTemplate": [
{
"target": "env",
"name": "my_api_key",
"label": "API key"
}
]
}
```
将 `cli` 替换为已安装的 MCP 服务器可执行文件。此方案需要 `sh`;目前尚不支持原生个人 OAuth 和 args 凭据字段。请通过 MCPHub 使用两个用户的绑定验证,而不只是在交互式 shell 中手动执行命令。
## 管理 API
使用仪表板 JWT(`x-auth-token`)、OIDC/session 登录或用户 OAuth access token。用户级 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 联调需要部署方的测试凭据。
# 服务器管理
Source: https://docs.mcphub.app/zh/features/server-management
通过热插拔配置集中管理多个 MCP 服务器
## 概述
MCPHub 的服务器管理系统允许您从单个仪表板集中配置、监控和控制多个 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 服务器示例
提供网页抓取和 HTTP 请求功能:
```json theme={null}
{
"fetch": {
"command": "uvx",
"args": ["mcp-server-fetch"]
}
}
```
**可用工具:**
* `fetch`: 发起 HTTP 请求
* `fetch_html`: 抓取网页
* `fetch_json`: 从 API 获取 JSON 数据
用于网页交互的浏览器自动化:
```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`: 列出目录内容
数据库操作:
```json theme={null}
{
"sqlite": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-sqlite", "/path/to/database.db"]
}
}
```
**可用工具:**
* `execute_query`: 执行 SQL 查询
* `describe_tables`: 获取表结构
* `create_table`: 创建新表
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 仓库操作:
```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 文件操作:
```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}`: 如果变量已设置则使用指定值
### 命令变体
指定服务器命令的不同方式:
```json theme={null}
{
"npm-server": {
"command": "npx",
"args": ["-y", "package-name", "--option", "value"]
}
}
```
```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 协议兼容性
## 下一步
将服务器组织成逻辑分组
设置 AI 驱动的工具发现
服务器管理 API 文档
详细配置选项
# 智能路由
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://localhost:3000/mcp/$smart
# 在特定分组内搜索
http://localhost:3000/mcp/$smart/{group}
```
```
# 搜索所有服务器
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 路由。
通过 `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` 只会在工具描述中列出服务器名称。若要同时显示服务器描述(或上游返回的 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` 或模型默认值):前缀会被保留,工具文本会相应提前截断。
每个 `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"}'
```
## 最佳实践
### 查询编写
**要具体描述**:在查询中使用具体、描述性的语言以获得更好的工具匹配。
**包含上下文**:提供有关您的任务或领域的相关上下文以获得更准确的结果。
**使用自然语言**:像向人类描述任务一样编写查询。
### 工具描述
**质量元数据**:确保 MCP 服务器提供高质量的工具描述和元数据。
**定期更新**:随着功能的发展保持工具描述的最新状态。
**一致的命名**:在工具和服务器中使用一致的命名约定。
### 系统维护
**定期重新索引**:定期重新索引工具以确保嵌入质量。**监控性能**:跟踪查询模式并根据使用情况进行优化。
**更新模型**:随着新嵌入模型的出现,考虑更新到更新的模型。
## 下一步
用户管理和访问控制
系统监控和分析
完整的智能路由 API 文档
高级配置选项
# 欢迎使用
Source: https://docs.mcphub.app/zh/index
MCPHub 是一个开源、自托管的 MCP 网关与控制平面,用于连接、控制和运行 MCP 服务器。
## 什么是 MCPHub?
MCPHub 是一个开源、自托管的 MCP 网关与控制平面,帮助您通过统一端点连接、控制、路由和运行多个 MCP 服务器。
### 核心功能
* **🏗️ 分组管理** - 灵活的服务器分组和配置管理
* **🚀 智能路由** - 基于语义检索的智能路由分发
* **🧩 工具结果压缩** - 在返回客户端前透明压缩大型文本工具输出
* **📊 实时监控** - 全面的性能指标和健康检查
* **🔐 安全认证** - 身份认证和访问控制
## 快速开始
立即开始使用 MCPHub,只需几分钟即可部署您的第一个 MCP 服务器。
跟随我们的快速开始指南,5 分钟内部署 MCPHub 并连接您的第一个 MCP 服务器
设置本地开发环境,了解 MCPHub 的架构和开发工作流
## 核心概念
了解 MCPHub 的核心概念,为深入使用做好准备。
学习 MCPHub 的智能路由算法和配置策略
掌握 MCP 服务器的添加、配置和管理技巧
使用内置的监控工具跟踪性能和识别问题
## 部署选项
MCPHub 支持多种部署方式,满足不同规模和场景的需求。
使用 Docker 容器快速部署,支持单机和集群模式
## API 和集成
MCPHub 提供完整的 RESTful API 和多语言 SDK,方便与现有系统集成。
完整的 API 接口文档,包含详细的请求示例和响应格式
## 社区和支持
加入 MCPHub 社区,获取帮助和分享经验。
查看源代码、提交问题和贡献代码
与其他开发者交流,获取实时帮助
支持 MCPHub 的开发和维护,帮助我们持续改进
# 安装指南
Source: https://docs.mcphub.app/zh/installation
各种平台的详细安装说明
## 先决条件
在安装 MCPHub 之前,请确保您具备以下先决条件:
* **Node.js** 20+ (用于本地开发)
* **Docker** (推荐用于生产环境)
* **pnpm** (用于本地开发)
智能路由的可选要求:
* **PostgreSQL** 带 pgvector 扩展
* **LLM 提供商(OpenAI 或兼容)API 密钥**
## 安装方法
### 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 包安装
将 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
```
在 Windows 上,您可能需要分别运行后端和前端:
```bash theme={null}
# 终端 1:后端
pnpm backend:dev
# 终端 2:前端
pnpm frontend:dev
```
### 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 密钥**
### 数据库设置
```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 实例:
```sql theme={null}
-- 连接到您的 PostgreSQL 实例
-- 创建数据库
CREATE DATABASE mcphub;
-- 连接到 mcphub 数据库
\c mcphub;
-- 启用 pgvector 扩展
CREATE EXTENSION IF NOT EXISTS vector;
```
对于云提供商(AWS RDS、Google Cloud SQL 等):
1. 在您的云提供商控制台中启用 pgvector 扩展
2. 创建名为 `mcphub` 的数据库
3. 记下连接详细信息
## 验证
安装后,验证 MCPHub 是否正常工作:
### 控制台访问
打开浏览器并导航到:
```
http://localhost:3000
```
## 故障排除
**端口已被使用:**
```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
```
**权限错误:**
```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 密钥是否有效
* 检查互联网连接
* 监控速率限制
## 下一步
配置您的 MCP 服务器和设置
5分钟内启动并运行
了解如何管理您的 MCP 服务器
探索完整的 API 文档
# 快速开始指南
Source: https://docs.mcphub.app/zh/quickstart
5 分钟内运行 MCPHub
## 安装
使用 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`。
将 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
```
首次登录后请及时修改管理员密码以确保安全。
### 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`
智能路由需要使用 pgvector 的 PostgreSQL 和 OpenAI API 密钥。
### 示例:添加热门 MCP 服务器
以下是一些您可以添加的热门 MCP 服务器:
```json theme={null}
{
"name": "fetch",
"command": "uvx",
"args": ["mcp-server-fetch"]
}
```
```json theme={null}
{
"name": "playwright",
"command": "npx",
"args": ["@playwright/mcp@latest", "--headless"]
}
```
```json theme={null}
{
"name": "amap",
"command": "npx",
"args": ["-y", "@amap/amap-maps-mcp-server"],
"env": {
"AMAP_MAPS_API_KEY": "your-api-key-here"
}
}
```
```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"
}
}
```
## 后续步骤
学习高级服务器配置和管理
将服务器组织成逻辑组
设置 AI 驱动的工具发现
探索完整的 API 文档
## 故障排除
* 检查 MCP 服务器命令是否在您的 PATH 中可访问
* 验证环境变量是否正确设置
* 检查 MCPHub 日志以获取详细错误信息
* 确保 MCPHub 在正确的端口上运行
* 检查防火墙设置
* 验证端点 URL 格式
* 验证凭据是否正确
* 检查 JWT 令牌是否有效
* 尝试清除浏览器缓存和 cookie
需要更多帮助?加入我们的 [Discord 社区](https://discord.gg/2BJehJZVH5) 获取支持!