loading…
Search for a command to run...
loading…
A TypeScript MCP server template with Zod validation, dual transport (stdio/HTTP), and modular architecture for building MCP-compatible tools, resources, and pr
A TypeScript MCP server template with Zod validation, dual transport (stdio/HTTP), and modular architecture for building MCP-compatible tools, resources, and prompts.
MCP (Model Context Protocol) server template with TypeScript, Zod validation, and dual transport support (stdio/HTTP). Compatible with any MCP client: Claude Code, Claude Desktop, Cursor, VS Code Copilot, Windsurf, Cline, and more.
createServer() for testabilitypnpm install
pnpm dev
| Script | Description |
|---|---|
pnpm dev |
Start with hot reload (tsx watch) |
pnpm build |
Compile TypeScript + resolve aliases |
pnpm start |
Run compiled server |
pnpm test |
Run tests |
pnpm lint |
Lint source code |
pnpm type-check |
Type check without emit |
Copy .env.example to .env and adjust:
| Variable | Default | Description |
|---|---|---|
MCP_TRANSPORT |
stdio |
Transport: stdio or http |
PORT |
3000 |
HTTP port (only for http transport) |
LOG_LEVEL |
info |
Pino log level |
NODE_ENV |
development |
Environment |
src/
├── main.ts # Entrypoint: transport selection
├── server.ts # createServer() factory
├── config/ # Env validation + constants
├── common/ # Logger, error helpers, types
├── tools/ # MCP tools (callable by LLMs)
├── resources/ # MCP resources (read-only data)
└── prompts/ # MCP prompts (reusable templates)
src/tools/my-tool.tool.ts:import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { z } from 'zod';
export function registerMyTool(server: McpServer): void {
server.registerTool(
'my_tool',
{
title: 'My Tool',
description: 'What this tool does',
inputSchema: {
param: z.string().describe('Parameter description'),
},
annotations: {
readOnlyHint: true,
destructiveHint: false,
idempotentHint: true,
openWorldHint: false,
},
},
async ({ param }) => ({
content: [{ type: 'text', text: `Result: ${param}` }],
}),
);
}
src/tools/index.ts:import { registerMyTool } from './my-tool.tool.js';
export function registerTools(server: McpServer): void {
registerGreetTool(server);
registerMyTool(server); // add here
}
src/tools/__tests__/my-tool.tool.spec.tsAdd to .claude/settings.json:
{
"mcpServers": {
"template-mcp": {
"command": "node",
"args": ["/absolute/path/to/template-mcp/dist/main.js"]
}
}
}
Add to claude_desktop_config.json:
{
"mcpServers": {
"template-mcp": {
"command": "node",
"args": ["/absolute/path/to/template-mcp/dist/main.js"]
}
}
}
Add to Cursor Settings > MCP Servers:
{
"mcpServers": {
"template-mcp": {
"command": "node",
"args": ["/absolute/path/to/template-mcp/dist/main.js"]
}
}
}
Add to .vscode/settings.json:
{
"mcp": {
"servers": {
"template-mcp": {
"command": "node",
"args": ["/absolute/path/to/template-mcp/dist/main.js"]
}
}
}
}
# Build
docker build -t template-mcp .
# Run (HTTP mode, used for remote access)
docker run -p 3000:3000 template-mcp
@modelcontextprotocol/sdk)Todo lo siguiente está comprobado y funcionando al 100%.
| Check | Comando |
|---|---|
| Lint + formato | pnpm lint |
| Tipado estricto | pnpm type-check |
| Build (tsc + alias) | pnpm build |
pnpm test
| Suite | Cubre |
|---|---|
greet.tool.spec.ts (5) |
Listado, estilos casual/formal/enthusiastic, rechazo de nombre vacío |
server-info.resource.spec.ts (2) |
Listado, campos JSON (name, version, uptime, timestamp) |
summarize.prompt.spec.ts (4) |
Listado, estilos brief/bullet-points, coerción numérica, defaults |
Sin red ni puertos — usa InMemoryTransport del SDK.
echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}' \
| MCP_TRANSPORT=stdio node dist/main.js
Respuesta JSON-RPC en stdout, logs en stderr.
MCP_TRANSPORT=http PORT=3100 node dist/main.js &
# Initialize → capturar Mcp-Session-Id del header
# tools/list, resources/list, prompts/list, tools/call greet, resources/read info://server
| Endpoint verificado | Resultado esperado |
|---|---|
tools/call greet {"name":"Freddy","style":"casual"} |
"Hey Freddy! How's it going?" |
resources/read info://server |
JSON con name, version, uptime, nodeVersion, timestamp |
docker build -t template-mcp . # multi-stage: base → deps → build → production
docker run -p 3000:3000 template-mcp # arranca en HTTP mode
pnpm install → pnpm lint → pnpm build → pnpm test
Corre en cada push y PR a main/master.
git commit → husky → lint-staged → eslint --fix + prettier --write (solo archivos staged)
InMemoryTransport; el transporte HTTP (StreamableHTTPServerTransport) solo se verificó manualmente con curl. Para producción remota añadir tests de integración con sesión real.claude/settings.json o Cursor y confirmando que tools/resources/prompts aparecen en el clienteДобавь это в claude_desktop_config.json и перезапусти Claude Desktop.
{
"mcpServers": {
"template-mcp": {
"command": "npx",
"args": []
}
}
}