Why this is a page at all
args_hash answers one question: were these two calls made with the same arguments? That is what separates a model retrying a reworded request from a client paging through results, and it is what first-call success is computed from.
The question only has an answer if every SDK turns the same arguments into the same bytes. Sorting keys and calling your language’s JSON encoder is not enough — no two languages agree on what that produces.
Canonical JSON (RFC 8785)
args_hash is the first 12 lowercase hex characters of the SHA-256 of the RFC 8785 canonical form of the arguments, hashed over its UTF-8 bytes.
{}, not as a failure. Arguments that cannot be represented as JSON hash as 000000000000.
The rules
RFC 8785 was written to match ECMAScript’s
JSON.stringify, which is why the TypeScript SDK is nearly free and every other language needs work.
What each language gets wrong by default
Every one of these silently sends the same call to a different bucket. None of them raise an error.
Java, Kotlin and .NET get key ordering free: their strings are already UTF-16.
The UTF-16 ordering rule only bites above the Basic Multiplane. U+1F680 (🚀) encodes as the surrogate pair
D83D DE80, so it sorts before U+FFFD (�) — while sorting by code point or UTF-8 byte puts it after.The conformance fixtures
Every SDK ships the same 23-case fixture file and runs it as a test. Each case carries an input, its canonical form, and the resulting hash:
Copy one into your own project and run it. If it passes, your hashes are interchangeable with all ten.
The three rules
Anything calling itself an MCPulse producer keeps these, in this order.1
Never throw
Every entry point swallows. If telemetry fails inside a tool call, the tool fails and the author blames the telemetry.
2
Never block
Record, buffer, return. Nothing waits on the network on the path a model is waiting on.
3
Never store customer data
Sizes and hashes leave the process. Arguments and results do not, and no option turns that off.
- Python — a bare
except:that catchesasyncio.CancelledErrorhangs the server on shutdown. CatchException, neverBaseException. - Python —
asyncio.create_taskwithout a strong reference lets the task be garbage collected mid-flight, and the payload vanishes silently. - Go — a send on an unbuffered channel from the request path blocks the request. Buffer it and use
selectwithdefault. - Java / Kotlin / .NET — an unbounded work queue is a memory leak. Bound it and drop the oldest.
Sessions
One session id and one buffer per process, per destination — keyed by endpoint + key, never a bare singleton. Not perwatch() call. A streamable-HTTP server builds a fresh server object per request, so watch() runs per request too. With a session per call there can never be a retry, and the server reports a perfect first-call-success score however badly it is doing.
Session id format: s_ followed by 12 lowercase hex characters from a CSPRNG. Random, never derived from the machine.
Sizes
response_bytes and schema_bytes are, today, the UTF-16 code unit length of the compact JSON — what JavaScript’s String.length returns.
Related
- What is sent — the payload shapes and the outcome vocabulary
- Ingest — the endpoint, batch limits and field caps
- First-call success — what
args_hashis for