Skip to content

How it works

Three decisions, and they are all the same decision

Nothing about your server should have to change so that you can see it — not its URL, not its auth, not its dependencies, and not its uptime.

01

Inside your server, not in front of it

An npm package you install, not a proxy. Directory-listed servers cannot change their URL and OAuth breaks the moment traffic is redirected — so the SDK sits beside your traffic instead of in it. If we are down, your server keeps serving.

{ email: "ada@…" } 9c1b4e2f0a11
02

Never your arguments, never your results

Sizes and hashes only. args_hash is twelve hex characters of a SHA-256 over the arguments with keys sorted: enough to tell whether two calls were the same, and not enough for anything else. There is no option to turn this off, because a guarantee you can switch off is not one.

import { watch } from "@mcpulse/sdk"

watch (server, { key })

same server back

03

Two lines, then it is measuring

One import, one wrap, after your tools are registered. watch() hands back the same server, so nothing downstream changes. No runtime dependencies at all — it will not drag anything into your tree.

What leaves your process

One object per tool call. Every field is a dimension, a duration, a size or a hash — there is no field that could hold customer data, which is a stronger statement than a promise about how the fields are used.

{
  "v": 1,
  "type": "call",
  "session_id": "s_7f2a91",
  "tool_name": "search_orders",
  "client_name": "claude-desktop",
  "started_at": "2026-08-09T14:22:31Z",
  "duration_ms": 240,
  "outcome": "ok",
  "response_bytes": 1420,
  "is_empty": false,
  "args_hash": "9c1b4e2f0a11"
}

And once at startup, the tool list with the byte size of each schema. That second payload is what makes dead tools findable: a tool with no calls has no rows anywhere, so absence has to be recorded from the other side.

Every call ends as exactly one of four things

Telling crashed from tool_error takes some doing. The MCP SDK catches everything a tool throws and turns it into a result with isError: true, so from outside the request handler a bug and a deliberate error are the same object. The SDK wraps your tool callbacks as well as the request, so what actually happened is known rather than guessed from an error message.

  • ok

    Ran and returned a result.

  • bad_args

    Arguments failed validation — your handler never ran.

  • tool_error

    Ran and returned isError: true.

  • crashed

    Threw.

A fifth state rides alongside them. is_empty marks a call that succeeded and returned nothing usable — an empty array, an empty object, a blank string. Those are the failures nobody reports: the protocol calls them success, the model gets nothing it can use, and you never hear about it.