
Pont proxy HTTP pour les tests de sécurité de serveurs MCP distants, permettant aux outils HTTP standard d'envoyer des messages JSON-RPC et de gérer les sessions.
Ce projet implémente un serveur HTTP qui agit comme un pont entre les requêtes HTTP/1.1 et un serveur MCP distant, en utilisant la bibliothèque Python mcp (GitHub).
L'objectif principal de cette initiative est de pouvoir utiliser des outils de sécurité HTTP pour tester des serveurs MCP distants via les mécanismes de transport à distance (HTTP+SSE ou Streamable HTTP).
Il a été observé que lors de l'envoi de messages JSON-RPC-V2 avec le même identifiant (id), en utilisant des threads concurrents, il peut arriver que les réponses soient fusionnées. Veuillez en tenir compte lors de l'utilisation de l'outil dans votre évaluation. En attendant que nous corrigions ce problème, assurez-vous de tester en utilisant un seul thread.
Pour commencer, clonez le dépôt et installez les dépendances requises :
git clone <repository-url>
cd http-mcp-bridge
pip install -r requirements.txt
Pour exécuter le serveur HTTP, lancez la commande suivante :
python3 main.py --remote-url="http://127.0.0.1:8787/mcp"
Le serveur HTTP écoutera sur l'interface et le port par défaut (http://127.0.0.1:8000), et la connexion MCP sera établie vers l'URL distante fournie. Un serveur MCP distant implémentant un mécanisme de transport pris en charge doit exister à l'URL donnée. Le serveur HTTP détectait automatiquement le bon mécanisme de transport, Streamable HTTP ou HTTP+SSE, mais cela créait des problèmes avec certains environnements de production, c'est pourquoi cette fonctionnalité est désormais désactivée. Il utilisait le mécanisme de transport Streamable HTTP par défaut. Nous créerons un indicateur (flag) pour imposer l'utilisation de HTTP+SSE mais, en attendant, vous pouvez simplement modifier le code et activer ce mécanisme de transport.
Vous pouvez ensuite envoyer des requêtes HTTP au serveur, qui les relaiera aux clients SSE/Streamable HTTP.
Le mécanisme implémenté dans le SDK Python établit un canal de lecture et un canal d'écriture pour communiquer avec le point de terminaison en utilisant le mécanisme de transport correspondant. Ce pont HTTP vers MCP transmet les requêtes HTTP au canal d'écriture et attend la réponse (le cas échéant) sur le canal de lecture. Une fois reçue, cette réponse est renvoyée comme réponse à la requête HTTP.
Les requêtes HTTP prennent en charge le paramètre timeout, qui limite le nombre maximal de secondes pendant lesquelles le pont attend la réponse sur le canal de lecture avant de renvoyer un message d'erreur. Si le délai d'attente (timeout) est nul, le pont HTTP vers MCP n'attend pas du tout.
Étant donné que le pont HTTP vers MCP prend en charge plusieurs sessions avec le serveur MCP, la première étape consiste à obtenir un identifiant de session, qui sera utilisé dans les requêtes suivantes.
Requête :
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
Réponse :
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"}
Cet identifiant de session est différent et indépendant de l'identifiant de session établi entre le client MCP et le serveur. Ce dernier est géré en interne par la bibliothèque mcp.
Il existe une méthode ping qui peut être invoquée pour vérifier que le service MCP est présent et que le mécanisme de transport choisi est correct.
Requête :
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}
Réponse :
[{"jsonrpc":"2.0","id":2,"result":{}}]
La première étape d'une communication MCP est la poignée de main d'initialisation, au cours de laquelle les deux pairs partagent leurs capacités disponibles.
Requête :
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}
Réponse :
[{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"Demo","version":"1.0.0"}}}]
La poignée de main doit être close à l'aide de ce message, qui n'a pas de réponse. Nous pouvons donc utiliser timeout=0 à ce moment-là. Nous recevrons un message d'erreur de délai d'attente, mais c'est attendu.
Requête :
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"}
Réponse :
{"message":"Timeout waiting for messages"}
Une fois la poignée de main terminée, nous pouvons invoquer les méthodes disponibles, telles que tools/list (liste des outils).
Requête :
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}
Réponse :
[{"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#"}}]}}]
Enfin, nous pouvons invoquer des outils ou utiliser d'autres capacités.
Requête :
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}
Réponse :
[{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"3"}]}}]
Les contributions sont les bienvenues ! Veuillez ouvrir une issue ou soumettre une pull request pour toute amélioration ou correction de bug.
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus de détails.