Inicio / Artículos / MCP Desde Cero, Parte 3: Conectar un cliente a su servidor

MCP Desde Cero, Parte 3: Conectar un cliente a su servidor

Construir un cliente MCP stdio que inicie hr_server.py, llame a search_employee y devuelva los resultados, sin tener que iniciar el servidor manualmente.

804 palabras

La entrega anterior dejó un servidor MCP en funcionamiento con una única responsabilidad: ofrecer la capacidad de búsqueda de recursos humanos.

HR MCP Server
      ↓
search_employee

Ese proceso comenzó con:

python hr_server.py

Quedaba una pregunta sin responder: ¿quién se comunica realmente con el servidor? Ese papel lo desempeña el cliente.

Client
   ↓
MCP
   ↓
HR MCP Server

Trate los términos de manera sencilla. El servidor ofrece capacidades; el cliente se conecta y las utiliza.

Creemos nuestro cliente

Junto a hr_server.py, agregue otro módulo:

hr_client.py

La estructura del proyecto queda así:

mcp-hr
│
├── hr_server.py
│
└── hr_client.py

Dado que el servidor ya está escrito, la atención se centra en el cliente.

Conéctese a nuestro servidor

Ponga lo siguiente en hr_client.py:

import asyncio

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server = StdioServerParameters(
        command="python",
        args=["hr_server.py"]
    )
    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()

asyncio.run(main())

A primera vista, la lista parece extensa. El fragmento decisivo es:

server = StdioServerParameters(
    command="python",
    args=["hr_server.py"]
)

Dichos parámetros indican al cliente que inicie y se conecte a hr_server.py. El cliente inicia el proceso del servidor y abre una sesión de stdio con él.

Ahora llamemos a nuestra herramienta

En la Parte 2 se definió una herramienta llamada:

search_employee

El cliente puede buscar a John de la siguiente manera:

await session.initialize()
result = await session.call_tool(
    "search_employee",
    {"name": "John"}
)

print(result.content[0].text)

En lenguaje sencillo, el cliente pide a la sesión que ejecute search_employee con el nombre John. El script completo del cliente es:

import asyncio

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client

async def main():
    server = StdioServerParameters(
        command="python3",
        args=["hr_server.py"]
    )
    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool(
                "search_employee",
                {"name": "John"}
            )
            print(result.content[0].text)

asyncio.run(main())

Ese archivo representa toda la parte del cliente de la demostración.

Ejecutémoslo

Desde una terminal, ejecute:

python hr_client.py

Un detalle útil: hr_server.py no necesita un inicio manual separado. El cliente lo inicia a través de:

command="python",
args=["hr_server.py"]

y luego se conecta al proceso en ejecución.

¿Qué sucede?

El cliente envía una solicitud de herramienta con el siguiente formato:

Tool:
  search_employee
Name:
  John

El servidor lo recibe y ejecuta:

search_employee("John")

Resuelve:

John → Finance

y devuelve ese resultado al que lo solicitó. De extremo a extremo, la ruta se ve así:

hr_client.py
     │
     │  search_employee("John")
     ↓
hr_server.py
     │
     ↓
Search employee list
     │
     ↓
John → Finance
     │
     ↓
hr_client.py

Ambos lados del protocolo están realizando ahora un verdadero viaje de ida y vuelta.

Volvamos a nuestro ejemplo del USB

Conectar un teclado a una computadora es una comparación útil.

Keyboard
   ↓
USB
   ↓
Computer

Ambos extremos acuerdan un protocolo de cable compartido. Aquí, el cliente utiliza MCP y el servidor también utiliza MCP. Ese contrato compartido es lo que les permite cooperar.

Pero, ¿ eligió la herramienta?

Hay una limitación que es fácil pasar por alto. Considere nuevamente el lugar donde se realiza la llamada:

session.call_tool(
    "search_employee",
    {"name": "John"}
)

¿Quién seleccionó search_employee? Fue el autor de la aplicación, al codificar manualmente el nombre de la herramienta. El cliente nunca leyó una pregunta en lenguaje natural como “¿Trabaja John en Finanzas?” y decidió qué funcionalidad invocar. La siguiente pieza que falta es la selección automática de herramientas.

¿Qué viene después?

Imagínese que el servidor cuenta con varias herramientas adicionales:

search_employee
create_employee
get_leave_balance
list_departments

Cuando un usuario pregunta a qué departamento pertenece John, algo debe determinar cuál es la funcionalidad adecuada:

search_employee

y no otra herramienta similar. Esa decisión depende de los nombres de las herramientas, sus descripciones y el catálogo publicitado. La siguiente sección explica cómo una aplicación elige entre las herramientas MCP cuando hay muchas disponibles.

Hasta entonces, la lección importante de este paso es de carácter mecánico: un cliente stdio puede iniciar el servidor, inicializar una sesión, llamar a una herramienta con nombre y argumentos, e imprimir la respuesta estructurada, sin necesidad de iniciar manualmente ningún proceso del servidor por separado.

Lecturas relacionadas

  • FastMCP Voice Stack Parte 1: Herramientas, Stdio/HTTP, STT y TTS — Crea un servidor de herramientas MCP con FastMCP, además de asistentes para clima y visión; luego conecta la entrada del micrófono y la salida de voz antes del bucle del agente.
  • ReAct desde cero: pensamiento, acción, pausa, observación sin un marco — Construye manualmente un pequeño bucle ReAct de Groq: primero observaciones manuales y luego llamadas a herramientas mediante expresiones regulares, para comprender por qué los marcos de trabajo modernos para agentes funcionan de la manera en que lo hacen.
  • Servidor MCP de Neo4j conectado a ChatGPT — Crea el servidor MCP, integra las herramientas de Neo4j, regístralo en ChatGPT y valida los caminos de lectura desde el chat hasta Cypher.