Saltar al contenido
sobolewski.proLa IA de hoy, cada tardeSuscribirme

Una vez al día. Date de baja con un clic. Política de privacidad

Cómo crear un servidor MCP en Python paso a paso

¿Cómo crear un servidor MCP? Escribimos herramientas en Python con el SDK 2.0, las probamos en MCP Inspector y las conectamos a Claude Code y Claude Desktop.

7 min de lectura

Este artículo en otros idiomas:العربيةDeutschEnglishFrançaisItaliano日本語Polski简体中文

En este artículo
  1. ¿Qué vamos a necesitar?
  2. Paso 1: ¿cómo preparar el proyecto?
  3. Paso 2: ¿cómo crear un servidor MCP con su primera herramienta?
  4. Paso 3: ¿por qué no se puede usar print()?
  5. Paso 4: ¿cómo probar el servidor en MCP Inspector?
  6. Paso 5: ¿cómo conectar el servidor a Claude Code?
  7. Paso 6: ¿cómo añadir el servidor a Claude Desktop?
  8. Paso 7: ¿cómo ejecutar un servidor MCP por HTTP?
  9. ¿En qué fijarse?
  10. FAQ

¿Cómo crear un servidor MCP? En Python basta con un archivo y el MCP Python SDK oficial: escribimos una función normal con tipos y docstring, le ponemos el decorador @mcp.tool() y el SDK la convierte en una herramienta que pueden invocar Claude, ChatGPT o Cursor. El servidor completo del ejemplo de la documentación ocupa poco más de una docena de líneas.

En este tutorial escribimos un servidor desde cero, lo probamos en MCP Inspector y lo conectamos a Claude Code y a Claude Desktop. Los comandos y el código salen del tutorial oficial «Build an MCP server», del README del SDK y de la documentación de Claude Code.

¿Qué vamos a necesitar?

Doy por hecho que ya sabemos qué es MCP, es decir, host, cliente, servidor y herramientas (si no, mejor empezar por el tutorial Qué es MCP). Para trabajar necesitamos:

  1. Python 3.11+ (es lo que pide el README del SDK actual),
  2. el gestor de paquetes uv, que es el que usa el tutorial oficial,
  3. Node.js 22.19.0 o posterior, si queremos probar el servidor en MCP Inspector,
  4. un host MCP para comprobar el conjunto, por ejemplo Claude Code o Claude Desktop.

Un aviso antes de empezar: en la versión 2.0 del SDK cambió el nombre de la clase principal. Según el resumen de cambios de la v2, el antiguo from mcp.server.fastmcp import FastMCP ahora es from mcp.server import MCPServer. Muchos tutoriales que circulan por la red siguen mostrando la versión antigua, y el tutorial oficial exige el SDK 2.0.0 o posterior.

Paso 1: ¿cómo preparar el proyecto?

Primero instalamos uv (macOS y Linux):

curl -LsSf https://astral.sh/uv/install.sh | sh

En Windows, desde PowerShell:

powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

Después de instalarlo reiniciamos la terminal para que el comando uv esté disponible. Luego creamos el proyecto e instalamos el SDK:

uv init mi-servidor
cd mi-servidor
uv venv
source .venv/bin/activate
uv add "mcp[cli]"
touch server.py

El extra [cli] instala el comando mcp (con los subcomandos mcp dev, mcp run y mcp install), que nos servirá para las pruebas (en Windows el entorno se activa con .venv\Scripts\activate).

Paso 2: ¿cómo crear un servidor MCP con su primera herramienta?

En el archivo server.py copiamos el ejemplo del README del SDK y añadimos al final el arranque del servidor, igual que en el tutorial oficial:

from mcp.server import MCPServer

mcp = MCPServer("Demo")


@mcp.tool()
def add(a: int, b: int) -> int:
    """Add two numbers."""
    return a + b


@mcp.resource("greeting://{name}")
def greeting(name: str) -> str:
    """Greet someone by name."""
    return f"Hello, {name}!"


if __name__ == "__main__":
    mcp.run(transport="stdio")

Esto ya es un servidor MCP completo, con una herramienta (add) y un recurso con plantilla de dirección (greeting://{name}). Lo curioso es que no escribimos ningún JSON Schema: según el README, la firma a: int, b: int es el esquema, y el docstring pasa a ser la descripción de la herramienta que lee el modelo. Del análisis de las peticiones, la validación y el protocolo se encarga el SDK.

Por eso conviene cuidar los docstrings. El modelo elige la herramienta por su nombre y su descripción, así que «Add two numbers.» es el mínimo, y en herramientas de verdad describimos también los argumentos, como en el ejemplo oficial del tiempo:

@mcp.tool()
async def get_forecast(latitude: float, longitude: float) -> str:
    """Get weather forecast for a location.

    Args:
        latitude: Latitude of the location
        longitude: Longitude of the location
    """

(este ejemplo usa la API del servicio meteorológico estadounidense NWS, así que solo funciona con ubicaciones de EE. UU.)

Paso 3: ¿por qué no se puede usar print()?

Es el error más habitual en un primer servidor. Un servidor local se comunica con el host por la entrada y la salida estándar (stdio), y los mensajes van en formato JSON-RPC. La documentación lo advierte sin rodeos: escribir en stdout corrompe los mensajes JSON-RPC y deja el servidor fuera de servicio, y print() escribe por defecto precisamente en stdout.

En su lugar usamos el módulo logging, que escribe en stderr:

import logging

logger = logging.getLogger(__name__)

logger.info("Processing request")  # va a stderr

Con los servidores HTTP el problema no aparece, porque los logs en stdout no se mezclan con las respuestas HTTP.

Paso 4: ¿cómo probar el servidor en MCP Inspector?

MCP Inspector es la herramienta oficial para probar servidores. Desde el SDK se lanza con un solo comando:

uv run mcp dev server.py

Se abrirá el navegador con el Inspector. Según el README, llamar a add con los argumentos a=1 y b=2 devuelve 3. Ahí mismo se ven la lista de herramientas y recursos y los mensajes JSON-RPC en crudo, que ayudan mucho cuando algo no funciona.

El Inspector tiene además una versión para la terminal (--tui) y un modo para scripts (--cli). Este último imprime, por ejemplo, la lista de herramientas y termina, así que encaja bien en CI:

npx @modelcontextprotocol/inspector --cli uv run server.py --method tools/list

(comprueba tu versión de Node.js con node --version, el Inspector exige la 22.19.0 o posterior)

Paso 5: ¿cómo conectar el servidor a Claude Code?

Un servidor local se añade como servidor de tipo stdio. Todo lo que va después de -- es el comando que ejecutará Claude Code:

claude mcp add --transport stdio demo -- uv --directory /RUTA/COMPLETA/mi-servidor run server.py

La ruta tiene que ser absoluta (la obtenemos con pwd en la carpeta del proyecto). El estado de la conexión lo muestra claude mcp get demo y, dentro de una sesión de Claude Code, el comando /mcp. Ahora podemos pedir, por ejemplo, «suma 1234 y 5678 con la herramienta add» y aprobar la llamada.

Hay dos cosas de la documentación de Claude Code que vienen bien desde el primer momento:

  1. el tiempo de arranque del servidor se cambia con la variable MCP_TIMEOUT; por ejemplo, MCP_TIMEOUT=10000 claude le da al servidor 10 segundos,
  2. Claude Code avisa cuando el resultado de una herramienta supera los 10 000 tokens y por defecto lo corta en 25 000 tokens (el límite se cambia con la variable MAX_MCP_OUTPUT_TOKENS), así que las herramientas no deberían devolver bases de datos enteras.

Paso 6: ¿cómo añadir el servidor a Claude Desktop?

En Claude Desktop los servidores locales se declaran en el archivo de configuración claude_desktop_config.json. Está en ~/Library/Application Support/Claude/ en macOS, en ~/.config/Claude/ en Linux y en %AppData%\Claude\ en Windows (si no existe, lo creamos). La entrada tiene este aspecto:

{
  "mcpServers": {
    "demo": {
      "command": "uv",
      "args": [
        "--directory",
        "/RUTA/COMPLETA/mi-servidor",
        "run",
        "server.py"
      ]
    }
  }
}

Después cerramos la aplicación del todo (en macOS con Cmd+Q, cerrar la ventana no basta) y la volvemos a abrir. El servidor debería aparecer en el menú «Connectors», bajo el icono del signo más. Si no está, la documentación aconseja revisar la sintaxis del JSON, que la ruta sea absoluta y poner en el campo command la ruta completa de uv (la muestra which uv). Los logs están en ~/Library/Logs/Claude (macOS) o en ~/.config/Claude/logs/ (Linux), en los archivos mcp.log y mcp-server-NOMBRE.log.

Paso 7: ¿cómo ejecutar un servidor MCP por HTTP?

Un servidor stdio solo funciona en nuestro ordenador. Para ofrecerlo como servidor remoto lo ejecutamos con Streamable HTTP, el transporte que, según el README del SDK, se despliega en producción:

uv run mcp run server.py --transport streamable-http

El servidor queda entonces a la escucha en http://localhost:8000/mcp, y en Claude Code lo añadimos como cualquier servidor remoto:

claude mcp add --transport http demo-http http://localhost:8000/mcp

Para que un servidor así funcione en la app de Claude del navegador como «custom connector», tiene que ser accesible desde internet, porque Claude se conecta a él desde la nube de Anthropic. Llegados ahí hará falta también un inicio de sesión: la documentación de MCP describe la autorización de servidores mediante OAuth 2.1.

¿En qué fijarse?

Primero, en lo que la herramienta permite hacer. Un servidor MCP le da al modelo capacidad real de actuar en nuestro ordenador, así que conviene escribir herramientas lo más acotadas posible: en vez de «ejecuta cualquier comando», mejor «lee un archivo de esta carpeta».

Segundo, en los datos que vienen de fuera. Si una herramienta descarga contenido de internet o de correos, puede traer consigo una inyección de prompts (prompt injection), es decir, instrucciones ocultas dirigidas al modelo. Escribí más sobre esto en el tutorial Seguridad de los agentes de IA.

Tercero, en los secretos. Las claves de API se pasan por variables de entorno (--env en Claude Code), no en el código. Según la documentación, el Inspector guarda los tokens en el llavero del sistema y, cuando no lo hay (por ejemplo, en un contenedor), en un archivo sin cifrar, ~/.mcp-inspector/secrets.json.

Un servidor como este se puede conectar después a un agente propio, como muestro en el tutorial Cómo crear un agente de IA en Python.

FAQ

¿Cómo crear un servidor MCP de la forma más sencilla?

El servidor más simple del README del SDK ocupa poco más de una docena de líneas: un objeto MCPServer, una función con el decorador @mcp.tool() y mcp.run(). El resto, es decir, el esquema de los argumentos y la gestión del protocolo, lo hace el SDK.

¿Qué es FastMCP?

Es el nombre antiguo de la clase principal del servidor en el MCP Python SDK. En la versión 2.0 pasó a llamarse MCPServer, y el import es from mcp.server import MCPServer.

¿Se puede escribir un servidor MCP en un lenguaje que no sea Python?

Sí. El tutorial oficial tiene versiones para TypeScript, Java, Kotlin, C#, Ruby, Rust y Go, entre otros.

¿Un servidor MCP solo funciona con Claude?

No. MCP es un estándar abierto, así que el mismo servidor funciona en cualquier host compatible con el protocolo, como ChatGPT, Visual Studio Code o Cursor.

  • IA
  • agentes de IA
  • Claude Code
  • programación
  • tutorial

La IA de hoy, cada tarde

Cada tarde, las noticias de IA más importantes del día y los nuevos artículos del blog. Breve y con fuentes.

Una vez al día. Date de baja con un clic. Política de privacidad