For MCP servers
Notarized tool calls
When your product is an MCP server, your customers' agents change real records through your tools. Give every tool result a receipt they can check without trusting your logs, and without trusting ours.
How it works
- Your server hashes the call. The tool name, the arguments it received, the result it returned and a fresh random salt, as canonical JSON, then SHA-256. Arguments and results never leave your server.
- Rightshold signs the digest.
POST /v1/tool-callswith your account key returns a receipt signed with the issuer key published at /.well-known/jwks.json, committed to the public ledger. - The result carries the receipt. The tool result gains
_meta["com.rightshold/tool-call"]. The caller recomputes the digest from what it sent and received, and checks the signature and the ledger entry.
Add it to your server
// npm install @modelcontextprotocol/sdk ; curl -O https://www.rightshold.com/sdks/rightshold-mcp-notary.cjs
const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js");
const { z } = require("zod");
const { notarize } = require("./rightshold-mcp-notary.cjs");
const server = new McpServer({ name: "acme-hr", version: "1.0.0" });
notarize(server, { server: "acme-hr" }); // reads RIGHTSHOLD_API_KEY
server.registerTool("update_salary", { inputSchema: { employee_id: z.string(), amount: z.number() } },
async (args) => ({ content: [{ type: "text", text: "Salary updated" }] }));
// Every result from this tool now carries a receipt in _meta.
Download the notary module (one file, Node 18+, no dependencies). Without an API key it does nothing. If Rightshold is slow or unreachable, your tool still returns its result, without a receipt.
What the caller receives
"_meta": {
"com.rightshold/tool-call": {
"spec": "rs-tool-call/1",
"id": "tc_…",
"server": "acme-hr",
"tool": "update_salary",
"arguments": { "employee_id": "e-17", "amount": 98000 },
"salt": "…",
"call_sha256": "…",
"receipt_url": "https://www.rightshold.com/v1/tool-calls/tc_…",
"verify_url": "https://www.rightshold.com/v1/tool-calls/verify"
}
}
How your customers check it
In code, offline signature check plus the ledger:
const { verifyToolResult } = require("./rightshold-mcp-notary.cjs");
const check = await verifyToolResult(result); // the CallToolResult your client received
// { valid: true, matches_call: true, id: "tc_…", ledger: { seq, recorded_at }, reasons: [] }
From an agent: the verify_tool_call MCP tool (free, no key) recomputes the digest for models that cannot hash. Over HTTP:
curl -s https://www.rightshold.com/v1/tool-calls/verify -H 'content-type: application/json' \
-d '{"id": "tc_…", "record": {"tool": "update_salary", "arguments": {"employee_id": "e-17", "amount": 98000}, "result": {"content": [{"type": "text", "text": "…"}]}, "salt": "…"}}'
The record sent to verify is hashed in memory and never stored.
What a receipt proves
The server presented this exact call record (tool, arguments and result, bound by SHA-256) for notarization at issued_at, and the record has not changed since.
It does not prove: That the tool's work was correct, that the server's own systems changed as the result says, or who operates the server.
Privacy and cost
- Rightshold receives the digest, your server's name and the tool name. The salt makes the public digest useless for guessing arguments.
- Verifying is free and needs no account. Each notarized call counts as one write against your account's monthly allowance; Rightshold credits have no cash value during the preview.
For implementers
The exact record format, canonical JSON rules and endpoints: GET /v1/tool-calls. Any language that can sort JSON keys and compute SHA-256 and Ed25519 can verify a receipt.
