Checks finished AI work after it is done: figures, citations and quotations, with a signed record.
Maintain this server? Claim it
Call this server's tools from your browser. Nothing to install.
A Sternly API key (sternly.ai, Settings), for clients that do not sign in with OAuth.
Requests go through mcptry because browsers can't call most MCP servers directly. Header values are sent to the server for that request only and are never stored or logged.
Tool list from our last check. Connect live to refresh it.
The status of a check Sternly runs (running, done or failed) and, once it is done, its result: its headline, counts, depth, findings and the link to its signed evidence package. Use the check id that submit_claim_bundle returned.
Pick your client. Values in capitals, like YOUR_TOKEN, are yours to fill in.
Run in a terminal
claude mcp add --transport http verify https://api.sternly.ai/mcp
Headers
X-API-Key (optional, not in the config above): A Sternly API key (sternly.ai, Settings), for clients that do not sign in with OAuth.The status of a check Sternly runs (running, done or failed) and, once it is done, its result: its headline, counts, depth, findings and the link to its signed evidence package. Use the check id that submit_claim_bundle returned.
| Argument | Type | Description |
|---|---|---|
| check_id* | string |
{
"type": "object",
"required": [
"check_id"
],
"properties": {
"check_id": {
"type": "string",
"pattern": "^chk_[0-9a-f]{32}$"
}
},
"additionalProperties": false
}How the person's workspace defines its terms (each with its version, owner and the date it took effect). Give the definition as it is; do not change it.
| Argument | Type | Description |
|---|---|---|
| terms | array |
{
"type": "object",
"properties": {
"terms": {
"type": "array",
"items": {
"type": "string",
"maxLength": 200
},
"maxItems": 50
}
},
"additionalProperties": false
}Send a definition the person wants to add, in their words, to the workspace, where a person approves it before it is used. Nothing proposed here takes effect by itself.
| Argument | Type | Description |
|---|---|---|
| definitions* | array |
{
"type": "object",
"required": [
"definitions"
],
"properties": {
"definitions": {
"type": "array",
"items": {
"type": "object",
"required": [
"term",
"definition"
],
"properties": {
"note": {
"type": "string",
"maxLength": 2000
},
"term": {
"type": "string",
"maxLength": 200,
"minLength": 1
},
"definition": {
"type": "string",
"maxLength": 4000,
"minLength": 1
}
},
"additionalProperties": false
},
"maxItems": 20,
"minItems": 1
}
},
"additionalProperties": false
}Hand finished work to Sternly for an independent check after the fact. Send the final text exactly as the person received it, what the person asked for in their own words, and the files, sources and programs you already used, with what each program printed. Send the work as it is: do not rewrite, shorten or add to it for Sternly, and do not mark which part came from where; Sternly reads the work itself. When the work is someone else's (the other side's brief, a filing before the court), say so in policy.mode, and name a producer only if you know who made it. Returns a check id and a link to give the person; get_check returns the result. With wait set to false, Sternly answers at once with the status running, and the check goes on.
| Argument | Type | Description |
|---|---|---|
| text* | string | |
| wait | boolean | When the answer comes. false: at once, with the check id and the status running, while the check goes on; get_check then returns the status and, once the check is done, its result. true: when the check is done. |
| files | array | |
| notes | any | |
| layout | array | |
| policy | object | The only part of a bundle that chooses what runs: closed enums and bounded numbers. `mode` is one of the enums and chooses nothing that runs: it names whose work this is, for the result's words (`capture/modes.py`). |
| capture |
We read every tool's name, description and inputs the way the model will, looking for instructions hidden from you. A clean result means no warning signs, not a guarantee. Scanned Nov 8, 2026.
| object |
| How the work reached Sternly, and what the channel could not see (recorded in the package; it lowers grades). |
| request | any |
| sources | array |
| producer | object | Who made the work. `vendor` decides independence: the judges are never from the producer's vendor. It is the maker of the producer's model (the lab), never where it was served. |
| programs | array |
| tool_log | array |
| bundle_version | integer |
| idempotency_key | any |
{
"type": "object",
"required": [
"text"
],
"properties": {
"text": {
"type": "string",
"title": "Text",
"maxLength": 2000000,
"minLength": 1
},
"wait": {
"type": "boolean",
"default": true,
"description": "When the answer comes. false: at once, with the check id and the status running, while the check goes on; get_check then returns the status and, once the check is done, its result. true: when the check is done."
},
"files": {
"type": "array",
"items": {
"type": "object",
"title": "FileRef",
"required": [
"id",
"name",
"kind"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"pattern": "^[A-Za-z0-9_.:-]{1,64}$"
},
"ref": {
"anyOf": [
{
"type": "string",
"maxLength": 4000
},
{
"type": "null"
}
],
"title": "Ref",
"default": null
},
"kind": {
"enum": [
"upload",
"file_param",
"connector",
"url",
"workspace"
],
"type": "string",
"title": "Kind"
},
"name": {
"type": "string",
"title": "Name",
"maxLength": 300,
"minLength": 1
},
"sha256": {
"anyOf": [
{
"type": "string",
"pattern": "^[0-9a-f]{64}$"
},
{
"type": "null"
}
],
"title": "Sha256",
"default": null
},
"media_type": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Media Type",
"default": null
}
},
"description": "A file the work used or the person attached. Sternly fetches and hashes it; `sha256` as sent is only a claim.",
"additionalProperties": false
},
"title": "Files",
"maxItems": 500
},
"notes": {
"anyOf": [
{
"type": "string",
"maxLength": 50000
},
{
"type": "null"
}
],
"title": "Notes",
"default": null
},
"layout": {
"type": "array",
"items": {
"type": "object",
"title": "LayoutBlock",
"required": [
"file_id",
"start",
"end",
"part"
],
"properties": {
"end": {
"type": "integer",
"title": "End",
"minimum": 0
},
"ref": {
"anyOf": [
{
"type": "string",
"maxLength": 120
},
{
"type": "null"
}
],
"title": "Ref",
"default": null
},
"bbox": {
"anyOf": [
{
"type": "array",
"maxItems": 4,
"minItems": 4,
"prefixItems": [
{
"type": "number"
},
{
"type": "number"
},
{
"type": "number"
},
{
"type": "number"
}
]
},
{
"type": "null"
}
],
"title": "Bbox",
"default": null
},
"page": {
"anyOf": [
{
"type": "integer",
"maximum": 100000,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Page",
"default": null
},
"part": {
"enum": [
"title",
"body",
"notes",
"table",
"chart",
"tile",
"cell",
"picture"
],
"type": "string",
"title": "Part"
},
"slide": {
"anyOf": [
{
"type": "integer",
"maximum": 10000,
"minimum": 1
},
{
"type": "null"
}
],
"title": "Slide",
"default": null
},
"start": {
"type": "integer",
"title": "Start",
"minimum": 0
},
"file_id": {
"type": "string",
"title": "File Id",
"pattern": "^[A-Za-z0-9_.:-]{1,64}$"
},
"section": {
"anyOf": [
{
"type": "integer",
"maximum": 100000,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Section",
"default": null
},
"shape_id": {
"anyOf": [
{
"type": "integer",
"minimum": 0
},
{
"type": "null"
}
],
"title": "Shape Id",
"default": null
},
"paragraph": {
"anyOf": [
{
"type": "integer",
"maximum": 1000000,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Paragraph",
"default": null
},
"ocr_confidence": {
"anyOf": [
{
"type": "number",
"maximum": 1,
"minimum": 0
},
{
"type": "null"
}
],
"title": "Ocr Confidence",
"default": null
}
},
"description": "One readable item of a document set as rendered into the bundle's text (capture/documents.py): which file, slide\nor page, part and cell the text between `start` and `end` came from, and, for a PDF page read from an image, the\nOCR's confidence. Data only: it places claims; no model reads it.",
"additionalProperties": false
},
"title": "Layout",
"maxItems": 20000
},
"policy": {
"type": "object",
"title": "Policy",
"properties": {
"mode": {
"enum": [
"own_work",
"opposing_party",
"court_review"
],
"type": "string",
"title": "Mode",
"default": "own_work",
"description": "Whose work this is: own_work (the person's own, or their AI's; the default), opposing_party (the other side's brief) or court_review (a filing before the court). It changes the words of the result, never what is checked."
},
"checks": {
"type": "array",
"items": {
"enum": [
"references",
"quotes",
"numbers",
"sheets",
"argument",
"consistency",
"intent",
"agent"
],
"type": "string"
},
"title": "Checks"
},
"max_depth": {
"enum": [
"D0",
"D1",
"D2",
"D3"
],
"type": "string",
"title": "Max Depth",
"default": "D3"
},
"budget_usd": {
"type": "number",
"title": "Budget Usd",
"default": 0.1,
"maximum": 20,
"minimum": 0
},
"include_files_in_package": {
"type": "boolean",
"title": "Include Files In Package",
"default": false
}
},
"description": "The only part of a bundle that chooses what runs: closed enums and bounded numbers. `mode` is one of the enums and\nchooses nothing that runs: it names whose work this is, for the result's words (`capture/modes.py`).",
"additionalProperties": false
},
"capture": {
"type": "object",
"title": "Capture",
"properties": {
"gaps": {
"type": "array",
"items": {
"type": "string"
},
"title": "Gaps",
"maxItems": 50
},
"channel": {
"enum": [
"upload",
"host_captured",
"model_supplied",
"org_captured",
"api"
],
"type": "string",
"title": "Channel",
"default": "api"
},
"request_kind": {
"enum": [
"producer_task",
"check_instruction"
],
"type": "string",
"title": "Request Kind",
"default": "producer_task"
}
},
"description": "How the work reached Sternly, and what the channel could not see (recorded in the package; it lowers grades).",
"additionalProperties": false
},
"request": {
"anyOf": [
{
"type": "string",
"maxLength": 50000
},
{
"type": "null"
}
],
"title": "Request",
"default": null
},
"sources": {
"type": "array",
"items": {
"type": "object",
"title": "SourceRef",
"required": [
"id",
"kind",
"ref"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"pattern": "^[A-Za-z0-9_.:-]{1,64}$"
},
"ref": {
"type": "string",
"title": "Ref",
"maxLength": 4000,
"minLength": 1
},
"kind": {
"enum": [
"url",
"doi",
"case",
"statute",
"regulation",
"filing",
"document"
],
"type": "string",
"title": "Kind"
},
"file_id": {
"anyOf": [
{
"type": "string",
"pattern": "^[A-Za-z0-9_.:-]{1,64}$"
},
{
"type": "null"
}
],
"title": "File Id",
"default": null
}
},
"description": "A source the work relied on or cited. `file_id` points at a file when the source's text is one of the files.",
"additionalProperties": false
},
"title": "Sources",
"maxItems": 500
},
"producer": {
"type": "object",
"title": "Producer",
"properties": {
"host": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Host",
"default": null
},
"model": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Model",
"default": null
},
"ai_tool": {
"enum": [
"chatgpt",
"claude",
"claude_code",
"copilot",
"gemini",
"agent",
"human",
"unknown"
],
"type": "string",
"title": "Ai Tool",
"default": "unknown"
}
},
"description": "Who made the work. `vendor` decides independence: the judges are never from the producer's vendor. It is the\nmaker of the producer's model (the lab), never where it was served.",
"additionalProperties": false
},
"programs": {
"type": "array",
"items": {
"type": "object",
"title": "ProgramRun",
"required": [
"id",
"language",
"code"
],
"properties": {
"id": {
"type": "string",
"title": "Id",
"pattern": "^[A-Za-z0-9_.:-]{1,64}$"
},
"code": {
"type": "string",
"title": "Code",
"maxLength": 65536,
"minLength": 1
},
"inputs": {
"type": "array",
"items": {
"type": "string"
},
"title": "Inputs",
"maxItems": 50
},
"printed": {
"anyOf": [
{
"type": "string",
"maxLength": 200000
},
{
"type": "null"
}
],
"title": "Printed",
"default": null
},
"language": {
"enum": [
"python",
"sql"
],
"type": "string",
"title": "Language"
},
"exit_code": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Exit Code",
"default": null
},
"captured_by": {
"enum": [
"hook",
"upload",
"model"
],
"type": "string",
"title": "Captured By",
"default": "model"
}
},
"description": "A program the AI *already ran*, as captured (the hook saw it run, the person uploaded it, or the model passed it\non as it was). Sternly re-runs it in its own sandbox; what it printed here is the producer's record, not evidence.",
"additionalProperties": false
},
"title": "Programs",
"maxItems": 500
},
"tool_log": {
"type": "array",
"items": {
"type": "object",
"title": "ToolEvent",
"required": [
"tool"
],
"properties": {
"tool": {
"type": "string",
"title": "Tool",
"maxLength": 100,
"minLength": 1
},
"summary": {
"anyOf": [
{
"type": "string",
"maxLength": 4000
},
{
"type": "null"
}
],
"title": "Summary",
"default": null
},
"exit_code": {
"anyOf": [
{
"type": "integer"
},
{
"type": "null"
}
],
"title": "Exit Code",
"default": null
},
"captured_by": {
"enum": [
"hook",
"model",
"trace"
],
"type": "string",
"title": "Captured By",
"default": "hook"
},
"output_excerpt": {
"anyOf": [
{
"type": "string",
"maxLength": 20000
},
{
"type": "null"
}
],
"title": "Output Excerpt",
"default": null
}
},
"description": "One tool call from the producer's own log, for the agent-action engine: the Claude Code plugin's PostToolUse\n(`hook`), an OpenTelemetry trace the caller sent (`trace`, capture/otel.py), or as the producing model passed it on.",
"additionalProperties": false
},
"title": "Tool Log",
"maxItems": 2000
},
"bundle_version": {
"type": "integer",
"const": 1,
"title": "Bundle Version",
"default": 1
},
"idempotency_key": {
"anyOf": [
{
"type": "string",
"maxLength": 200
},
{
"type": "null"
}
],
"title": "Idempotency Key",
"default": null
}
},
"description": "Finished work as it is: the final text, what the person asked for, and the files, sources and programs already used (each program with what it printed).",
"additionalProperties": false
}