
Ponte proxy HTTP per test di sicurezza di server MCP remoti, che consente agli strumenti HTTP standard di inviare messaggi JSON-RPC e gestire sessioni.
Questo progetto implementa un server HTTP che funge da ponte tra le richieste HTTP/1.1 e un server MCP remoto, utilizzando la libreria python mcp (GitHub).
Lo scopo principale di questa iniziativa è poter utilizzare strumenti di sicurezza HTTP per testare server MCP remoti utilizzando i meccanismi di trasporto remoti (HTTP+SSE o Streamable HTTP).
È stato osservato che quando si inviano messaggi JSON-RPC-V2 con lo stesso id, utilizzando thread concorrenti, può accadere che le risposte vengano unite. Tienilo presente quando utilizzi lo strumento nella tua valutazione. Nel frattempo, finché non risolviamo questo problema, assicurati di eseguire i test utilizzando un singolo thread.
Per iniziare, clona il repository e installa le dipendenze richieste:
git clone <repository-url>
cd http-mcp-bridge
pip install -r requirements.txt
Per eseguire il server HTTP, esegui il seguente comando:
python3 main.py --remote-url="http://127.0.0.1:8787/mcp"
Il server HTTP sarà in ascolto sull'interfaccia e sulla porta predefinite (http://127.0.0.1:8000), e la connessione MCP verrà stabilita verso l'URL remoto fornito. Un server MCP remoto che implementa un meccanismo di trasporto supportato deve esistere all'URL specificato. Il server HTTP un tempo rilevava automaticamente il meccanismo di trasporto corretto, Streamable HTTP o HTTP+SSE, ma creava problemi con alcuni ambienti di produzione, quindi ora è disabilitato. Utilizzava il meccanismo di trasporto Streamable HTTP come predefinito. Creeremo un flag per imporre l'utilizzo di HTTP+SSE ma, nel frattempo, puoi semplicemente modificare il codice e abilitare quel meccanismo di trasporto.
Puoi quindi inviare richieste HTTP al server, che le inoltrerà ai client SSE/Streamable HTTP.
Il meccanismo implementato nell'SDK python stabilisce un canale di lettura e un canale di scrittura per comunicare con l'endpoint utilizzando il corrispondente meccanismo di trasporto. Questo HTTP to MCP Bridge inoltra le richieste HTTP al canale di scrittura e attende la risposta (se prevista) sul canale di lettura. Una volta ricevuta, quella risposta viene inoltrata come risposta della richiesta HTTP.
Le richieste HTTP supportano il parametro timeout, che limita il numero massimo di secondi che il bridge attende la risposta sul canale di lettura prima di restituire un messaggio di errore. Se timeout è zero, l'HTTP to MCP Bridge non attende affatto.
Poiché l'HTTP to MCP Bridge supporta più sessioni con il server MCP, il primo passo è ottenere un id di sessione, che verrà utilizzato nelle richieste successive.
Richiesta:
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
Risposta:
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"}
Questo id di sessione è diverso e indipendente dall'id di sessione stabilito tra il client MCP e il server. Quest'ultimo è gestito internamente dalla libreria mcp.
Esiste un metodo ping che può essere invocato per verificare che il servizio MCP sia presente e che il meccanismo di trasporto scelto sia corretto.
Richiesta:
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}
Risposta:
[{"jsonrpc":"2.0","id":2,"result":{}}]
Il primo passo in una comunicazione MCP è l'handshake di inizializzazione, in cui entrambi i peer condividono le proprie capacità disponibili.
Richiesta:
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}
Risposta:
[{"jsonrpc":"2.0","id":0,"result":{"protocolVersion":"2024-11-05","capabilities":{"tools":{}},"serverInfo":{"name":"Demo","version":"1.0.0"}}}]
L'handshake deve essere chiuso utilizzando questo messaggio, che non prevede una risposta, quindi in questo momento possiamo usare timeout=0. Riceveremo un messaggio di errore di timeout, ma è previsto.
Richiesta:
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"}
Risposta:
{"message":"Timeout waiting for messages"}
Una volta completato l'handshake, possiamo invocare i metodi disponibili, come tools/list (elenco degli strumenti).
Richiesta:
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}
Risposta:
[{"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#"}}]}}]
Infine, possiamo invocare gli strumenti o utilizzare altre capacità.
Richiesta:
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}
Risposta:
[{"jsonrpc":"2.0","id":2,"result":{"content":[{"type":"text","text":"3"}]}}]
I contributi sono benvenuti! Apri un issue o invia una pull request per eventuali miglioramenti o correzioni di bug.
Questo progetto è concesso in licenza sotto la Licenza MIT. Consulta il file LICENSE per maggiori dettagli.