Custom HTTP client
Drive the HTTP + SSE transport directly from curl or any custom client.
Last updated
Custom HTTP client
This guide drives the MCP server's HTTP transport with raw curl. Use it
as a reference when building a bespoke client in any language — the wire
format is JSON-RPC 2.0 over HTTP + Server-Sent Events.
Start the server
Per Installation → Self-hosted HTTP:
PLAINWORK_API_KEY=pw_agent_… \
MCP_TRANSPORT=http \
node apps/mcp/dist/index.js
Default port 9090. All traffic hits /mcp; any other path returns
404.
1. Initialize
curl -i -X POST http://localhost:9090/mcp \
-H 'Content-Type: application/json' \
-d '{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": { "name": "curl-demo", "version": "0.1" }
}
}'
The response headers include mcp-session-id: <uuid>. Capture it; every
later request must send it back as a header.
HTTP/1.1 200 OK
mcp-session-id: 6f1e8c3a-…
Content-Type: application/json
2. List tools
SESSION=6f1e8c3a-…
curl -s -X POST http://localhost:9090/mcp \
-H 'Content-Type: application/json' \
-H "mcp-session-id: $SESSION" \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' | jq
The response lists all nine tools with their JSON schemas — the same schemas documented on this site's Tools pages.
3. Call a tool
curl -s -X POST http://localhost:9090/mcp \
-H 'Content-Type: application/json' \
-H "mcp-session-id: $SESSION" \
-d '{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "list_collections",
"arguments": {}
}
}' | jq
The JSON-RPC envelope wraps a result.content array; the first element is
a { "type": "text", "text": "<serialized JSON>" } block.
4. Stream server messages (SSE)
curl -N -H "mcp-session-id: $SESSION" \
http://localhost:9090/mcp
The response stays open and streams text/event-stream frames. Most
consumers don't need this; tools respond directly to the matching POST.
5. Terminate the session
curl -s -X DELETE http://localhost:9090/mcp \
-H "mcp-session-id: $SESSION"
Omitting the DELETE is fine — sessions die when the transport closes or the process exits — but explicit cleanup is nicer in a script.
Building a real client
- Use a JSON-RPC 2.0 library if your language has one; the envelope is
boring but unforgiving about
id/jsonrpc/method/params. - Keep the session ID in a single place in your client; losing it forces a re-initialize and the next call starts with no prior tool capability info.
- Treat tool responses as opaque text blocks — even numeric or boolean
returns come through as stringified JSON inside
content[0].text. - Errors surface as either HTTP errors (4xx/5xx from the transport layer)
or JSON-RPC
errorobjects (from the tool layer). Handle both.
See also
- Transports — endpoint table and session rules
- HTTP transport issues