A curl-like CLI tool for testing and debugging MCP (Model Context Protocol) servers over HTTP.
- Auto-initialization - Handles MCP handshake automatically
- Session management - Reuse sessions across requests
- SSE streaming - Print events as they arrive
- Pretty output - Formatted JSON by default
- Raw mode - Skip initialization for custom flows
- Verbose mode - Show request/response details
# Build
make build
# Basic request
./bin/mcpsnag http://localhost:3000/mcp -d '{"method":"tools/list"}'# Clone the repository
git clone https://github.com/bigbag/mcpsnag.git
cd mcpsnag
# Build
make build
# Or install to GOPATH/bin
make install-d, --data- JSON body (method + params) required-H, --header- HTTP header (repeatable)--raw- Skip auto-initialization--session- Use existing session ID--init-only- Only initialize, print session-c, --compact- Compact JSON output--no-stream- Wait for full response-v, --verbose- Show request/response details--timeout- Request timeout (default: 30s)
By default, mcpsnag handles the MCP initialization handshake:
1. POST -> initialize request
<- capabilities + Mcp-Session-Id
2. POST -> notifications/initialized
3. POST -> user's request (with session header)
<- response
Use --raw to skip this and send requests directly.
List available tools:
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/list"}'Call a tool with arguments:
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/call","params":{"name":"search","arguments":{"query":"hello world"}}}'Call a tool with complex arguments:
mcpsnag http://localhost:3000/mcp -d '{
"method": "tools/call",
"params": {
"name": "create_file",
"arguments": {
"path": "/tmp/test.txt",
"content": "Hello, World!"
}
}
}'List available resources:
mcpsnag http://localhost:3000/mcp -d '{"method":"resources/list"}'Read a specific resource:
mcpsnag http://localhost:3000/mcp -d '{"method":"resources/read","params":{"uri":"file:///path/to/file.txt"}}'List resource templates:
mcpsnag http://localhost:3000/mcp -d '{"method":"resources/templates/list"}'Subscribe to resource changes:
mcpsnag http://localhost:3000/mcp -d '{"method":"resources/subscribe","params":{"uri":"file:///path/to/watch"}}'List available prompts:
mcpsnag http://localhost:3000/mcp -d '{"method":"prompts/list"}'Get a prompt with arguments:
mcpsnag http://localhost:3000/mcp -d '{"method":"prompts/get","params":{"name":"code_review","arguments":{"language":"go"}}}'With Bearer token:
mcpsnag http://localhost:3000/mcp \
-H "Authorization: Bearer your-token-here" \
-d '{"method":"tools/list"}'With API key:
mcpsnag http://localhost:3000/mcp \
-H "X-API-Key: your-api-key" \
-d '{"method":"tools/list"}'With multiple headers:
mcpsnag http://localhost:3000/mcp \
-H "Authorization: Bearer token" \
-H "X-Request-ID: req-123" \
-H "X-Tenant-ID: tenant-456" \
-d '{"method":"tools/list"}'Initialize and capture session:
export MCP_SESSION=$(mcpsnag http://localhost:3000/mcp --init-only | jq -r '.sessionId')
echo "Session: $MCP_SESSION"Reuse session for multiple requests:
mcpsnag http://localhost:3000/mcp --session "$MCP_SESSION" -d '{"method":"tools/list"}'
mcpsnag http://localhost:3000/mcp --session "$MCP_SESSION" -d '{"method":"resources/list"}'
mcpsnag http://localhost:3000/mcp --session "$MCP_SESSION" -d '{"method":"prompts/list"}'Verbose mode (show request/response details):
mcpsnag http://localhost:3000/mcp -v -d '{"method":"tools/list"}'Raw mode (skip initialization, send custom request):
mcpsnag http://localhost:3000/mcp --raw -d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2025-03-26",
"capabilities": {},
"clientInfo": {"name": "test", "version": "1.0"}
}
}'With custom timeout:
mcpsnag http://localhost:3000/mcp --timeout 60s -d '{"method":"tools/call","params":{"name":"slow_operation"}}'Pretty print (default):
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/list"}'Compact JSON (for scripting):
mcpsnag http://localhost:3000/mcp -c -d '{"method":"tools/list"}'Disable streaming (wait for complete response):
mcpsnag http://localhost:3000/mcp --no-stream -d '{"method":"tools/list"}'Extract tool names with jq:
mcpsnag http://localhost:3000/mcp -c -d '{"method":"tools/list"}' | jq -r '.tools[].name'Check if a specific tool exists:
mcpsnag http://localhost:3000/mcp -c -d '{"method":"tools/list"}' | jq -e '.tools[] | select(.name == "search")' > /dev/null && echo "Found"Loop through and call multiple tools:
for tool in search fetch analyze; do
echo "Calling $tool..."
mcpsnag http://localhost:3000/mcp -c -d "{\"method\":\"tools/call\",\"params\":{\"name\":\"$tool\",\"arguments\":{}}}"
doneSave response to file:
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/list"}' > tools.jsonPipe JSON from file:
cat request.json | xargs -0 mcpsnag http://localhost:3000/mcp -dCheck exit code:
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/call","params":{"name":"invalid"}}' || echo "Request failed"Capture errors:
result=$(mcpsnag http://localhost:3000/mcp -c -d '{"method":"tools/list"}' 2>&1)
if echo "$result" | jq -e '.error' > /dev/null 2>&1; then
echo "Error: $(echo "$result" | jq -r '.error.message')"
else
echo "Success"
fiTest local development server:
mcpsnag http://localhost:3000/mcp -d '{"method":"tools/list"}'Test remote server:
mcpsnag https://api.example.com/mcp -H "Authorization: Bearer $TOKEN" -d '{"method":"tools/list"}'Compare two servers:
diff <(mcpsnag http://localhost:3000/mcp -c -d '{"method":"tools/list"}' | jq -S .) \
<(mcpsnag http://localhost:3001/mcp -c -d '{"method":"tools/list"}' | jq -S .)make build # Build binary to bin/mcpsnag
make run # Build and run
make run/quick # Run without rebuild
make test # Run tests
make test-race # Run tests with race detection
make coverage # Run tests with coverage report
make coverage-html # Generate HTML coverage report
make fmt # Format code
make vet # Run go vet
make lint # Run fmt and vet
make tidy # Tidy Go modules
make clean # Remove build artifacts
make install # Install to GOPATH/bin
make build-all # Build for linux/darwin/windows amd64/arm64# Run all tests
make test
# Run tests with verbose output
go test -v ./...
# Run tests with coverage
make coverage
# Run tests with race detection
make test-raceMIT License - see LICENSE file.