Skip to main content

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.

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 column (private / public, with group reserved for a future user→group plumbing) that controls which non-admin users see the server in tools/list and the dashboard. Admins always see every server regardless of this setting; the field only affects the filter applied by dataService.filterData to non-admin requests.
  • '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': reserved. Stored if set but treated as 'private' by the filter until user→group membership ships.
The visibility is editable per server from the dashboard (Server form → Visibility). 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:
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:
curl http://localhost:3000/api/servers \
  -H "x-auth-token: <jwt>"
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. The first admin account is created at startup by initializeDefaultUser(). If no users exist, MCPHub creates admin and uses ADMIN_PASSWORD if set, otherwise it generates a random password and prints it to the server logs.

Bearer keys

Bearer keys are long-lived static tokens managed at /api/auth/keys (admin only). Each key has:
FieldDescription
nameHuman-readable label.
tokenThe secret value sent in the Authorization: Bearer ... header.
accessTypeall (dashboard + MCP), or a scoped value enforced inside sseService.ts for MCP-only access.
enabledDisabled keys are ignored without being deleted.
Only keys with accessType === 'all' are accepted by the dashboard / management API middleware. Restricted 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 & PathPurpose
GET /oauth/authorize, POST /oauth/authorizeAuthorization endpoint (PKCE supported).
POST /oauth/tokenToken endpoint.
GET /oauth/userinfoOIDC-style userinfo (also validates an access token).
GET /.well-known/oauth-authorization-serverRFC 8414 metadata.
GET /.well-known/oauth-protected-resourceProtected resource metadata.
POST /oauth/register, GET/PUT/DELETE /oauth/register/:clientIdRFC 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 only loads when configured. Required environment variables (see .env.example):
  • BETTER_AUTH_URL — public base URL of MCPHub (used to build redirect URIs).
  • GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET — for Google login.
  • GITHUB_CLIENT_ID / GITHUB_CLIENT_SECRET — for GitHub login.
  • OIDC_CLIENT_ID / OIDC_CLIENT_SECRET — for a local OIDC provider.
For a local issuer (for example Keycloak, Authentik, or Dex), add systemConfig.auth.betterAuth.providers.oidc to mcp_settings.json with:
  • enabled
  • providerId
  • discoveryUrl
  • optional scopes, pkce, and prompt
The discoveryUrl should point at the provider’s /.well-known/openid-configuration endpoint. 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 systemConfig.auth.betterAuth.trustedOrigins to allow that origin to start the login flow. If trustedOrigins is omitted, MCPHub automatically trusts systemConfig.install.baseUrl when it is 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). 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.

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 — local user CRUD.
  • OAuth API — OAuth client management endpoints.
  • MCP Settings — where routing, oauth, and auth.betterAuth configuration lives.