Repository navigation
MCP Management
ChatHub provides a settings page for managing MCP plugins at Settings → MCP library. It uses a two-panel layout (list + detail) for browsing installed plugins.
ChatHub supports Streamable HTTP MCP servers only. The application server owns the MCP connection, OAuth credentials, protocol client, and tool-result persistence. It does not start local commands or provide stdio MCP transport.
HTTP MCP tool calls continue after you close the tab, using tokens already stored in PostgreSQL. ChatHub remains HTTP-only; stdio MCP is still unsupported. See Background Conversation Generation.
Navigate to Settings (gear icon) → MCP library. This page lists all installed MCP plugins and lets you add, edit, or remove them.
The MCP library uses a split layout:
- Left panel: Scrollable list of installed MCP plugins with a search bar and an Add MCP Plugin action.
- Right panel: Detail view for the selected plugin showing metadata, configuration, and action buttons (Edit, Uninstall).
When no plugin is selected, the right panel shows an empty state prompting you to select a plugin.
- Click Add MCP Plugin
- Enter the plugin name, description, and MCP server URL
- Configure authentication:
- OAuth 2.1: If the server supports auto-discovery, click Authorize to complete the OAuth flow. If not, enter the Client ID manually.
- API Key: Enter the API key directly.
- None: No authentication required.
- Save
Marketplace entries that provide only a local command are not shown. Custom manifests without an HTTP URL are rejected.
Click a plugin in the left panel, then click Edit in the detail panel. This opens PluginDevModal with the plugin's current configuration pre-filled.
PluginDevModal populates its form in a useEffect that runs only on mount. Without a key prop, switching between plugins leaves stale form data.
Always pass key={identifier || 'create'} to PluginDevModal to force React to unmount and remount the component when switching plugins.
| Prop | Type | Notes |
|---|---|---|
mode |
'create' or 'edit'
|
Controls drawer title and footer |
open |
boolean |
|
onOpenChange |
(open: boolean) => void |
|
value |
LobeToolCustomPlugin |
Plugin data to pre-fill (edit mode) |
onSave |
(value: LobeToolCustomPlugin) => Promise<void> |
Call installCustomPlugin (create) or updateCustomPlugin (edit) |
onDelete |
() => void |
Edit mode only -- call uninstallCustomPlugin
|
onValueChange |
(value: Partial<LobeToolCustomPlugin>) => void |
Always pass updateNewCustomPlugin from store |
key |
string |
CRITICAL -- identifier for edit, 'create' for add |
Select a plugin, then click Uninstall in the detail panel. This removes the plugin from the database and the installed list.
An existing plugin record created by an older desktop edition may still contain a stdio command. ChatHub marks that record unsupported and allows it to be removed, but it cannot connect to or edit it into a runnable local process.
When adding an MCP plugin with OAuth 2.1 authentication:
- Paste the MCP server URL and click Discover
- The server's OAuth metadata is auto-discovered via RFC 9728 + RFC 8414
- If dynamic client registration succeeds, fill in Client ID automatically
- If registration fails, enter the Client ID manually
- Click Authorize -- a popup window opens to the OAuth provider's authorization page
- Complete authorization in the popup
- The popup closes and the form is pre-filled with the result
OAuth tokens are stored server-side in the mcp_oauth_tokens PostgreSQL table. The MCP client automatically:
- Injects the access token into MCP requests
- Refreshes expired tokens proactively
- Retries on 401 with a fresh token
See the MCP OAuth wiki for full details.
In the codebase, installed plugins live in useToolStore(pluginSelectors.installedPlugins). MCP plugins are those with customParams?.mcp:
const installedPlugins = useToolStore(pluginSelectors.installedPlugins, isEqual);
const mcpPlugins = installedPlugins.filter(p => !!p.customParams?.mcp);For the list display (with metadata like title, description, avatar), use pluginSelectors.installedPluginMetaList.
const [installCustomPlugin, updateCustomPlugin, uninstallCustomPlugin, updateNewDevPlugin] =
useToolStore((s) => [
s.installCustomPlugin, // create new plugin
s.updateCustomPlugin, // update existing (id, value)
s.uninstallCustomPlugin, // remove by identifier
s.updateNewCustomPlugin, // update draft state for DevModal
]);Always call useFetchInstalledPlugins() to load the installed plugin list from the database on mount.
When no MCP plugins are installed, the page shows an empty state with an Add MCP Plugin button.
MCP connections recover automatically from common failures:
- Expired server sessions: if the MCP server drops the streamable-HTTP session, the tool call fails over to a fresh connection and retries once. Tool-level errors returned by the server are never retried, so a mutating tool is never executed twice. That guarantee is strict: only a raw connection-level failure (the server never saw your request) triggers the retry — an error response the MCP server itself produced is always surfaced to the model as-is, even when its text happens to mention the session.
- Slow handshakes: connecting to an unresponsive MCP server times out after 30 seconds instead of hanging.
- Repeatedly failing servers: after 3 consecutive connection-level failures, calls to that server fail fast for 60 seconds (instead of each attempt hanging for the full tool timeout), then a trial request is allowed through.
- Dead connections: a crashed or unreachable server is dropped from the connection cache immediately, and idle connections are reclaimed automatically.
- Cancellation: stopping a generation (or closing the browser request) aborts the in-flight MCP call instead of letting it run to the full timeout.
Tool metadata and results are sanitized before they reach the model:
- Description scrubbing: MCP tool descriptions are server-controlled text. Classic prompt-injection imperatives (for example "ignore all previous instructions") are redacted and descriptions are capped at 1,200 characters when tools are discovered.
-
Protocol metadata stripped:
_metafields on tool results (reserved for MCP protocol extensions) are removed before the result is shown to the model; the tool's own data payload is passed through untouched. - Stable tool ordering: the tool list sent to the model is sorted by tool name, so identical tool sets produce byte-identical requests across turns — this keeps provider prompt caches warm.
Operator environment variables:
| Variable | Default | Purpose |
|---|---|---|
MCP_TOOL_TIMEOUT |
60000 |
Maximum time (ms) for a single MCP tool call |
MCP_INITIALIZATION_TIMEOUT |
30000 |
Maximum time (ms) to establish an MCP connection |
MCP_CLIENT_IDLE_TTL_MS |
1800000 |
Evict cached MCP connections idle longer than this |
MCP_CLIENT_CACHE_MAX_SIZE |
100 |
Maximum cached MCP connections (least-recently-used evicted) |
- Home
- Quick Start
- Docker Deployment and Upgrades
- Authentication
- Model Providers
- Knowledge Base and RAG
- MarkItDown Sidecar
- Sandbox (OpenSandbox)
- OpenAI Compatible Cache Matrix
- Model Extension Options
- Memory & Context Compaction
- Background Conversation Generation
- Pasted Text
- Context Export
- Data Backup and Restore
- Image Generation
- Artifacts
- Inline SVG Diagrams
- Tools Hub
- MCP OAuth
- MCP Management
- Agent Skills
- Database
- Development
- Debug and Diagnostics
- Architecture Overview
- Docker Release
- Release Workflow