Ticker

6/recent/ticker-posts

Desarrolla tu primer Servidor MCP (Model Context Protocol) con FastMCP y Python


El Model Context Protocol (MCP) es un protocolo abierto, estandarizado por Anthropic, que define una forma común de conectar modelos de lenguaje (LLMs) con fuentes de datos y herramientas externas. Antes de MCP, cada integración entre un cliente de IA (como Claude Desktop) y un sistema externo (una base de datos, una API, el sistema de archivos, etc.) requería código de conexión personalizado. MCP resuelve esto proponiendo una interfaz uniforme: un servidor MCP expone herramientas (tools), recursos (resources) y prompts, y cualquier cliente MCP compatible (Claude Desktop, Claude Code, IDEs, etc.) puede descubrirlos y utilizarlos sin necesidad de integraciones ad-hoc.

En este artículo construiremos, paso a paso, un servidor MCP mínimo escrito en Python utilizando FastMCP, un framework de alto nivel que abstrae los detalles del protocolo (JSON-RPC, manejo de transporte, serialización de esquemas) y permite exponer funciones de Python como herramientas invocables por un LLM con apenas un decorador.

Concretamente, construiremos un servidor llamado MiServidorPython que expone una única herramienta, saludar, la cual recibe un nombre como parámetro y devuelve un saludo personalizado. Aunque el ejemplo es deliberadamente simple, cubre el ciclo completo: instalación de dependencias, definición del servidor, transporte de comunicación, verificación mediante interfaz gráfica y consideraciones prácticas de depuración.

1. Instalación de dependencias

FastMCP es un paquete de Python que envuelve el SDK oficial de MCP y añade una API declarativa basada en decoradores. Se instala mediante pip:

pip install fastmcp

Recomendación: usar un entorno virtual

Es una práctica recomendada aislar las dependencias del proyecto en un entorno virtual (venv), evitando así conflictos con el intérprete de Python global del sistema:

# Crear el entorno virtual dentro de la carpeta del proyecto
python -m venv venv

# Activarlo (Windows - PowerShell)
.\venv\Scripts\Activate.ps1

# Activarlo (Windows - CMD)
.\venv\Scripts\activate.bat

# Activarlo (macOS / Linux)
source venv/bin/activate

# Instalar la dependencia dentro del entorno activo
pip install fastmcp

Esta distinción es relevante porque, como se detalla en la sección 5, uno de los errores más comunes al desplegar un servidor MCP en un cliente como Claude Desktop es que el intérprete de Python invocado por el cliente no es el mismo en el que se instaló fastmcp, generando un ModuleNotFoundError en tiempo de ejecución.

Verificación de la instalación

python -c "import fastmcp; print(fastmcp.__version__)"

Si el comando imprime un número de versión sin lanzar excepciones, la dependencia está correctamente instalada en el intérprete activo.

2. Código del servidor (server.py)

from fastmcp import FastMCP

# 1. Inicializamos el servidor FastMCP con un nombre descriptivo
server = FastMCP("MiServidorPython")

# 2. Definimos una herramienta (Tool) usando el decorador @mcp.tool()
@server.tool()
def saludar(nombre: str) -> str:
    """Devuelve un saludo personalizado."""
    return f"Hola {nombre}!"

# 3. Punto de entrada para ejecutar el servidor MCP
if __name__ == "__main__":
    server.run(transport="stdio")

Este archivo constituye la totalidad del servidor: no requiere configuración adicional, archivos de manifiesto ni infraestructura externa para funcionar en modo local.

3. ¿Cómo funciona este código?

Analicemos cada bloque en detalle.

3.1. Instanciación del servidor

server = FastMCP("MiServidorPython")

FastMCP es la clase principal del framework. Al instanciarla, se crea un objeto servidor que internamente:

  • Registra un nombre de servidor ("MiServidorPython"), el cual se reporta al cliente durante el handshake inicial del protocolo (el intercambio de mensajes initialize definido por la especificación MCP).
  • Prepara un registro interno de capacidades (tools, resources, prompts) que se irá poblando a medida que se apliquen los decoradores correspondientes.
  • Configura, de forma transparente, el manejo de mensajes JSON-RPC 2.0, que es el formato subyacente sobre el cual se construye toda comunicación MCP, independientemente del transporte utilizado.

Este objeto server actúa como el punto de orquestación de toda la aplicación.

3.2. Definición de una herramienta con @server.tool()

@server.tool()
def saludar(nombre: str) -> str:
    """Devuelve un saludo personalizado."""
    return f"Hola {nombre}!"

Este es el núcleo conceptual de MCP: convertir una función de Python ordinaria en una herramienta invocable por un modelo de lenguaje. El decorador @server.tool() realiza varias acciones automáticamente:

  1. Introspección de la firma de la función. FastMCP inspecciona los type hints (nombre: str) y genera a partir de ellos un JSON Schema que describe los parámetros de entrada esperados. Este esquema es lo que el cliente MCP (y, por extensión, el modelo) recibe cuando solicita la lista de herramientas disponibles (tools/list).

  2. Extracción del docstring como descripción. La cadena """Devuelve un saludo personalizado.""" no es un comentario decorativo: se convierte en el campo description de la herramienta dentro del esquema MCP. El modelo utiliza este texto para decidir cuándo y cómo invocar la herramienta, por lo que su redacción tiene impacto funcional directo, no solo documental.

  3. Registro en la tabla interna de herramientas del servidor. A partir de este punto, saludar queda disponible para ser listada y ejecutada remotamente a través del protocolo, sin que el desarrollador tenga que escribir manualmente el manejo de la petición tools/call.

  4. Validación automática de tipos en tiempo de ejecución. Cuando el cliente invoca la herramienta, FastMCP valida que el argumento recibido corresponda al tipo declarado (str en este caso) antes de ejecutar el cuerpo de la función, rechazando la llamada con un error estructurado si el tipo no coincide.

El cuerpo de la función es lógica de negocio ordinaria: recibe nombre, construye un string mediante f-string y lo retorna. FastMCP se encarga de serializar el valor de retorno en la respuesta JSON-RPC correspondiente.

3.3. Punto de entrada y selección de transporte

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

El método server.run() inicia el bucle de eventos del servidor y lo deja escuchando peticiones. El parámetro transport determina el canal de comunicación entre el cliente MCP y este proceso. FastMCP soporta principalmente dos modalidades:

  • stdio (entrada/salida estándar): el cliente MCP (por ejemplo, Claude Desktop) lanza este script como un proceso hijo y se comunica con él escribiendo mensajes JSON-RPC en su stdin y leyendo las respuestas desde su stdout. Es el modelo de transporte estándar para servidores MCP locales, y es el que utilizan clientes de escritorio como Claude Desktop cuando el servidor se define con un command y args en el archivo de configuración.

  • http / sse (transporte remoto): alternativa para exponer el servidor como un servicio de red accesible remotamente, típicamente usada cuando el servidor no corre en la misma máquina que el cliente.

Un detalle crítico del transporte stdio, y fuente frecuente de errores difíciles de diagnosticar: dado que stdout está reservado exclusivamente para los mensajes del protocolo JSON-RPC, cualquier uso de print() estándar dentro del servidor corrompe el canal de comunicación. Cualquier salida de depuración debe redirigirse explícitamente a stderr (por ejemplo, mediante print(..., file=sys.stderr) o el módulo logging configurado hacia stderr), canal que los clientes como Claude Desktop capturan por separado y almacenan en logs específicos por servidor.

La cláusula if __name__ == "__main__": es una convención estándar de Python que garantiza que server.run() solo se ejecute cuando el archivo se invoca directamente (python server.py), y no cuando se importa como módulo desde otro script.

4. ¿Cómo probar la interfaz gráfica de FastMCP?

Antes de conectar el servidor a un cliente de producción como Claude Desktop, es una buena práctica de ingeniería validarlo de forma aislada. Para ello se utiliza MCP Inspector, una herramienta oficial del ecosistema MCP que levanta una interfaz web local desde la cual se puede inspeccionar el handshake, listar las herramientas expuestas y ejecutarlas manualmente con distintos parámetros, sin depender de un LLM real.

Desde Visual Studio Code, usando la terminal Bash

  1. Abre la terminal integrada de VS Code y, si por defecto usa PowerShell o CMD, cámbiala a Bash: haz clic en la flecha desplegable junto al ícono + del panel de terminal y selecciona "Git Bash" (en Windows, requiere tener Git for Windows instalado) o simplemente usa WSL si está configurado.

  2. Navega hasta la carpeta del proyecto:

    cd /d/Hadson.TECH/LEARNING/MCP-Learning
    

    (En Git Bash, las rutas de Windows D:\... se referencian con el prefijo /d/....)

  3. Ejecuta MCP Inspector apuntando a tu script, usando npx para invocar el paquete sin necesidad de instalación global previa:

    npx -y @modelcontextprotocol/inspector python server.py
    

    Desglose del comando:

    • npx: ejecutor de paquetes de Node.js que descarga y corre el paquete indicado en un solo paso.
    • -y: responde automáticamente "sí" a la confirmación de descarga, evitando el prompt interactivo.
    • @modelcontextprotocol/inspector: el paquete oficial de Inspector.
    • python server.py: el comando que Inspector usará internamente para lanzar tu servidor MCP como subproceso, exactamente igual a como lo haría Claude Desktop.
  4. Al ejecutarse, Inspector imprime en la terminal una URL local (típicamente http://localhost:6274 o similar) junto con un token de sesión. Ábrela en el navegador.

  5. En la interfaz gráfica de Inspector:

    • La pestaña "Tools" debe listar saludar, junto con el esquema de parámetros generado automáticamente (nombre: string) y la descripción extraída del docstring.
    • Puedes ingresar un valor para nombre y ejecutar la herramienta manualmente, observando en tiempo real la petición JSON-RPC enviada y la respuesta recibida ("Hola <nombre>!").
    • La pestaña de notificaciones/logs de Inspector muestra cualquier mensaje que el servidor escriba a stderr, útil para depurar errores antes de conectar el servidor a un cliente real.

Si el servidor responde correctamente en Inspector, existe una fuerte garantía de que también funcionará al conectarlo a Claude Desktop u otro cliente MCP, ya que Inspector reproduce fielmente el ciclo de vida del protocolo (initializetools/listtools/call) sobre el mismo transporte stdio.

Resumen

  • MCP estandariza la forma en que los modelos de lenguaje descubren y ejecutan herramientas externas, eliminando la necesidad de integraciones personalizadas por cliente.
  • FastMCP reduce la construcción de un servidor MCP a un patrón declarativo: instanciar FastMCP(nombre) y decorar funciones de Python con @server.tool() para exponerlas automáticamente, incluyendo generación de esquema JSON y extracción de documentación desde el docstring.
  • El transporte stdio es el modelo de comunicación estándar para servidores locales: el cliente lanza el script como proceso hijo y se comunica por entrada/salida estándar, razón por la cual el canal stdout debe reservarse exclusivamente para el protocolo, redirigiendo cualquier log a stderr.
  • MCP Inspector, invocado vía npx, permite validar el servidor de forma aislada —listando y ejecutando herramientas desde una interfaz web— antes de integrarlo con un cliente de producción, aislando así si un fallo posterior de conexión proviene del código del servidor o de la configuración del cliente.
  • La instalación correcta de dependencias en el intérprete de Python correcto (idealmente dentro de un entorno virtual referenciado explícitamente en la configuración del cliente) es un requisito frecuentemente subestimado, y su omisión es la causa más común de errores de tipo "Server disconnected" al desplegar el servidor.

Con este flujo de instalación, definición declarativa de herramientas, selección de transporte y validación con Inspector se cubre el ciclo completo de desarrollo de un servidor MCP en Python, listo para integrarse en clientes como Claude Desktop o Claude Code mediante su respectivo archivo de configuración.

Publicar un comentario

0 Comentarios