Skip to main content
Glama
ahmedalbanna

mcp-server-base

by ahmedalbanna

MCP Server Base v3.0 — Escala y Empresa (2026)

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

Servidor moderno de Model Context Protocol que utiliza el stack más reciente:

  • MCP SDK 1.12+ — API de alto nivel McpServer + StreamableHTTPServerTransport (nuevo) y StdioServerTransport

  • TypeScript 5.7 ESM + módulo NodeNext

  • Zod validación → JSON Schema automático + validación de entorno (src/config.ts:1)

  • Express 4 + helmet + CORS allowlist + rate-limit + health/ready + Admin UI

  • Transporte dual: STDIO (Claude Desktop) y Streamable HTTP (remoto, especificación 2025-03, sin estado + reanudación con estado mediante RedisEventStore)

  • Módulos estructurados de herramientas/recursos/prompts + RAG híbrido (BM25+vector), Web (con caché), integraciones GitHub

  • Plugin SDK (src/plugin/index.ts:1) + integraciones (slack/notion/linear), Registry (smithery.yaml, mcpName), Prompt Playground en /admin

  • v3.0 Enterprise: Multi-tenant (X-Tenant-Id, almacenes con espacios de nombres), SSO OIDC, CRUD de plano de control, modo clúster, stack Prometheus/Grafana

  • OTEL trazado/métricas (src/utils/otel.ts:1), Tasks (experimental + create_task), pruebas de carga k6

  • tsx watch, vitest (178 pruebas, 88% cobertura), apagado elegante, docker-compose (redis, postgres, qdrant, otel-collector, prometheus, grafana)


🚀 Inicio rápido

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 se ejecuta en push/PR a main con matriz Node 20+22: lint, format:check, typecheck, test:coverage, build, docker build.


Related MCP server: TokenHub MCP

🔌 Transportes

Transporte

Uso

Comando

STDIO

Clientes locales (Claude Desktop)

node dist/index.js

Streamable HTTP

Remoto / Docker / Cloud

node dist/index.js --http

Streamable HTTP es el nuevo estándar que reemplaza a SSE (obsoleto desde marzo de 2025).


🧰 Herramientas (40)

Herramienta

Descripción

Entrada

echo

Mensaje de eco

message, uppercase?

calculator

add/sub/mul/div

operation, a, b

get_time

Hora actual

timezone?

fetch_url

Obtener URL

url, maxLength?

list_files

Listar archivos bajo ALLOWED_ROOT

path?, recursive?

read_file

Leer archivo (límite 1MB)

path

write_file

Escribir archivo + activa recurso cambiado

path, content

search_files

Buscar texto dentro de archivos

query, path?, maxResults?

memory_set

Establecer KV en memoria

key, value

memory_get

Obtener KV

key

memory_delete

Eliminar KV

key

memory_list

Listar KVs

—

memory_clear

Borrar todo

—

database_query

SQL mediante alasql (users, notes)

sql

database_tables

Listar recuentos de filas de tablas

—

shell_execute

Shell (lista blanca, deshabilitado por defecto)

command, timeout?

collect_user_info

Demo de elicitación (contacto/preferencias)

infoType?

generate_with_sampling

Demo de muestreo (LLM)

prompt, maxTokens?

rag_ingest

Ingerir texto (fragmentado, incrustado)

text, id?, metadata?, chunk?

rag_search

Búsqueda híbrida (vector+BM25)

query, topK?, threshold?, mode?

rag_list

Listar documentos

—

rag_clear

Limpiar almacén de vectores

—

brave_search

API Brave (simulado si no hay clave)

query, count?

tavily_search

API Tavily (simulado si no hay clave)

query, maxResults?, includeAnswer?

web_fetch

Obtención web con caché

url, useCache?, maxLength?

github_search_repos

Búsqueda de repositorios en GitHub

query, perPage?

github_get_repo

Obtener repositorio de GitHub

repo

github_get_issue

Obtener issue de GitHub

repo, issueNumber

create_task

Crear tarea en segundo plano

duration?, payload?

get_task

Obtener estado de la tarea

taskId

get_task_result

Obtener resultado de la tarea

taskId

slack_list_channels

Canales de Slack (plugin)

—

slack_post_message

Publicar en Slack (plugin)

channel, text

slack_search

Búsqueda en Slack (plugin)

query, count?

notion_search

Búsqueda en Notion (plugin)

query, page_size?

notion_get_page

Página de Notion (plugin)

page_id

notion_create_page

Crear en Notion (plugin)

title, content?

linear_list_issues

Issues de Linear (plugin)

team?, limit?

linear_create_issue

Crear en Linear (plugin)

title, description?, team?

linear_get_issue

Issue de Linear (plugin)

id

📦 Recursos (6)

  • config://server-info — metadatos del servidor (JSON, ahora incluye features)

  • greeting://{name} — plantilla de saludo dinámica

  • file:///{+path} — archivo en sandbox (ALLOWED_ROOT), listar + completar, file:///notes.txt

  • memory://{key} — KV de memoria, listar + completar

  • db://{table}/{id} — fila de BD de demostración (users/notes), listar + completar

  • docs://{id} — fragmento RAG (ingerido mediante rag_ingest), listar + completar

💬 Prompts (4)

  • code-review — argumentos: language, code

  • explain-concept — argumentos: concept, level

  • summarize — argumentos: text, length (short/medium/long), style (bullets/paragraph/tldr)

  • research — argumentos: topic, depth (overview/deep), audience (beginner/expert/executive)


⚙️ Configuración del cliente

Claude Desktop (claude_desktop_config.json)

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

Cliente HTTP

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

Demo RAG (ingerir → buscar → 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

📁 Estructura

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

Para añadir una nueva herramienta: crea src/tools/my.tool.ts → exporta registerMyTool(server) → añádela a src/tools/index.ts.


🔐 Seguridad (Fase 2)

  • Helmet cabeceras (x-dns-prefetch-control, x-frame-options, x-content-type-options, etc.) mediante helmet@7 (src/index.ts:1)

  • CORS allowlist (CORS_ORIGIN=* o lista separada por comas) con manejo de credenciales cors (src/config.ts:60)

  • Autenticación AUTH_MODE=none|apiKey|bearer en src/middleware/auth.ts:1 — 401 sin X-API-Key válida o Authorization: Bearer (health/ready y OPTIONS excluidos)

  • Limitación de tasa express-rate-limit (por defecto 100/15min) en /mcp — 429 Too Many Requests (src/middleware/rateLimit.ts:1)

  • RequestId (X-Request-Id randomUUID, cabecera de eco, correlación de registros secundarios) (src/middleware/requestId.ts:1)

  • Validación de entorno Zod (src/config.ts:1) — parseEnv() valida PORT, AUTH_MODE, API_KEY entre campos, falla rápido en entorno no válido

  • Registro estructurado JSON/texto, [REDACTED] para authorization, apiKey, token (src/utils/logger.ts:24)

  • Reanudación InMemoryEventStore (src/utils/eventStore.ts:1) + mapa de sesiones con estado cuando RESUMABILITY_ENABLED=true (reproducción mediante Last-Event-ID, GET /mcp stream, DELETE cierre)

  • Endurecimiento de Docker appuser no root + HEALTHCHECK (Dockerfile:1)

  • Pruebas: 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 pruebas → 130 total con Fase 5, 90.89% cobertura

🔗 Integraciones (Fase 4)

  • Caché MemoryCache TTL (src/utils/cache.ts:1) — defaultCache para web/github, SimpleQueue (src/utils/queue.ts:1)

  • RAG vector local (hash embedding 128-dim, coseno, fragmento 500/50) en src/tools/rag.tool.ts:1 — rag_ingest (fragmentado + sendResourceListChanged), rag_search (topK, threshold), rag_list, rag_clear + recurso docs://{id}

  • Web src/tools/web.tool.ts:1 — brave_search (simulado si no hay BRAVE_API_KEY), tavily_search (simulado), web_fetch (con caché mediante defaultCache, CACHE_TTL_MS)

  • GitHub src/tools/github.tool.ts:1 — github_search_repos, github_get_repo, github_get_issue (con caché, GITHUB_TOKEN para límite de tasa)

  • Stack docker-compose.yml:1 (app + redis:7 + postgres:16 + qdrant:v1.12.4) con healthchecks

  • Demo rag_ingest → rag_search → docs:// E2E verificado en tests/integrations.test.ts:1 (21 pruebas)

🏢 Escala y Empresa (Fase 5 — v3.0) — NUEVO

  • Multi-tenant src/middleware/tenant.ts:1 — cabecera X-Tenant-Id (o ?tenant=), tenantMiddleware rechaza 400 cuando TENANT_REQUIRED=true y falta, memoria con ámbito de tenant getTenantMemory(tid) (almacenes aislados), claves de caché con espacio de nombres tenant:{id}:key, registro createTenant/deleteTenant

  • SSO OIDC src/middleware/auth.ts:1 — AUTH_MODE=oidc: validación estructural de JWT Bearer (3 partes, exp, iss vs OIDC_ISSUER, aud vs OIDC_AUDIENCE), claims adjuntados a req.oidcClaims; en producción se intercambia por verificación de firma JWKS

  • Plano de control src/routes/controlplane.ts:1 — CRUD /admin/tenants (POST/GET/PATCH/DELETE + 409 duplicado/400 id inválido), POST /admin/tenants/:id/rotate-key, GET /admin/tenants/:id/store inspección de aislamiento; protegido por token de administrador

  • Escalado en tiempo de ejecución src/utils/cluster.ts:1 — initCluster() bifurca CLUSTER_WORKERS (por defecto CPU-1), reinicio automático al salir el worker, CLUSTER_MODE=true; sin estado + RedisEventStore para escalado horizontal real

  • Pila de monitoreo docker-compose.override.yml:1 — Prometheus (prometheus.yml recopila app:3000/metrics) en :9090 + Grafana en :3001

  • Pruebas tests/v3_0.test.ts:1 (15 pruebas: extracción/aislamiento de tenant/requerido-400, validación de token OIDC + emisor/audiencia + HTTP 401/200, ciclo de vida del plano de control 201/409/400/404/rotate-key/inspección de almacén/deshabilitado-404, cluster no-op, servicios de compose) — total 178

🔌 Ecosistema y DX (Fase 5 — v2.2)

  • SDK de plugins src/plugin/index.ts:1 — definePlugin({name, version, register}), registerPlugin(server, plugin) (tolerante a duplicados), getRegisteredPlugins(), notificaciones automáticas sendToolListChanged

  • Integraciones src/integrations/ — slackPlugin (listar/publicar/buscar), notionPlugin (buscar/obtener/crear), linearPlugin (listar/crear/obtener) — todos simulados sin tokens (SLACK_TOKEN, NOTION_TOKEN); auto-registrados en src/server.ts:1

  • Registro smithery.yaml:1 — configuración de Smithery + nombre del Registro MCP io.github.ahmedalbanna/mcp-server-base, campos mcpName/files en package.json:1

  • RAG híbrido src/tools/rag.tool.ts:1 — modos rag_search vector|bm25|hybrid (por defecto hybrid, alpha 0.5), BM25 (k1=1.5, b=0.75) normalizado + re-clasificación por coseno; conjunto de evaluación tests/eval/rag-eval.json (20 Q/A) con p@5 ≥0.8 verificado en tests/v2_2.test.ts:1

  • Prompt Playground src/routes/admin.ts:1 — POST /admin/prompts/:name/preview renderiza prompts en el servidor; interfaz de playground en el panel /admin

  • Pruebas tests/v2_2.test.ts:1 (16 pruebas: SDK de plugins define/register/dup/herramientas invocables, archivos de registro, modos híbridos, evaluación p@5, vista previa/UI/contador de playground) — total 163

📈 Escalado y Operabilidad (Fase 5 — v2.0)

  • MCP versionado v2.0.0 (package.json:1, config.MCP_SERVER_VERSION) con instrucciones por versión menor (src/server.ts:1)

  • OTEL trazado/métricas (src/utils/otel.ts:1) — createSpan/withSpan, incrementCounter/recordHistogram, getMetrics/getSpans, stub de exportación JSON para OTEL_EXPORTER_OTLP_ENDPOINT, bandera OTEL_ENABLED

  • RedisEventStore (src/utils/redisEventStore.ts:1) — implementación de EventStore con storeEvent/replayEventsAfter, respaldo en memoria, eventStoreFactory.create() para escalado horizontal (EVENT_STORE_TYPE=memory|redis, REDIS_URL)

  • Interfaz de administración (src/routes/admin.ts:1) — GET /admin (panel HTML), /admin/tools|resources|prompts|metrics|spans|stores|health (JSON), protegido mediante ADMIN_TOKEN (X-Admin-Token), bandera ADMIN_ENABLED

  • Tareas (src/tools/tasks.tool.ts:1) — delay_task experimental (si hay tareas SDK disponibles) + respaldo create_task/get_task/get_task_result (en memoria, sondeo), infraestructura SimpleQueue/MemoryCache

  • 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 ya incluye redis/postgres/qdrant para escalado

  • Pruebas: tests/scale.test.ts:1 (spans/métricas OTEL, reproducción de RedisEventStore, TTL de caché, cola, HTML/métricas/token/ready de administración, creación/sondeo de tareas, versión, script k6) — 130 en total (ahora 147 con v2.1)

  • Despliegue listo para Fly.io/Cloud Run (sin estado + RedisEventStore), GHCR mediante release.yml, npm 2.0.0

🔒 Endurecimiento y Observabilidad (Fase 5 — v2.1) — NUEVO

  • OTEL Real src/observability/otel-real.ts:1 — initOtel() importación dinámica de @opentelemetry/sdk-node + OTLPTraceExporter (respaldo de consola), OTEL_ENABLED + OTEL_EXPORTER_OTLP_ENDPOINT, shutdown elegante en SIGTERM

  • SLOs src/observability/slo.ts:1 — checkSlo() (memoria/rag/caché/tiempo de actividad, latencyMs), GET /health → {status, checks, uptime, version, otel, eventStore} + GET /ready → 503 si no es ok, GET /metrics → Prometheus text/plain mediante prom-client (mcp_http_requests_total, mcp_http_request_duration_ms, mcp_sessions_active)

  • RBAC src/middleware/rbac.ts:1 — reader < writer < admin (cabecera X-Role, stub JWT), mapa TOOL_ROLES (31 herramientas), mcpRbacMiddleware inspecciona el cuerpo de tools/call → 403 si reader intenta write_file/shell_execute, GET /admin → requiere admin

  • Respaldo src/utils/persistence.ts:1 — saveBackup()/loadBackup()/scheduleSave() (debounce de 500ms) a ALLOWED_ROOT/.backup.json (getBackupFile() dinámico), registra stub de sincronización Redis cuando REDIS_URL está configurado, loadBackup() al inicio en src/index.ts:1

  • Métricas src/utils/metrics.ts:1 — Registry de prom-client + collectDefaultMetrics, httpRequestsTotal/httpRequestDuration/mcpToolCallsTotal/mcpSessionsActive, manejador GET /metrics

  • Pruebas tests/v2_1.test.ts:1 (13 pruebas: comprobaciones SLO, RBAC hasRole/X-Role 403/200, SLO health/metrics 200 + texto Prometheus, span OTEL, guardar/cargar respaldo + scheduleSave mediante memory_set, initOtel deshabilitado/habilitado) + tests/unit/rbac.test.ts:1 (4 pruebas) — total 147

  • Logger seguro para stderr, nunca registra secretos (redacción)

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

  • Tiempo de espera en fetch (10s) + errores estructurados

  • Apagado elegante (SIGINT/SIGTERM)

  • Health (GET /health) y ready (GET /ready) separados de MCP

  • Sin estado por defecto (sessionIdGenerator: undefined), con estado cuando RESUMABILITY_ENABLED=true (src/index.ts:22)

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

  • Cobertura 85% líneas / 70% ramas aplicada (vitest.config.ts:1), 178 pruebas: unitarias + e2e HTTP/seguridad/capacidades/integraciones/escalado/v2.1/v2.2/v3.0

📚 Documentación

Documentación completa en docs/:

  • Arquitectura — diagrama, mapa de módulos, ciclo de vida de solicitudes

  • Configuración — cada variable de entorno + reglas de validación

  • Referencia de API — herramientas/recursos/prompts/endpoints

  • Seguridad — autenticación, RBAC, OIDC, tenencia, lista de verificación de endurecimiento

  • Plugins — guía del SDK de plugins + distribución de registro

  • Despliegue — Docker, escalado de clúster/multi-réplica, monitoreo, k6

  • Pruebas — mapa de suites, patrones, CI

🤝 Contribuciones

Consulta CONTRIBUTING.md — nvm use, npm test, añade herramienta/recurso/prompt, asegúrate de que lint/typecheck/test pasen. Consulta CODE_OF_CONDUCT.md.


📚 Documentación de MCP

Related MCP Connectors

Related MCP Servers

  • A
    license
    B
    quality
    C
    maintenance
    A production-packaged Model Context Protocol server for coding agents that routes large file, git, web, database, and other tasks through token-budgeted tools and workflows.
    6
    2 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    A production-ready Model Context Protocol suite over Streamable HTTP providing a sandboxed file server with tools, resources, prompts, and both manual and AI-driven clients.
    MIT
  • A
    license
    B
    quality
    C
    maintenance
    Enables running a Model Context Protocol server with a validated echo tool over stdio or Streamable HTTP, including strict schemas, tests, and hardened container deployment.
    1
    1
    MIT