---
title: "Cómo crear un servidor MCP en TypeScript paso a paso"
excerpt: "Crea tu primer servidor MCP con el SDK oficial de TypeScript: código completo, prueba con MCP Inspector y conéctalo a Claude y Cursor desde cero."
date: "2026-06-27T11:00:00.000Z"
lastModified: "2026-06-27T11:00:00.000Z"
category: "Inteligencia Artificial"
tech_article: true
author:
  name: "angel cruz"
  picture: "https://angelcruzdevcdn.nyc3.cdn.digitaloceanspaces.com/images/me/angel-cruz.png"
ogImage:
  url: "/images/open-graph/og-image.png"
seo_title: "Cómo crear un servidor MCP en TypeScript (paso a paso)"
seo_description: "Crea tu primer servidor MCP con el SDK oficial de TypeScript: código completo, prueba con MCP Inspector y conéctalo a Claude y Cursor. Paso a paso."
---

Crear un servidor MCP es más sencillo de lo que parece: con el SDK oficial de TypeScript necesitas un archivo, un par de dependencias y unas 30 líneas de código para tener una herramienta que Claude o Cursor pueden invocar. En esta guía construimos uno desde cero, lo probamos con el MCP Inspector y lo conectamos a tus editores. Todo el código está completo y funciona tal cual.

Si todavía no tienes claro **qué es MCP**, lo expliqué a fondo en el post de [Context7, el servidor MCP de documentación](/post/context7-documentacion-actualizada-asistentes-codigo-ia). En resumen: el **Model Context Protocol** es un estándar abierto que estandariza cómo un asistente de IA se conecta a herramientas y fuentes de datos externas. Un *servidor MCP* es justo eso: un proceso que expone capacidades (tools, resources, prompts) que el modelo puede usar.

Aquí nos centramos en **tools**, que es el 90% de los casos de uso: funciones que el modelo llama con argumentos y que devuelven un resultado.

## Qué vas a necesitar

- **Node.js 18 o superior** (lo verificas con `node --version`).
- Un editor (Cursor, VS Code, lo que uses).
- Claude Code o Cursor para la conexión final (opcional, pero es lo divertido).

No necesitas ninguna API key ni cuenta de pago. El servidor corre en tu máquina.

## Paso 1: Inicializa el proyecto

Crea la carpeta e instala las dependencias. Usamos el SDK oficial (`@modelcontextprotocol/sdk`) y `zod` para validar los argumentos de entrada:

```bash
mkdir mi-servidor-mcp && cd mi-servidor-mcp
npm init -y
npm install @modelcontextprotocol/sdk zod
npm install -D typescript @types/node
```

El SDK habla MCP por ti (el protocolo JSON-RPC, el handshake, la negociación de capacidades). Tú solo escribes la lógica de tus tools.

Edita el `package.json` para que use módulos ES y tenga un script de build:

```json
{
	"name": "mi-servidor-mcp",
	"version": "1.0.0",
	"type": "module",
	"bin": {
		"mi-servidor-mcp": "./build/index.js"
	},
	"scripts": {
		"build": "tsc"
	},
	"dependencies": {
		"@modelcontextprotocol/sdk": "^1.29.0",
		"zod": "^3.24.0"
	},
	"devDependencies": {
		"@types/node": "^22.0.0",
		"typescript": "^5.6.0"
	}
}
```

> **`"type": "module"` es obligatorio.** El SDK es ESM puro; sin esta línea verás errores de `import` al ejecutar.

Crea un `tsconfig.json`:

```json
{
	"compilerOptions": {
		"target": "ES2022",
		"module": "Node16",
		"moduleResolution": "Node16",
		"outDir": "./build",
		"rootDir": "./src",
		"strict": true,
		"esModuleInterop": true,
		"skipLibCheck": true
	},
	"include": [
		"src/**/*.ts"
	]
}
```

## Paso 2: Escribe el servidor (tu primer tool)

Crea `src/index.ts`. Empezamos con un tool mínimo para entender la forma: una calculadora que suma dos números.

```typescript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// 1. Crea la instancia del servidor
const server = new McpServer({
	name: "mi-servidor-mcp",
	version: "1.0.0",
});

// 2. Registra un tool
server.registerTool(
	"calcular",
	{
		description: "Suma dos números y devuelve el resultado",
		inputSchema: {
			a: z.number().describe("Primer número"),
			b: z.number().describe("Segundo número"),
		},
	},
	async ({ a, b }) => ({
		content: [{ type: "text", text: `El resultado es ${a + b}` }],
	})
);

// 3. Conecta el servidor por stdio
async function main() {
	const transport = new StdioServerTransport();
	await server.connect(transport);
	console.error("Servidor MCP en marcha (stdio)");
}

main().catch((error) => {
	console.error("Error fatal:", error);
	process.exit(1);
});
```

Hay tres piezas y conviene entenderlas:

1. **`McpServer`** es la API de alto nivel. Le das un nombre y una versión.
2. **`registerTool`** recibe el nombre del tool, sus metadatos (`description` + `inputSchema`) y el handler. El `inputSchema` es un objeto de campos Zod —no un `z.object(...)`—; el SDK lo convierte automáticamente a JSON Schema para que el modelo sepa qué argumentos pasar. Los `.describe()` ayudan al modelo a usar el tool correctamente.
3. **`StdioServerTransport`** comunica el servidor con el cliente vía stdin/stdout. Es el transporte para integraciones locales: el editor lanza tu servidor como proceso hijo y hablan por esa tubería.

> **Regla de oro del stdio:** nunca uses `console.log` en un servidor stdio. Stdout es el canal del protocolo; si escribes ahí corrompes los mensajes. Para logs usa siempre **`console.error`** (va a stderr).

Compílalo:

```bash
npm run build
```

Esto genera `build/index.js`. Ya tienes un servidor MCP funcional.

## Paso 3: Pruébalo con el MCP Inspector

Antes de conectarlo a nada, pruébalo de forma aislada. El **MCP Inspector** es una herramienta oficial que levanta una UI web para invocar tus tools a mano:

```bash
npx @modelcontextprotocol/inspector node build/index.js
```

Abre la URL que imprime en consola, verás tu servidor conectado. En la pestaña **Tools** aparece `calcular`: pulsa *List Tools*, selecciona el tool, introduce `a` y `b`, y ejecuta. Si devuelve "El resultado es...", todo funciona.

Este paso te ahorra horas: si algo falla, lo ves aquí sin el ruido de un editor de por medio.

## Paso 4: Un tool que sirve de verdad

Sumar números no impresiona a nadie. La gracia de MCP es darle al modelo acceso a datos que no tiene. Añadamos un tool que consulta la API pública de GitHub y devuelve las estrellas y el lenguaje de un repositorio. Agrégalo en `src/index.ts`, antes de `main()`:

```typescript
server.registerTool(
	"info_repo",
	{
		description:
			"Obtiene estrellas, lenguaje y descripción de un repositorio público de GitHub",
		inputSchema: {
			owner: z.string().describe("Usuario u organización dueña del repo"),
			repo: z.string().describe("Nombre del repositorio"),
		},
	},
	async ({ owner, repo }) => {
		const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
			headers: { "User-Agent": "mi-servidor-mcp" },
		});

		if (!res.ok) {
			return {
				content: [
					{ type: "text", text: `No encontré ${owner}/${repo} (HTTP ${res.status}).` },
				],
				isError: true,
			};
		}

		const data = await res.json();
		const resumen = [
			`Repositorio: ${data.full_name}`,
			`Descripción: ${data.description ?? "—"}`,
			`Lenguaje: ${data.language ?? "—"}`,
			`Estrellas: ${data.stargazers_count}`,
			`Forks: ${data.forks_count}`,
		].join("\n");

		return { content: [{ type: "text", text: resumen }] };
	}
);
```

Dos detalles importantes:

- **Manejo de errores con `isError: true`.** Cuando algo sale mal, devuelve el error dentro del `content` y marca `isError`. Así el modelo entiende que falló y puede reaccionar (reintentar, avisarte), en vez de recibir una excepción opaca.
- **Devuelve texto claro y estructurado.** El modelo lee la respuesta como contexto; cuanto más legible, mejor la usa.

Recompila (`npm run build`) y vuelve a probar `info_repo` en el Inspector con, por ejemplo, `owner: upstash` y `repo: context7`.

## Paso 5: Conéctalo a Claude Code

La forma más limpia de registrar el servidor en Claude Code es con el comando `claude mcp add`. No tienes que editar JSON a mano: el comando escribe la configuración por ti en `~/.claude.json` (el archivo de config en tu directorio home).

Para un servidor stdio local, pasa el comando que lo lanza después de `--`. **Usa la ruta absoluta** al `build/index.js`:

```bash
claude mcp add mi-servidor --scope user -- node /ruta/absoluta/a/mi-servidor-mcp/build/index.js
```

Un par de detalles:

- Todo lo que va **después de `--`** es el comando que ejecuta tu servidor (`node build/index.js`); lo de antes son opciones de Claude.
- **`--scope user`** guarda el servidor en `~/.claude.json` y lo deja disponible en todos tus proyectos. Sin ese flag se usa el scope `local` (por defecto), que también vive en `~/.claude.json` pero solo se carga en el proyecto actual.

Verifica que quedó registrado:

```bash
claude mcp list
```

Dentro de una sesión de Claude Code, el comando `/mcp` te muestra los servidores conectados y sus tools. Pídele algo como *"usa info_repo para ver cuántas estrellas tiene upstash/context7"* y llamará a tu servidor.

## Paso 6: Conéctalo a Claude Desktop

Claude Desktop tiene dos vías. Para un servidor que acabas de programar, lo más directo es editar su configuración desde la propia app (no busques el archivo a mano):

1. Abre el menú **Claude** en la barra de menús del sistema —no los ajustes de dentro de la ventana— y elige **Settings…**.
2. Ve a la pestaña **Developer** y pulsa **Edit Config**. Eso crea (o abre) el archivo `claude_desktop_config.json`:
   - **macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
   - **Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
3. Añade tu servidor dentro de `mcpServers`, con la **ruta absoluta** al `build/index.js`:

```json
{
	"mcpServers": {
		"mi-servidor": {
			"command": "node",
			"args": [
				"/ruta/absoluta/a/mi-servidor-mcp/build/index.js"
			]
		}
	}
}
```

Guarda y **reinicia Claude Desktop por completo** (ciérralo del todo, no solo la ventana). Al reabrirlo verás el indicador de MCP en la esquina inferior derecha del cuadro de mensaje; púlsalo para ver tus tools.

> Si en vez de un servidor propio quieres instalar uno ya empaquetado, Claude Desktop también soporta **Desktop Extensions** (archivos `.mcpb`) desde **Settings → Extensions**, sin tocar JSON.

## Paso 7: Conéctalo a Cursor

Cursor usa el mismo formato que Claude Desktop. Crea `.cursor/mcp.json` en tu proyecto (o `~/.cursor/mcp.json` para que esté disponible en todos):

```json
{
	"mcpServers": {
		"mi-servidor": {
			"command": "node",
			"args": [
				"/ruta/absoluta/a/mi-servidor-mcp/build/index.js"
			]
		}
	}
}
```

En **Settings → MCP** lo verás listado. A partir de ahí, el agente de Cursor puede invocar tus tools dentro del chat.

## Próximos pasos

Ya tienes la base. Desde aquí puedes:

- **Añadir más tools** repitiendo el patrón de `registerTool`. Cada uno es una capacidad nueva.
- **Exponer *resources*** (datos de solo lectura que el modelo puede leer, como archivos o registros) y *prompts* (plantillas reutilizables).
- **Pasar a transporte HTTP** (`StreamableHTTPServerTransport`) si quieres un servidor remoto en vez de local.
- **Empaquetarlo y compartirlo.** Si ya trabajas con skills, te interesa [cómo crear un plugin para Claude Code](/post/crear-plugin-claude-cowork-claude-code-desde-skills), que incluye conectar un MCP remoto por OAuth.

Y si lo que buscas es no escribir tools de documentación tú mismo, [Context7](/post/context7-documentacion-actualizada-asistentes-codigo-ia) ya es un servidor MCP listo que inyecta docs actualizadas de más de 1000 librerías.

## Preguntas frecuentes

### ¿Qué es un servidor MCP?

Es un proceso que expone capacidades —tools, resources y prompts— a un asistente de IA a través del Model Context Protocol, un estándar abierto. Permite que modelos como Claude o el agente de Cursor ejecuten funciones y accedan a datos externos de forma estandarizada.

### ¿Necesito una API key para crear un servidor MCP?

No. Un servidor MCP local corre en tu máquina y se comunica con el editor por stdio. Solo necesitarías credenciales si tus tools consumen una API externa que las requiera.

### ¿Puedo escribir un servidor MCP en otro lenguaje?

Sí. Existe SDK oficial para TypeScript, Python y otros lenguajes. En esta guía usamos el de TypeScript porque encaja con el ecosistema de Cursor, VS Code y Node, pero el protocolo es el mismo en todos.

### ¿Por qué no debo usar console.log en un servidor MCP?

Porque con el transporte stdio la salida estándar (stdout) es el canal del protocolo. Cualquier `console.log` corrompe los mensajes JSON-RPC y rompe la comunicación. Para depurar usa `console.error`, que escribe en stderr.

### ¿Cómo pruebo un servidor MCP sin conectarlo a un editor?

Con el MCP Inspector: `npx @modelcontextprotocol/inspector node build/index.js`. Levanta una interfaz web donde puedes listar e invocar tus tools a mano antes de integrarlo en Claude o Cursor.

---

## Sitemap

Índice completo del sitio: [/sitemap.md](https://www.angelcruz.dev/sitemap.md)

Canónico HTML: [https://www.angelcruz.dev/post/como-crear-un-servidor-mcp](https://www.angelcruz.dev/post/como-crear-un-servidor-mcp)
