mcp-server-base

mcp-server-base

Provides a production-ready Model Context Protocol server with dual STDIO and Streamable HTTP transports, enabling file operations, memory, database queries, RAG, web search, GitHub integration, background tasks, and prompt-based workflows.

Category
Visit Server

README

MCP Server Base v3.0 — Scale & Enterprise (2026)

CI Node 20+ MCP SDK 1.12.1 TypeScript 5.7 License MIT Coverage 88% Version 3.0.0 Smithery

Modern Model Context Protocol server using the latest stack:

  • MCP SDK 1.12+ — McpServer high-level API + StreamableHTTPServerTransport (new) & StdioServerTransport
  • TypeScript 5.7 ESM + NodeNext module
  • Zod validation → auto JSON Schema + env validation (src/config.ts:1)
  • Express 4 + helmet + CORS allowlist + rate-limit + health/ready + Admin UI
  • Dual transport: STDIO (Claude Desktop) and Streamable HTTP (remote, 2025-03 spec, stateless + stateful resumability via RedisEventStore)
  • Structured tool/resource/prompt modules + RAG hybrid (BM25+vector), Web (cached), GitHub integrations
  • Plugin SDK (src/plugin/index.ts:1) + integrations (slack/notion/linear), Registry (smithery.yaml, mcpName), Prompt Playground at /admin
  • v3.0 Enterprise: Multi-tenant (X-Tenant-Id, namespaced stores), SSO OIDC, Control plane CRUD, cluster mode, Prometheus/Grafana stack
  • OTEL tracing/metrics (src/utils/otel.ts:1), Tasks (experimental + create_task), k6 load tests
  • tsx watch, vitest (178 tests, 88% coverage), graceful shutdown, docker-compose (redis, postgres, qdrant, otel-collector, prometheus, grafana)

šŸš€ Quick Start

npm install
npm run build

# STDIO (for Claude Desktop, Cursor, opencode, etc.)
npm start

# HTTP (Streamable HTTP - latest)
npm run start:http
# → http://localhost:3000/mcp
# → health http://localhost:3000/health

Dev

npm run dev          # stdio watch
npm run dev:http     # http watch (Streamable HTTP at http://localhost:3000/mcp)
npm test             # unit + e2e (InMemory + HTTP)
npm run test:coverage # coverage 80% thresholds
npm run lint         # eslint 9 flat config
npm run format:check # prettier
npm run typecheck    # tsc --noEmit
npm run build

CI

.github/workflows/ci.yml runs on push/PR to main with Node 20+22 matrix: lint, format:check, typecheck, test:coverage, build, docker build.


šŸ”Œ Transports

Transport Use Command
STDIO Local clients (Claude Desktop) node dist/index.js
Streamable HTTP Remote / Docker / Cloud node dist/index.js --http

Streamable HTTP is the new standard replacing SSE (deprecated March 2025).


🧰 Tools (40)

Tool Description Input
echo Echo message message, uppercase?
calculator add/sub/mul/div operation, a, b
get_time Current time timezone?
fetch_url Fetch URL url, maxLength?
list_files List files under ALLOWED_ROOT path?, recursive?
read_file Read file (1MB limit) path
write_file Write file + triggers resource changed path, content
search_files Search text inside files query, path?, maxResults?
memory_set Set KV in memory key, value
memory_get Get KV key
memory_delete Delete KV key
memory_list List KVs —
memory_clear Clear all —
database_query SQL via alasql (users, notes) sql
database_tables List tables row counts —
shell_execute Shell (allowlist, disabled by default) command, timeout?
collect_user_info Elicitation demo (contact/preferences) infoType?
generate_with_sampling Sampling demo (LLM) prompt, maxTokens?
rag_ingest Ingest text (chunked, embedded) text, id?, metadata?, chunk?
rag_search Hybrid search (vector+BM25) query, topK?, threshold?, mode?
rag_list List docs —
rag_clear Clear vector store —
brave_search Brave API (mock if no key) query, count?
tavily_search Tavily API (mock if no key) query, maxResults?, includeAnswer?
web_fetch Cached web fetch url, useCache?, maxLength?
github_search_repos GitHub search repos query, perPage?
github_get_repo GitHub get repo repo
github_get_issue GitHub get issue repo, issueNumber
create_task Create background task duration?, payload?
get_task Get task status taskId
get_task_result Get task result taskId
slack_list_channels Slack channels (plugin) —
slack_post_message Slack post (plugin) channel, text
slack_search Slack search (plugin) query, count?
notion_search Notion search (plugin) query, page_size?
notion_get_page Notion page (plugin) page_id
notion_create_page Notion create (plugin) title, content?
linear_list_issues Linear issues (plugin) team?, limit?
linear_create_issue Linear create (plugin) title, description?, team?
linear_get_issue Linear issue (plugin) id

šŸ“¦ Resources (6)

  • config://server-info — server metadata (JSON, now includes features)
  • greeting://{name} — dynamic greeting template
  • file:///{+path} — sandboxed file (ALLOWED_ROOT), list + complete, file:///notes.txt
  • memory://{key} — memory KV, list + complete
  • db://{table}/{id} — demo DB row (users/notes), list + complete
  • docs://{id} — RAG chunk (ingested via rag_ingest), list + complete

šŸ’¬ Prompts (4)

  • code-review — args: language, code
  • explain-concept — args: concept, level
  • summarize — args: text, length (short/medium/long), style (bullets/paragraph/tldr)
  • research — args: topic, depth (overview/deep), audience (beginner/expert/executive)

āš™ļø Client Config

Claude Desktop (claude_desktop_config.json)

{
  "mcpServers": {
    "mcp-server-base": {
      "command": "node",
      "args": ["/absolute/path/to/mcp-server/dist/index.js"]
    }
  }
}

HTTP Client

import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
import { Client } from '@modelcontextprotocol/sdk/client/index.js';

const client = new Client({ name: 'my-client', version: '1.0.0' });
await client.connect(new StreamableHTTPClientTransport(new URL('http://localhost:3000/mcp')));
const tools = await client.listTools();

Inspector

npm run inspect
# or
npx @modelcontextprotocol/inspector node dist/index.js
npx @modelcontextprotocol/inspector http://localhost:3000/mcp

🐳 Docker

# Single container
docker build -t mcp-server-base .
docker run -p 3000:3000 --env TRANSPORT=http mcp-server-base

# Full stack (app + redis + postgres + qdrant) — see docker-compose.yml
docker compose up -d
docker compose logs -f app
# → http://localhost:3000/health, http://localhost:3000/mcp
# → redis :6379, postgres :5432, qdrant :6333

RAG Demo (ingest → search → docs://)

# via MCP tools (Inspector or Client)
# 1. ingest
rag_ingest { "text": "MCP is Model Context Protocol...", "id": "mcp-intro" }
# 2. search
rag_search { "query": "what is MCP?", "topK": 3 }
# 3. read resource
# docs://mcp-intro  → returns ingested text

šŸ“ Structure

src/
ā”œā”€ā”€ index.ts              # entry: stdio + http (helmet/cors/rateLimit/auth/RBAC/OTEL/metrics)
ā”œā”€ā”€ server.ts             # createMcpServer() factory (v2.1.0, instructions)
ā”œā”€ā”€ config.ts             # zod env (AUTH, CORS, rateLimit, RAG, cache, integrations, OTEL, admin, tasks)
ā”œā”€ā”€ types.ts              # Zod schemas
ā”œā”€ā”€ middleware/auth.ts    # AUTH_MODE none|apiKey|bearer
ā”œā”€ā”€ middleware/rateLimit.ts
ā”œā”€ā”€ middleware/requestId.ts
ā”œā”€ā”€ middleware/rbac.ts    # RBAC reader/writer/admin + mcpRbacMiddleware (v2.1)
ā”œā”€ā”€ observability/otel-real.ts # OTEL NodeSDK init (v2.1)
ā”œā”€ā”€ observability/slo.ts  # checkSlo() for /health/ready (v2.1)
ā”œā”€ā”€ utils/logger.ts       # stderr, JSON/text, redaction, child(requestId)
ā”œā”€ā”€ utils/eventStore.ts   # InMemoryEventStore
ā”œā”€ā”€ utils/redisEventStore.ts # RedisEventStore (scale)
ā”œā”€ā”€ utils/cache.ts        # MemoryCache (TTL) + defaultCache
ā”œā”€ā”€ utils/queue.ts        # SimpleQueue
ā”œā”€ā”€ utils/metrics.ts      # prom-client Registry (v2.1)
ā”œā”€ā”€ utils/persistence.ts  # save/load backup (v2.1)
ā”œā”€ā”€ utils/otel.ts         # stub OTEL spans/metrics
ā”œā”€ā”€ tools/                # 40 tools: echo, fs, memory, db, shell, rag (hybrid), web, github, elicitation, sampling, tasks
│   ā”œā”€ā”€ filesystem.tool.ts, memory.tool.ts, database.tool.ts, shell.tool.ts
│   ā”œā”€ā”€ rag.tool.ts, web.tool.ts, github.tool.ts, elicitation.tool.ts, sampling.tool.ts, tasks.tool.ts
ā”œā”€ā”€ plugin/index.ts       # Plugin SDK: definePlugin/registerPlugin (v2.2)
ā”œā”€ā”€ integrations/         # slack/notion/linear plugins (v2.2)
ā”œā”€ā”€ middleware/tenant.ts  # Multi-tenant X-Tenant-Id + scoped stores (v3.0)
ā”œā”€ā”€ routes/controlplane.ts# Tenants CRUD + key rotation (v3.0)
ā”œā”€ā”€ utils/cluster.ts      # Cluster mode horizontal scale (v3.0)
ā”œā”€ā”€ resources/            # 6 resources: config, greeting, file, memory, db, docs
ā”œā”€ā”€ routes/admin.ts       # Admin UI + metrics/spans/stores (v2.0) + /metrics Prometheus (v2.1)
└── prompts/              # 4 prompts: code-review, explain-concept, summarize, research

Add a new tool: create src/tools/my.tool.ts → export registerMyTool(server) → add to src/tools/index.ts.


šŸ” Security (Phase 2)

  • Helmet headers (x-dns-prefetch-control, x-frame-options, x-content-type-options, etc.) via helmet@7 (src/index.ts:1)
  • CORS allowlist (CORS_ORIGIN=* or comma list) with cors credentials handling (src/config.ts:60)
  • Auth AUTH_MODE=none|apiKey|bearer at src/middleware/auth.ts:1 — 401 without valid X-API-Key or Authorization: Bearer (health/ready & OPTIONS excluded)
  • Rate limiting express-rate-limit (default 100/15min) on /mcp — 429 Too Many Requests (src/middleware/rateLimit.ts:1)
  • RequestId (X-Request-Id randomUUID, echo header, child logger correlation) (src/middleware/requestId.ts:1)
  • Zod env validation (src/config.ts:1) — parseEnv() validates PORT, AUTH_MODE, API_KEY cross-field, fails fast on invalid env
  • Structured logger JSON/text, [REDACTED] for authorization, apiKey, token (src/utils/logger.ts:24)
  • Resumability InMemoryEventStore (src/utils/eventStore.ts:1) + stateful session map when RESUMABILITY_ENABLED=true (replay via Last-Event-ID, GET /mcp stream, DELETE close)
  • Docker hardening non-root appuser + HEALTHCHECK (Dockerfile:1)
  • Tests: tests/unit/auth.test.ts, tests/unit/logger.test.ts, tests/unit/eventStore.test.ts, tests/e2e/security.test.ts (helmet/auth/rateLimit/resumability) — 67 tests → 130 total with Phase 5, 90.89% coverage

šŸ”— Integrations (Phase 4)

  • Cache MemoryCache TTL (src/utils/cache.ts:1) — defaultCache for web/github, SimpleQueue (src/utils/queue.ts:1)
  • RAG local vector (hash embedding 128-dim, cosine, chunk 500/50) at src/tools/rag.tool.ts:1 — rag_ingest (chunked + sendResourceListChanged), rag_search (topK, threshold), rag_list, rag_clear + docs://{id} resource
  • Web src/tools/web.tool.ts:1 — brave_search (mock if no BRAVE_API_KEY), tavily_search (mock), web_fetch (cached via defaultCache, CACHE_TTL_MS)
  • GitHub src/tools/github.tool.ts:1 — github_search_repos, github_get_repo, github_get_issue (cached, GITHUB_TOKEN for rate limit)
  • Stack docker-compose.yml:1 (app + redis:7 + postgres:16 + qdrant:v1.12.4) with healthchecks
  • Demo rag_ingest → rag_search → docs:// E2E verified in tests/integrations.test.ts:1 (21 tests)

šŸ¢ Scale & Enterprise (Phase 5 — v3.0) — NEW

  • Multi-tenant src/middleware/tenant.ts:1 — X-Tenant-Id header (or ?tenant=), tenantMiddleware rejects 400 when TENANT_REQUIRED=true and missing, tenant-scoped memory getTenantMemory(tid) (isolated stores), namespaced cache keys tenant:{id}:key, registry createTenant/deleteTenant
  • SSO OIDC src/middleware/auth.ts:1 — AUTH_MODE=oidc: Bearer JWT structural validation (3-part, exp, iss vs OIDC_ISSUER, aud vs OIDC_AUDIENCE), claims attached to req.oidcClaims; production swaps in JWKS signature verification
  • Control plane src/routes/controlplane.ts:1 — CRUD /admin/tenants (POST/GET/PATCH/DELETE + 409 dup/400 invalid-id), POST /admin/tenants/:id/rotate-key, GET /admin/tenants/:id/store isolation inspection; admin-token protected
  • Runtime scale src/utils/cluster.ts:1 — initCluster() forks CLUSTER_WORKERS (default CPU-1), auto-restart on worker exit, CLUSTER_MODE=true; stateless + RedisEventStore for true horizontal
  • Monitoring stack docker-compose.override.yml:1 — Prometheus (prometheus.yml scrapes app:3000/metrics) at :9090 + Grafana at :3001
  • Tests tests/v3_0.test.ts:1 (15 tests: tenant extraction/isolation/required-400, OIDC token validation + issuer/audience + HTTP 401/200, control plane lifecycle 201/409/400/404/rotate-key/store-inspection/disabled-404, cluster no-op, compose services) — total 178

šŸ”Œ Ecosystem & DX (Phase 5 — v2.2)

  • Plugin SDK src/plugin/index.ts:1 — definePlugin({name, version, register}), registerPlugin(server, plugin) (duplicate-tolerant), getRegisteredPlugins(), auto sendToolListChanged notifications
  • Integrations src/integrations/ — slackPlugin (list/post/search), notionPlugin (search/get/create), linearPlugin (list/create/get) — all mocked without tokens (SLACK_TOKEN, NOTION_TOKEN); auto-registered in src/server.ts:1
  • Registry smithery.yaml:1 — Smithery config + MCP Registry name io.github.ahmedalbanna/mcp-server-base, package.json:1 mcpName/files fields
  • Hybrid RAG src/tools/rag.tool.ts:1 — rag_search modes vector|bm25|hybrid (default hybrid, alpha 0.5), BM25 (k1=1.5, b=0.75) normalized + cosine re-rank; eval set tests/eval/rag-eval.json (20 Q/A) with p@5 ≄0.8 verified in tests/v2_2.test.ts:1
  • Prompt Playground src/routes/admin.ts:1 — POST /admin/prompts/:name/preview renders prompts server-side; playground UI in /admin dashboard
  • Tests tests/v2_2.test.ts:1 (16 tests: Plugin SDK define/register/dup/tools callable, registry files, hybrid modes, eval p@5, playground preview/UI/count) — total 163

šŸ“ˆ Scale & Operability (Phase 5 — v2.0)

  • Versioned MCP v2.0.0 (package.json:1, config.MCP_SERVER_VERSION) with instructions per minor (src/server.ts:1)
  • OTEL tracing/metrics (src/utils/otel.ts:1) — createSpan/withSpan, incrementCounter/recordHistogram, getMetrics/getSpans, JSON export stub for OTEL_EXPORTER_OTLP_ENDPOINT, OTEL_ENABLED flag
  • RedisEventStore (src/utils/redisEventStore.ts:1) — EventStore impl with storeEvent/replayEventsAfter, in-memory fallback, eventStoreFactory.create() for horizontal scale (EVENT_STORE_TYPE=memory|redis, REDIS_URL)
  • Admin UI (src/routes/admin.ts:1) — GET /admin (HTML dashboard), /admin/tools|resources|prompts|metrics|spans|stores|health (JSON), protected via ADMIN_TOKEN (X-Admin-Token), ADMIN_ENABLED flag
  • Tasks (src/tools/tasks.tool.ts:1) — experimental delay_task (if SDK tasks available) + fallback create_task/get_task/get_task_result (in-memory, polling), SimpleQueue/MemoryCache infra
  • Bench k6/load.js:1 — http_req_duration p(95)<100ms, stages 10→50 VUs, checks >99%, npm run bench / bench:local
  • Compose docker-compose.yml:1 already includes redis/postgres/qdrant for scale
  • Tests: tests/scale.test.ts:1 (OTEL spans/metrics, RedisEventStore replay, cache TTL, queue, admin HTML/metrics/token/ready, tasks create/poll, version, k6 script) — 130 total (now 147 with v2.1)
  • Deploy ready for Fly.io/Cloud Run (stateless + RedisEventStore), GHCR via release.yml, npm 2.0.0

šŸ”’ Hardening & Observability (Phase 5 — v2.1) — NEW

  • OTEL Real src/observability/otel-real.ts:1 — initOtel() dynamic import of @opentelemetry/sdk-node + OTLPTraceExporter (console fallback), OTEL_ENABLED + OTEL_EXPORTER_OTLP_ENDPOINT, graceful shutdown on SIGTERM

  • SLOs src/observability/slo.ts:1 — checkSlo() (memory/rag/cache/uptime, latencyMs), GET /health → {status, checks, uptime, version, otel, eventStore} + GET /ready → 503 if not ok, GET /metrics → Prometheus text/plain via prom-client (mcp_http_requests_total, mcp_http_request_duration_ms, mcp_sessions_active)

  • RBAC src/middleware/rbac.ts:1 — reader < writer < admin (X-Role header, JWT stub), TOOL_ROLES map (31 tools), mcpRbacMiddleware inspects tools/call body → 403 if reader tries write_file/shell_execute, GET /admin → admin required

  • Backup src/utils/persistence.ts:1 — saveBackup()/loadBackup()/scheduleSave() (500ms debounce) to ALLOWED_ROOT/.backup.json (dynamic getBackupFile()), logs Redis sync stub when REDIS_URL set, loadBackup() at startup in src/index.ts:1

  • Metrics src/utils/metrics.ts:1 — prom-client Registry + collectDefaultMetrics, httpRequestsTotal/httpRequestDuration/mcpToolCallsTotal/mcpSessionsActive, GET /metrics handler

  • Tests tests/v2_1.test.ts:1 (13 tests: SLO checks, RBAC hasRole/X-Role 403/200, SLO health/metrics 200 + Prometheus text, OTEL span, backup save/load + scheduleSave via memory_set, initOtel disabled/enabled) + tests/unit/rbac.test.ts:1 (4 tests) — total 147

  • Logger stderr-safe, never logs secrets (redaction)

  • Zod → JSON Schema via SDK (src/types.ts:1, src/tools/*.tool.ts)

  • Timeout on fetch (10s) + structured errors

  • Graceful shutdown (SIGINT/SIGTERM)

  • Health (GET /health) & ready (GET /ready) separate from MCP

  • Stateless default (sessionIdGenerator: undefined), stateful when RESUMABILITY_ENABLED=true (src/index.ts:22)

  • Type-safe, strict TS + ESLint flat + Prettier + husky + lint-staged

  • Coverage 85% lines / 70% branches enforced (vitest.config.ts:1), 178 tests: unit + e2e HTTP/security/capabilities/integrations/scale/v2.1/v2.2/v3.0

šŸ“š Docs

Full documentation in docs/:

  • Architecture — diagram, module map, request lifecycle
  • Configuration — every env var + validation rules
  • API Reference — tools/resources/prompts/endpoints
  • Security — auth, RBAC, OIDC, tenancy, hardening checklist
  • Plugins — Plugin SDK guide + registry distribution
  • Deployment — Docker, cluster/multi-replica scale, monitoring, k6
  • Testing — suite map, patterns, CI

šŸ¤ Contributing

See CONTRIBUTING.md — nvm use, npm test, add tool/resource/prompt, ensure lint/typecheck/test pass. See CODE_OF_CONDUCT.md.


šŸ“š MCP Docs

  • Spec: https://spec.modelcontextprotocol.io
  • SDK: https://github.com/modelcontextprotocol/typescript-sdk
  • Inspector: https://github.com/modelcontextprotocol/inspector

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured
E2B

E2B

Using MCP to run code via e2b.

Official
Featured