oathline-guard
An AI agent's yes means exactly what the person saw.
oathline-guard sits between your MCP client (Claude Desktop, Claude Code, Cursor) and a real stdio MCP server. Reads you list pass straight through. Every other tool call waits: a person, in a second terminal, sees the exact call in the guard's own words and types its four-character code. Then it runs once, with exactly those arguments, and Oathline's hash-chained log records what was shown, heard, confirmed and run.
In April 2026 a coding agent deleted a production database and its backups in nine seconds, with no approval step. This is the same request, through the guard:
db / drop_database
name: prod
code: 44NF
Type the code to run it, or n to refuse: n
Refused and recorded (c-508e50ec). Nothing will run.
The agent only ever gets "Not run ... waiting for a person's yes"; the code is never sent to it, so it cannot approve itself.
Quickstart (about 5 minutes, with the demo database)
1. Install (Python 3.10 or later):
pip install oathline-guard
2. Prepare the demo:
oathline-guard demo
It writes the read list (the tools that pass without a yes) to ~/oathline-guard-demo/reads.json:
{"server": "db", "reads": ["list_databases", "list_tables"]}
and prints the block for Claude Desktop with your machine's real paths (it runs everything through the Python you installed
with, so PATH and python vs python3 do not matter). It approves nothing and runs nothing.
3. Add the block to Claude Desktop. Open claude_desktop_config.json (Settings > Developer > Edit Config; on macOS it is
in ~/Library/Application Support/Claude/, on Windows in %APPDATA%\Claude\), paste the printed block, then restart Claude
Desktop. By hand, the block has this shape:
{
"mcpServers": {
"db": {
"command": "oathline-guard",
"args": ["run", "--server", "db", "--reads", "/path/to/reads.json", "--human", "you",
"--", "python", "-m", "oathline_guard.demo.fake_db", "--state", "/path/to/db.json"]
}
}
}
oathline_guard.demo.fake_db is a tiny database server with two reads and two writes (drop_database, delete_backups) and
no protection of its own. Claude Desktop starts it with a short PATH: written by hand, if oathline-guard is not found, use
the full path that pip printed, and if python is not found (on macOS often only python3 exists), write python3 or the
interpreter's full path. The block oathline-guard demo prints avoids both.
4. Open the approve window in a second terminal and leave it open:
oathline-guard approve --server db
5. Ask Claude: "clean up the old database". The reads run. The drop waits, and the approve window shows it as above: type
n to refuse, or the code to let it run once. After a yes, the agent's next identical call runs it; a call with different
arguments is a new request that waits again.
To guard a real server, put its own command after -- and list its read-only tools in reads.json. Tools not on the list
always wait, whatever the server's own annotations (readOnlyHint) say.
What it guarantees, and what it does not
- Reads on your list pass untouched; everything else is held, and the real server is not called until the yes.
- The words in the window are the guard's: the tool and every argument, never the server's description. A value cannot add a
line of its own; long values are shown whole and bound by hash, up to 16,000 characters of window text and Oathline Core's
8 KB cap on a call's items. A call beyond either is refused before anything is shown (
text_too_large,items_too_large): split it. - The yes runs the stored arguments exactly once. A wrong code decides nothing; a no is recorded, and a later yes is too late.
- The approve window reads only from a terminal: an agent's shell tool cannot pipe the code in.
Limits, said plainly:
- A pseudo-terminal passes the terminal check. It stops a pipe, not a determined local process with a pty.
- An agent with a raw shell or a raw token goes around any MCP guard. The guard governs the calls that pass through it; switch off the client's own write tools for the session if the guard must be the only way to act.
- No timeout on an upstream call yet, and the agent retries after the yes (no waiting notification yet). On Windows, when
the upstream is started through a virtual environment's
python.exe(a launcher that holds the pipes), an upstream that closes its output but keeps running is noticed only when it exits. - stdio servers only; one person per guarded server; the terminal is the only approval channel.
- Secret arguments are not masked yet: a secret in an argument is shown in the window.
- Heard is not understood: the record proves the words were shown and answered, not that they were read.
The record
Each guarded server keeps its own folder (default ~/.oathline-guard/<server>/) with Oathline Core's audit chain and token
store. The chain for one call reads REQUEST AUTHORIZED PROPOSED_BY_AGENT SHOWN HEARD CONFIRMED_BY_HUMAN EXECUTING EXECUTED, or
ends at REFUSED_BY_HUMAN; verify() on the audit log checks every link.
Licence
MIT. Copyright (c) 2026 Nexxt Nest Group Pty Ltd. Built on Oathline Core.
Metadata
Release files for oathline-guard 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| oathline_guard-0.1.0.tar.gz | 26.6 kB | Without provenance | Details for oathline_guard-0.1.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| oathline_guard-0.1.0-py3-none-any.whl | Python 3 | none | any | Details for oathline_guard-0.1.0-py3-none-any.whl |
Total release size: 55.0 kB
Release files / oathline_guard-0.1.0.tar.gz
| Download URL | oathline_guard-0.1.0.tar.gz |
|---|---|
| Size | 26.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
916d7f04a1dbf0bf4a17bf54c998affc1944180ecc75949d385d44fb22491611
|
|
BLAKE2b-256 checksum How to use checksums |
1f1fd969a062e320decc13440da2178f17290a85f76bdbefbdcb0485314c590c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Release files / oathline_guard-0.1.0-py3-none-any.whl
| Download URL | oathline_guard-0.1.0-py3-none-any.whl |
|---|---|
| Size | 28.4 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a38b95a17dac191884479674b7aba920e6708ed08032ecf89384cac779e863e0
|
|
BLAKE2b-256 checksum How to use checksums |
07330d336c05f5dc0172430064822cdc02eca8a96e07b13b2915c43f5bece2e2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/7.0.0 CPython/3.12.0
|
Security for oathline-guard 0.1.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.