The Etherscan MCP Server uses the modern StreamableHTTP transport as specified in the MCP protocol. This provides session-based communication with SSE streaming support.
| Endpoint | Method | Purpose |
|---|---|---|
/health |
GET | Health check - returns server status |
/mcp |
POST | Initialize session or send requests |
/mcp |
GET | Establish SSE stream for session |
/mcp |
DELETE | Terminate session |
npm start
# or
npm run devServer starts on http://localhost:3000 by default.
First request must be an initialize method without a session ID:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": { "tools": {} },
"clientInfo": {
"name": "my-client",
"version": "1.0.0"
}
}
}'Response Headers:
mcp-session-id: Your session identifier (e.g.,a4e1e210-ede0-4e7a-a7c4-8335faafe814)
Response Body: SSE format with initialization result
Send requests with the session ID header:
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: a4e1e210-ede0-4e7a-a7c4-8335faafe814" \
-d '{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
}'Response: SSE format with tools list
curl -X POST http://localhost:3000/mcp \
-H "Content-Type": application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: a4e1e210-ede0-4e7a-a7c4-8335faafe814" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "etherscan_gas_oracle",
"arguments": {
"chainid": "1"
}
}
}'For real-time notifications and events:
curl -N -X GET http://localhost:3000/mcp \
-H "mcp-session-id: a4e1e210-ede0-4e7a-a7c4-8335faafe814" \
-H "Accept: text/event-stream"This keeps the connection open and streams events.
When done:
curl -X DELETE http://localhost:3000/mcp \
-H "mcp-session-id: a4e1e210-ede0-4e7a-a7c4-8335faafe814"All responses use Server-Sent Events (SSE) format:
event: message
data: {"jsonrpc":"2.0","id":1,"result":{...}}
To parse SSE responses:
- Look for lines starting with
data: - Extract the JSON after
data: - Parse as JSON-RPC response
Simple JSON response (not SSE):
curl http://localhost:3000/healthResponse:
{
"status": "ok",
"tools": 65,
"transport": "streamable-http",
"protocol": "MCP",
"sessions": 1
}Content-Type: application/jsonAccept: application/json, text/event-stream(both required!)mcp-session-id: <session-id>(except for initialization)
mcp-session-id: <session-id>Accept: text/event-stream
- Sessions are created on first
initializerequest - Session IDs are generated server-side (UUID format)
- Sessions persist until explicitly terminated or server restart
- Each session maintains its own transport state
# 1. Initialize
RESPONSE=$(curl -i -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"test","version":"1.0.0"}}}')
# 2. Extract session ID
SESSION_ID=$(echo "$RESPONSE" | grep -i "mcp-session-id:" | cut -d ' ' -f 2 | tr -d '\r')
echo "Session ID: $SESSION_ID"
# 3. List tools
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
# 4. Call tool
curl -X POST http://localhost:3000/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-H "mcp-session-id: $SESSION_ID" \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"etherscan_gas_oracle","arguments":{"chainid":"1"}}}'
# 5. Terminate
curl -X DELETE http://localhost:3000/mcp \
-H "mcp-session-id: $SESSION_ID"// 1. Initialize session
const initResponse = await fetch('http://localhost:3000/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream'
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 1,
method: 'initialize',
params: {
protocolVersion: '2024-11-05',
capabilities: { tools: {} },
clientInfo: { name: 'my-app', version: '1.0.0' }
}
})
});
const sessionId = initResponse.headers.get('mcp-session-id');
console.log('Session:', sessionId);
// 2. Parse SSE response
const initText = await initResponse.text();
const initData = parseSSE(initText); // Extract JSON from SSE format
// 3. List tools
const toolsResponse = await fetch('http://localhost:3000/mcp', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json, text/event-stream',
'mcp-session-id': sessionId
},
body: JSON.stringify({
jsonrpc: '2.0',
id: 2,
method: 'tools/list',
params: {}
})
});
const toolsText = await toolsResponse.text();
const toolsData = parseSSE(toolsText);
console.log('Tools:', toolsData.result.tools.length);
// Helper to parse SSE
function parseSSE(sseText) {
const lines = sseText.split('\n');
for (const line of lines) {
if (line.startsWith('data: ')) {
return JSON.parse(line.substring(6));
}
}
return null;
}- Missing or invalid session ID
- Session not found
- Invalid request format
- Missing required Accept headers
- Must accept both
application/jsonandtext/event-stream
- Server-side error
- Check Etherscan API key
- Check server logs
Run the included test:
node test-full-mcp.mjsThis tests:
- Session initialization
- Tools listing
- Tool execution
- SSE streaming
- Session management
| Old (deprecated) | New (StreamableHTTP) |
|---|---|
Single endpoint /sse |
Session-based /mcp |
| No session management | Explicit session IDs |
| Protocol 2024-11-05 | Current protocol |
| Single transport | Per-session transports |
- Session Cleanup: Implement session timeout/cleanup
- Rate Limiting: Add per-session rate limits
- Authentication: Add auth middleware if needed
- CORS: Configure for web clients
- Monitoring: Track active sessions
- Load Balancing: Sessions are server-local, use sticky sessions
→ Initialize a session first via POST without session ID
→ Include both Accept types: application/json, text/event-stream
→ Response is SSE format, parse data: lines
→ Session may have expired or server restarted
- MCP Specification: https://modelcontextprotocol.io/
- Etherscan API: https://docs.etherscan.io/
- Test Script:
test-full-mcp.mjs