Skip to content

Instantly share code, notes, and snippets.

@beatrizsmerino
Last active January 29, 2026 12:18
Show Gist options
  • Select an option

  • Save beatrizsmerino/21efda1595c25d558e31ff140590d9b4 to your computer and use it in GitHub Desktop.

Select an option

Save beatrizsmerino/21efda1595c25d558e31ff140590d9b4 to your computer and use it in GitHub Desktop.
Tutorial: Cómo crear un MCP Server en TypeScript con un tool de clima usando Open-Meteo API
{
"mcpServers": {
"weather": {
"command": "npx",
"args": [
"-y",
"tsx",
"/ruta/a/tu/proyecto/01-start/main.ts"
]
}
}
}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from 'zod'
// 1. Crear el servidor
// Es la interfaz principal con el protocolo MCP. Maneja la comunicacion entre el cliente y el servidor.
const server = new McpServer({
name: 'Demo',
version: '1.0.0'
})
// 2. Definir las herramientas
// Las herramientas le permite al LLM realizar acciones a través de tu servidor.
server.tool(
'fetch-weather',
'Tool to fetch the weather of a city',
{
city: z.string().describe('City name'),
},
async ({ city }) => {
// Geocoding: obtener latitud y longitud de la ciudad
const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`)
const data = await response.json()
if (data.length === 0) {
return {
content: [
{
type: 'text',
text: `No se encontró información para la ciudad ${city}`
}
]
}
}
const { latitude, longitude } = data.results[0]
// Forecast: obtener el clima actual
const weatherResponse = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m&current=temperature_2m,precipitation,is_day,rain&forecast_days=1`)
const weatherData = await weatherResponse.json()
return {
content: [
{
type: 'text',
text: JSON.stringify(weatherData, null, 2)
}
]
}
}
)
// 3. Escuchar las conexiones del cliente
const transport = new StdioServerTransport()
await server.connect(transport)

MCP Server - 01-start

Paso 1: Inicializar el proyecto

mkdir 01-start && cd 01-start
pnpm init

Esto genera el package.json inicial:

{
  "name": "01-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.8.0"
}

Paso 2: Configurar ES Module y crear main.ts

Se agrega "type": "module" al package.json y se crea el archivo main.ts.

package.json actualizado:

{
  "name": "01-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.8.0"
}

Estructura del proyecto:

01-start/
├── main.ts
└── package.json

Paso 3: Instalar el SDK de MCP

pnpm add @modelcontextprotocol/sdk

Instala @modelcontextprotocol/sdk@1.9.0 (81 paquetes).

package.json actualizado:

{
  "name": "01-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.8.0",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.9.0"
  }
}

Paso 4: Instalar Zod (validacion de esquemas)

pnpm add zod

Instala zod@3.24.2.

package.json actualizado:

{
  "name": "01-start",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "packageManager": "pnpm@10.8.0",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.9.0",
    "zod": "^3.24.2"
  }
}

Paso 5: Crear el servidor MCP basico

main.ts:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from 'zod'

// 1. Crear el servidor
// Es la interfaz principal con el protocolo MCP. Maneja la comunicacion entre el cliente y el servidor.

const server = new McpServer({
  name: 'Demo',
  version: '1.0.0'
})

Paso 6: Definir una herramienta (tool)

Las herramientas le permiten al LLM realizar acciones a traves de tu servidor.

Se agrega server.tool() en main.ts:

// 2. Definir las herramientas
// Las herramientas le permite al LLM realizar acciones a través de tu servidor.
server.tool(
  'fetch-weather', // titulo de la herramienta
  'Tool to fetch the weather of a city', // descripción de la herramienta
  {
    city: z.string().describe('City name'),
  },
  async ({ city }) => {
    return {
      content: [
        {
          type: 'text',
          text: `El clima de ${city} es soleado`
        }
      ]
    }
  }
)

Paso 7: Conectar el transporte (stdio)

Se agrega la conexion del servidor usando StdioServerTransport para escuchar las conexiones del cliente:

// 3. Escuchar las conexiones del cliente
const transport = new StdioServerTransport()
await server.connect(transport)

main.ts completo:

import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from 'zod'

// 1. Crear el servidor
// Es la interfaz principal con el protocolo MCP. Maneja la comunicacion entre el cliente y el servidor.

const server = new McpServer({
  name: 'Demo',
  version: '1.0.0'
})

// 2. Definir las herramientas
// Las herramientas le permite al LLM realizar acciones a través de tu servidor.
server.tool(
  'fetch-weather', // titulo de la herramienta
  'Tool to fetch the weather of a city', // descripción de la herramienta
  {
    city: z.string().describe('City name'),
  },
  async ({ city }) => {
    return {
      content: [
        {
          type: 'text',
          text: `El clima de ${city} es soleado`
        }
      ]
    }
  }
)

// 3. Escuchar las conexiones del cliente
const transport = new StdioServerTransport()
await server.connect(transport)

Paso 8: Ejecutar el servidor

npx -y tsx main.ts

El servidor queda escuchando conexiones via stdio.


Paso 9: Configurar en Claude Desktop

Para que Claude Desktop use este servidor MCP, se edita el archivo de configuracion claude_desktop_config.json:

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

Se agrega el servidor weather dentro de mcpServers:

{
  "mcpServers": {
    "weather": {
      "command": "npx",
      "args": [
        "-y",
        "tsx",
        "/Users/midudev/Dev/mcp-clase/01-start/main.ts"
      ]
    }
  }
}

Una vez configurado y reiniciado Claude Desktop, la herramienta fetch-weather aparece disponible en la lista de "Available MCP tools":

  • fetch-weather - Tool to fetch the weather of a city - From server: weather

Paso 10: Depurar con MCP Inspector

El MCP Inspector es una herramienta de depuracion que permite probar servidores MCP sin necesidad de un cliente como Claude Desktop.

Se ejecuta directamente con npx:

npx -y @modelcontextprotocol/inspector npx -y tsx main.ts

Esto levanta el Inspector en http://127.0.0.1:6274 con la siguiente configuracion:

  • Transport Type: STDIO
  • Command: npx
  • Arguments: -y tsx main.ts

Paso 11: Probar la herramienta en el Inspector

En el MCP Inspector (v0.9.0):

  1. Ir a la pestaña Tools
  2. Hacer clic en List Tools para ver las herramientas disponibles
  3. Seleccionar fetch-weather
  4. Introducir un valor en el campo city (ej: Barcelona)
  5. Hacer clic en Run Tool

Resultado:

{
  "content": [
    {
      "type": "text",
      "text": "El clima de Barcelona es soleado"
    }
  ]
}

Tool Result: Success


Paso 12: Conectar con la API real de Open-Meteo

Se actualiza la herramienta fetch-weather para que consulte datos reales del clima usando la API gratuita de Open-Meteo.

El flujo es:

  1. Geocoding: Buscar las coordenadas de la ciudad usando geocoding-api.open-meteo.com
  2. Forecast: Obtener el clima actual usando api.open-meteo.com con las coordenadas
  3. Devolver el JSON completo al LLM para que lo interprete

La respuesta del geocoding viene dentro de data.results[]:

{
  "results": [
    {
      "id": 2950159,
      "name": "Berlin",
      "latitude": 52.52437,
      "longitude": 13.41053,
      "elevation": 74.0,
      "feature_code": "PPLC"
    }
  ]
}

main.ts actualizado (callback de server.tool):

server.tool(
  'fetch-weather',
  'Tool to fetch the weather of a city',
  {
    city: z.string().describe('City name'),
  },
  async ({ city }) => {
    // Geocoding: obtener latitud y longitud de la ciudad
    const response = await fetch(`https://geocoding-api.open-meteo.com/v1/search?name=${city}&count=1&language=en&format=json`)
    const data = await response.json()

    if (data.length === 0) {
      return {
        content: [
          {
            type: 'text',
            text: `No se encontró información para la ciudad ${city}`
          }
        ]
      }
    }

    const { latitude, longitude } = data.results[0]

    // Forecast: obtener el clima actual
    const weatherResponse = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&hourly=temperature_2m&current=temperature_2m,precipitation,is_day,rain&forecast_days=1`)

    const weatherData = await weatherResponse.json()

    return {
      content: [
        {
          type: 'text',
          text: JSON.stringify(weatherData, null, 2)
        }
      ]
    }
  }
)

Se devuelve JSON.stringify(weatherData, null, 2) con todos los datos para que el LLM los interprete.

Al probarlo en el Inspector con Barcelona, el resultado incluye los datos reales:

{
  "latitude": 41.4375,
  "longitude": 2.1875,
  "generationtime_ms": 0.052928924560546875,
  "utc_...": "..."
}

Tool Result: Success


Paso 13: Probar en Claude Desktop con datos reales

Al preguntar en Claude Desktop: "¿Qué tiempo hace hoy en Barcelona?"

Claude usa automaticamente la herramienta fetch-weather del servidor weather (local) y es capaz de interpretar el JSON devuelto por la API de Open-Meteo para generar una respuesta en lenguaje natural:

"Actualmente en Barcelona hace 14.3°C, con una ligera precipitación de 0.2 mm y algo de lluvia (0.1 mm). Es de día y según los datos, la temperatura ha sido moderada durante el día, alcanzando un máximo de aproximadamente 15.2°C alrededor de las 14:00 horas.

Para el resto del día se espera que la temperatura baje gradualmente hasta llegar a unos 10.7°C a medianoche."

El LLM recibe el JSON crudo y lo convierte en informacion comprensible para el usuario.


Paso 14: Configurar el servidor MCP en VS Code (GitHub Copilot)

El mismo servidor MCP se puede usar desde VS Code con GitHub Copilot en modo Agente.

14.1 Habilitar el modo Agente

  1. Abrir la paleta de comandos (Cmd+Shift+P / Ctrl+Shift+P)
  2. Buscar >prePreferencias: Abrir configuración de usuario (JSON)
  3. Verificar que el modo agente esta habilitado:
    • Chat > Agent: Enabled → activado
    • Chat > Agent: Max Requests → 15

14.2 Agregar el servidor MCP

Desde la paleta de comandos buscar >mcp:

  • MCP: Agregar servidor... / MCP: Add Server...
  • MCP: Enumerar servidores / MCP: List Servers

O directamente editar el settings.json de VS Code (~/Library/Application Support/Code/User/settings.json):

{
  "chat.agent.enabled": true,
  "mcp": {
    "servers": {
      "weather": {
        "command": "npx",
        "args": [
          "-y",
          "tsx",
          "/Users/midudev/Dev/mcp-clase/01-start/main.ts"
        ]
      }
    }
  }
}

Una vez guardado, VS Code muestra el estado del servidor directamente en el settings.json:

✓ En ejecución | Detener | Reiniciar | Herramientas de 1

14.3 Seleccionar herramientas

Al abrir el selector de herramientas en Copilot Chat se ven los servidores MCP disponibles:

  • Servidor MCP: weather - Desde Global en Code (En ejecución)fetch-weather
  • Servidor MCP: postgres - Desde Escritorio de Claude (En ejecución)query
  • Servidor MCP: filesystem - Desde Escritorio de Claude (En ejecución)read_file, read_multiple_files, etc.

Paso 15: Usar la herramienta desde GitHub Copilot

En el chat de Copilot (modo Agente), al preguntar por el clima, Copilot detecta el servidor MCP y solicita permiso para ejecutar la herramienta:

GitHub Copilot: "Voy a consultar el tiempo en Madrid para ti. Veo que tienes configurado un servidor MCP para el clima, así que puedo utilizar esa herramienta para obtener la información."

→ Ejecutar "fetch-weather"

[Continuar] | [Cancelar]

Al hacer clic en Continuar, Copilot ejecuta la herramienta fetch-weather y devuelve los datos del clima interpretados.

{
"name": "01-start",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"packageManager": "pnpm@10.8.0",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.9.0",
"zod": "^3.24.2"
}
}
{
"chat.agent.enabled": true,
"mcp": {
"servers": {
"weather": {
"command": "npx",
"args": [
"-y",
"tsx",
"/ruta/a/tu/proyecto/01-start/main.ts"
]
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment