Skip to main content
Glama
Anggelie

Chatbot MCP para gestión de reservas de salas de reuniones

by Anggelie

MCP para gestión de reservas de salas de reuniones

Proyecto 1 del curso Redes CC3067 de la Universidad del Valle de Guatemala.

Este proyecto implementa un chatbot de terminal capaz de comunicarse con varios servidores MCP mediante un cliente y mensajes JSON-RPC construidos manualmente. El caso de uso principal es la gestión de reservas de salas de reuniones empresariales.

El sistema puede ejecutarse localmente mediante stdio y el servidor propio de reservas también puede ejecutarse mediante HTTP/HTTPS. El mismo cliente MCP manual puede comunicarse con el servidor local o con el servidor desplegado en Render.

Objetivo del proyecto

El objetivo es aplicar conceptos de redes y comunicación entre procesos mediante:

  • JSON-RPC 2.0 construido y validado manualmente.

  • MCP implementado manualmente para el servidor propio y el cliente del proyecto.

  • Comunicación local mediante stdin y stdout.

  • Comunicación remota mediante HTTP/HTTPS.

  • Integración de un chatbot con un LLM y herramientas provenientes de varios servidores MCP.

  • Registro de solicitudes y respuestas para analizar posteriormente el tráfico de red.

No se utilizan FastMCP, MCP SDK ni librerías que oculten la implementación del protocolo MCP o JSON-RPC en nuestro cliente y servidor propio.

Arquitectura

Usuario en terminal
        |
        v
Chatbot Host
        |
        v
Claude mediante API de Anthropic
        |
        | tool_use con nombres unicos
        v
McpManager
        |
        +--> reservations --> McpClient --> StdioTransport o HttpTransport
        |                         |              |
        |                         |              +--> MCP local de reservas
        |                         |              +--> MCP remoto en Render
        |                         |
        +--> filesystem -------> McpClient --> Filesystem MCP oficial local
        |
        +--> git -------------> McpClient --> Git MCP oficial local

Chatbot Host

ChatbotHost mantiene el contexto de la sesión y coordina la conversación con Claude. Recibe el catálogo unificado de herramientas desde McpManager, entrega ese catálogo al LLM y procesa los bloques tool_use que Claude solicita.

El host no conoce los detalles de stdio, HTTP ni JSON-RPC. Cuando Claude solicita una herramienta, el host la entrega al manager, recibe el resultado y lo devuelve a Claude como tool_result para que el modelo genere una respuesta natural.

Claude

Claude se consume mediante la librería oficial de Anthropic únicamente para realizar llamadas al LLM. La librería no implementa MCP ni JSON-RPC en este proyecto.

El LLM recibe nombres de herramientas únicos, por ejemplo:

reservations__list_rooms
filesystem__write_file
git__git_status

McpManager

McpManager mantiene los clientes MCP identificados por servidor, inicializa los clientes, descubre sus herramientas y enruta las llamadas.

Los nombres internos conservan el formato:

reservations.list_rooms
filesystem.write_file
git.git_status

La conversión hacia los nombres expuestos a Claude se construye automáticamente a partir de tools/list.

Servidores MCP

El proyecto trabaja con tres servidores:

  • reservations: servidor MCP propio para reservas.

  • filesystem: servidor Filesystem MCP oficial, ejecutado localmente.

  • git: servidor Git MCP oficial, ejecutado localmente.

Implementación manual de MCP y JSON-RPC

El protocolo de comunicación del cliente y del servidor propio se implementa manualmente. El módulo src/json_rpc.py construye y valida:

  • solicitudes;

  • respuestas exitosas;

  • respuestas de error;

  • notificaciones;

  • identificadores para correlacionar solicitudes y respuestas.

El servidor propio utiliza McpProtocolHandler, que contiene la lógica común de MCP:

  • initialize;

  • notifications/initialized;

  • tools/list;

  • tools/call;

  • errores JSON-RPC y errores de herramientas.

El handler es independiente del transporte. Por eso el mismo protocolo se puede utilizar mediante stdio o HTTP.

Servidor MCP de reservas

El servidor propio administra salas y reservas mediante archivos JSON. Su lógica de negocio está implementada en ReservationService y es reutilizada por el servidor local y por el servidor HTTP remoto.

Herramientas

list_rooms

Lista las salas disponibles y sus características. No requiere parámetros.

check_availability

Consulta si una sala está disponible durante un intervalo.

Parámetros:

{
  "room_id": "room-001",
  "date": "2099-12-15",
  "start_time": "10:00",
  "end_time": "11:00"
}

create_reservation

Crea una reserva si la sala existe, el intervalo es válido y no hay otra reserva superpuesta.

Parámetros:

{
  "room_id": "room-001",
  "date": "2099-12-15",
  "start_time": "10:00",
  "end_time": "11:00",
  "reserved_by": "Anggelie",
  "title": "Reunión de proyecto"
}

La herramienta genera identificadores como RES-0001.

cancel_reservation

Cancela una reserva existente.

Parámetros:

{
  "reservation_id": "RES-0001"
}

list_reservations

Lista las reservas existentes. Acepta filtros opcionales:

{
  "date": "2099-12-15",
  "room_id": "room-001"
}

Las reglas de negocio impiden reservas superpuestas, exigen que la hora inicial sea menor que la hora final y verifican que la sala exista.

Transporte local mediante stdio

El servidor propio puede ejecutarse localmente con:

python -m src.mcp_server

El cliente McpClient utiliza StdioTransport, que inicia el proceso y transmite un mensaje JSON-RPC por línea mediante stdin y stdout.

Este transporte se utiliza para el servidor local de reservas y también como base para ejecutar servidores MCP locales externos.

Transporte remoto mediante HTTP/HTTPS

El servidor HTTP propio puede ejecutarse localmente con:

python -m src.mcp_http_server

Por defecto escucha en:

HOST=127.0.0.1
PORT=8000

En un entorno como Render, el proceso puede recibir:

HOST=0.0.0.0
PORT=<puerto inyectado por Render>

El servidor expone:

GET /health
POST /mcp

GET /health devuelve:

{
  "status": "ok"
}

POST /mcp recibe un mensaje JSON-RPC MCP con Content-Type: application/json y devuelve la respuesta generada por McpProtocolHandler. Las notificaciones sin respuesta reciben HTTP 204 No Content.

El cliente HttpTransport utiliza solicitudes HTTP POST, controla el timeout, valida el status HTTP y entrega la respuesta al mismo McpClient utilizado con StdioTransport.

Configuración

Selección local o remota de reservas

La variable MCP_RESERVATIONS_MODE controla qué transporte utiliza el servidor propio:

$env:MCP_RESERVATIONS_MODE = "local"

Usa el servidor MCP local mediante stdio.

$env:MCP_RESERVATIONS_MODE = "remote"

Usa el servidor MCP remoto mediante HTTP/HTTPS.

El valor predeterminado es local. Si se proporciona otro valor, el programa muestra un error de configuración en lugar de continuar silenciosamente.

URL remota

La URL se configura mediante:

$env:MCP_RESERVATIONS_URL = "https://mcp-reservas.onrender.com/mcp"

El valor predeterminado configurado para el cliente es el endpoint remoto de Render utilizado durante las validaciones del proyecto.

MCP_RESERVATIONS_URL pertenece al cliente. No es una variable necesaria para arrancar el servidor HTTP.

API de Anthropic

Para utilizar el chatbot con Claude se debe configurar la API key en la sesión local de PowerShell:

$env:ANTHROPIC_API_KEY = "tu_clave_real_de_anthropic"
$env:ANTHROPIC_MODEL = "claude-3-5-haiku-latest"

Nunca se debe escribir una clave real en el código, en el README, en Git ni en los logs. .env.example contiene únicamente valores de ejemplo.

El servidor MCP HTTP de reservas no necesita ANTHROPIC_API_KEY porque no utiliza Claude.

Directorio de datos

La persistencia utiliza JSON. Por defecto se emplea:

data/rooms.json
data/reservations.json

La ruta puede configurarse mediante:

$env:MCP_DATA_DIR = "C:\ruta\de\datos"

Si la ruta no existe, el servicio crea el directorio y genera las salas iniciales junto con un archivo vacío de reservas. En Render, el filesystem puede ser efímero; la persistencia permanente no forma parte de esta fase.

Despliegue en Render

El servidor HTTP está preparado como Web Service en Render mediante render.yaml.

Configuración utilizada:

buildCommand: pip install -r requirements.txt
startCommand: python -m src.mcp_http_server
healthCheckPath: /health

El endpoint remoto validado en el proyecto es:

https://mcp-reservas.onrender.com/mcp

El health check remoto es:

https://mcp-reservas.onrender.com/health

Para el servidor remoto no se requiere ANTHROPIC_API_KEY. Render inyecta el valor de PORT; no se fija un puerto cloud específico en el código ni en render.yaml.

Filesystem MCP oficial

Filesystem MCP se ejecuta como servidor local oficial mediante npx y transporte stdio. El proyecto lo configura con un directorio permitido, por ejemplo:

sandbox/chatbot_workspace

El servidor solo puede operar dentro del directorio configurado. Entre sus herramientas disponibles se encuentran:

read_file
read_text_file
write_file
create_directory
list_directory
search_files
get_file_info
list_allowed_directories

El acceso no se extiende automáticamente a todo el disco.

Git MCP oficial

Git MCP se ejecuta localmente mediante uvx mcp-server-git y utiliza el cliente MCP manual del proyecto.

Sus herramientas incluyen:

git_status
git_add
git_commit
git_log
git_diff
git_branch
git_checkout
git_show

El servidor oficial mcp-server-git no proporciona una herramienta git_init. Por eso, cuando una demostración necesita un repositorio nuevo, se ejecuta externamente:

git init ruta\del\repositorio

Después de ese paso, las operaciones de estado, staging, commit e historial se realizan mediante Git MCP.

Nombres de herramientas expuestos a Claude

Para evitar colisiones entre servidores, Claude recibe nombres con el servidor como prefijo:

reservations__list_rooms
reservations__check_availability
reservations__create_reservation

filesystem__write_file
filesystem__read_text_file
filesystem__create_directory

git__git_status
git__git_add
git__git_commit
git__git_log

El manager transforma internamente esos nombres a rutas MCP:

filesystem__write_file -> filesystem.write_file
git__git_status        -> git.git_status
reservations__list_rooms -> reservations.list_rooms

Claude solo recibe el catálogo de herramientas. No conoce el transporte ni construye JSON-RPC.

Logs de interacciones MCP

Las interacciones se registran en:

logs/mcp_interactions.log

Cada registro puede incluir:

  • timestamp;

  • server;

  • direction (CLIENT_TO_SERVER o SERVER_TO_CLIENT);

  • method;

  • id;

  • mensaje JSON completo.

Los servidores lógicos se identifican como:

reservations
reservations_remote
filesystem
git

El script de captura remota utiliza un archivo separado:

logs/wireshark_mcp_capture.log

Los archivos de logs están excluidos de Git.

Captura y análisis de red

El script scripts/remote_mcp_capture_demo.py genera tráfico MCP remoto controlado. Ejecuta únicamente operaciones de lectura:

python scripts/remote_mcp_capture_demo.py

La secuencia es:

initialize
notifications/initialized
tools/list
tools/call list_rooms
tools/call check_availability

No crea ni cancela reservas.

Para un análisis de red se pueden observar las siguientes capas:

  • DNS, si se resuelve el dominio durante la captura;

  • TCP, incluyendo IPs, puertos y establecimiento de conexión;

  • TLS, incluyendo el handshake HTTPS;

  • tráfico cifrado de aplicación.

El contenido JSON-RPC/MCP viaja dentro de HTTPS y normalmente no puede leerse directamente en Wireshark sin configurar descifrado TLS. El log de aplicación permite correlacionar las operaciones MCP (initialize, tools/list y tools/call) con los tiempos y paquetes observados en la captura.

No se desactiva la validación TLS ni se configura descifrado en esta fase.

Instalación y ejecución en Windows

Requisitos principales:

  • Python 3.10 o superior;

  • Node.js y npx para Filesystem MCP;

  • uv y uvx para Git MCP;

  • Git.

Desde PowerShell, en la raíz del proyecto:

python -m pip install -r requirements.txt

Para ejecutar el chatbot local:

$env:MCP_RESERVATIONS_MODE = "local"
python -m src.main

Para utilizar el servidor remoto de reservas, sin cambiar Filesystem MCP ni Git MCP:

$env:MCP_RESERVATIONS_MODE = "remote"
$env:MCP_RESERVATIONS_URL = "https://mcp-reservas.onrender.com/mcp"
python -m src.main

Comandos útiles para ejecutar solo el servidor propio:

python -m src.mcp_server
python -m src.mcp_http_server

El chatbot termina con:

salir

El contexto de conversación se reinicia con:

/clear

Pruebas

Para ejecutar toda la suite:

python -m unittest discover -s tests -v

También se pueden ejecutar las validaciones estáticas:

python -m compileall -q src tests
git diff --check

La suite cubre JSON-RPC, el servidor de reservas, el cliente MCP, los transportes stdio y HTTP, los servidores externos, el coordinador multi-MCP y las integraciones locales controladas.

Estructura principal

.
|-- data/
|   |-- rooms.json
|   `-- reservations.json
|-- scripts/
|   `-- remote_mcp_capture_demo.py
|-- src/
|   |-- chatbot_host.py
|   |-- config.py
|   |-- conversation.py
|   |-- json_rpc.py
|   |-- llm_client.py
|   |-- main.py
|   |-- mcp_client.py
|   |-- mcp_external_servers.py
|   |-- mcp_http_server.py
|   |-- mcp_http_transport.py
|   |-- mcp_interaction_logger.py
|   |-- mcp_manager.py
|   |-- mcp_protocol_handler.py
|   |-- mcp_server.py
|   |-- mcp_transport.py
|   `-- reservation_service.py
|-- tests/
|-- .env.example
|-- .gitignore
|-- render.yaml
|-- requirements.txt
`-- README.md

Conclusiones

El proyecto demuestra un flujo completo de comunicación MCP construido manualmente. El mismo servidor de reservas puede utilizarse localmente mediante stdio y remotamente mediante HTTP/HTTPS, sin duplicar la lógica de reservas ni depender de un SDK MCP.

La integración con Filesystem MCP y Git MCP permite coordinar herramientas de distintos servidores mediante un catálogo unificado. Los logs de aplicación y el script de captura permiten relacionar las operaciones JSON-RPC/MCP con el tráfico de red. La persistencia actual en archivos JSON es suficiente para la demostración, pero puede ser efímera en Render y deberá reemplazarse por una solución persistente en una fase posterior si el proyecto lo requiere.

Dificultades y lecciones aprendidas

Durante el desarrollo se presentaron varias dificultades técnicas relacionadas con la implementación manual del protocolo y con los distintos entornos de ejecución.

  • Implementar MCP y JSON-RPC manualmente exigió construir las solicitudes, respuestas, errores, notificaciones y correlaciones mediante id, sin delegar estas responsabilidades en FastMCP o en un SDK de MCP.

  • Coordinar varios servidores desde un solo chatbot requirió separar claramente el ChatbotHost, el McpManager, los clientes MCP y los transportes. También fue necesario generar nombres únicos como reservations__list_rooms, filesystem__write_file y git__git_status.

  • El transporte local stdio y el transporte remoto HTTP/HTTPS tienen comportamientos diferentes. stdio utiliza procesos hijos y flujos de entrada y salida, mientras HTTP utiliza solicitudes POST hacia /mcp y un endpoint independiente /health.

  • En Windows se presentaron dificultades con npx, uvx y los procesos hijos. Fue necesario utilizar cmd /c para algunos comandos, controlar el cierre de procesos descendientes y considerar advertencias de limpieza de Popen durante las pruebas de servidores externos.

  • Git MCP no proporciona una herramienta git_init. Por esa razón, la inicialización de repositorios de demostración se realiza externamente con git init, mientras que el estado, staging, commit e historial se ejecutan mediante Git MCP.

  • Render requiere que el servidor escuche en 0.0.0.0 y utilice el puerto entregado mediante PORT. La configuración conserva 127.0.0.1:8000 para uso local y permite que Render inyecte sus valores sin fijar un puerto cloud específico.

  • El análisis directo de JSON-RPC en Wireshark está limitado cuando la comunicación utiliza HTTPS, porque el contenido de aplicación está cifrado mediante TLS. Por ello, la captura permite observar DNS, TCP, IPs, puertos, handshake TLS y tráfico cifrado, pero no necesariamente el JSON-RPC en texto plano.

  • Los logs de aplicación resultaron necesarios para correlacionar las operaciones MCP con la captura de red. El archivo de log conserva servidor, dirección, método, identificador y mensaje JSON, lo que permite relacionar una operación lógica con sus tiempos de red.

  • Separar la lógica del protocolo del transporte permitió reutilizar el mismo McpProtocolHandler con stdio y HTTP, sin duplicar la implementación de MCP ni la lógica de ReservationService.

Lecciones aprendidas

El proyecto permitió comprender que MCP organiza capacidades y herramientas, mientras JSON-RPC define la estructura de los mensajes, sus identificadores y sus errores. La separación entre ambos conceptos facilita explicar el protocolo y probar cada capa de forma independiente.

También se comprendió la importancia de una arquitectura cliente-servidor en la que el cliente descubre herramientas, el servidor ejecuta la operación y el resultado se correlaciona con la solicitud original. El transporte puede cambiar sin modificar la lógica del protocolo: el mismo servidor de reservas funciona mediante stdio y mediante HTTP/HTTPS.

La integración de HTTPS mostró que la seguridad del transporte modifica lo que puede observarse en un analizador de protocolos. TLS protege el contenido JSON-RPC frente a observadores de red, por lo que el análisis debe combinar evidencias de las capas inferiores con logs de aplicación controlados. Finalmente, la configuración explícita de rutas, puertos, procesos y logs demostró que las pruebas reproducibles son esenciales cuando se trabaja con servidores locales, procesos externos y despliegues remotos.