Docs · API · v0
HTTP API
Three public endpoints behind the polygraphso CLI and the polygraph MCP tools. Same data, same shape — pick whichever you prefer to integrate against.
Overview
The API exposes three endpoints: a lookup for a single server, a full list of everything graded, and a request queue for servers we haven’t graded yet. All are anonymous and serve the same data the CLI displays; the first two are read-only.
- Base URL ·
https://polygraph.so - Auth · none on these endpoints; subscriber endpoints land later under a separate path.
- Rate limit · per-IP:
check60/min,grade-request20/min. Over-budget calls get a429withRetry-After. - Content type ·
application/jsonin and out.
Prefer the CLI for ergonomics: npx polygraphso check <ref> and npx polygraphso list. The CLI hits these same routes. So does the hosted MCP endpoint just below, the fastest path in for an agent (no install step), and the polygraph MCP tools shipped with @polygraphso/litmus for local use (check_server / list_servers). Hosted request_grade is discontinued.
MCP endpoint (hosted)
The fastest way in for an agent: the same three lookups, served as a hosted MCP server over Streamable HTTP. Any MCP-capable client, an agent, an IDE, a connector directory, reads polygraph grades without installing anything.
Point an MCP client at https://polygraph.so/api/mcp. It serves exactly three tools, check_server, list_servers, and request_grade. request_grade now returns gone: hosted grading is discontinued. The read surface is unchanged. To grade a server yourself, run the open harness (npx @polygraphso/litmus) locally.
Add it to a client
{
"mcpServers": {
"polygraph": { "url": "https://polygraph.so/api/mcp" }
}
}Note · the tools return the same grades as the HTTP endpoints below; a not_available result means unevaluated (neither safe nor unsafe), not a failing grade. For local, one-command grading and the CLI, install the polygraph plugin.
Listing without the whole corpus
list_serversdefaults to the first 25 rows (capped at 100 per call), so a single call doesn’t spend an agent’s whole context budget. Pass grade to restrict to one letter, and limit / offset to page further. summary (a total plus a count per grade) always covers the full graded corpus, regardless of filtering or paging. See the full list_servers reference below for the shared response shape.
Hosted grade requests
request_grade is discontinued. The tool remains registered so existing clients do not break on a missing name; it returns gone. Grade a server yourself with the open harness ( the self_grade command on a check_server miss).
Server-ref format
Every server we track is addressed by a registry-prefixed reference. Three variants, each matching its registry's native namespace:
npm/<package>@<version> name (unscoped) or @scope/name (scoped)
pypi/<name>@<version> flat — no owner segment
github/<owner>/<repo>@<version> owner requiredReal examples:
npm/@modelcontextprotocol/server-filesystem
npm/@notionhq/notion-mcp-server
pypi/mcp-server-git
pypi/mcp-server-fetchThe version segment is optional and version-aware. A pinned @versionreturns the grade for that exact version. A bare ref resolves the version in play — the installed version (CLI) or the registry’s current latest — and returns its grade; if that version isn’t graded yet, the most recent graded version is returned and flagged (version_match: false).
Check a server
Looks up one server’s published polygraph grade, or a notify URL if it hasn’t been graded yet.
Request
{
"server_ref": "npm/@modelcontextprotocol/server-filesystem",
"source": "mcp", // optional: "mcp" or "cli"
"agent_id": "claude-code/2.1", // optional: calling client, name/version
"agent_meta": { // optional: what the client declared
"title": "Claude Code",
"capabilities": ["sampling", "roots"]
}
}The identity fields are optional and aggregate-only: they feed per-agent usage counters (software metadata — never IPs, never request logs). Callers that send nothing are counted by a normalized User-Agent.
Response · graded
{
"status": "graded",
"polygraph": "A",
"polygraph_detail": {
"grade": "A",
"c01": "pass",
"c02": "pass",
"c03": "pass",
"tool_defs_fingerprint": "0x256a…66db6",
"methodology_version": "litmus-v17",
"resolved_version": "1.4.0",
"rationale": "All three categories passed.",
"computed_at": "2026-06-11T14:14:04Z"
},
"current_version": "1.4.0",
"version_match": true,
"notify_url": "https://www.polygraph.so/notify?for=npm/@modelcontextprotocol/server-filesystem"
}Note · polygraph is one of "A", "B", "C", "D", "F" (see the rubric). polygraph_detail.resolved_version is the version that was graded; current_version is the version in play, and version_match is falsewhen an older graded version is returned because the current one isn’t graded yet.
Response · not graded
{
"status": "not_available",
"notify_url": "https://polygraph.so/notify?for=npm/some-owner/some-package",
"message": "No published polygraph for npm/some-owner/some-package yet — treat it as unevaluated (neither safe nor unsafe). Hosted grading is discontinued; grade it yourself with the self_grade command.",
"self_grade": "npx -y -p @polygraphso/litmus polygraphso-litmus litmus npm/some-owner/some-package"
}message spells out the next steps for an agent; self_grade is a one-shot command to run the open litmus yourself. Hosted grade requests are discontinued. A miss bumps an anonymous demand counter so we can see which ungraded servers are most in demand. No request body is logged beyond the ref itself.
curl
curl -X POST https://polygraph.so/api/cli/check \
-H 'content-type: application/json' \
-d '{"server_ref":"npm/@modelcontextprotocol/server-filesystem"}'Errors
400— missing, malformed, or too-longserver_ref. The body includes a shorterrorstring.429— rate limited (per-IP). Back off and retry after theRetry-Afterinterval.500— lookup failed server-side. Safe to retry.
List graded servers
Returns every server with a published polygraph grade, sorted by grade (A first), then alphabetically. With no query params this is the full set, unpaged, matching the original v0 shape. Three optional query params narrow and page it:
grade· one ofA,B,C,D,F; restricts the listing to that letter.limit· a positive integer. Unset returns every matching row.offset· a non-negative integer, rows to skip before takinglimit.
summary is always computed over the full graded corpus, total plus a count per grade, regardless of grade, limit, or offset, so a filtered or paged caller can still see the shape of the whole set.
Response shape
{
"servers": [
{
"server_ref": "npm/@modelcontextprotocol/server-filesystem",
"polygraph": "A"
},
{
"server_ref": "npm/@upstash/context7-mcp",
"polygraph": "D"
}
],
"total": 6,
"summary": {
"total": 111,
"byGrade": { "A": 88, "B": 12, "C": 3, "D": 6, "F": 2 }
}
}polygraph is one of "A", "B", "C", "D", "F" (see the rubric). total counts rows matching grade before paging (it equals summary.total when grade is omitted). Only graded servers appear; check a specific ungraded server with /api/cli/check.
curl
curl https://polygraph.so/api/cli/list
curl "https://polygraph.so/api/cli/list?grade=A&limit=5"Request a grade
Discontinued. This endpoint, the x402 rail, and the website checkout all return 410 Gone. Existing published grades are unchanged; look them up with /api/cli/check. To grade a server yourself, run the open harness:
npx -y -p @polygraphso/litmus polygraphso-litmus litmus npm/some-owner/some-packageResponse
{
"error": "Hosted grading is discontinued. Existing published grades remain available. To grade a server yourself, run the open harness: npx -y -p @polygraphso/litmus polygraphso-litmus litmus <server>."
}Versioning
v0 changes are additive. We add fields; we don't remove or rename them. A breaking change — a removed field, a renamed key, a changed shape — would ship under a new path (/api/v1/...), and the v0 path would keep its existing behavior for a deprecation window we'll announce here.
The harness version that produced a given polygraph will be exposed on the grade payload once behavioral grading lands, so grades stay comparable within a harness version.
Stability
These endpoints are the contract the CLI is built against, so the URLs and field names are stable. The notify URL pattern (polygraph.so/notify?for=<ref>) is stable too; it's the same place the website's "tell me when this is graded" funnel writes to.
What isn't stable yet: the cadence of behavioral grade publication. The methodology and rubric are documented at /methodology.
Embeddable badge
A live grade badge any server can embed — in a README, on npm, or on a docs site. Three artifacts, all keyed by the same server ref, all served from the canonical grade so they update themselves when a grade changes.
A small inline SVG pill (image/svg+xml) — polygraph · A, colored by grade. An ungraded server renders a muted unratedpill rather than an error, so it’s safe to embed before a grade exists.
A larger card image (image/png) with the grade, the three category slots, and the methodology version.
The human-readable grade report the badge and card link to — category breakdown, fingerprint, and the command to reproduce the grade. It also hosts ready-to-copy embed snippets.
Markdown
[](https://polygraph.so/mcp/npm/@modelcontextprotocol/server-filesystem)Note · the ref is passed unencoded in the query string (/ and @ are legal there). Images are cached at the CDN; a regrade propagates within the hour.