Skip to content

feat: add a local dev script for testing MCP tools without manual curl handshake #22

@shahfazal

Description

@shahfazal

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions