-
Notifications
You must be signed in to change notification settings - Fork 93
Description
Testing tools locally currently requires manually executing the full MCP protocol handshake via curl — three sequential steps, with session ID extraction between calls:
Step 1 — initialize (capture session ID from response header)
SESSION_ID=$(curl -s -D - -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Host: localhost" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"0.1"}}}' \
| grep -i "mcp-session-id" | awk '{print $2}' | tr -d '\r')
Step 2 — confirm initialized
curl -s -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Host: localhost" \
-H "mcp-session-id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}'
Step 3 — call a tool
curl -s -X POST http://localhost:8000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "Host: localhost" \
-H "mcp-session-id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/call","params":{"name":"search_datasets","arguments":{"query":"IRVE"}}}'
This is a significant friction point for contributors who want to quickly verify tool behaviour after making changes. It's also error-prone: a missing Host header, a dropped session ID, or an out-of-order step produces cryptic errors with no obvious explanation.
Proposal
Add a scripts/call_tool.py dev helper that wraps the full handshake using the MCP Python client SDK — which is already a dependency of this project (mcp>=1.25.0), so no new packages are needed.
The mcp package ships mcp.client.streamable_http.streamable_http_client and mcp.client.session.ClientSession, which handle protocol negotiation, session management, and SSE parsing automatically.
A minimal script could look like:
# scripts/call_tool.py
# Usage: python scripts/call_tool.py search_datasets '{"query": "IRVE", "sort": "-created"}'
import asyncio, json, sys
from mcp.client.streamable_http import streamable_http_client
from mcp.client.session import ClientSession
async def main(tool_name: str, args: dict):
async with streamable_http_client("http://localhost:8000/mcp") as (read, write):
async with ClientSession(read, write) as session:
await session.initialize()
result = await session.call_tool(tool_name, args)
print(result.content[0].text)
tool = sys.argv[1]
arguments = json.loads(sys.argv[2]) if len(sys.argv) > 2 else {}
asyncio.run(main(tool, arguments))
Contributors could then run:
python scripts/call_tool.py search_datasets '{"query": "IRVE", "sort": "-created"}'
python scripts/call_tool.py get_resource_info '{"resource_id": "..."}'
Benefits
Zero new dependencies — uses the mcp package already installed
No session management or header wrangling for contributors
Quicker feedback loop when developing or debugging a tool
Could double as a smoke-test reference for the README
Happy to implement this — I already have a PR open for this project (#20) and would be glad to contribute the test script as a follow-up. Let me know if the approach looks good or if there's a preferred location/naming convention.