Installation
Install MCPulse in two lines
Add the package, paste your key, wrap your server. It runs inside your own process, not in front of it — so your URL, your OAuth and your uptime are untouched.
-
01
Add the package
No runtime dependencies at all, so it will not drag anything into your tree.
$ npm install @mcpulse/sdk $ pnpm add @mcpulse/sdk $ yarn add @mcpulse/sdk $ bun add @mcpulse/sdk -
02
Put the key in your environment
An ingest key is a secret — anyone holding it can write data into this MCP. Keep it out of the repository and out of your client bundle. You are shown the full key once, at creation, because only a hash of it is stored.
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx -
03
Wrap your server
One import, one call, after your tools are registered.
watchreturns the same server, so nothing downstream changes.import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { watch } from "@mcpulse/sdk"; const server = new McpServer({ name: "my-server", version: "1.0.0" }); // … your registerTool calls … watch(server, { key: process.env.MCPULSE_KEY });Serving over HTTP instead? Those servers build a new
McpServerper request, so there is no long-lived instance to wrap — put it inside the factory, beside the tool registration. Callingwatchonce per request is expected and cheap: the session and its buffer are shared for the life of the process, which is what keeps your calls grouped into one session rather than one per request.function createServer() { const server = new McpServer({ name: "my-server", version: "1.0.0" }); // … your registerTool calls … return watch(server, { key: process.env.MCPULSE_KEY }); } -
04
Restart and check back
On start the SDK sends one payload listing your tools and their schema sizes — that is what fills the tools table, so a server that has booted but never been called still shows what it costs per session. Tool calls follow as they happen, batched every 30 calls or 5 seconds.
Nothing arriving? Set
debug: trueto log every batch to stderr, and confirm the key has not been revoked.
Options
Two, and there will not be more than there need to be. Every option is another way for something to go wrong inside a process we do not control.
| Option | Type | Default | What it does |
|---|---|---|---|
| key | string | — | Required. Without one the SDK does nothing. |
| debug | boolean | false | Log every batch to stderr — never stdout, which is the transport. |
Requires Node 20.12 or newer and @modelcontextprotocol/sdk as a peer
— whatever version your server already uses. The package is on
npm.
Install for Python
Wrap the server you already built. It is handed straight back, so nothing downstream changes.
-
1 · Add the package
pip install mcpulse-sdk
-
2 · Wrap your server
from mcp.server.mcpserver import MCPServer from mcpulse import watch mcp = MCPServer("my-server") watch(mcp, key="mp_live_…") -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for C#
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
dotnet add package MCPulse
-
2 · Wrap your handler
using MCPulse; McPulse.Configure(new McPulseOptions { Key = "mp_live_…" }); // Around your tool handler: return await McPulse.RecordAsync( toolName: request.Params.Name, arguments: request.Params.Arguments, clientName: server.ClientInfo?.Name, invoke: () => next(request, cancellationToken)); -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Go
Wrap the server you already built. It is handed straight back, so nothing downstream changes.
-
1 · Add the package
go get github.com/getmcpulse/mcpulse-go
-
2 · Wrap your server
import ( "github.com/modelcontextprotocol/go-sdk/mcp" mcpulse "github.com/getmcpulse/mcpulse-go" ) server := mcp.NewServer(&mcp.Implementation{Name: "my-server"}, nil) mcpulse.Watch(server, mcpulse.Options{Key: "mp_live_…"}) -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Rust
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
cargo add mcpulse
-
2 · Wrap your handler
use mcpulse::{McPulse, Options}; let mcpulse = McPulse::new(Options::new("mp_live_…")); // Around your tool handler: let result = mcpulse.record("search", Some(&arguments), || { my_handler(&arguments) }); -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Java
Wrap the server you already built. It is handed straight back, so nothing downstream changes.
-
1 · Add the package
<dependency> <groupId>com.mcpulse</groupId> <artifactId>mcpulse</artifactId> <version>0.1.0</version> </dependency>
-
2 · Wrap your server
import com.mcpulse.MCPulse; import com.mcpulse.Options; var specs = MCPulse.instrument(Options.key("mp_live_…"), searchSpec, fetchSpec); McpSyncServer server = McpServer.sync(transport) .serverInfo("my-server", "1.0.0") .tools(specs) .build(); -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Ruby
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
bundle add mcpulse
-
2 · Wrap your handler
require "mcpulse" MCPulse.configure(key: "mp_live_…") # Around your tool handler: MCPulse.record("search", arguments, client_name: client) do my_handler.call(arguments) end -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Swift
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
.package(url: "https://github.com/getmcpulse/mcpulse-swift.git", from: "0.1.0")
-
2 · Wrap your handler
import MCPulse let mcpulse = MCPulse(options: Options(key: "mp_live_…")) // Around your tool handler: let result = try await mcpulse.record("search", arguments: arguments, result: { $0 }) { try await handler(arguments) } -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for PHP
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
composer require mcpulse/mcpulse
-
2 · Wrap your handler
use MCPulse\MCPulse; use MCPulse\Options; MCPulse::configure(new Options('mp_live_…')); // Around your tool handler: $result = MCPulse::record('search', $arguments, fn () => $handler($arguments), $clientName); -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Install for Kotlin
Wrap your handler body. There is no server object to attach to here, so recording goes around the call instead of in front of it.
-
1 · Add the package
implementation("com.mcpulse:mcpulse:0.1.0") -
2 · Wrap your handler
import com.mcpulse.MCPulse import com.mcpulse.Options MCPulse.configure(Options(key = "mp_live_…")) // Around your tool handler: val result = MCPulse.record("search", arguments, clientName) { handler(arguments) } -
3 · Add your key
MCPULSE_KEY=mp_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Shown once, when you create the MCP. An ingest key is a secret — anyone holding it can write data into this MCP.
Or skip the package entirely
Ingest is one authenticated
POST
with a bearer key and two payload shapes — one when your server boots, one per tool
call. Any language that can make an HTTP request can report today; the package only
exists to save you writing it.
Get your key, then run these two lines
Create a server in the dashboard and copy the key into the .env above. It takes about a minute, and your dead tools and schema cost show up straight away.
Free forever. No card, no trial to expire. All ten MCP SDKs supported.