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.Documentation Index
Fetch the complete documentation index at: https://docs.mcphub.app/llms.txt
Use this file to discover all available pages before exploring further.
Process model
MCPHub runs as a single Node.js (Express) process. The entrypoint issrc/index.ts, which delegates to AppServer in src/server.ts. AppServer is responsible for:
- Initializing i18n (
src/utils/i18n.ts). - Bootstrapping the default admin user (
src/models/User.ts:initializeDefaultUser). - Initializing the optional upstream-MCP OAuth client provider (
src/services/oauthService.ts) and the optional MCPHub OAuth authorization server (src/services/oauthServerService.ts). - Wiring middlewares (
src/middlewares/index.ts) and HTTP routes (src/routes/index.ts). - Spawning / connecting all enabled upstream MCP servers via
initUpstreamServers()insrc/services/mcpService.ts. - Mounting the SSE / streamable-HTTP MCP routes (
/mcp/:group,/sse/:group, and their user-scoped variants/:user/mcp/...,/:user/sse/...). - Serving the built frontend from
frontend/dist/(unlessDISABLE_WEB=true).
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 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 insrc/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:File mode (default)
All persistent state lives inmcp_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 insrc/db/entities/:
User,BearerKey,OAuthClient,OAuthTokenServer,Group,SystemConfig,UserConfigBuiltinPrompt,BuiltinResourceActivity— request/response logging (only populated in DB mode).VectorEmbedding— pgvector-backed table used by smart routing.
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 for setup details.
Request paths
There are three classes of HTTP route, all defined insrc/routes/index.ts and mounted by AppServer:
- Public endpoints (no auth):
GET /healthGET ${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.
- Authenticated dashboard / management API under
${basePath}/api/*. Authentication is enforced bysrc/middlewares/auth.ts, which accepts (in order):- A configured bearer key (
/api/auth/keys) whoseaccessTypeisall. - An OAuth access token issued by MCPHub’s authorization server (when enabled).
- A Better Auth session cookie (when enabled).
- An
x-auth-tokenJWT issued by/api/auth/login, or the same value as a?token=query. - If
systemConfig.routing.skipAuthistrue, unauthenticated dashboard API calls receive a syntheticguestuser with admin privileges.
- A configured bearer key (
- 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 viamcpConnectionRateLimiterand pass throughsseUserContextMiddlewarebefore being dispatched bysseService.ts. Per-route bearer key scopes (accessType !== 'all') are enforced insidesseService.ts.
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 themcphub_activitytable. Database mode only.
/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 underfrontend/. 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.