notary-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@notary-mcplist drafts awaiting approval"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
notary-mcp
The human approves a hash, not a promise.
An MCP approval gate for agent actions. An agent proposes an action; the server normalizes it to canonical bytes and hashes them into a reviewed revision; a human sees a field-by-field manifest of exactly those bytes and approves that revision — and the agent may then execute only those bytes. Anything drifts by one character, the gate refuses.
Built for Push to Prod: Building at the Frontier (Bengaluru, 8 Aug 2026).
Why
Today, agent approval is a sentence in a chat window: the agent describes what it will do, you say yes, and then it does something — possibly not that thing, because it re-planned, or state moved underneath it, or a prompt injection rewrote its intent. The description you approved and the action that executes are not connected by anything.
notary-mcp makes consent content-addressed:
Canonical bytes. Every proposed action is normalized (RFC 8785 JSON canonicalization, NFC Unicode, surrogate rejection, whitespace collapse) so there is exactly one byte representation — nothing to smuggle in formatting.
A domain-separated hash of those bytes is the reviewed revision. The human's approval names this hash, not the draft, not the intent.
A complete manifest. The review step shows every leaf the hash covers — including values stored but hidden from the public, so approval is never blind.
Fail-closed execution.
execute_posttakes the revision as an argument. Wrong hash, drifted draft, superseded approval, consumed approval: refused. There is no code path from agent input to side effect that skips the human.Idempotency. Operations carry a client
operation_idplus a server-computed input fingerprint. Replays return the recorded result; the same id with different input is a terminal conflict. Retries can never double-fire.
The model explains; the hash authorizes. The model never gets to authorize.
Related MCP server: gomission-mcp
Quickstart (zero credentials)
git clone https://github.com/salmanneedsajob/notary-mcp
cd notary-mcp
npm install
npm run prove # 56 checks — every attack in the table below, refused
npm run demo # the full loop over a real MCP stdio connectionConnect it to Claude:
claude mcp add notary -- npx tsx /absolute/path/to/notary-mcp/src/main.tsThen ask Claude to draft a post with propose_post. Approve it yourself — the agent cannot:
npm run review # list drafts awaiting review
npm run review <draftId> # see the exact bytes + hash, approve y/NOnly after your approval does execute_post with that exact revision succeed. The published artifact — the approved canonical bytes — lands in out/<slug>.json.
The attack table
Every row is a check in npm run prove, run against the real executor and store:
Attack | Result |
Execute before any human approval |
|
Flip one hex character of the approved revision |
|
Edit the draft after approval, execute the old approved revision |
|
Execute the drifted bytes without fresh approval |
|
Replay a completed operation | Served from the record, |
Reuse an |
|
Publish again with a consumed approval |
|
Prompt injection inside a field ("IGNORE ALL PREVIOUS INSTRUCTIONS…") | It's data. Hashed like everything else. Still needs a human. |
Credential (bearer token) in the captured source |
|
Unicode smuggling (unpaired surrogates, formatting drift) | Rejected or normalized before hashing |
Two posts with identical titles | Distinct permanent slugs — published URLs are never reused |
Hidden-from-public field changed after review | Different hash — hidden values are inside the approval boundary |
Architecture
agent ──MCP──▶ server.ts ──▶ executor.ts ──▶ ports.ts ──▶ store.ts ──▶ out/
(contract (pure (5-function (JSON file;
boundary) orchestration) port) swap for any DB)
│
canonical.ts ───┤ RFC 8785 canonical JSON,
review.ts ──────┤ normalization, revision hash,
review-manifest ┘ input fingerprint, field manifest
human ──▶ scripts/review.ts (manifest + y/N → approval of one revision)src/contracts/v1.ts— the versioned tool contract. Strict validation schemas plus permissive transport schemas, so malformed input becomes a structuredINVALID_ARGUMENTresult instead of a protocol error.src/canonical.ts— canonicalization and the two hash frames (revision + operation fingerprint), both domain-separated and length-prefixed.src/executor.ts— pure orchestration. Every effect flows through the five-function port insrc/ports.ts; capability checks, rate ceiling, and retry classification live here.src/store.ts— the standalone port implementation (JSON file). In production this is a set ofSECURITY DEFINERPostgres functions; the executor cannot tell the difference, which is the point.The demo action is "publish a post" — the payload schema is an example. The gate pattern (propose → hash → human approves hash → execute exact bytes) applies to any consequential agent action: sending money, merging code, sending email.
Disclosure of prior work
This project comes out of Slopfolio, my invite-only portfolio platform for AI-assisted work, where this mechanism runs in production behind OAuth with a Postgres transactional boundary.
Existed before the hackathon: the underlying capture gateway — the canonicalization scheme, revision-hash and fingerprint framing, the V1 tool contract, the executor orchestration, the review-manifest builder, and the pure-logic checks in prove-contract.ts / prove-executor.ts (lifted from that codebase, renamed).
Built during Push to Prod: the hackathon work spans Slopfolio and this repo. On the Slopfolio side: the agent verification system built on top of the gate, and taking the MCP live for the public. This repo is the third piece — the standalone extraction of the gate mechanism, so it can be run and verified independently of Slopfolio: cutting it free of Supabase/OAuth/Next.js into a product-agnostic gate; the persistence port (ports.ts) and standalone store (store.ts); the stdio server (main.ts); the human review CLI (scripts/review.ts); the end-to-end adversarial gate suite (scripts/prove-gate.ts); and the wire demo (scripts/demo.ts).
License
MIT
This server cannot be deployed
Maintenance
Related MCP Connectors
Human-in-the-loop review and approval for AI agents. Audit trail, approval policies, native MCP.
Zero-secret MCP gateway for AI agents: risk-scored, audited calls with human-in-the-loop approval.
Pre-execution policy gate for consequential agent actions with durable trust receipts.
Human-in-the-loop approval for agent actions, with verifiable action-bound receipts.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceHuman-in-the-Loop authorization gateway for AI Agents. Securely pause MCP workflows and route high-risk actions to human approvers via Slack or Email.64 npm1MIT
- AlicenseNot gradedqualityCmaintenanceMission MCP is a Trust Graduation gate for AI agents, with visible approval ceremonies and receipt-backed boundaries for consequential actions.MIT
- AlicenseAqualityDmaintenanceEnables MCP-capable agents to gate their actions behind human consent, checking consent rules and requesting approval via Telegram before proceeding with high-stakes operations.218 npmMIT
- FlicenseNot gradedqualityBmaintenanceProvides a secure MCP boundary for AI agents, intercepting and validating tool calls, redacting secrets, and requiring human approval for sensitive actions with a tamper-evident audit trail.-