MCP Canary

Docs

Everything an agent or a human needs to run MCP Canary. The same text for agents is at /llms.txt.

Install

# macOS, Linux
curl -fsSL https://mcpcanary.com/install.sh | sh
# Windows PowerShell
irm https://mcpcanary.com/install.ps1 | iex

The installer never asks a question and never needs sudo. It verifies the SHA-256 checksum and installs to the first writable directory on your PATH (~/.local/bin, ~/bin, /usr/local/bin, /opt/homebrew/bin), else to ~/.local/bin. Set CANARY_INSTALL_DIR or CANARY_VERSION=v0.1.0 to change that. On Windows it installs to %LOCALAPPDATA%\Programs\McpCanary and adds it to the user PATH.

Builds: macOS arm64 and x64, Linux x64 and arm64 (glibc 2.34 or later; Alpine is not supported yet), Windows x64. Releases: github.com/xakpc-dev-labs/canary/releases.

Quick start

canary inspect https://api.example.com/mcp
canary monitor set customer-search-prod --url https://api.example.com/mcp \
  --tool search_customer --args '{"email":"canary@example.com"}' \
  --assert '$.customers[0].email == "canary@example.com"' --interval 5m
canary monitor test customer-search-prod

inspect connects, lists the tools with their safety class, and never calls a tool. The first monitor set creates an anonymous workspace, saves its token, starts the 72-hour trial, and runs the first check.

To keep the configuration in the repository, put the monitor set line and a monitor test line in your deploy workflow. monitor set is idempotent: the same command again reports "changed": false.

Commands

canary inspect URL [--bearer-env VAR] [--header-env Name=VAR]...
canary monitor set NAME --url URL [--tool T] [--args JSON] [--assert EXPR]... [--interval 5m] [--timeout 30s]
                   [--bearer-env VAR] [--header-env Name=VAR]... [--allow-side-effects] [--alert-on-change]
canary monitor list | get ID | delete ID | test ID [--timeout 90s] | checks ID [--limit N]
canary incident list [--status open|resolved] [--monitor ID] [--limit N] | get ID
canary webhook set NAME --url URL [--event E]... | list | get ID | delete ID | deliveries ID [--limit N]
canary pricing | entitlement
canary billing upgrade --plan solo|pro | billing portal
canary subscription cancel

ID is a name or an id. Every command takes --json. monitor set gives the full desired state: an option you leave out goes back to its default. Durations: 90, 30s, 15m, 1h.

Safety

A check calls a tool only when the server declares readOnlyHint: true for it. A tool that is mutating, possibly mutating, or has no hints needs --allow-side-effects. The rule is checked on every check, so a server that changes its hints later is not called. Without --tool, a check only connects and lists tools.

Target auth

Secrets come only from environment variables, never from command-line values:

MCP_TOKEN=... canary monitor set prod --url https://api.example.com/mcp --bearer-env MCP_TOKEN
X_KEY=...     canary monitor set prod --url https://api.example.com/mcp --header-env X-Api-Key=X_KEY

Values are encrypted at rest and never returned. A monitor set without these options keeps the stored headers. OAuth is not supported yet.

Assertions

'$.customers[0].email == "a@b.c"'    equals (the right side is JSON, so strings need quotes)
'$.count != 0'                       not equals
'$.items exists'                     the path is present
'$.name contains "acme"'             substring, or an array element
'$.id matches "^cus_"'               regular expression
'duration < 3000ms'                  tool call time (ms, s, m, h)

$ is the tool's structuredContent, else its first text block parsed as JSON. Paths support .name, ['name'], and [0]. A tool result with isError: true always fails the check.

Exit codes

Code Meaning
0 Success. For inspect and monitor test: the check passed.
2 monitor set saved the monitor, but the first check failed.
3 The target is unhealthy: assertion failed, tool error, auth or protocol problem.
10 No token or a bad token.
11 Permission denied, or a limit that no plan allows.
20 Invalid input (bad flag, bad JSON, missing environment variable).
21 Not found.
30 MCP Canary is unavailable or rate limited. Retry later.
31 The target is unreachable or timed out.
40 Payment required. The error lists offers with checkout links.

JSON output and errors

With --json, stdout has only the API's JSON, also for errors, and stderr is empty. Every error has the same shape:

{ "ok": false, "code": "PLAN_LIMIT_REACHED", "retryable": false,
  "message": "Monitor limit reached: the trial plan allows 1 monitors. ...",
  "details": { "limit": "monitors", "current_plan": "trial", "required_plan": "solo" },
  "offers": [ { "plan": "solo", "amount": 2400, "currency": "USD", "interval": "month", "checkout_url": "https://..." } ] }

A failed check is not an error: inspect and monitor test return "status": "failed" with a failure object (code, message, details). Failure codes include TARGET_UNAVAILABLE, TARGET_TIMEOUT, TOOL_RETURNED_ERROR, ASSERTION_FAILED, and SIDE_EFFECTS_NOT_ALLOWED.

Incidents and contract changes

Only scheduled checks open incidents, and only after a confirming re-check. The next passing scheduled check resolves it. monitor test never opens or resolves one, so a CI run does not page anyone.

Each check compares the tool contract (names and schemas, not descriptions) with the last one and records the difference: tool_added, tool_removed, argument_added, argument_removed, argument_type_changed, and more. With --alert-on-change, a change also opens an incident.

Webhooks

canary webhook set ops --url https://hooks.example.com/canary          # default events
canary webhook set ops --url https://hooks.example.com/canary --event '*'

Events: monitor.created, monitor.updated, monitor.deleted, check.passed, check.failed, contract.changed, incident.opened, incident.resolved, entitlement.activated, entitlement.expiring, entitlement.expired. Default: contract.changed, incident.*, entitlement.expiring, entitlement.expired.

The signing secret is shown once, on create. Each request has X-McpCanary-Signature: t=<unix seconds>,v1=<hex>, where v1 is HMAC-SHA256 of t + "." + body with the secret. Compare in constant time and reject old timestamps. Failed deliveries are retried with the same event id.

Plans and payment

canary pricing shows the plans; canary entitlement shows yours. When a write needs a plan, the error lists offers with a checkout_url: give it to a human. A link is valid for 7 days. canary subscription cancel cancels at the end of the paid period. When access ends, checks pause; nothing is deleted.

Configuration

Variable Use
MCP_CANARY_TOKEN Workspace token. Wins over the saved token.
MCP_CANARY_API_URL API base URL. Default https://api.mcpcanary.com.

The token is saved per API URL in ~/Library/Application Support/McpCanary/credentials.json (macOS), ~/.config/mcpcanary/credentials.json (Linux), or %LOCALAPPDATA%\McpCanary\credentials.json (Windows), with mode 0600.

HTTP API

The CLI is a thin client: everything it does is an HTTP call to https://api.mcpcanary.com/v1/... with Authorization: Bearer <token>. The OpenAPI document is at https://api.mcpcanary.com/openapi.json.