-
Notifications
You must be signed in to change notification settings - Fork 248
Description
Overview
This guide demonstrates how to interact with MCP (Model Context Protocol) servers using raw JSON-RPC commands via curl
or through STDIO. This is useful for developers who want to understand the protocol at a low level or integrate MCP into custom applications.
This also serves as a sanity check to test / debug the MCP implementation provided by the gateway.
Prerequisites
- MCP Gateway server running (typically on
http://localhost:4444
) - Authentication token generated (if required)
curl
command-line tooljq
for JSON formatting (optional but recommended)
Authentication Setup
Before making any requests, generate a bearer token:
# Generate authentication token
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)
echo ${MCPGATEWAY_BEARER_TOKEN}
# Test connectivity
curl -s -k -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" http://localhost:4444/health
MCP Protocol Flow
1. Initialize the Protocol
Every MCP session must start with an initialization handshake. Note: There's currently a bug with the initialize
method via /rpc
endpoint, so use the dedicated protocol endpoint:
# Initialize the protocol (RECOMMENDED - works around /rpc bug)
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"protocol_version":"2025-03-26",
"capabilities":{},
"client_info":{"name":"demo","version":"0.0.1"}
}' \
http://localhost:4444/protocol/initialize
# Alternative: Via /rpc (currently has a bug - see troubleshooting)
# curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
# -H "Content-Type: application/json" \
# -d '{
# "jsonrpc":"2.0",
# "id":1,
# "method":"initialize",
# "params":{
# "protocolVersion":"2025-03-26",
# "capabilities":{},
# "clientInfo":{"name":"demo","version":"0.0.1"}
# }
# }' \
# http://localhost:4444/rpc
Expected Response:
{
"jsonrpc":"2.0",
"id":1,
"result":{
"protocolVersion":"2025-03-26",
"capabilities":{
"experimental":{},
"prompts":{"listChanged":false},
"resources":{"subscribe":false,"listChanged":false},
"tools":{"listChanged":false}
},
"serverInfo":{"name":"mcpgateway-wrapper","version":"0.2.0"}
}
}
2. Send Initialization Notification
After successful initialization, send the initialized notification:
# Send initialized notification
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"method":"notifications/initialized",
"params":{}
}' \
http://localhost:4444/rpc
Alternative: Server-Sent Events (SSE) Approach
For more robust communication, especially when dealing with multiple requests or when the /rpc
endpoint has issues, you can use the SSE-based approach:
Establishing SSE Connection
# Terminal 1: Start SSE connection (keeps connection open)
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/sse
This will establish a persistent connection and you'll see responses in real-time.
Sending Messages via SSE
In a separate terminal, send JSON-RPC messages:
# Initialize
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":1,
"method":"initialize",
"params":{
"protocolVersion":"2025-03-26",
"capabilities":{},
"clientInfo":{"name":"demo","version":"0.0.1"}
}
}' \
http://localhost:4444/message
# List tools
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}' \
http://localhost:4444/message
# Call a tool
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":3,
"method":"tools/call",
"params":{
"name":"get_current_time",
"arguments":{"timezone":"Europe/Dublin"}
}
}' \
http://localhost:4444/message
The SSE approach provides real-time responses and avoids the async/await issues present in the /rpc
endpoint.
Working with Tools
List Available Tools
# List all tools
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}' \
http://localhost:4444/rpc
Expected Response (no tools):
{
"jsonrpc":"2.0",
"id":2,
"result":{"tools":[]}
}
Expected Response (with tools):
{
"jsonrpc":"2.0",
"id":2,
"result":{
"tools":[
{
"name":"get_current_time",
"description":"Get current time in a specific timezone",
"inputSchema":{
"type":"object",
"properties":{
"timezone":{
"type":"string",
"description":"IANA timezone name (e.g., 'America/New_York', 'Europe/London'). Use 'America/New_York' as local timezone if no timezone provided by the user."
}
},
"required":["timezone"]
}
}
]
}
}
Call a Tool
# Call the get_current_time tool
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":3,
"method":"tools/call",
"params":{
"name":"get_current_time",
"arguments":{"timezone":"Europe/Dublin"}
}
}' \
http://localhost:4444/rpc
Expected Response:
{
"jsonrpc":"2.0",
"id":3,
"result":{
"content":[
{
"type":"text",
"text":"{\n \"timezone\": \"Europe/Dublin\",\n \"datetime\": \"2025-06-08T21:47:07+01:00\",\n \"is_dst\": true\n}"
}
],
"isError":false
}
}
Working with Prompts
List Available Prompts
# List all prompts
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":4,
"method":"prompts/list"
}' \
http://localhost:4444/rpc
Get a Specific Prompt
# Get a prompt with arguments
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":5,
"method":"prompts/get",
"params":{
"name":"greeting",
"arguments":{"user":"Bob"}
}
}' \
http://localhost:4444/rpc
Working with Resources
List Available Resources
# List all resources
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":6,
"method":"resources/list"
}' \
http://localhost:4444/rpc
Read a Resource
# Read a specific resource
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":7,
"method":"resources/read",
"params":{
"uri":"https://example.com/some.txt"
}
}' \
http://localhost:4444/rpc
Utility Methods
Ping the Server
# Ping to test connectivity
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":8,
"method":"ping"
}' \
http://localhost:4444/rpc
Expected Response:
{
"jsonrpc":"2.0",
"id":8,
"result":{}
}
Using with STDIO
For STDIO usage, you can use the MCP Gateway Wrapper:
# Set environment variables
export MCP_AUTH_TOKEN=${MCPGATEWAY_BEARER_TOKEN}
export MCP_SERVER_CATALOG_URLS=http://localhost:4444/servers/1
export MCP_TOOL_CALL_TIMEOUT=120
# Run the wrapper
python3 -m mcpgateway.wrapper
Then send JSON-RPC commands directly to stdin:
{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"demo","version":"0.0.1"}}}
{"jsonrpc":"2.0","method":"notifications/initialized","params":{}}
{"jsonrpc":"2.0","id":2,"method":"tools/list"}
Error Handling
MCP follows JSON-RPC 2.0 error handling. Common error responses:
{
"jsonrpc":"2.0",
"id":1,
"error":{
"code":-32602,
"message":"Invalid params",
"data":"Missing required parameter: name"
}
}
Common Error Codes
-32700
: Parse error-32600
: Invalid Request-32601
: Method not found-32602
: Invalid params-32603
: Internal error
Best Practices
- Always initialize first: Start every session with the initialize method
- Use appropriate timeouts: Set reasonable timeouts for tool calls
- Handle errors gracefully: Check for error objects in responses
- Use unique IDs: Ensure each request has a unique ID for correlation
- Follow the protocol: Send the initialized notification after successful initialization
Example Complete Session
Here's a complete example session with proper formatting using the working endpoints:
#!/bin/bash
# Set up authentication
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)
# Function to make JSON-RPC calls via /rpc (for methods other than initialize)
make_rpc_call() {
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d "$1" \
http://localhost:4444/rpc | jq
}
# Function to make protocol calls (for initialize)
make_protocol_call() {
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d "$1" \
http://localhost:4444/protocol/initialize | jq
}
# 1. Initialize (using protocol endpoint to avoid bug)
echo "=== Initializing ==="
make_protocol_call '{
"protocol_version":"2025-03-26",
"capabilities":{},
"client_info":{"name":"demo","version":"0.0.1"}
}'
# 2. Send initialized notification (optional for HTTP-based calls)
echo "=== Sending initialized notification ==="
make_rpc_call '{
"jsonrpc":"2.0",
"method":"notifications/initialized",
"params":{}
}'
# 3. List tools
echo "=== Listing tools ==="
make_rpc_call '{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}'
# 4. Call a tool (if available)
echo "=== Calling tool ==="
make_rpc_call '{
"jsonrpc":"2.0",
"id":3,
"method":"tools/call",
"params":{
"name":"get_current_time",
"arguments":{"timezone":"Europe/Dublin"}
}
}'
# 5. Ping test
echo "=== Ping test ==="
make_rpc_call '{
"jsonrpc":"2.0",
"id":4,
"method":"ping"
}'
Alternative: Complete SSE Session
For a more robust approach using SSE:
#!/bin/bash
# Set up authentication
export MCPGATEWAY_BEARER_TOKEN=$(python3 -m mcpgateway.utils.create_jwt_token -u admin --secret my-test-key)
echo "Starting SSE connection in background..."
# Start SSE connection in background
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/sse &
SSE_PID=$!
# Give SSE time to connect
sleep 2
# Function to send messages via SSE
send_sse_message() {
curl -s -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d "$1" \
http://localhost:4444/message
}
echo "=== Sending initialize message ==="
send_sse_message '{
"jsonrpc":"2.0",
"id":1,
"method":"initialize",
"params":{
"protocolVersion":"2025-03-26",
"capabilities":{},
"clientInfo":{"name":"demo","version":"0.0.1"}
}
}'
sleep 1
echo "=== Sending tools/list message ==="
send_sse_message '{
"jsonrpc":"2.0",
"id":2,
"method":"tools/list"
}'
sleep 1
echo "=== Sending tool call message ==="
send_sse_message '{
"jsonrpc":"2.0",
"id":3,
"method":"tools/call",
"params":{
"name":"get_current_time",
"arguments":{"timezone":"Europe/Dublin"}
}
}'
# Clean up
sleep 5
kill $SSE_PID
Integration with Claude Desktop
To use this with Claude Desktop, create a configuration that points to the STDIO wrapper:
{
"mcpServers": {
"mcpgateway-wrapper": {
"command": "python3",
"args": ["-m", "mcpgateway.wrapper"],
"env": {
"MCP_AUTH_TOKEN": "<your-token>",
"MCP_SERVER_CATALOG_URLS": "http://localhost:4444/servers/1",
"MCP_TOOL_CALL_TIMEOUT": "120"
}
}
}
}
Troubleshooting
Common Issues
- Authentication errors: Ensure your bearer token is valid and not expired
- Connection refused: Check that the MCP Gateway server is running
- Method not found: Verify the method name and that required tools/resources are registered
- Timeout errors: Increase the timeout value for long-running tools
- Protocol version mismatch: Ensure you're using a supported protocol version
Known Issues
Initialize Method Error: 'coroutine' object has no attribute 'model_dump'
If you encounter this error when calling the initialize
method via /rpc
:
{"jsonrpc":"2.0","error":{"code":-32000,"message":"Internal error","data":"'coroutine' object has no attribute 'model_dump'"},"id":1}
This is a bug in the /rpc
endpoint's initialize handler where an async function is not being properly awaited.
Workaround 1: Use the dedicated protocol endpoint
Instead of using /rpc
, use the /protocol/initialize
endpoint:
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"protocol_version":"2025-03-26",
"capabilities":{},
"client_info":{"name":"demo","version":"0.0.1"}
}' \
http://localhost:4444/protocol/initialize
Workaround 2: Use Server-Sent Events (SSE)
Use the SSE-based approach which has proper async handling:
# Terminal 1: Establish SSE connection
curl -N -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
http://localhost:4444/sse
# Terminal 2: Send initialization message
curl -X POST -H "Authorization: Bearer $MCPGATEWAY_BEARER_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"jsonrpc":"2.0",
"id":1,
"method":"initialize",
"params":{
"protocolVersion":"2025-03-26",
"capabilities":{},
"clientInfo":{"name":"demo","version":"0.0.1"}
}
}' \
http://localhost:4444/message
The SSE approach allows you to maintain a persistent connection and receive responses in real-time, which is closer to how MCP is intended to work.