Skip to main content
@mcpulse/sdk is how data gets out of your MCP server. It is installed inside your own process, not in front of it.

Install

Requirements

The package ships ESM and CJS builds and its own types.
There are no runtime dependencies on purpose. @mcpulse/schemas was going to be the one exception and is not: it brings Zod with it, which would mean validating our own output inside every customer’s server when the API already validates it on arrival. The payload types are restated in the SDK’s own types.ts, and a contract test pins every field name so the two cannot drift.

Wrap your server

watch instruments the server in place and returns the same object, so the call can be dropped around an existing one without moving anything else. Two things matter about placement:
  • After your tools are registered. McpServer does not create its tools/call handler until the first tool exists. The SDK re-applies its wrapper after each later registration too, so both orders work — but registering first is the shape to write.
  • Once per server. Watching the same server twice is a no-op, guarded by a WeakSet. Left unguarded it would open a second session and report every call under both, doubling your numbers.

Which shape do you have?

stdio

One long-lived server. Wrap it once, at the top level.

Streamable HTTP

A fresh server per request. Wrap inside the factory.

Other languages

There are ten MCPulse SDKs, one for each official MCP SDK. All ten produce identical args_hash values for identical arguments — they run the same conformance fixtures to prove it — so a customer running servers in two languages sees one set of numbers, not two.
Each repository’s README carries the wrapping example for that language, the options table, and what that SDK can and cannot report.

What differs between them

Only TypeScript, Python and Go attach with a single call and see every tool automatically — those are the MCP SDKs with a supported interception point. The rest wrap your tool handler, which costs one thing: bad_args is not reported outside TypeScript, Python and Go. Those SDKs validate arguments before the handler runs, so a rejected call never reaches the wrapper. Reporting it anyway would mean reading the difference out of an error string, which is not an interface anyone promised to keep. ok, tool_error and crashed are exact everywhere. Go needs mcpulse.WrapTool(handler) for the full split, because it reports a schema rejection as a result with IsError set and no error returned — byte for byte what a failing handler produces. Any language can post to the ingest endpoint directly. The packages exist to save you writing that, not to gate it: it is one authenticated POST with a batch of JSON objects. See What is sent for the payloads, Wire format for the hash, and Ingest for the endpoint and limits. There are no dates on the cards. A promise with a month on it is a promise to be wrong about.

Get a key

An ingest key is minted per MCP in the dashboard and shown once. The install page creates keys itself, so setup never bounces between pages. See Create a key. Without a key, watch() is a silent no-op — a server started without its key configured should be quiet, not a source of 401s on every flush.

Next