Skip to main content
Glama

Belay

CI License: MIT Conformance: L3

PyPI badge intentionally omitted: belay-mcp is not published yet (see "Release status" below) — a badge pointing at a nonexistent PyPI project would 404, so it's left out rather than faked.

Safe, reversible tool execution for AI agents.

Belay is a transactional safety layer for AI agent tool calls, in two complementary modes: an MCP proxy that sits between an agent and its tool servers (mediated — Belay itself makes the call), and a Native Agent Gate (belay hooks install) that gates an agent's own native tool calls in place (observed — Claude Code makes the call, Belay decides whether it's allowed to). Either way, the goal is the same: turn "the agent can call anything" into "every tool call is declared, previewable, gated, and — when it goes wrong — reversible."

Status: v0.1.0 tagged, L3 conformance. E0-E9 (docs/plan.md) shipped the full lifecycle — contracts (§4), ledger (§9), the L1 proxy (§3, §4.6, Appendix C), planner + policy (§5, §6), approvals (§7), the saga executor (§8), and rewind (§10), diagrammed in docs/architecture.md. Eleven further entregas (E10-E20, docs/plan-v2.md) shipped past v0.1.0 without breaking L3 — see "What's new since v0.1.0" below (E18 is a first slice — Claude Code only, said plainly in its own section below; R1.6, see CHANGELOG.md, closed six correctness gaps in that gate). Passing on real Linux/macOS/Windows CI (not just one dev machine — see E19.7 below and the CI badge above for the current run), plus docs/traceability.md proving every normative MUST in the spec has a real test (CI-enforced, not a stale doc). The protocol is specified in docs/spec.md (Belay Specification 0.1). Branch coverage: 81.34% repo-wide, measured 2026-08-12 (fail_under = 81, CI-enforced floor against regressions — raised as more lands, never lowered silently; see ADR 0027). The spec-normative core stays high where it matters — contracts/ 92-100%, policy/ 88-100%, ledger/ 93-100%, rewind/ 87-94%, intent/ (scope enforcement) 79-100% — it's the newer adoption/DX modules (dashboard, explore, export-pr's git plumbing) pulling the global number down, not the safety-critical path.

Why

Agents that can delete, spend, or send are one bad plan away from an incident. Belay's answer isn't "trust the model more" — it's infrastructure:

  • Contracts (spec §4) declare, per tool, whether an action is reversible, irreversible, or conditional, and — if reversible — what the concrete undo call is.

  • Dry-run planning (spec §5) predicts effects (create, update, delete, send, spend, ...) before anything executes, honestly marking what's estimated versus exact.

  • Policy (spec §6) enforces blast-radius caps (row counts, spend limits, recipient counts, quiet hours) and picks the most restrictive verdict.

  • Human approval (spec §7) parks anything the policy pauses, with no-self-approval enforced structurally — an agent cannot approve its own action through any surface Belay exposes.

  • Saga execution (spec §8) commits actions as staged steps and materializes each compensation at commit time, so undo never re-evaluates live state.

  • Rewind (spec §10) runs compensations in reverse order and reports honestly what was undone, what's irreversible, and what's indeterminate — it never claims "fully rewound" unless that's true.

  • An append-only, hash-chained ledger (spec §9) makes every decision and every tool call independently verifiable and replayable.

No LLM sits on the safety path. Belay is deterministic end to end.

Related MCP server: MIDAS

How it fits

Agent (LLM) ──MCP──▶ Belay ──MCP──▶ tool servers
                       │
   contracts · policy · planner · approvals · saga executor · rewind
                       │
                event ledger (append-only, hash-chained)

See docs/architecture.md for the full diagram and docs/spec.md §3 for the normative request lifecycle.

Install

belay-mcp isn't published to PyPI/npm yet (see "Release status" below), so pip install belay-mcp / npx belay-mcp don't work for anyone but the maintainer. The one-line installers below install the exact same package straight from GitHub instead — same belay command, no registry needed — the same pattern rustup/deno/uv use before (or alongside) a package manager release:

# macOS / Linux / WSL
curl -fsSL https://raw.githubusercontent.com/Jairogelpi/belay-mcp/main/scripts/install.sh | sh
# Windows PowerShell
irm https://raw.githubusercontent.com/Jairogelpi/belay-mcp/main/scripts/install.ps1 | iex

Both scripts: find a Python 3.12+ interpreter, install with pipx if it's present (an isolated venv, belay on PATH — the standard way to install a Python CLI tool without touching any project's own environment) or fall back to pip install --user, then print the next command to run (belay bootstrap ..., see below). Piping a downloaded script into a shell requires trusting the source — read scripts/install.sh/scripts/install.ps1 first if you'd rather not; they're short and do nothing besides pip/pipx install from this repo.

Once published, pip install belay-mcp / npx belay-mcp ... will work directly — an npm wrapper (npm/) already exists for the latter; see "Release status" below. It pip installs the matching version under the hood and needs a Python 3.12+ interpreter on PATH.

For development:

git clone https://github.com/Jairogelpi/belay-mcp.git
cd belay-mcp
pip install -e ".[dev]"
pytest

One-command setup: belay bootstrap

belay bootstrap ./sandbox --command npx --arg -y \
  --arg @modelcontextprotocol/server-filesystem --arg ./sandbox \
  --client all

Runs draft-contracts (skip with --contracts <file> if you already have one), wrap, init against every known client (--client claude-desktop,claude-code,cursor,codex,opencode, or all), and upserts a standing-instruction block into ./AGENTS.md and ./CLAUDE.md telling whatever agent reads them to use Belay's MCP tools by default, without being told each session — those files are the one thing every agent actually re-reads automatically, so that's where a durable instruction has to live, not a one-off chat message. Safe to re-run: the block is idempotent (marked, replaced in place, never duplicated), and other content in those files is left untouched.

Registering Belay as an MCP server does not, by itself, mean an agent's every tool call goes through it — Claude Code/Cursor/Codex/OpenCode can still reach for their own native Bash/file-edit tools without touching MCP at all. The AGENTS.md/CLAUDE.md instruction is the durable nudge; a deterministic hook-based gate that actually intercepts native tool calls too is belay hooks install (E18), below.

Zero-config: belay connect (E22)

belay connect
# ... some time later, or on a different machine:
belay disconnect

The one-command version of the above for the single most common case — you already have Codex CLI and/or Claude Code CLI (and/or Claude Desktop) installed and want the current directory's files protected by Belay with no flags at all. belay connect, run with no arguments:

  • Detects which of Codex CLI, Claude Code CLI, and Claude Desktop are actually installed on this machine — at least one is required; it never invents a client that isn't there.

  • Generates a real, protected proxy for the current directory only — the Filesystem MCP server (@modelcontextprotocol/server-filesystem), pinned to the exact version this pack was verified against (packs/filesystem/pack.yaml) — not "whatever npx resolves today". No other upstream server is offered by connect; wrapping something else still means belay wrap/belay init (above) by hand.

  • Names the connection deterministically from the project directory (an ASCII slug of its basename plus an 8-hex-char hash of its full resolved path — see belay/cli/connection_models.py), so re-running connect in the same directory is idempotent and two different directories that happen to share a basename never collide. Override with --name.

  • Registers with each detected client through that client's own official CLI (codex mcp add ... -- ..., claude mcp add --scope user --transport stdio ... -- ...) — never by hand-editing ~/.codex/config.toml/~/.claude.json itself. Claude Desktop has no registration CLI, so it's the one exception: a surgical JSON merge of just mcpServers.<name>, leaving everything else in that file untouched.

  • For Claude Code specifically, also installs a project-scoped PreToolUse/PostToolUse hook at <project>/.claude/settings.json (not a global/user-scope hook) — the same Native Agent Gate belay hooks install provides, scoped to just this project.

  • Codex gets MCP-only protection — there is no Codex-side hook mechanism this integrates with (said plainly: Codex has no claimed native hook integration), so only tool calls that actually go through the registered MCP server are gated; Codex's own native Bash/file-edit tools are not.

  • Proves the exact command it's about to register actually works, twice: once before touching any client (a real MCP initialize/list_tools through the generated proxy), and once after, reading back each client's own recorded registration rather than assuming it matches.

  • Every tool call goes through the same append-only, hash-chained ledger (.belay/belay.db) as belay wrap/belay runbelay disconnect never deletes it, with or without --purge-runtime.

belay disconnect removes only the entries belay connect itself registered (compare-and-swap: an entry hand-edited since is left alone, reported, never silently overwritten) and leaves .belay/belay.wrap.json and .belay/belay.db in place unless you pass --purge-runtime, which still never touches the ledger database. belay doctor/belay repair understand this connection's health too — see their --help.

Quickstart

belay wrap examples/fs-server --contracts examples/contracts/fs.yaml
belay run &
# any standard MCP client now talks to Belay instead of fs-server directly:
# tools with a contract or readOnlyHint pass through, everything else is
# refused with contract_missing (spec §4.6) — logged to belay.db either way.
belay verify belay.db

Every command above was re-run against a clean checkout while writing this README; belay verify belay.db prints chain: OK / coherence: OK on an empty, freshly-wrapped ledger.

Demo

The 3-minute portfolio demo (spec-driven scenario in docs/plan.md §10) is a real, runnable script — not a mock:

python examples/demo.py         # bulk delete -> pause -> narrow -> approve -> execute -> rewind
python examples/demo.py --oops  # same, plus a wrong-filter mistake that rewind then undoes

It shells out to the real belay CLI (wrap, approvals list/approve, rewind --dry-run, rewind --by) and drives a real MCP session against examples/crm-mock, ending in chain: OK / coherence: OK and "session fully compensated" — genuine output, generated live each run.

belay approvals approve --narrow <filter> does not exist as CLI surface (documented gap, see ADR 0007 and ADR 0009); the demo's "narrowing" step is the equivalent E7 actually built and tested — the agent retries with a different, narrower filter, which is a new plan the human approves instead of the original one.

Recording: a self-contained VHS tape script (examples/demo.tape, fixed up in E23 — deterministic typing speed/timing, output path, no cd assumptions) is checked in for whoever has the vhs binary to render a GIF from:

vhs examples/demo.tape   # from the repository root -> docs/assets/belay-demo.gif

vhs was not available in either the sandbox E9 was built in or the environment E23 was built in, so docs/assets/belay-demo.gif still does not exist and nothing is embedded here yet. This is an honest gap, not a placeholder GIF — the tape itself was re-verified in E23 by running the exact command it types (python examples/demo.py --oops) directly, which produced real chain: OK / coherence: OK / "session fully compensated" output; only the actual VHS render step could not run. See ADR 0009 and ADR 0027's release-truth precedent for why this stays stated plainly instead of silently dropped or faked.

Advanced setup

Native Agent Gate: belay hooks install (E18)

belay hooks install --yes
# -> installed PreToolUse/PostToolUse hooks in .claude/settings.json
# -> approvals queued by this hook -- review with
#    belay hooks approvals list/approve/reject --db belay-hooks.db
# -> restart the agent for the hooks to take effect

A deterministic (no LLM), no-cold-start local supervisor gates an agent's native tool calls (Bash, file edits, and native MCP calls made outside belay run's own proxy) -- Claude Code only, first slice. Bash commands are classified against a narrow read-only allowlist (everything else pauses); native Edit/Write/NotebookEdit calls are allowed by default and captured for rewind (belay hooks rewind <event_id>); native mcp__server__tool calls always pause unconditionally, since they never pass through Belay's own contract-enforcing proxy. See the CHANGELOG's E18 entry for the full detail (supervisor authentication, the 8 P0s E18.1 hardening closed, and what's still UNKNOWN trust tier), and docs/security/threat-model.md for what this gate does and does not protect against.

belay supervisor status --db belay-hooks.db   # running -- listening on ...

Opt-in contract check (R1 first slices, ADR 0021): --contracts <file> makes native file edits resolve against a real ContractSet, the same way belay run's MCP proxy resolves a tool -- a tool with no matching contract denies (contract_missing) instead of the default allow. The same file also lets a declared, all-read native MCP tool (mcp__server__tool) auto-allow instead of always pausing -- anything not explicitly declared read-only still pauses exactly as before. Off unless you opt in. Note this resolves by the tool's literal Claude Code name (Write/Edit/NotebookEdit, or mcp__<server>__<tool> for native MCP calls) -- not a downstream MCP server's own tool names, so packs/filesystem/contracts.yaml (which declares read_file/write_file/ etc. for the proxy path) is not a valid argument here:

belay hooks install --contracts packs/claude-code-native/contracts.yaml --yes

That pack covers the three native file-edit tools. Auto-allowing a native MCP tool call is install-specific -- it needs a contract keyed by your server's actual name, e.g. a mcp__github__list_issues entry with an all-read effects list, added to the same contracts file.

Session fencing (R1 third slice, ADR 0022): belay hooks fence <host_session_id> closes a hook session to every surface -- Bash, file edits, native MCP -- the same durable, cross-process way belay rewind already fences an MCP session. No unfence; start a new session with the agent instead.

belay hooks fence s1 --host claude-code --db belay-hooks.db --yes

Per-OS-user quota (R1 fourth slice, ADR 0023): once an OS user has this many approved hook-gated actions within the window, a new pause-worthy action (Bash, native MCP, oversized file edit) denies outright instead of being queued -- an operator must intervene directly. Off unless you opt in:

belay hooks install --quota-max 20 --quota-window 1d --yes

Extra Bash allowlist (R1 fifth slice, ADR 0024; exact-match syntax added R1.6): add your own literal, safe commands to the built-in read-only allowlist -- not a PolicyEngine for Bash, just an extensible list of commands your project already trusts. Entries are checked after the same shell-metacharacter guard everything else is, so they can never become a chaining/redirection bypass. A bare entry also allows any trailing arguments (npm run lint allows npm run lint --fix too) -- append ! to require an exact match instead, since a trailing argument can turn a read-only command into a mutating one:

echo "npm run lint!" > my-safe-commands.txt   # exact match only -- `--fix` still pauses
belay hooks install --allowlist-extra my-safe-commands.txt --yes

Live conformance (E18.7): trust_tier="T1" for Claude Code's Bash surface

tests/hooks/test_live_conformance.py is a real, opt-in (spends real Anthropic API usage), pinned-version end-to-end suite against the actual installed claude CLI, confirming a denied Bash command's side effect genuinely never happens on disk. "T1" is earned by this suite existing and passing, not asserted ahead of the evidence -- every other surface (Edit/Write/MCP, and the Codex/OpenCode adapters) still honestly reports UNKNOWN. See docs/adapter-compatibility.md for the full per-host, per-surface matrix.

Wrapping a non-Python MCP server

belay wrap ./sandbox --contracts contracts/fs.yaml \
  --command npx --arg -y --arg @modelcontextprotocol/server-filesystem --arg ./sandbox

Verified action packs (E20)

packs/filesystem/ and packs/git/ are real, tested contract sets for the actual official @modelcontextprotocol/server-filesystem (npm) and mcp-server-git (PyPI) servers -- not the illustrative contracts/fs.yaml above:

belay wrap ~/projects/my-repo --contracts packs/filesystem/contracts.yaml \
  --command npx --arg -y --arg @modelcontextprotocol/server-filesystem --arg ~/projects/my-repo

See ADR 0019 for scope and known limitations per pack.

Registering with an MCP client

belay init --client claude-desktop --config belay.wrap.json
belay init --client codex,opencode --config belay.wrap.json

Merges a belay entry into the client's own config, in its own native format -- other MCP servers already configured are left untouched, and re-running is idempotent:

Client

File

Format

Claude Desktop

OS-specific (autodetected)

JSON mcpServers

Claude Code

.mcp.json (project root)

JSON mcpServers

Cursor

.cursor/mcp.json (project root)

JSON mcpServers

Codex CLI

.codex/config.toml (project root, default) or ~/.codex/config.toml (--scope user)

TOML [mcp_servers.<name>]

OpenCode

opencode.json (project root)

JSON mcp.<name>

belay init previews every file it would touch and asks one confirmation before writing (--dry-run to only preview, --yes to skip the prompt); belay doctor/belay uninstall/belay repair use a .belay-manifest.json alongside each config to know exactly what changed since install. belay detect/--client auto registers only clients actually installed on this machine. See the CHANGELOG's E19 entry for disable-bypass, hooks doctor --deep, repair, native binaries, and signed release bundles.

Drafting contracts from a live server

belay draft-contracts ./sandbox --command npx --arg -y \
  --arg @modelcontextprotocol/server-filesystem --arg ./sandbox \
  -o contracts_draft.yaml

Reads each tool's MCP readOnlyHint/destructiveHint and name (no LLM) and proposes a starting contract per tool. Every draft is provenance.verified: false -- review and correct each one before use.

Dashboard

belay dashboard --db belay.db -o dashboard.html

A static HTML snapshot of one ledger: every session's steps with their verdicts, and pending approvals with the exact command to resolve them.

What's new since v0.1.0

Seven of the eleven entregas that landed on top of the v0.1.0 lifecycle (docs/plan-v2.md, ADRs 0010-0018) are spec-numbered lifecycle extensions, listed below; the remaining four (E17-E20) are adoption/DX work, covered in the "Adoption/DX" subsection right after. Each is additive — none weakened an existing test or broke L3 conformance:

  • Statistical anomaly baselines (E10) — a per-session rolling mean/stddev (Welford's algorithm, no LLM, no manual threshold) pauses an action that's wildly outside its own session's normal pattern, even with zero Cap configured for that tool. examples/demo_anomaly.py.

  • Real SQL dry-run (E11) — instead of a declared estimate, a BEGIN ... ROLLBACK against the actual database reports the real affected-row count before a human approves anything, never committing. examples/demo_sql.py.

  • Counterfactual replay (E12) — belay counterfactual <session> --at-step N --override '{"verdict":"deny"}' answers "what would have happened if a human had decided differently here" entirely offline, from the ledger alone — never calling the real upstream, never touching the real session's chain. Honest by construction: it only ever reports unchanged, diverged (with the real basis), or unknown — never a fabricated concrete outcome. examples/demo_counterfactual.py.

  • Signed, offline-verifiable evidence (E13) — belay verify-export + belay verify-evidence produce and check an Ed25519-signed bundle that needs nothing but the file itself and a public key: no belay.db, no network. Tamper detection is precise (chain vs. signature vs. summary mismatch), not a single opaque pass/fail. examples/demo_signed_evidence.py.

  • Identity attribution (E14) — every session is bound to an explicit --initiated-by (and optional --on-behalf-of) identity, folded into E13's signature so forging who triggered a session is caught exactly like tampering with the ledger. examples/demo_attribution.py.

  • Per-identity irreversible-action quota (E15) — beyond E4's per-call caps, a rolling window limits how many irreversible actions one identity can accumulate, so "I approved this once" can't silently become "the agent did it 200 times." examples/demo_quota.py.

  • Blast-radius self-explanation (E16) — the governed response back to the agent itself (not just the human's CLI) carries a structured, template-filled explanation of why a call paused/was denied, with a deterministic suggested_action when one mechanically applies. In examples/demo_self_explain.py the agent reads its own explanation, narrows its request, and gets allow — with zero human approval step.

Adoption/DX (not spec-numbered — onboarding, not lifecycle)

Full detail and examples for each of these are in CHANGELOG.md's "Adoption/DX" section:

  • belay wrap --command/--arg launches any stdio MCP server, not just python server.py.

  • belay draft-contracts proposes a starting contract per upstream tool from its live MCP schema (see "Drafting contracts" above).

  • belay dashboard renders a static HTML snapshot of a ledger (see "Dashboard" above).

  • belay approvals list --triage sorts the pending queue highest-risk first — never approves or rejects anything itself.

  • Intent contracts (belay run --intent-contract <file>) mechanically enforce allowed_scope/forbidden_scope/forbidden_tools/ budgets.files_changed, hash-pinned from session start so belay export-pr can label a PR's "what was asked" as verified or not.

  • belay verify-test --runner pytest|jest|go independently runs a step's declared test instead of trusting the agent's own claim it passed.

  • belay causal <session> assembles a requirement → decision → test → undo graph straight from the ledger (--format mermaid).

  • belay rewind --intent/--keep undoes exactly one agent-tagged subgoal while keeping another.

  • belay learn <approval_id> compiles a human's rejection into a durable, mechanical IntentContract rule.

  • belay explore <session_id>... compares already-run session variants side by side — a table, not an LLM verdict.

  • belay export-pr packages a committed session's file changes as a real git branch + commit with signed evidence attached, and (with --intent-contract/--config) a proof-carrying PR body.

  • belay replay re-executes a real session against the live upstream with one step's args overridden, through the real governed lifecycle.

Roadmap

Built entrega by entrega per docs/plan.md; each closes a slice of docs/spec.md:

Entrega

Delivers

Spec sections

Status

E0

Repo scaffolding, CI, tooling

done

E1

Contracts + expression language

§4

done

E2

Event ledger

§9

done

E3

L1 proxy + CLI (first publishable milestone)

§3, §4.6, App. C

done

E4

Planner + policy engine

§5, §6

done

E5

Approvals

§7

done

E6

Saga executor

§8

done

E7

Rewind (closes L3 conformance)

§10

done

E8

Public conformance suite + example packs

§13

done

E9

Demo, docs, portfolio polish, v0.1.0 release

done (tag/PyPI pending, see below)

E10

Statistical anomaly baselines

— (plan-v2)

done

E11

Real SQL dry-run adapter

§5.3 (extended)

done

E12

Counterfactual replay

§9.4 (extended)

done

E13

Signed, offline-verifiable evidence

§9 (extended)

done

E14

Identity attribution

§9, §12 (extended)

done

E15

Per-identity irreversible-action quota

§6 (extended)

done

E16

Blast-radius self-explanation

§6, §7 (extended)

done

E17

Safe installer lifecycle — manifest, belay init --dry-run/--yes, belay uninstall, belay doctor, reinstall-idempotent and crash-safe (E17.1 hardening) — plus docs/traceability.md generator, CI-enforced

§8 (plan.md), adoption/DX

done

E18

Native Agent Gate: authenticated local supervisor (multiprocessing.connection, named pipe/Unix socket, fail-closed, bounded concurrency), belay hooks install, deterministic Bash risk classifier, context-bound approvals routed through the same ApprovalQueue as the MCP path. E18.1 hardening closed 8 P0s found in independent review: JSON wire format (not pickle), private off-project approvals storage, durable idempotency, full-context approval binding, belay-internal-path protection, honest trust_tier, Slowloris resistance, hard-kill recovery. E18.2: PostToolUse recording (exit code, duration, output digest) into a durable, hash-chained ledger — the same LedgerStore/belay verify as the MCP path, no new evidence format. E18.3: native Edit/Write/NotebookEdit capture-on-allow + content-addressed snapshot store + belay hooks rewind/list-edits, conflict-safe restore-or-delete compensation, oversized files pause instead of silently going uncaptured. E18.4: native mcp__server__tool calls pause and queue through the same ApprovalQueue (no free pass for a server merely named "belay" — this layer can't confirm a call actually reached belay's own proxy), reviewed via the new belay hooks approvals (hook-queued approvals live in the private belay home, not a literal --db file the top-level belay approvals opens); belay doctor now flags other MCP servers configured alongside belay as an ungated bypass route, belay-managed or not. E18.5: Codex adapter, normalize/render only (verified against the real installed codex-cli binary's own app-server JSON schema) — deliberately not wired to a live session, since Codex's approval mechanism is a bidirectional JSON-RPC protocol, not a one-shot hook subprocess; a real integration needs session proxy infrastructure this slice doesn't build, said plainly rather than claimed. E18.6: OpenCode adapter, normalize/render only, verified two ways against the real installed opencode-ai binary (an actually-installed third-party plugin's production usage, plus locating the literal tool.execute.before/.after trigger call sites inside the compiled bundle) — not wired live because OpenCode's hooks are in-process TS/JS plugin calls with no Python-reachable seam, a new language/packaging surface this slice doesn't build. Cursor: skipped outright — the installed cursor binary is only the GUI launcher CLI, with no way found to headlessly verify its actual agent hook payload shape, so no adapter was written against docs alone. E18.7: tests/hooks/test_live_conformance.py, TRUTH-004's pinned-version end-to-end bypass suite, opt-in only (spends real Anthropic API usage) — spawns the real installed claude CLI with belay's hooks actually installed, confirms a denied Bash command's side effect genuinely never happens on disk (not just "the model said so") while a safe one still works normally; claude_code_adapter._VERIFIED_TRUST_TIER is now "T1" for Claude Code's Bash surface specifically, because this suite exists and passed, not asserted ahead of the evidence

§7 (extended); FILE-001–008, ARCH-001–008, TRUTH-004/010 (ADR 0020, not docs/spec.md) (adoption/DX)

first slice — Claude Code Bash surface is T1-verified; Edit/Write/MCP surfaces and Codex/OpenCode adapters remain UNKNOWN; Cursor not attempted

E19

One-command lifecycle and exclusive routing. E19.1: belay detect/belay init --client auto — real binary-presence + version detection (shutil.which, best-effort --version) instead of blindly registering every client type regardless of whether it's installed; caught and fixed a real P0 during manual verification (a loop variable shadowing the --name parameter, which briefly mis-registered a real machine's Claude Desktop config under the wrong key before being caught and reverted from its own backup). E19.2: belay disable-bypass — the write half of E18.4's bypass detection, atomically removing one named non-belay MCP server entry from a client config, without attempting to auto-rewrap the removed server. E19.3: belay hooks doctor --deep — real reachability checks (interpreter exists and can import belay, supervisor genuinely reachable) instead of only comparing config-file hashes; opt-in since it's not purely read-only. E19.4: belay repair — detects every belay-managed registration gone BROKEN (MCP clients and hooks) and restores all of them in one command, reusing init's/hooks install's own atomic-write machinery. E19.5: standalone belay(.exe) binaries via PyInstaller (scripts/build_binary.py), built and smoke-tested on real Linux/macOS/Windows CI runners — ~500 MB, said plainly (heavy transitive deps via --collect-all, size-reduction is real follow-up work, not done here). E19.6: belay release sign/verify — Ed25519 authenticity signing of a release bundle (SHA256SUMS.txt + signature + public key), reusing E13's SigningKey/verify_signature; explicitly NOT OS-level code-signing/notarization (Authenticode/Apple notarization need a paid, identity-verified certificate this project doesn't have — Windows SmartScreen/macOS Gatekeeper will still warn regardless of this signature). E19.7: cross-platform-clean-room CI job — the fast suite on real ubuntu-latest/macos-latest/windows-latest runners, verified by CI actually passing there, not asserted from one dev machine

ARCH-007/008 (ADR 0020, not docs/spec.md §14, which has no subsections), adoption/DX

done

E20

Verified action packs — real, useful transactions, not just a framework. Scoped to Filesystem + Git (user's choice — GitHub/Odoo need real API credentials/an instance this environment doesn't have, and the spec's own exit criterion requires a real disposable service, not mocks). packs/filesystem/ and packs/git/ — real Contract sets (loaded via the existing belay/contracts/loader.py, no new loader) targeting the actual official @modelcontextprotocol/server-filesystem (npm) and mcp-server-git (PyPI) servers, hand-corrected past belay draft-contracts's naive heuristic (edit_file/move_file made properly reversible; create_directory fixed from a nonsensical draft undo to honestly irreversible, since the real server has no delete tool at all; write_file is conditional — reversible only if the file already existed). Git pack is smaller by real necessity, not by choice: mcp-server-git returns plain text only, and the contract expression grammar deliberately has no string-parsing capability, so most of its mutations (git_commit/git_create_branch/git_checkout) are honestly irreversible — only git_add (undone via the argument-free git_reset) is reversible. tests/packs/test_filesystem_pack.py/test_git_pack.py: E20's own exit criterion applied literally — a real multi-step saga against the real server, a real injected mid-saga failure, auto_compensate verified against real on-disk/on-repo state, plus a drift check that the pack's tool list matches what the real server actually advertises. pack.yaml per pack is metadata only — the full spec §11 packaging infrastructure (signed registry index, trust states, revocation, a pack-install CLI, an authoring SDK) is real, separate infrastructure work with its own hosting/trust-root decisions, not built here; both packs honestly declare trust_state: unverified. See ADR 0019

§11, adoption/DX

partial — Filesystem + Git packs done and tested against real servers; GitHub/Odoo packs and the full packaging/registry infrastructure not attempted

Conformance

Belay is L3 conformant (contracts + plans/policy/approvals + sagas/rewind, spec §13), verified by the belay-conformance suite:

belay-conformance run --target belay --level 3

belay-conformance is a separate, target-agnostic package: any MCP proxy that implements the ~6-method ConformanceTarget adapter can run the same suite against itself.

Beyond conformance, docs/traceability.md proves the narrower claim that every normative MUST in docs/spec.md has at least one real, named test — a hand-curated list of MUSTs cross-referenced against @spec("X.Y") markers on test functions, generated by scripts/traceability.py. CI runs the generator and fails the build if any MUST is left uncovered, so the claim can't silently rot into a stale doc.

How Belay compares

Belay isn't a gateway, an observability tool, or an enterprise workflow engine — it overlaps with pieces of each without being a drop-in replacement for any:

  • MCP gateways/routers (e.g. mcp-gateway, various vendor "MCP proxy" products) focus on auth, rate limiting, and routing across multiple MCP servers. Belay assumes you already have (or don't need) that layer and adds contract-based reversibility on top — its concern is "what happens if this specific call was a mistake", not multiplexing.

  • Observability/tracing for agents (e.g. LangSmith, Langfuse) record what an agent did after the fact. Belay's ledger (spec §9) is adjacent but exists to make actions governable and undoable, not to analyze prompts/latency/cost.

  • Enterprise workflow/saga engines (e.g. Temporal, AWS Step Functions) implement the saga pattern generally, for developer-authored workflows. Belay narrowly targets one thing: an agent-invoked MCP tool call, undone via a contract the tool integrator declares once — it is not a general orchestration engine and doesn't try to be.

Release status

v0.1.0 is tagged (git tag v0.1.0) and immutable, but it is a historical, incomplete release candidate: it contains package version 0.1.0.dev0, its PyPI Trusted Publishing workflow failed because Trusted Publishing was never configured (so it did not publish to PyPI), and it did not meet the historical global Definition of Done (§0) — 90% branch coverage and a clean-clone test run under 60 seconds. See ADR 0027 for the full record; it will not be moved, recreated, or force-updated to change that history.

main is currently ahead of v0.1.0 with E10-E20 (see "What's new since v0.1.0" above; E21-E23 are release/governance work — quality gates, zero-config connect, and this prerelease process itself — tracked in CHANGELOG.md rather than the entrega table, since they don't add spec-numbered product behavior). v0.2.0a1 is the planned next GitHub prerelease — see .github/workflows/release.yaml, docs/release-runbook.md (E23 — the exact pre-tag gate, immutable-tag recovery procedure, and GitHub settings rollout order), and CHANGELOG.md; it has not been cut yet, and no GitHub Release or PyPI publication should be assumed to exist until it is. When it is cut, the release will build source + wheel + Linux/macOS/Windows binaries from one immutable tagged commit, gated on nine required CI checks (scripts/release_preflight.py REQUIRED_CHECK_NAMES) all green on that exact commit first — see the runbook for the full procedure. main protection and mandatory checks are applied only after that first verified prerelease exists (E23 Task 9), not before.

An npm wrapper package (npm/, also belay-mcp) is written and locally verified but likewise not published to npm yet — same manual step, same reason.

Contributing

See CONTRIBUTING.md and AGENTS.md (rules for any human or AI agent working on this repo).

Security

See SECURITY.md for how to report a vulnerability and docs/security/threat-model.md for what Belay's Native Agent Gate and MCP proxy actually protect against.

License

MIT — see LICENSE. The specification text (docs/spec.md) is additionally available under CC-BY-4.0.

A
license - permissive license
-
quality - not tested
B
maintenance

Maintenance

Maintainers
Response time
Release cycle
2Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

  • A
    license
    A
    quality
    A
    maintenance
    Local zero-trust permission gateway for AI agents. Enforces policy-based tool authorization, human approvals, scoped permissions, and cryptographically verifiable audit logs.
    4
    5
    Apache 2.0
  • A
    license
    B
    quality
    A
    maintenance
    A governance proxy for AI tools — every MCP/agent tool call is policy-gated, secret-redacted, and written to a hash-chained, offline-verifiable audit trail.
    13
    MIT

View all related MCP servers

Related MCP Connectors

  • Runtime permission, approval, and audit layer for AI agent tool execution.

  • See, price, and control every tool call your AI agents make: policy checks, cost, and audit tools.

  • Git-backed platform for skills, tools, and context for AI agents

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Jairogelpi/belay-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server