Gestion MCP Server
MCP server that exposes the GestionIntegrantes REST API as tools, enabling authentication, management of personas, and user queries via natural language.
README
Human Resources Management — MCP Server
Servidor MCP (Model Context Protocol) para el sistema de gestión de recursos humanos. Expone tools que un LLM o agente puede invocar mediante Streamable HTTP (y opcionalmente stdio).
Stack
| Componente | Tecnología |
|---|---|
| Runtime | Node.js 20 Alpine |
| Lenguaje | TypeScript 5.7 (compilado con tsc, ejecutado con node) |
| Sistema de módulos | ESM ("type": "module" en package.json) |
| SDK MCP | @modelcontextprotocol/server v2.0.0 |
| Transporte HTTP | WebStandardStreamableHTTPServerTransport (stateless) |
| Transporte Stdio | StdioServerTransport (desde @modelcontextprotocol/server/stdio) |
| Validación | Zod v4 |
| Servidor HTTP | node:http nativo (createServer). Sin Express ni frameworks. |
| Contenedor | Docker (Dockerfile en ../docker/mcp.Dockerfile) |
Arquitectura: Clean Architecture con módulos
El código sigue Clean Architecture (Puertos y Adaptadores). Las dependencias siempre apuntan hacia adentro:
Infrastructure ──→ Application ──→ Domain
(adaptadores) (casos de uso) (lógica pura)
src/shared/types.ts es accesible desde cualquier capa.
Estructura de directorios
src/
├── main.ts ← Entry point: elige transporte según --http / --stdio
│
├── shared/
│ └── types.ts ← Tipos transversales: MCPToolResponse, Result<T>
│
├── template/ ← MÓDULO: tools de plantilla/demo
│ ├── domain/
│ │ └── entities/
│ │ └── HealthStatus.ts ← Entidad de dominio: estado del servidor
│ ├── application/
│ │ ├── dto/
│ │ │ └── index.ts ← DTOs planos: GreetInput/Output, CalculateInput/Output
│ │ └── use-cases/
│ │ ├── greet/
│ │ │ ├── IGreetUseCase.ts ← Puerto (interfaz)
│ │ │ └── GreetUseCase.ts ← Implementación del caso de uso
│ │ ├── calculate/
│ │ │ ├── ICalculateUseCase.ts
│ │ │ └── CalculateUseCase.ts
│ │ └── health-check/
│ │ ├── IHealthCheckUseCase.ts
│ │ └── HealthCheckUseCase.ts
│ └── infrastructure/
│ └── mcp/
│ └── schemas.ts ← Zod schemas + registerTemplateTools()
│
├── infrastructure/ ← Infraestructura COMPARTIDA entre módulos
│ ├── config/
│ │ └── env.ts ← Tipado y carga de variables de entorno
│ ├── mcp/
│ │ └── server-factory.ts ← Orquestador: crea McpServer y registra tools de todos los módulos
│ └── transport/
│ ├── http-server.ts ← Servidor HTTP (StreamableHTTP, stateless)
│ └── stdio-server.ts ← Servidor stdio (para MCP Inspector)
│
└── (futuro) personnel/ ← EJEMPLO: futuro módulo que conectará al Backend
├── domain/...
├── application/...
└── infrastructure/mcp/schemas.ts
Reglas de dependencia por capa
| Capa | Puede importar de | NO puede importar de |
|---|---|---|
domain/ |
shared/ |
application/, infrastructure/ |
application/ |
domain/, shared/, application/dto/ |
infrastructure/ |
infrastructure/ (compartida) |
domain/, application/, shared/ |
— |
<modulo>/infrastructure/ |
domain/, application/, shared/ del mismo módulo |
otros módulos directamente |
Responsabilidad de cada archivo
| Archivo | Capa | Función |
|---|---|---|
main.ts |
— | Punto de entrada. Parsea --http/--stdio. Por defecto HTTP. |
shared/types.ts |
Shared | MCPToolResponse (formato que espera registerTool), Result<T> |
infrastructure/config/env.ts |
Infra | loadEnvironment() → tipa NODE_ENV, PORT, BACKEND_URL, LOG_LEVEL |
infrastructure/mcp/server-factory.ts |
Infra | createServerFactory() → instancia McpServer, llama a registerTemplateTools(). Aquí se agregan futuros módulos. |
infrastructure/transport/http-server.ts |
Infra | startHttpServer(env) → crea node:http server, convierte IncomingMessage → Web Request, invoca transport.handleRequest(). Stateless. |
infrastructure/transport/stdio-server.ts |
Infra | startStdioServer() → StdioServerTransport + server.connect() |
template/domain/entities/HealthStatus.ts |
Domain | Entidad pura con factory methods create() y degraded(), método toSummary() |
template/application/dto/index.ts |
Application | DTOs planos: GreetInput, GreetOutput, CalculateInput, CalculateOutput, HealthCheckOutput |
template/application/use-cases/*/I*UseCase.ts |
Application | Puerto: interfaz del caso de uso (execute method) |
template/application/use-cases/*/*UseCase.ts |
Application | Implementación del caso de uso (lógica de negocio) |
template/infrastructure/mcp/schemas.ts |
Infra (módulo) | Zod schemas + registerTemplateTools(server, version). Adaptador que conecta use cases con McpServer. |
Flujo de una tool call
┌─────────────────────────────────────────────────────────────────────┐
│ 1. Cliente HTTP POST /mcp │
│ Body: {"jsonrpc":"2.0","method":"tools/call","params":{...}} │
│ Headers: Accept: application/json, text/event-stream │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 2. http-server.ts: node:http.createServer │
│ readRequestBody() → toWebRequest() → transport.handleRequest() │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 3. WebStandardStreamableHTTPServerTransport (stateless) │
│ Parsea JSON-RPC, enruta al McpServer │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 4. McpServer (creado en server-factory.ts) │
│ Valida input con Zod schema → ejecuta handler de la tool │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 5. template/infrastructure/mcp/schemas.ts │
│ Handler: Zod valida params → crea DTO → llama UseCase.execute() │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 6. UseCase.execute(dto) → lógica pura │
│ Puede usar entidades de domain/ │
└────────────────────────────┬────────────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────────┐
│ 7. Handler retorna { content: [{ type: "text", text: "..." }] } │
│ McpServer → Transport → JSON-RPC response → HTTP response │
└─────────────────────────────────────────────────────────────────────┘
Tools actuales (módulo template)
| Tool | Descripción | Input | Output |
|---|---|---|---|
greet |
Saludo personalizado | { name: string } |
"Hello, {name}! Welcome to the HRM MCP Server." |
calculate |
Operaciones aritméticas | { operation: enum, a: number, b: number } |
"5 add 3 = 8" |
health_check |
Estado del servidor | {} |
"Status: healthy | Uptime: 4m 0s | Version: 1.0.0 | ..." |
Guía: Cómo agregar una nueva tool
Dentro de un módulo existente (ej: template)
Paso 1: Agregar DTOs en src/template/application/dto/index.ts
export class MyInput {
constructor(public readonly param: string) {}
}
export class MyOutput {
constructor(public readonly result: string) {}
}
Paso 2: Crear interfaz src/template/application/use-cases/my-tool/IMyUseCase.ts
import type { MyInput, MyOutput } from '../../dto/index.js';
export interface IMyUseCase {
execute(input: MyInput): Promise<MyOutput>;
}
Paso 3: Crear implementación src/template/application/use-cases/my-tool/MyUseCase.ts
import type { IMyUseCase } from './IMyUseCase.js';
import { MyInput, MyOutput } from '../../dto/index.js';
export class MyUseCase implements IMyUseCase {
public async execute(input: MyInput): Promise<MyOutput> {
return new MyOutput(`Processed: ${input.param}`);
}
}
Paso 4: Agregar Zod schema y handler en src/template/infrastructure/mcp/schemas.ts
const myInputSchema = z.object({
param: z.string().describe('Description of param'),
});
// Dentro de registerTemplateTools():
server.registerTool(
'my_tool',
{
description: 'What this tool does.',
inputSchema: myInputSchema,
},
async (params: z.infer<typeof myInputSchema>) => {
const input = new MyInput(params.param);
const output = await myUseCase.execute(input);
return {
content: [{ type: 'text' as const, text: output.result }],
};
}
);
Errores en tools
Para reportar errores al LLM, retorna isError: true:
catch (error) {
return {
content: [{ type: 'text' as const, text: `Error: ${error.message}` }],
isError: true,
};
}
Guía: Cómo crear un nuevo módulo
Cuando necesites una funcionalidad completamente nueva (ej: conexión al backend para gestión de personal), crea un módulo:
Paso 1: Crear la estructura de carpetas
src/personnel/
├── domain/
│ └── entities/
│ └── Employee.ts
├── application/
│ ├── dto/
│ │ └── index.ts
│ └── use-cases/
│ └── get-employee/
│ ├── IGetEmployeeUseCase.ts
│ └── GetEmployeeUseCase.ts
└── infrastructure/
└── mcp/
└── schemas.ts ← export function registerPersonnelTools(server, version)
Paso 2: El schemas.ts del módulo debe exportar una función con esta firma:
import type { McpServer } from '@modelcontextprotocol/server';
import { z } from 'zod';
export function registerPersonnelTools(server: McpServer, version: string): void {
const useCase = new GetEmployeeUseCase(/* dependencias */);
server.registerTool('get_employee', {
description: 'Get employee by ID',
inputSchema: z.object({ id: z.string() }),
}, async ({ id }) => {
const output = await useCase.execute(new GetEmployeeInput(id));
return { content: [{ type: 'text' as const, text: JSON.stringify(output) }] };
});
}
Paso 3: Registrar el módulo en src/infrastructure/mcp/server-factory.ts
import { registerTemplateTools } from '../../template/infrastructure/mcp/schemas.js';
import { registerPersonnelTools } from '../../personnel/infrastructure/mcp/schemas.js'; // ← nuevo
export function createServerFactory(): McpServer {
const server = new McpServer({ name: 'hrm-mcp', version: SERVER_VERSION });
registerTemplateTools(server, SERVER_VERSION);
registerPersonnelTools(server, SERVER_VERSION); // ← nuevo
return server;
}
Transportes
HTTP (Streamable HTTP) — por defecto
- Endpoint:
POST /mcp(también aceptaPOST /) - Headers requeridos:
Accept: application/json, text/event-stream - Modo: Stateless (
sessionIdGenerator: undefined). Cada request es independiente. - Puerto:
3001(configurable conPORT) - Health check:
GET /health→{"status":"ok"} - Arranque:
node dist/main.jsonode dist/main.js --http
Stdio — opcional
- Arranque:
node dist/main.js --stdio - Uso: MCP Inspector o clientes que lancen el proceso como hijo
- Logs: Usar
console.error(stdout es el canal del protocolo)
Docker
Dockerfile
Ubicado en ../docker/mcp.Dockerfile. Tiene 3 stages:
| Stage | Propósito | Comando |
|---|---|---|
builder |
Clona repo, npm ci, npm run build, npm prune --production |
— |
production |
Imagen mínima con solo dist/ + node_modules prod |
node dist/index.js |
development |
Copia source, instala deps, compila y ejecuta | npm run build && node dist/main.js --http |
Comandos Docker Compose
# Construir sin caché
docker compose -f docker-compose.dev.yml build --no-cache mcp-server
# Ejecutar
docker compose -f docker-compose.dev.yml up mcp-server
# Ejecutar en background
docker compose -f docker-compose.dev.yml up -d mcp-server
# Ver logs
docker compose -f docker-compose.dev.yml logs -f mcp-server
# Detener
docker compose -f docker-compose.dev.yml down
Volumes en desarrollo
volumes:
- ./HumanResourcesManagement-MCP:/app # Código fuente (live)
- mcp_node_modules:/app/node_modules # node_modules preservado
El CMD del stage development ejecuta npm run build && node dist/main.js --http, por lo que recompila el TypeScript al iniciar el contenedor.
Testing manual
Health check
curl http://localhost:3001/health
MCP: Listar tools
curl -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
MCP: Inicializar conexión
curl -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"initialize","params":{"protocolVersion":"2026-07-28","capabilities":{},"clientInfo":{"name":"test","version":"1.0"}},"id":1}'
MCP: Llamar una tool
curl -X POST http://localhost:3001/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"greet","arguments":{"name":"World"}},"id":2}'
Variables de entorno
| Variable | Default | Descripción |
|---|---|---|
NODE_ENV |
development |
Entorno (development, production, test) |
PORT |
3001 |
Puerto del servidor HTTP |
BACKEND_URL |
http://localhost:8080 |
URL del backend (para futura conexión) |
LOG_LEVEL |
info |
Nivel de log (debug, info, warn, error) |
Se cargan desde ../.env.dev en el contenedor de desarrollo.
Convenciones de código
Imports
Todos los imports relativos llevan extensión .js al final (requerido por "moduleResolution": "NodeNext"):
// ✅ Correcto
import { GreetUseCase } from '../../application/use-cases/greet/GreetUseCase.js';
// ❌ Incorrecto
import { GreetUseCase } from '../../application/use-cases/greet/GreetUseCase';
Logging
Usar console.error para todos los logs. En modo stdio, stdout es el canal del protocolo JSON-RPC.
console.error('Server started on port 3001'); // ✅
console.log('Server started on port 3001'); // ❌ (corrompe el protocolo en stdio)
Tipos literales en tool responses
return {
content: [{ type: 'text' as const, text: 'Hello' }],
};
El as const es necesario para que TypeScript infiera el tipo literal 'text' en lugar de string.
Entidades de dominio
- Constructor
private+ factory methods estáticos (create,reconstitute) - Propiedades privadas con prefijo
_(_status,_version) - Getters públicos para acceso de solo lectura
- Sin imports de
application/niinfrastructure/
DTOs
- Clases con
constructor(public readonly ...)— objetos planos, sin lógica - Sin dependencias externas
- Sin validaciones de negocio
Use cases
- Interfaz (
I*UseCase) + implementación (*UseCase) en archivos separados - Reciben DTOs de entrada, retornan DTOs de salida
- Pueden usar entidades de dominio y
shared/types.ts - No conocen MCP, HTTP, ni Zod
Dependencias npm
{
"dependencies": {
"@modelcontextprotocol/server": "^2.0.0",
"zod": "^4.4.0"
},
"devDependencies": {
"@types/node": "^20.0.0",
"typescript": "^5.7.0"
}
}
Nota: El paquete @modelcontextprotocol/server v2 exporta desde su entry point principal WebStandardStreamableHTTPServerTransport y McpServer. StdioServerTransport se importa desde el subpath @modelcontextprotocol/server/stdio.
Scripts npm
npm run build # tsc → dist/
npm start # node dist/main.js (HTTP por defecto)
npm run start:http # node dist/main.js --http
npm run start:stdio # node dist/main.js --stdio
Resumen para un LLM
Si eres un LLM que va a modificar este código, recuerda:
- La lógica de negocio va en use cases (
application/use-cases/), nunca en schemas ni en handlers. - Los DTOs son planos, sin comportamiento. Se crean en
application/dto/. - Cada tool se registra en
<modulo>/infrastructure/mcp/schemas.tsconserver.registerTool(name, config, handler). - El handler de la tool convierte
params(validados por Zod) → DTO → UseCase → DTO →{ content: [...] }. - Para crear un nuevo módulo, replica la estructura de
template/y registra su función enserver-factory.ts. - Nunca uses
console.log— usaconsole.errorpara logs. - Todos los imports relativos llevan
.jsal final. - No instales dependencias en el host — todo se prueba dentro del contenedor Docker.
推荐服务器
Baidu Map
百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。
Playwright MCP Server
一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。
Magic Component Platform (MCP)
一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。
Audiense Insights MCP Server
通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。
VeyraX
一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。
graphlit-mcp-server
模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。
Kagi MCP Server
一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。
e2b-mcp-server
使用 MCP 通过 e2b 运行代码。
Neon MCP Server
用于与 Neon 管理 API 和数据库交互的 MCP 服务器
Exa MCP Server
模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。