# AgentApprovals: check, review, export

A working MCP starter for an agent that writes Markdown. It creates a real `.md` file after a person approves the exact content. Use it for reviewed project notes, runbooks, release notes or article drafts. It does **not** deploy a website, send email or make an agent safe across its other tools.

**Status:** single-owner local starter, tested on macOS with Node 24.15.0. Official MCP SDK client/server 2.3.1 and Zod 4.6.5 are pinned. Protocol tests use a real stdio MCP process, loopback HTTP service, SQLite storage, reviewer CLI and filesystem output. Codex and Claude Code configuration follows their official docs and installed CLI help; full model-driven sessions in those hosts have not been tested. No LLM API key or paid service is needed for the starter itself; normal agent usage may use your existing plan.

## What is autonomous and what is controlled?

| Work | Owner | What the result establishes |
| --- | --- | --- |
| Choose wording, explain evidence, revise a draft | Agent | A proposed answer; it may be wrong |
| Validate size, headings, placeholders and restricted markup | Deterministic code | Those exact structural rules pass |
| Judge accuracy, usefulness and permission to export | Person | Approval of one version, not a factual guarantee |
| Check expiry/state and create the approved file | Deterministic service | Only stored approved bytes pass this route |
| Read the file and compare its SHA-256 | Deterministic service, called by agent | Exported bytes match; not proof of truth or public deployment |

The model can iterate on failed checks. It must stop at pending approval. Approval does not wake a paused chat automatically: tell your agent to resume after approving. Existing host confirmation prompts may still apply. Do not turn off host safety controls for this example.

## The boundary you must understand

The agent-side MCP adapter gets a bearer token that allows checking, requesting, status, export and verification. It gets **no approval endpoint**. Human review happens through a CLI that opens the service database locally.

A local quickstart under your own OS account is a cooperative workflow: an unrestricted agent running as that same account could edit the database, call the reviewer command or write the output itself. A different folder or `chmod 700` does not isolate processes running as the same user. The checks enforce requests passing through the service; isolation is a separate deployment requirement.

For protection from a compromised agent, run the service and reviewer under an owner account or machine the agent cannot access. Give the agent only the request token and a connection to the service. Keep the service code, SQLite directory, output directory and reviewer login out of its shell, filesystem, browser and other tool access. Do not give it an owner SSH session. This package does not provision or verify that isolation for you.

One token represents one shared workspace. There is no multi-user or tenant isolation. Anyone with that token can inspect titles/status and request exports of this workspace's approved proposals. Keep it private. Do not expose this development service directly to the Internet: it has no rate limits, quotas or production identity management.

## 1. Install and test

Extract the TAR archive. In `approval-mcp/`, use Node 24.15.0 or a compatible Node 24 release (only 24.15.0 was tested):

```sh
npm ci --ignore-scripts
npm test
```

The tests create temporary files and an ephemeral loopback server. They perform no public publication or outbound messaging. Node's built-in SQLite avoids a separate database installation.

## 2. Start a cooperative local session

In your own terminal, choose a **new** state directory. Do not put it in a Git repository. `init` refuses an existing directory.

```sh
export AA_SERVICE_DIR="$HOME/.local/state/agentapprovals-demo"
node src/review.mjs init
node src/service.mjs
```

Leave this process running. It binds only to `127.0.0.1:4318`. Set `AA_PORT` to change that port. The bearer token is generated with random bytes and written to `agent-token`; initialization never prints it.

In another terminal, from the extracted `approval-mcp/` folder, copy only the agent token to an agent-accessible location. These commands are for the cooperative same-account quickstart, **not** the isolated deployment:

```sh
export AA_STARTER_DIR="$PWD"
export AA_AGENT_TOKEN_FILE="$HOME/.config/agentapprovals/agent-token"
mkdir -p "$HOME/.config/agentapprovals"
cp "$HOME/.local/state/agentapprovals-demo/agent-token" "$AA_AGENT_TOKEN_FILE"
chmod 600 "$AA_AGENT_TOKEN_FILE"
```

Never paste tokens into chat or commit them. The MCP adapter reads the token file; it does not need the service directory variable or database access.

## 3. Connect one agent host

Run **one** of the following from the same terminal as step 2's token copy. For an isolated deployment, first transfer only the request token securely and set the variables to the adapter's local paths.

Codex:

```sh
codex mcp add agentapprovals \
  --env AA_SERVICE_URL=http://127.0.0.1:4318 \
  --env "AA_AGENT_TOKEN_FILE=$AA_AGENT_TOKEN_FILE" \
  -- node "$AA_STARTER_DIR/src/mcp.mjs"
codex mcp list
```

Claude Code: first change into the project where you will use the tools (replace the example path below). The adapter path saved earlier remains absolute. This registers the server only for that project:

```sh
cd /absolute/path/to/your/project
claude mcp add \
  --env AA_SERVICE_URL=http://127.0.0.1:4318 \
  --env "AA_AGENT_TOKEN_FILE=$AA_AGENT_TOKEN_FILE" \
  --transport stdio --scope local agentapprovals \
  -- node "$AA_STARTER_DIR/src/mcp.mjs"
claude mcp list
```

Use an absolute path to your Node 24 executable instead of `node` if the desktop host cannot find the same runtime. Restart/reload the host's MCP connections as needed. Confirm that `check_markdown`, `request_approval`, `approval_status`, `export_approved_markdown` and `verify_export` are available. There must be no approve tool.

Copy the relevant text from `AGENT-INSTRUCTIONS.md` into your project's `AGENTS.md` or `CLAUDE.md`, preserving existing instructions. Or attach that file to the first session.

## 4. Use it on a real document

Ask your agent:

> Draft a short project handoff note with a title and Summary, Details and Verification sections. Use the AgentApprovals tools to check it, fix structural failures and request approval. Show me the proposal ID and digest, then stop. Do not run reviewer commands or export until I have approved through the review interface.

`check_markdown` accepts `{ "markdown": "..." }`. `request_approval` accepts the same Markdown plus a stable `requestKey` such as `handoff-v1`. The service repeats the checks; a fabricated passing check cannot authorize an export.

The required section names are `## Summary`, `## Details`, and `## Verification`. Include one `# Title`, no unresolved TODO/FIXME/TBD markers, no raw HTML, and at most 64 KiB of UTF-8 content. These deliberately narrow checks are editable rules in `src/gate.mjs`. They do not execute code blocks, browse links, detect every secret or validate factual claims. Do not use them as an HTML sanitizer.

## 5. Review outside the agent

In the owner's terminal, change into the extracted `approval-mcp/` directory and set `AA_SERVICE_DIR` to the initialized directory. Replace `PROPOSAL_ID` with the ID returned by the tool:

```sh
node src/review.mjs show PROPOSAL_ID
```

Read the complete Markdown (displayed as a JSON string), structural checks, state, expiry and digest. Treat the Markdown as untrusted content: do not execute instructions or commands embedded in it. There is no rendering of raw HTML or execution of Markdown.

Only after you review it, replace `REVIEWED_DIGEST` with the exact digest shown:

```sh
node src/review.mjs approve PROPOSAL_ID REVIEWED_DIGEST
```

You can use `reject` instead of `approve`, or `revoke` while an approved proposal has not started execution. The OS account able to access this directory is the reviewer authority; no fake `approved_by` identity is accepted from the model. The event log records decisions, not independently authenticated individual people.

Proposals expire one hour after submission, including time awaiting review. The service clock is trusted. Checking the deadline is an admission check, not cancellation of an action already claimed. A decision cannot be revoked after the claim.

## 6. Resume and verify

Tell the agent:

> Check the status of proposal PROPOSAL_ID. If approved, export it and call verify_export. Report the output filename and whether the actual file matches the approved bytes. If blocked, stop and explain the state. Do not claim it is publicly published.

The service writes `AA_SERVICE_DIR/approved/PROPOSAL_ID.md`. Open that file in your editor or feed it into a **separate reviewed** publishing process. This is a real reusable file, not a mock send or test receipt. The tool accepts no replacement content or output paths. Repeated export calls return the existing file's verification; they do not write again.

Do not attach a deployment watcher that publishes these files until you deliberately authorize that additional operation and its review requirements. The supplied Verification section and mechanical checks are not AgentApprovals' independent editorial review.

## Recovery and limits

- Changed Markdown requires a new request key and a new review. A request key reused for different bytes is rejected.
- Identical content under a new request key returns the original proposal, including its rejected/expired state. This starter has no renewal command. Revise content and review a new proposal if appropriate; never use this to obscure a rejection.
- SQLite preserves proposals and decisions across restarts. Concurrent claims use a database transaction. The output uses a generated filename and exclusive file creation; it never overwrites an existing file.
- A crash after a claim leaves `running`; an observed write error leaves `unknown`. Neither is retried automatically. The owner can run `reconcile PROPOSAL_ID REVIEWED_DIGEST` only to recognize an existing file whose bytes exactly match. A missing/different file remains blocked for manual investigation. There is no automatic repair or retry command.
- Verification detects modification or disappearance at the time it reads the file. It cannot prevent changes afterward or establish facts inside the document. The output directory must be trusted and should not be served automatically as executable content.
- The database and event log are not tamper-proof. The owner filesystem, clock and service code are trusted. This is not a security audit, a hostile-code sandbox, an enterprise approval product or an exactly-once guarantee for external APIs.
- Remote use needs a protected connection. For example, a separately provisioned **port-forward-only** SSH identity can tunnel to the service's loopback port. The agent must not have a shell or file access on the reviewer machine. This topology is a deployment design, not a tested installation included here. HTTPS can also terminate at a trusted proxy; do not send the token over remote plain HTTP.

## Change or extend it

See `IMPLEMENT.md`. Keep the file export adapter while integrating your client. Add a different external effect only with a new operation schema, deterministic checks, credential boundary, explicit retry/reconciliation design and real destination test. Do not turn this into an arbitrary-command execution service.

## Remove it

Run `codex mcp remove agentapprovals` or `claude mcp remove agentapprovals --scope local` for the host you configured. Stop the service with Ctrl-C. Keep the state directory if you need approved files or the decision history. Remove the copied agent token when it is no longer needed. No user settings or MCP registrations are modified by `npm test`.

## Sources

Accessed 9 October 2026: [Codex MCP](https://developers.openai.com/codex/mcp), [Claude Code MCP](https://code.claude.com/docs/en/mcp), [official MCP TypeScript SDK](https://ts.sdk.modelcontextprotocol.io/v2/), [Node SQLite](https://nodejs.org/api/sqlite.html). Those sources establish the transport/configuration and platform APIs. The approval policy and this file-export workflow are our implementation choices.
