npm batchwatch-mcpstdioupdated 8d ago
batchwatch only works if people instrument their pipelines, and nobody writes raw HTTP calls to donate data. These are the libraries that make it two lines.
What can you do with batchwatch mcp?
Client libraries
batchwatch only works if people instrument their pipelines, and nobody writes raw HTTP calls to donate data. These are the libraries that make it two lines.
| Package | Language | Tests | State |
|---|---|---|---|
python/ |
Python 3.8+ | 96, all passing | works; PyPI release on the way |
typescript/ |
TypeScript / JS, Node 20+ | 84, all passing (built + tested in CI) | works; built and tested in CI; npm release on the way |
dotnet/ |
C#, net8.0 |
built and tested in CI (xunit) | works; NuGet release on the way |
go/ |
Go 1.21+ | 83, all passing (incl. -race) |
works; module-proxy release on the way |
ruby/ |
Ruby 3.0+ | 87, all passing (minitest) | works; RubyGems release on the way |
php/ |
PHP 8.2+ | all passing | works; on Packagist, tagged release on the way |
java/ |
Java 17+ | 88, all passing | works; Maven Central release on the way |
rust/ |
Rust 1.63+ | 94, all passing (cargo test) |
works, std-only; http-only (see note); crates.io release on the way |
cpp/ |
C++17 (POSIX) | 85, all passing | works, stdlib+sockets only; source-only by design; http-only (see note) |
All nine expose the same surface, and a conformance check fails CI if any language falls behind ā see The same surface, in all nine.
client/batchwatch.py in the repo root is the original single-file client
and is left untouched. clients/python/ is the packaged version of it, plus
spooling.
A note on TLS (Rust and C++). Every client except Rust and C++ gets
TLS from its standard library and talks to https://batchwatch.dev directly.
Rust's and C++'s standard libraries have no TLS, and both are written with
zero external dependencies on purpose, so their transport is http://
only. Pointed at the default https:// URL they spool rather than
deliver (the measurement is kept, not lost) until you point them at an
http:// endpoint or a local TLS-terminating proxy. Their READMEs say so.
What every client does the same way
It fails open. A batchwatch outage must never stop a user's job. Every
submission happens off the caller's thread with a short timeout, every error
is swallowed and logged at debug level, and the only call you await ā
should_batch() ā returns your default when it cannot answer, never a
guess. The default is "run it synchronously": being wrong that way costs
money, being wrong the other way blows a deadline. Each package has a test
that runs against a dead port and a hung socket.
Two lines to adopt. should_batch() before you submit, track() around
the call.
It never sends content. No prompts, no completions, no file names. The
body is built from one allowlist ā provider, model, mode, endpoint, request
count, token counts, timestamps, status ā and everything else is dropped by a
single function - _scrub in Python, Scrub in Go, clean in TypeScript,
sanitize in PHP, Ruby and Java, strip in Rust and C++ - on the way out.
Each package has
a test that asserts this on what the server actually received, with a
positive control so it cannot pass by sending nothing at all.
output_tokens defaults to null, never 0. Output costs five to six times
as much as input, so a saving computed on zero output is systematically too
low ā 3.4x too low in the case that led to this rule ā and nothing in the
response reveals it. Absence must stay absence all the way to the server.
Explicitly passing 0 still sends 0: zero is a measurement.
It spools to disk. An undeliverable completed measurement is appended
to a JSONL file and replayed later via POST /v1/calls/complete. Losing
measurements when the network is bad means losing them exactly when they are
most interesting.
It does the annoying parts. Beyond the two-line advisory path, every client carries the same high-level surface so you never hand-roll it:
- The high-level batch job ā
batch(...)hands the client the two callables (batch-create + a synchronous fallback) and it owns the rest: a deadline guard that shifts to the fallback when the wait runs long, a poll loop with exponential backoff, jitter, a rate-limit floor and a first cadence informed by the model's measured p50, and partial-completion handling that splits a batch into landed / failed / expired mapped bycustom_id(never by index) with an idempotent retry of only the failed subset. We take the callable, never the payload ā the deadline fallback is reported down the same accuracy path a completion uses, so nothing new is sent. - Read your own contributions ā
my_calls()andkey_status()(GET /v1/calls/mine,/v1/keys/current): the per-key readback for verifying a measurement landed and checking your tier/quota. - Subscribe to outage alerts ā
subscribe()/subscriptions()/unsubscribe()against/v1/subscriptions, the "own the outage moment" channel.
Unlike the measurement path, these last two do not fail open: they are explicit actions against a per-key route, so without a key they raise rather than silently pretend. The job path is the user's own job, so a misuse (a result before a submit, a deadline with no fallback) raises loudly too ā only telemetry fails open.
No dependencies. Standard library only, in all nine.
The same surface, in all nine
Every client promises the same capabilities, and ā since a feature can land in
one language, its card be closed in good faith, and the other eight silently lag
ā conformance/ is the check that stops that. manifest.json
declares the promised surface; check.py greps each SDK's own source and its
own tests (per language, never a loose cross-language match) and fails CI on
any unexplained gap. Exemptions must be explicit and justified in the manifest.
Run it with python clients/conformance/check.py --list.
The spool format
One JSON object per line, in the shape /v1/calls/complete accepts:
{"provider":"openai","model":"gpt-5.6-sol","mode":"batch","requests":1,
"endpoint":null,"input_tokens":9720,"output_tokens":null,"status":"completed",
"started_at":"2026-08-25T10:00:00Z","ended_at":"2026-08-25T10:04:00Z"}
Identical across all nine clients, so a file written by one can be flushed
by another. Default location is $BATCHWATCH_SPOOL, otherwise
batchwatch-spool.jsonl in the temp directory.
Two consequences worth knowing before you rely on it:
- Spooling needs an API key.
/v1/calls/completetakes the caller's own timestamps, so it is closed to anonymous callers ā see the reasoning insrc/index.js. A client without a token therefore does not spool at all: a file that can never be sent is a disk leak, not data safety. - Replay can duplicate. If the original
PATCHreached the server but the response did not, the spooled copy arrives as a second row. That is the deliberate trade: a duplicate is visible in the dataset, a lost measurement is not.
The file is capped (5 MB by default). Past the cap, measurements are dropped rather than filling the user's disk.
Publishing
Every client works today: install it from the repo (each README shows how) and it runs. Registry publishing is the next step, and it is in flight ā the PHP package is already on Packagist, the .NET and TypeScript clients build and test in CI on every push, and the rest are being wired up (PyPI, npm, a Go module proxy, RubyGems, Maven Central, crates.io). Client CI itself lands in #184.
Two properties are deliberate design decisions, not gaps, and each has a workaround in the relevant README:
- Rust and C++ speak
http://only ā their standard libraries carry no TLS and both are zero-dependency by design. Point them at a local TLS-terminating proxy in front ofbatchwatch.dev, and they deliver directly (see the TLS note above). - The POSIX-socket clients (C++) are first-class on Linux and other POSIX platforms; Windows needs a Winsock shim.
Install
Add batchwatch mcp to your client. Pick the one you use.
claude mcp add batchwatch-mcp -- npx -y batchwatch-mcpcodex mcp add batchwatch-mcp -- npx -y batchwatch-mcpamp mcp add batchwatch-mcp -- npx -y batchwatch-mcp{
"mcpServers": {
"batchwatch-mcp": {
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}Add to `claude_desktop_config.json`, then restart Claude Desktop.
{
"mcpServers": {
"batchwatch-mcp": {
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}Add to `~/.cursor/mcp.json`, or `.cursor/mcp.json` for a single project.
code --add-mcp '{"name":"batchwatch-mcp","command":"npx","args":["-y","batchwatch-mcp"]}'Or add the block manually to `.vscode/mcp.json` under `servers`.
{
"mcpServers": {
"batchwatch-mcp": {
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}Add to `~/.codeium/windsurf/mcp_config.json`.
{
"mcpServers": {
"batchwatch-mcp": {
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}Add to `cline_mcp_settings.json` via the MCP Servers panel.
{
"mcpServers": {
"batchwatch-mcp": {
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}Add to `~/.gemini/settings.json`.
{
"mcpServers": {
"batchwatch-mcp": {
"type": "local",
"command": "npx",
"args": [
"-y",
"batchwatch-mcp"
],
"tools": [
"*"
]
}
}
}Add to `~/.copilot/mcp-config.json`, or run `/mcp add` inside the CLI.
{
"context_servers": {
"batchwatch-mcp": {
"command": {
"path": "npx",
"args": [
"-y",
"batchwatch-mcp"
]
}
}
}
}Add to your Zed `settings.json`.
npx -y batchwatch-mcpRun `goose configure`, choose **Add Extension ā Command-line Extension**, and paste this command.
Score
39 / 100
Incomplete
- Documentation20/25
- Maintenance19/25
- Trust6/20
- Capability0/15
- Install experience12/15
- Documents what it does and how to connect
- Has a resolvable package or endpoint
- Exposes at least one tool, prompt or resource
- README has substantive content
- Includes a code example
- Documents its configuration
- Mentions credentials or security posture
- Last commit 1 days ago
- Has a release history
- Repository is not archived
- No licence detected
- Namespace verified in the official MCP registry
- Claimed by its owner
- Published under an organisation
- 0 tool(s) documented
- Provides prompt templates
- Provides resources
- 12 documented install method(s)
- Published to a package registry
- Offers a hosted endpoint ā no local install
Version history
| Versions | Published |
|---|---|
| 0.1.1Latest | Aug 31, 2026 |