Authoritative instructions for AI agents and contributors working in this repo. Trust this file first; only fall back to wider search when something here is missing or contradicts the code. When you discover a contradiction, fix the code or fix this file — never let them drift.
This guide is intentionally short. It points to where the details live rather than restating them. Read the linked file when you need depth.
@samanhappy/mcphub is a TypeScript/Node (ESM) hub that aggregates multiple MCP (Model Context Protocol) servers behind a single HTTP surface, with a React/Vite dashboard.
| Layer | Entry | Notes |
|---|---|---|
| Backend | src/index.ts → src/server.ts | Express + TypeORM (optional) + MCP SDK |
| Frontend | frontend/src/ | Vite + React + Tailwind, i18n via react-i18next |
| Config | mcp_settings.json | MCP server defs, users, groups, OAuth state |
| Docs | docs/ | Public docs (Mintlify). See docs/development/ for architecture |
Layout map (read the directory when you need specifics, don't expand it here):
src/{routes,controllers,services,dao,db,middlewares,models,utils,types,config,clients}/src/services/mcpService.ts— core MCP connection/lifecycle logicsrc/dao/+src/db/— dual datasource (JSON file by default, PostgreSQL whenUSE_DB=true)tests/mirrorssrc/; colocated*.test.tsalso picked uplocales/{en,fr,tr,zh}.json— i18n stringsdist/,frontend/dist/,coverage/— generated; never hand-edit
| Purpose | Command |
|---|---|
| Install | pnpm install |
| Dev (both) | pnpm dev |
| Dev backend only | pnpm backend:dev |
| Dev frontend | pnpm frontend:dev |
| Lint | pnpm lint |
| Format | pnpm format |
| Test (CI mode) | pnpm test:ci |
| Test (watch) | pnpm test:watch |
| Build all | pnpm build |
| Verify dist | node scripts/verify-dist.js |
| Start prod | pnpm start |
Backend listens on :3000 (or $PORT). Frontend dev server :5173 proxies API calls to the backend.
pnpm lint && pnpm test:ci && pnpm buildThe runtime requirement is ^18.0.0 || >=20.0.0 (see package.json); CI runs on Node 20.x, and the published Docker image is built on Node 22 (see Dockerfile). If a hook fails, fix the root cause — do not bypass with --no-verify.
- Touched backend startup / MCP wiring →
pnpm dev, hitGET /health, scan logs forSuccessfully connected client for server: …. - Touched frontend UI → open the page in
pnpm frontend:devand exercise the path; tests don't catch UX regressions. - Some MCP servers fail to connect without API keys — that is expected locally and not a regression signal.
- ESM imports: always use
.jsextensions for relative imports (e.g.import { x } from './foo.js'). TS files included. - TypeScript strict is on; do not weaken it. Don't paper over types with
any— narrow or define a type. - Comments in English only.
- Formatting: 2-space indent, single quotes — let Prettier decide (
pnpm format). ESLint config is the flat eslint.config.mjs. - Naming:
- Services / DAOs:
XxxService,XxxDao(suffix-based). - React components & their files:
PascalCase. - Utility modules:
camelCase.
- Services / DAOs:
- Shared types live in src/types/. Don't redefine DTOs locally.
- Conventional Commits (
feat:,fix:,chore:,refactor:, …), imperative present tense. - PRs: describe behavior change, list manual + automated tests, attach before/after for UI work, link issues. Keep generated artifacts out of the diff.
USE_DB=true + DB_URL switches storage from mcp_settings.json to Postgres via TypeORM.
When adding/changing a persisted field, update every layer or migrations will silently drop it:
| # | File | What to do |
|---|---|---|
| 1 | src/types/index.ts |
Add/modify the interface |
| 2 | src/dao/*Dao.ts |
JSON impl (if behavior changes) |
| 3 | src/db/entities/*.ts |
TypeORM @Column (mark nullable, use simple-json for objects) |
| 4 | src/dao/*DaoDbImpl.ts |
Map field in create/update |
| 5 | src/db/repositories/*.ts |
Update if the wrapper exposes it |
| 6 | src/utils/migration.ts |
Include in JSON→DB migration |
| 7 | mcp_settings.json |
Update the example if user-facing |
Model ↔ DAO ↔ entity ↔ JSON-path mapping: inspect the DAO files in src/dao/ directly — they are the source of truth, and any table reproduced here would drift.
Common pitfalls: forgetting step 6 → silent migration drop; missing nullable: true → DB write fails; complex object stored without simple-json → serialization error.
Bearer keys have two explicit kinds: legacy and operator-created keys are kind: 'system'; user-level keys are kind: 'user' and must have an owner. Do not infer kind from whether owner is empty. User-level keys restore the owner's live user context on MCP transport requests and are never dashboard API credentials.
/mcp/{group|server}— route to a specific group or single server./mcp/$smart— smart routing./:user/mcp/{group|server},/:user/sse/{group}— user-scoped variants; the:userprefix selects which owner's servers/groups are visible. SeeAppServer.initialize()in src/server.ts.- Auth: JWT (HS256) + bcrypt for the dashboard, layered with bearer keys (
/api/auth/keys), MCPHub's own OAuth authorization server (@node-oauth/oauth2-server), and optional Better Auth (GitHub/Google). Default admin password is randomized unlessADMIN_PASSWORDis set on first launch.
For deeper architecture context, read docs/development/architecture.mdx.
- Jest with
ts-jestESM preset. Shared setup in tests/setup.ts, helpers in tests/utils/. - Place suites under
tests/<area>/mirroringsrc/, or colocate assrc/**/*.test.ts. Both are picked up. - Name files
*.test.ts/*.spec.ts. - Touching auth, OAuth, or SSE flows? Add or extend an integration test under tests/integration/. Don't lower coverage on these paths.
- Do not modify tests to make them pass unless the behavior change is the point of the PR.
| Task | Where to start |
|---|---|
| Add an MCP server | Edit mcp_settings.json, restart backend, check connect log line |
| Add an HTTP API | src/routes/ → src/controllers/ → types in src/types/ → tests |
| Add a frontend page | frontend/src/pages/ (+ frontend/src/components/), wire in router |
| Add a CLI subcommand | src/cli/commands/<name>.ts + register in src/cli/main.ts dispatcher + src/cli/help.ts + tests in tests/cli/commands/ |
| Add a translation key | Add to all four locales/*.json files |
| Update public docs | docs/ (Mintlify); reflect major changes in README.md |
- MCP server fails to start: validate JSON in
mcp_settings.json; check thecommand/argsresolves on PATH (e.g.uvxis required for Python-based servers that use it, such as the defaultfetchserver). - Frontend not served: production mode needs
pnpm frontend:buildfirst. - Type errors:
pnpm backend:buildshows the full TS output. - Port conflict: change
PORTor kill the holder.
gh auth status
gh api repos/samanhappy/mcphub/security-advisories/<ghsa_id>
gh api repos/samanhappy/mcphub/code-scanning/alerts/<alert_number>
gh api repos/samanhappy/mcphub/code-scanning/alerts/<alert_number>/instancesCompare advisory details against current main and the tagged fix commit before deciding if the issue is still live.
For CodeQL/code-scanning alerts, prefer the API calls above over opening github.com/.../security/code-scanning/... directly — the web page may 404 or hide details without the right GitHub session, while alerts/<id> returns the rule, severity, most_recent_instance, and message, and /instances confirms the concrete locations.
This file is a living contract. Keep it accurate; resist the urge to grow it.
Update when:
- A path, command, or invariant referenced here no longer matches the code.
- The user states a durable preference (style, workflow, tool choice) that future agents need to honor.
- A new architectural invariant emerges (something that, if violated, will silently break things) — like the dual-datasource checklist above.
Don't add:
- Step-by-step build logs, command timings, or "NEVER CANCEL" warnings — agents read the docs, not the stopwatch.
- Anything trivially discoverable by
ls,grep, or reading the file the section already points to. - Per-task narratives or recent-change summaries — those belong in commits and PRs.
How to edit:
- Find the smallest section that owns the fact. Edit in place.
- Prefer a pointer (
see X) over copying details. One source of truth per fact. - If a section grows past a screen, it's probably duplicating something downstream — push detail into the relevant subdirectory's README/AGENTS.md instead.
- After editing, skim the rest for stale references to what you changed.
Scoped guides: when a subsystem accumulates rules that don't generalize (e.g. frontend-only conventions), prefer a nested frontend/AGENTS.md over bloating this file. Agents read the nearest AGENTS.md to the files they're editing.
GitHub Issues for samanhappy/mcphub, accessed via the gh CLI. See docs/agents/issue-tracker.md.
Five canonical labels: needs-triage, needs-info, ready-for-agent, ready-for-human, wontfix. See docs/agents/triage-labels.md.
Single-context layout: CONTEXT.md at repo root + docs/adr/ for architectural decision records. See docs/agents/domain.md.