Ahora que nuestro servidor MCP está funcionando, es momento de construir el lado del cliente. El cliente es lo que permite que nuestra aplicación se comunique con el servidor MCP y acceda a su funcionalidad.
Entendiendo la arquitectura del cliente
Antes de entrar al código, aclaremos un punto importante sobre los proyectos MCP. Normalmente, implementarías un cliente MCP o un servidor MCP, no ambos. Estamos construyendo ambos en este proyecto solo para que puedas ver cómo funcionan juntos.

El cliente MCP consta de dos componentes principales:
- MCP Client - Una clase personalizada que creamos para facilitar el uso de la sesión
- Client Session - La conexión real con el servidor (parte del SDK de Python de MCP)

La sesión del cliente requiere limpieza de recursos cuando terminamos de usarla, por eso la envolvemos en nuestra clase personalizada. Esto maneja la gestión de la conexión y la limpieza automáticamente.
Cómo encaja el cliente en nuestra aplicación
¿Recuerdas el flujo de nuestra aplicación? Nuestro código CLI necesita interactuar con Claude de dos maneras clave:

El cliente habilita ambas interacciones al exponer la funcionalidad del servidor a nuestro código base.
Implementando las funciones principales del cliente
Necesitamos implementar dos funciones esenciales: list_tools y call_tool.
Función List Tools
Esta función obtiene todas las herramientas disponibles del servidor:
async def list_tools(self) -> list[types.Tool]:
result = await self.session().list_tools()
return result.toolsEs sencillo: accedemos a nuestra sesión (la conexión con el servidor MCP), llamamos a la función integrada list_tools, y devolvemos las herramientas del resultado.
Función Call Tool
Esta función ejecuta una herramienta específica en el servidor:
async def call_tool(
self, tool_name: str, tool_input: dict
) -> types.CallToolResult | None:
return await self.session().call_tool(tool_name, tool_input)Pasamos el nombre de la herramienta y los parámetros de entrada (proporcionados por Claude) al servidor y devolvemos el resultado.
Probando el cliente
Para verificar que nuestra implementación funciona, podemos probarla directamente. El archivo del cliente incluye un arnés de pruebas que se conecta al servidor MCP y ejecuta comandos contra él. Haz que llame a list_tools() e imprima el resultado:
async with MCPClient(
command="uv", args=["run", "mcp_server.py"]
) as client:
result = await client.list_tools()
print(result)Ejecutar uv run mcp_client.py debería devolver una lista de herramientas disponibles con sus descripciones y esquemas de entrada. Deberías ver herramientas como read_doc_contents y edit_document que definimos en nuestro servidor.
Prueba de extremo a extremo
Ahora que tanto el cliente como el servidor están funcionando, podemos probar el flujo completo. Ejecutar nuestra aplicación principal y preguntarle a Claude "¿Cuál es el contenido del documento report.pdf?" debería:
- Enviar la lista de herramientas disponibles a Claude
- Claude decide usar la herramienta
read_doc_contents - Nuestro cliente llama a la herramienta en el servidor
- El servidor devuelve el contenido del documento
- Claude responde con la información
El cliente actúa como el puente entre el código de tu aplicación y el servidor MCP, facilitando el acceso a la funcionalidad del servidor sin tener que lidiar directamente con los detalles de conexión de bajo nivel.