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.
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
- Ejecutar herramientas MCP como servicios sin estado después de la especificación 2026-07-28 — Eliminar sesiones persistentes: la especificación MCP 2026-07-28 hace que las solicitudes sean autónomas gracias al enrutamiento por encabezados, a los intentos de reintentar cuando sea necesario y a las claves requestState compartidas entre réplicas.
- Construir tus primeros flujos de trabajo LangGraph antes de los sistemas multi-agente — Aprender LangGraph con ejemplos compatibles con Colab: estado compartido, nodos secuenciales, un único paso de LLM y una pipeline de blog en múltiples etapas.