Tocsin API

Base URL: https://tocsin.sh. Every answer is JSON in UTF-8 unless this page says otherwise. Reads need no account and no key.

Endpoints

Method and pathWhat it answers
GET /v1/health{ "ok": true, "version": "<x.y.z>" } when the worker is up.
GET /v1/eventsOne page of the feed: { "events": [...], "next_cursor": <int>, "has_more": <bool> }.
GET /v1/events/{id}One advisory by its Tocsin id. 404 when it does not exist.
GET /v1/events/cve/{cve}The earliest advisory, by feed cursor, that lists the CVE id (CVE-YYYY-NNNN) in any of its cve_ids, not only the first one. GET /cve/{cve} with Accept: application/json lists every such advisory. 400 for a malformed id, 404 when none.
GET /advisories/{id}One advisory as an HTML page. HTML is the default: no Accept header, */* and any Accept that names text/html get HTML. An Accept that names application/json (any q-value) and not text/html gets the advisory JSON. ETag with 304, Cache-Control: public, max-age=300. 404 when it does not exist.
GET /cve/{cve}Every advisory that carries a CVE id (any of its cve_ids), as one HTML page. Accept: application/json returns { "cve", "advisories": [...] }. 404 when none.
GET /sitemap.xmlEvery advisory page and every CVE page, with no lastmod. Past one file (50,000 URLs at most) it is an index of /sitemap-{n}.xml files.
GET /v1/checkWhich runtimes are affected, from query parameters such as ?node=24.22.0&php=8.4.26.
POST /v1/checkWhich dependencies are affected, from lockfiles (multipart) or JSON.
GET /v1/statusEach enabled source with last_run_at, last_ok_at and last_error, plus the newest feed cursor.
GET /.well-known/tocsin.jsonThe discovery document: every endpoint URL and the MCP URL.
GET /llms.txtA plain-text guide for agents.
GET /schema/advisory.v1.jsonThe JSON Schema of one advisory.
MCP https://mcp.tocsin.sh/mcpThe remote MCP server (streamable HTTP).

Feed filters

All filters on GET /v1/events are optional.

An invalid filter answers 400 with { "error": "<reason>" }.

Check

GET /v1/check?node=24.22.0&php=8.4.26 checks runtimes only. POST /v1/check takes one or more lockfiles as multipart fields, where the field name is the file name (package-lock.json, npm-shrinkwrap.json, pnpm-lock.yaml, yarn.lock, requirements.txt, go.sum, composer.lock, Cargo.lock, Gemfile.lock), or a JSON body:

curl -F "package-lock.json=@package-lock.json" https://tocsin.sh/v1/check
curl -H "Content-Type: application/json" -d '{"dependencies":[{"ecosystem":"npm","name":"left-pad","version":"1.3.0"}],"runtimes":{"node":"24.22.0"}}' https://tocsin.sh/v1/check

Runtime parameters are node=, php=, python=, go=, deno= and bun=. go= takes the Go toolchain version as go version prints it (go=1.22.3 or go=go1.22.3). It matches the OSV Go advisories on the toolchain itself (package stdlib, toolchain or cmd/<tool>) by their OSV range events, and fixed_in is the lowest OSV fixed version above yours. Those advisories stay Go advisories for module matching; the runtime match does not add a second advisory. An affected item for go=1.22.3 has dependency { "ecosystem": "go", "name": "go", "version": "1.22.3" }. The version is echoed as given: go=go1.22.3 answers "version": "go1.22.3".

The answer is { "checked": <int>, "affected": [...], "skipped": [...], "feed_cursor": <int> }. Each affected item has dependency, advisory_id, cve_ids, severity, exploited, fixed_in, action and url, critical first, then exploited first. skipped lists what could not be checked; it is not a clean result. Send feed_cursor as since= to /v1/events to get every advisory published after the check.

Status

GET /v1/status answers:

{ "sources": [{ "id": "osv", "kind": "osv", "last_run_at": "...", "last_ok_at": "...", "last_error": null }], "cursor": 1234 }

cursor is the newest feed cursor, or 0 when the feed is empty. The same data is on the page /status.

Advisory schema

Each event is one advisory. The schema is at /schema/advisory.v1.json.

FieldTypeMeaning
idstringThe Tocsin id.
cve_idsstring[]CVE ids. Can be empty.
ghsa_idstringGitHub advisory id, when there is one.
ecosystemstringOSV ecosystem (npm, PyPI, Go, Packagist, crates.io) or runtime (node, php, python, go, deno, bun).
componentstringPackage or runtime name.
affectedobject[]Version ranges: introduced, fixed, last_affected.
fixed_versionsstring[]Versions that fix it.
severityobjectlabel (critical, high, medium, low, unknown) and an optional score.
exploitedbooleanKnown to be exploited.
epssnumberEPSS probability, when known.
published_atstringISO 8601 time.
modified_atstringISO 8601 time.
summarystringOne line from the source.
actionstringWhat to do, computed from the data (for example the fixed version).
referencesstring[]Links.
sourcesobject[]Each source record: source, source_id, url.

Cursor rule

  1. Store next_cursor.
  2. Send it back as since=<next_cursor> on the next call.
  3. Repeat while has_more is true. When has_more is false, you are up to date.

An empty page returns your own since as next_cursor, so you never lose your place. Each page has a strong ETag. Send it back in If-None-Match; an unchanged page answers 304.

Rate limits

Anonymous calls are limited per IP per minute. The limits are settings rows, so they can change without a deploy. The defaults:

Over a limit the answer is 429 with Retry-After in seconds.

Webhook signature

Not live yet. Watch lists, API keys and webhook delivery are planned and not in the API today. The planned format: each delivery is a POST of the advisory JSON with these headers:

Compute the HMAC over the raw body bytes and compare it with the header in constant time.

Connect command

npx tocsin connect

Add to Cursor Add to VS Code

The tocsin CLI (Node 20 or later) finds the agent tools on this machine and adds the tocsin MCP server (https://mcp.tocsin.sh/mcp) to each one: Claude Code (~/.claude.json, plus the Tocsin skill), Cursor (~/.cursor/mcp.json), Claude Desktop (claude_desktop_config.json, through npx -y mcp-remote) and Codex (~/.codex/config.toml).

Connect also installs the Tocsin skill for Codex (~/.codex/skills/tocsin/SKILL.md) and as a Cursor rule (~/.cursor/rules/tocsin.mdc).

A second run changes nothing. Connect never writes a file it cannot parse, never replaces a tocsin entry that is already there and never replaces a skill copy that you edited. The same CLI has npx tocsin check, npx tocsin latest and npx tocsin watch.

To add the server by hand in Claude Code:

claude mcp add --transport http tocsin https://mcp.tocsin.sh/mcp

Codex

npx tocsin connect --codex

This adds the MCP server to ~/.codex/config.toml and the skill to ~/.codex/skills/tocsin/SKILL.md. Codex also reads AGENTS.md in a repo. npx tocsin connect --print-agents-md prints this block for it:

## Security advisories (Tocsin)

At session start and before a deploy, run `npx tocsin check --json`.
Exit 1: a dependency has an advisory. Read `affected[]` and upgrade to `fixed_in`.
Exit 3: the check failed. Do not report the project as safe.
Docs: https://tocsin.sh/docs