
Puente proxy HTTP para pruebas de seguridad de servidores MCP remotos, que permite a las herramientas HTTP estándar enviar mensajes JSON-RPC y gestionar sesiones.
Este proyecto implementa un servidor HTTP que actúa como puente entre peticiones HTTP/1.1 y un servidor MCP remoto, utilizando la librería mcp de Python (GitHub).
El propósito principal de esta iniciativa es poder utilizar herramientas de seguridad HTTP para probar servidores MCP remotos mediante los mecanismos de transporte remotos (HTTP+SSE o Streamable HTTP).
Se ha observado que al enviar mensajes JSON-RPC-V2 con el mismo id, usando hilos concurrentes, puede ocurrir que las respuestas se fusionen. Ten esto en cuenta al usar la herramienta en tu evaluación. Mientras arreglamos este problema, asegúrate de estar probando con un único hilo.
Para empezar, clona el repositorio e instala las dependencias necesarias:
git clone <repository-url>
cd http-mcp-bridge
pip install -r requirements.txt
Para ejecutar el servidor HTTP, usa el siguiente comando:
python3 main.py --remote-url="http://127.0.0.1:8787/mcp"
El servidor HTTP escuchará en la interfaz y puerto por defecto (http://127.0.0.1:8000), y la conexión MCP se establecerá con la URL remota proporcionada. En dicha URL debe existir un servidor MCP remoto que implemente un mecanismo de transporte compatible. El servidor HTTP solía detectar automáticamente el mecanismo de transporte correcto, Streamable HTTP o HTTP+SSE, pero esto estaba causando problemas en algunos entornos de producción, por lo que ahora está deshabilitado. Usaba el mecanismo de transporte Streamable HTTP por defecto. Crearemos una opción para forzar el uso de HTTP+SSE pero, mientras tanto, puedes simplemente editar el código y habilitar ese mecanismo de transporte.
A continuación puedes enviar peticiones HTTP al servidor, que las retransmitirá a los clientes SSE/Streamable HTTP.
El mecanismo implementado en el SDK de Python establece un canal de lectura y un canal de escritura para comunicarse con el endpoint mediante el mecanismo de transporte correspondiente. Este Bridge HTTP a MCP reenvía las peticiones HTTP al canal de escritura y espera la respuesta (si aplica) en el canal de lectura. Una vez recibida, esa respuesta se reenvía como respuesta de la petición HTTP.
Las peticiones HTTP admiten el parámetro timeout, que limita el número máximo de segundos que el bridge espera la respuesta en el canal de lectura antes de devolver un mensaje de error. Si timeout es cero, el Bridge HTTP a MCP no espera en absoluto.
Dado que el Bridge HTTP a MCP soporta varias sesiones con el servidor MCP, el primer paso es obtener un id de sesión, que se usará en peticiones posteriores.
Solicitud:
GET /mcp/messages HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Content-Length: 0
Respuesta:
HTTP/1.1 400 Bad Request
date: Fri, 02 May 2025 15:40:32 GMT
server: uvicorn
content-length: 87
content-type: application/json
{"detail":"Invalid session id. Try /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c"}
Este id de sesión es diferente e independiente del id de sesión establecido entre el cliente MCP y el servidor. Este último es gestionado internamente por la librería mcp.
Existe un método ping que se puede invocar para verificar que el servicio MCP está disponible y que el mecanismo de transporte elegido es correcto.
Solicitud:
POST /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Authorization: Bearer [REDACTED]
Content-Length: 40
{"method":"ping","jsonrpc":"2.0","id":2}
Respuesta:
[{"jsonrpc":"2.0","id":2,"result":{}}]
El primer paso en una comunicación MCP es el saludo de inicialización, donde ambos pares comparten sus capacidades disponibles.
Solicitud:
POST /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Authorization: Bearer [REDACTED]
Content-Length: 213
{"method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {"sampling": {}, "roots": {"listChanged": true}}, "clientInfo": {"name": "mcp", "version": "0.1.0"}}, "jsonrpc": "2.0", "id": 0}
Respuesta:
[{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"Demo","version":"1.0.0"}}}]
El handshake debe cerrarse con este mensaje, que no tiene respuesta, por lo que podemos usar timeout=0 en este momento. Recibiremos un mensaje de error de timeout, pero es lo esperado.
Solicitud:
POST /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c?timeout=0 HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Authorization: Bearer [REDACTED]
Content-Length: 54
{"method":"notifications/initialized","jsonrpc":"2.0"}
Respuesta:
{"message":"Timeout waiting for messages"}
Una vez completado el handshake, podemos invocar los métodos disponibles, como tools/list (listar herramientas).
Solicitud:
POST /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Content-Length: 46
{"method":"tools/list","jsonrpc":"2.0","id":1}
Respuesta:
[{"jsonrpc":"2.0","id":1,"result":{"tools":[{"name":"add","inputSchema":{"type":"object","properties":{"a":{"type":"number"},"b":{"type":"number"}},"required":["a","b"],"additionalProperties":false,"$schema":"http://json-schema.org/draft-07/schema#"}}]}}]
Finalmente, podemos invocar herramientas o hacer uso de otras capacidades.
Solicitud:
POST /mcp/messages/7fc2cce5-3b0b-4d63-9df6-e703c1df091c HTTP/1.1
Host: 127.0.0.1:8000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
User-Agent: python-httpx/0.28.1
Content-Type: application/json
Cache-Control: no-store
Content-Length: 100
{"method":"tools/call","params":{"name":"add","arguments":{"a":1, "b":2 }},"jsonrpc":"2.0","id":2}
Respuesta:
[{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"3"}]}}]
¡Las contribuciones son bienvenidas! Abre un issue o envía un pull request para cualquier mejora o corrección de errores.
Este proyecto está licenciado bajo la Licencia MIT. Consulta el archivo LICENSE para más detalles.