Cambiar tema

Introducción al desarrollo de MCP Server: crea tu primer servicio MCP desde cero

Easton editorial illustration: trace beacon network

Introducción: Cuando escribes código en Cursor, ¿alguna vez quisiste que la IA consultara directamente la última versión de las dependencias de tu proyecto? ¿O al analizar datos en Claude, que pudiera leer información de tu base de datos? Si lo implementas por separado, tendrías que escribir código de adaptación para cada herramienta de IA. Con un MCP Server, lo escribes una vez y todos los clientes compatibles con MCP pueden usarlo. Este artículo te guía desde cero para escribir a mano un MCP Server completo con TypeScript.

30 min
Tiempo de inicio
De cero a funcionando
3 tipos
Capacidades clave
Tools/Resources/Prompts
1000+
Servidores MCP
Comunidad open source en GitHub
Source: Datos oficiales de MCP (2025)

¿Qué es MCP? Entiende los conceptos clave en 3 minutos

La historia de un puerto USB

Si usaste productos digitales hace unos años, recordarás esa época incómoda: el ratón con conector redondo, el teclado cuadrado, la impresora con puerto paralelo — cada dispositivo exigía su propio conector. Luego llegó USB y un solo puerto resolvió todo.

MCP (Model Context Protocol) está convirtiéndose en el «estándar USB» del mundo de las herramientas de IA.

Sin MCP, si quieres que la IA acceda a una fuente de datos, debes escribir una capa de adaptación para cada herramienta: un plugin para Claude, una extensión para Cursor, otra para Windsurf… La complejidad es N × M (N fuentes de datos × M herramientas de IA).

Con MCP, solo escribes un MCP Server y todos los clientes compatibles pueden llamarlo directamente. La complejidad baja a N + M.

La arquitectura de tres capas es sencilla:

+-------------+     +-------------+     +-------------+
|    Host     | ->  |   Client    | ->  |   Server    |
|  (Claude)   |     | (Cliente MCP)|     | (Tu servicio)|
+-------------+     +-------------+     +-------------+
  • Host: la aplicación de IA en sí, como Claude Desktop o Cursor
  • Client: el cliente MCP, responsable de comunicarse con el Host
  • Server: el servicio que escribes tú, que ofrece funcionalidad concreta

Las tres capacidades de un MCP Server

Un MCP Server puede ofrecer tres tipos de funcionalidad:

CapacidadUsoEjemplo
Tools (herramientas)Ejecutar accionesConsultar el tiempo, enviar mensajes, leer base de datos
Resources (recursos)Proporcionar datosContenido de archivos, respuestas de API, configuración
Prompts (indicaciones)Plantillas predefinidasPlantilla de revisión de código, plantilla de informe diario

Piensa en Tools como «funciones» — la IA las invoca para ejecutar una acción; Resources como «fuentes de datos» — la IA puede leer su contenido; Prompts como «plantillas» — ayudan a la IA a entender la tarea más rápido.

Diferencia con otros artículos: Si has visto otros tutoriales de MCP, quizá conoces versiones con Python y FastMCP. Este artículo usa el SDK nativo de TypeScript, más adecuado para desarrolladores frontend y full-stack. Ambas implementaciones son equivalentes; elige el lenguaje que domines.

"https://modelcontextprotocol.io"


Preparación del entorno de desarrollo

Requisitos previos

Este artículo asume que ya:

  • Tienes Node.js 18+ o Bun 1.0+ instalado
  • Has escrito TypeScript y conoces interface y async/await
  • Tienes Claude Desktop u otro cliente compatible con MCP (Cursor, Windsurf, etc.)

Si no has usado Bun, te recomiendo probarlo — es mucho más rápido que npm e incluye soporte TypeScript integrado, sin configurar ts-node.

Inicializar el proyecto

# Crear directorio del proyecto
mkdir mcp-weather-server && cd mcp-weather-server

# Inicializar (con Bun o npm)
bun init -y
# o npm init -y

# Instalar MCP TypeScript SDK
bun add @modelcontextprotocol/sdk zod
# o npm install @modelcontextprotocol/sdk zod

Aquí usamos dos dependencias:

  • @modelcontextprotocol/sdk: SDK oficial de MCP para TypeScript
  • zod: validación de tipos en tiempo de ejecución para definir el schema de parámetros de herramientas

Puntos clave de la configuración TypeScript

Si usas bun init, tsconfig.json ya viene configurado. Si lo haces a mano, fíjate en estas opciones:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "ESNext",
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "strict": true
  }
}

moduleResolution: "bundler" es importante para módulos ESM; si no, puedes encontrarte con errores del tipo «xxx is not defined».


Práctica: escribir un MCP Server de consulta del tiempo

Este tutorial te lleva a completar un MCP Server que:

  1. Recibe solicitudes de invocación de la IA
  2. Consulta la API de OpenWeatherMap para obtener el tiempo en tiempo real
  3. Devuelve resultados formateados

Diseño de la estructura del proyecto

mcp-weather-server/
+-- src/
|   +-- index.ts      # Archivo de entrada
|   +-- weather.ts    # Implementación de la herramienta del tiempo
|   +-- resources.ts  # Definición de recursos
+-- package.json
+-- tsconfig.json

El código puede ir todo en index.ts (como en este artículo), pero dividirlo en módulos facilita el mantenimiento.

Paso 1: crear el esqueleto del MCP Server

Empecemos por lo más simple — un MCP Server que arranque:

// src/index.ts
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

// Crear instancia del servidor
const server = new McpServer({
  name: "weather-service",
  version: "1.0.0",
});

// Registrar herramienta (Tools)
server.tool(
  "get_weather",
  "Obtener información meteorológica actual de una ciudad",
  {
    city: z.string().describe("Nombre de la ciudad, ej.: Beijing, Shanghai"),
  },
  async ({ city }) => {
    // La implementación se desarrolla en la siguiente sección
    return { content: [{ type: "text", text: `Consultando el tiempo en ${city}...` }] };
  }
);

// Iniciar el servidor
const transport = new StdioServerTransport();
await server.connect(transport);

McpServer es la clase principal del SDK; debes pasar name y version. El método tool() registra una herramienta: primer argumento el nombre, segundo la descripción, tercero el schema de parámetros y por último la función de ejecución.

Paso 2: implementar la herramienta de consulta del tiempo (código principal)

Ahora hagamos que la herramienta funcione de verdad. Usamos la API gratuita de OpenWeatherMap:

// src/weather.ts
import { z } from "zod";

// Definir tipo de respuesta de OpenWeatherMap API
interface WeatherResponse {
  name: string;
  main: { temp: number; feels_like: number; humidity: number };
  weather: [{ description: string }];
  wind: { speed: number };
}

// Implementación de la herramienta de consulta del tiempo
server.tool(
  "get_weather",
  "Obtener información meteorológica actual de una ciudad",
  {
    city: z.string().describe("Nombre de la ciudad, ej.: Beijing, Shanghai"),
  },
  async ({ city }) => {
    const API_KEY = process.env.OPENWEATHER_API_KEY;
    const url = `https://api.openweathermap.org/data/2.5/weather?q=${city}&appid=${API_KEY}&units=metric&lang=zh_cn`;

    try {
      const response = await fetch(url);
      if (!response.ok) {
        throw new Error(`Error en la solicitud API: ${response.status}`);
      }

      const data: WeatherResponse = await response.json();

      // Devolver resultado formateado
      return {
        content: [
          {
            type: "text",
            text: JSON.stringify({
              city: data.name,
              temperature: `${data.main.temp}°C`,
              feels_like: `${data.main.feels_like}°C`,
              description: data.weather[0].description,
              humidity: `${data.main.humidity}%`,
              wind_speed: `${data.wind.speed} m/s`,
            }, null, 2),
          },
        ],
      };
    } catch (error) {
      return {
        content: [
          {
            type: "text",
            text: `Consulta fallida: ${error instanceof Error ? error.message : 'Error desconocido'}`,
          },
        ],
        isError: true,
      };
    }
  }
);

Ten en cuenta:

  1. API Key desde variable de entorno: nunca escribas la clave directamente en el código
  2. Manejo de errores: devuelve isError: true para que el cliente sepa que la invocación falló
  3. Definición de tipos: la interfaz WeatherResponse permite que TypeScript verifique la estructura de datos

Regístrate en OpenWeatherMap para obtener una API Key gratuita y configura la variable de entorno:

export OPENWEATHER_API_KEY=your_api_key_here

Paso 3: añadir Resources (opcional pero recomendado)

Los Resources permiten que tu Server ofrezca datos de solo lectura. Por ejemplo, un recurso con el estado del servidor:

// src/resources.ts

// Proporcionar información del estado del servidor
server.resource(
  "server-status",
  "status://server",
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: JSON.stringify({
          name: "Weather Service",
          version: "1.0.0",
          status: "running",
          timestamp: new Date().toISOString(),
        }, null, 2),
      },
    ],
  })
);

// Proporcionar documentación de la API
server.resource(
  "api-docs",
  "docs://api",
  async (uri) => ({
    contents: [
      {
        uri: uri.href,
        text: `
# Weather MCP Server API

## Tools
- get_weather(city: string): Obtener el tiempo de una ciudad

## Resources
- status://server - Estado del servidor
- docs://api - Documentación de la API
        `.trim(),
      },
    ],
  })
);

Los dos primeros argumentos de resource() son el nombre del recurso y el URI; el tercero es la función de lectura. El URI puede usar cualquier scheme, como status:// o docs://, siempre que puedas distinguirlos.

Paso 4: añadir Prompts (funcionalidad avanzada)

Los Prompts son plantillas de conversación predefinidas. Por ejemplo, una plantilla de «informe meteorológico» que la IA rellena automáticamente con el nombre de la ciudad:

// Plantilla predefinida de informe meteorológico
server.prompt(
  "weather_report",
  "Generar un informe meteorológico formateado",
  {
    city: z.string().describe("Nombre de la ciudad"),
    include_tips: z.boolean().optional().describe("Incluir consejos de vestimenta"),
  },
  ({ city, include_tips }) => ({
    messages: [
      {
        role: "user",
        content: {
          type: "text",
          text: `Genera un informe meteorológico para ${city}. ${include_tips ? "Incluye también consejos de vestimenta." : ""}`,
        },
      },
    ],
  })
);

El valor de retorno de prompt() es un array de mensajes, cada uno con role y content. Así la IA obtiene el contexto predefinido al usarlo.

Paso 5: completar el archivo de entrada

Integra todo el código anterior en src/index.ts y añade manejo de errores:

// src/index.ts (versión completa)
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";

const server = new McpServer({
  name: "weather-service",
  version: "1.0.0",
});

// Registrar todas las herramientas, recursos e indicaciones
// ... (código anterior)

// Manejo de errores
process.stdin.on("error", (err) => {
  console.error("Error en entrada estándar:", err);
  process.exit(1);
});

process.stdout.on("error", (err) => {
  console.error("Error en salida estándar:", err);
  process.exit(1);
});

// Salida elegante
process.on("SIGINT", async () => {
  await server.close();
  process.exit(0);
});

// Iniciar el servidor
const transport = new StdioServerTransport();
await server.connect(transport);

console.error("MCP Weather Server iniciado, esperando conexión...");

StdioServerTransport usa entrada y salida estándar para comunicarse, por lo que el manejo de errores de stdin/stdout es importante. El manejo de SIGINT permite detener el servicio con Ctrl+C de forma limpia.

Ejecuta bun run src/index.ts; si ves el mensaje «iniciado», todo está bien.


Configurar el cliente: que Claude use tu Server

El Server está listo; ahora hay que hacer que Claude Desktop o Cursor puedan invocarlo.

Configuración de Claude Desktop

Localiza el archivo de configuración:

  • macOS: ~/Library/Application Support/Claude/claude_desktop_config.json
  • Windows: %APPDATA%\Claude\claude_desktop_config.json

Añade la configuración de tu Server:

{
  "mcpServers": {
    "weather": {
      "command": "bun",
      "args": ["run", "/absolute/path/to/mcp-weather-server/src/index.ts"],
      "env": {
        "OPENWEATHER_API_KEY": "tu API Key"
      }
    }
  }
}

Nota: la ruta en args debe ser absoluta. Una ruta relativa hará que el Server falle al arrancar.

Configuración de Cursor / Windsurf

Cursor y Windsurf se configuran de forma similar: en los ajustes del IDE, busca la configuración MCP y añade la entrada del servidor (mismo formato).

El archivo de configuración de Cursor suele estar en:

  • macOS: ~/Library/Application Support/Cursor/User/globalStorage/state.vscdb
  • O dentro del IDE: Ajustes -> AI -> MCP -> Añadir servidor

Probar tu Server

  1. Reinicia Claude Desktop / Cursor
  2. En el diálogo escribe: «Consulta el tiempo en Beijing»
  3. Claude debería invocar automáticamente tu MCP Server

Si ves una salida similar a esta, ha funcionado:

{
  "city": "Beijing",
  "temperature": "18°C",
  "feels_like": "16°C",
  "description": "nublado",
  "humidity": "65%",
  "wind_speed": "3.2 m/s"
}

Solución de problemas frecuentes

ProblemaPosible causaSolución
Server no conectadoRuta incorrectaComprueba la ruta absoluta en args
API Key inválidaVariable de entorno no pasadaConfirma que env está bien configurado
Sin respuestaError de compilación TypeScriptCompila primero con bun build o tsc
Error de permisosPermisos del archivo de configuraciónAsegúrate de que el archivo sea legible

"https://github.com/modelcontextprotocol/typescript-sdk"


Extensiones y recomendaciones de despliegue

Añadir más herramientas

La consulta del tiempo es solo el comienzo. También puedes:

  • Consulta histórica del tiempo: llamar a una API de datos históricos
  • Comparación multi-ciudad: consultar varias ciudades y devolver una tabla comparativa
  • Suscripción a alertas meteorológicas: comprobar si hay avisos de mal tiempo

El registro de estas herramientas es idéntico al de get_weather; solo cambia la lógica de implementación.

Comparación de opciones de despliegue

Si quieres compartir el Server con tu equipo, la transmisión stdio local no basta. Aquí tienes varias formas de desplegarlo:

Forma de despliegueEscenarioVentajasDesventajas
stdio localUso personal, pruebasSimple, seguroNo compartible
HTTP/SSEEquipo, multiusuarioAcceso remotoRequiere autenticación
ServerlessProducciónEscalado automáticoLatencia de arranque en frío

Consideraciones para producción

Autenticación: en despliegue HTTP debes implementar autenticación. MCP soporta OAuth 2.1; también puedes usar una API Key simple:

// Comprobar API Key en el encabezado de la solicitud
const apiKey = request.headers.get("Authorization");
if (apiKey !== `Bearer ${process.env.API_KEY}`) {
  return new Response("Unauthorized", { status: 401 });
}

Limitación de tasa: evita llamadas maliciosas que agoten tu cuota de API. Puedes usar express-rate-limit o el rate limiting integrado de Cloudflare Workers.

Registros: usa pino o winston para registrar invocaciones de herramientas y facilitar la depuración:

import pino from "pino";
const logger = pino();

server.tool("get_weather", /* ... */, async ({ city }) => {
  logger.info({ city }, "Consultando el tiempo");
  // ...
});

Monitorización: rastrea tasa de éxito y tiempo de respuesta. Prometheus + Grafana es una combinación habitual.


Resumen

Este artículo explica cómo escribir un MCP Server desde cero con TypeScript. Los contenidos incluyen:

  • Entender los conceptos clave de MCP y la arquitectura de tres capas
  • Crear un servidor con el MCP TypeScript SDK
  • Implementar la herramienta de consulta del tiempo (Tools)
  • Añadir recursos de estado del servidor (Resources)
  • Definir plantillas de informe meteorológico (Prompts)
  • Configurar Claude Desktop / Cursor para invocar el Server

Ahora puedes:

  1. Construir wrappers MCP para las APIs que uses a diario (GitHub, Slack, Notion, etc.)
  2. Crear interfaces MCP para sistemas internos (CRM, bases de datos)
  3. Explorar lo que ya existe en la comunidad MCP

Recursos para profundizar:

Si quieres profundizar en los principios del protocolo MCP, lee Guía profunda del protocolo MCP.

FAQ

¿Qué conocimientos necesito para desarrollar un MCP Server?
Necesitas bases de JavaScript/TypeScript. Este artículo usa el MCP TypeScript SDK; con async/await y definición de tipos es suficiente para empezar.
¿Cuál es la diferencia entre MCP Server y FastMCP?
FastMCP es un framework en Python, ideal para desarrolladores Python. Este artículo usa el SDK nativo de TypeScript, más adecuado para frontend/full-stack. Ambos son funcionalmente equivalentes; la elección depende de tu stack.
¿Cómo pruebo si el MCP Server funciona correctamente?
Tras configurar Claude Desktop, escribe una solicitud en lenguaje natural (por ejemplo, «consulta el tiempo en Beijing»). Si Claude invoca la herramienta automáticamente y devuelve el resultado, el Server funciona bien.
¿Se puede desplegar un MCP Server en un servidor remoto?
Sí. Este artículo usa transmisión stdio, adecuada para desarrollo local. En producción puedes usar HTTP/SSE; necesitarás autenticación OAuth y protección contra rate limiting.

11 min de lectura · Publicado el: 19 mar 2026 · Actualizado el: 21 ago 2026

Comentarios

Inicia sesión con GitHub para dejar un comentario

Easton BlogEaston Blog