AgentStack
MCP verified Apache-2.0 Self-run

Diplomat Gate

mcp-diplomat-ai-diplomat-gate · by Diplomat-ai

Runtime approval gates for AI agent tool calls. Intercept payments and emails before execution

No reviews yet
0 installs
18 views
0.0% view→install

Install

$ agentstack add mcp-diplomat-ai-diplomat-gate

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Diplomat Gate? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

diplomat-gate

[](https://pypi.org/project/diplomat-gate/) [](https://pypi.org/project/diplomat-gate/) [](https://github.com/Diplomat-ai/diplomat-gate/blob/main/LICENSE) [](https://github.com/Diplomat-ai/diplomat-gate/actions/workflows/ci.yml)

> The enforcement layer for AI agents. > > Deterministic. No LLM. Hash-chained audit. Works with OpenClaw, browser-use, LangChain, OpenAI Agents SDK.

Your AI agent just emailed your insurance company. You didn't ask it to.

An AI assistant inferred the claims address from a document the user uploaded and sent a legal rebuttal — autonomously, without confirmation. Nothing in the framework stopped it.

This is what happens without a deterministic policy layer. Here is the fix:

pip install "diplomat-gate[yaml]"
# 10 lines of YAML
policies:
  - id: email.domain_blocklist
    blocked: ["*@lemonade.com", "*@*insurance*", "*@*legal*"]
    on_fail: STOP
  - id: email.rate_limit
    max: 2
    window: 1h
    on_fail: REVIEW
audit:
  enabled: true
$ python demos/openclaw/run.py --ci

SCENARIO 1 — OpenClaw agent, no diplomat-gate
  Emails sent without approval : 1
  Recipient                    : claims@lemonade.com
  🔥 Legal email sent to insurance company without user approval.

SCENARIO 2 — Same agent, behind diplomat-gate
  Verdict: STOP
    - email.domain_blocklist: Domain 'lemonade.com' is on the blocklist
  🛡  Email blocked before reaching the SMTP server.
  Emails actually sent: 0

  to: alice@example.com               Verdict: CONTINUE
  to: bob@example.com                 Verdict: REVIEW  (email.rate_limit)

SCENARIO 3 — Every verdict is hash-chained
  $ diplomat-gate audit verify
  OK: chain valid (3 record(s) checked)

No API key. No Docker. No setup. Run it yourself: python demos/openclaw/run.py


The problem

AI agents call APIs with real-world side effects — send email, charge a card, delete files, POST to a webhook — and most orchestration frameworks treat hard enforcement as the operator's responsibility. In practice that means there is none.

Running diplomat-agent scan on a typical agent codebase:

$ diplomat-agent scan ./my_agent

Scanning for tool calls with external side effects...

  email.send            12 call sites    0 / 12 have a deterministic policy check
  payment.charge         4 call sites    0 /  4 have a deterministic policy check
  files.delete           3 call sites    0 /  3 have a deterministic policy check
  webhook.post           2 call sites    0 /  2 have a deterministic policy check
  browser.navigate       7 call sites    0 /  7 have a deterministic policy check

  28 call sites with side effects found.
   0 are protected by a deterministic policy layer.

Recommended: diplomat-gate

diplomat-gate is the missing deterministic policy layer. It intercepts calls, evaluates them against a YAML policy file, and returns CONTINUE / REVIEW / STOP before execution — in ~20 µs for a single-policy evaluation, ~500 µs mean for a 5-policy set, with no LLM call, no network request.

Works with every framework

diplomat-gate is framework-agnostic. Any call that can be represented as a Python dict works out of the box. Adapters for popular SDKs are included.

| Framework | Integration | How | |---|---|---| | OpenClaw | ✓ dict API | gate.evaluate({"action": ..., ...}) | | browser-use | ✓ dict API | gate.evaluate({"action": ..., ...}) | | LangChain | ✓ built-in adapter | from diplomat_gate.adapters.langchain import gated_tool | | OpenAI Agents SDK | ✓ built-in adapter | from diplomat_gate.adapters.openai import filter_allowed | | Any Python agent | ✓ dict API | if it calls an API, it can be gated |

Also works with Anthropic tool_use, CrewAI, AutoGen, PythonClaw, and any agent framework that exposes dict-like tool calls.

What's new in 0.3.0

  • Reproducible OpenClaw demopython demos/openclaw/run.py shows

the insurance email incident in under 60 seconds, no API key needed.

  • Release validation pipelinescripts/validate_release.py runs a

14-step gate: lint → tests → benchmarks → build → install → smoke → demo.

What's new in 0.2.0

  • Hash-chained audit trail — every verdict is sealed with a SHA-256

record hash that links to its predecessor. Tampering with a historical row breaks the chain and is detected by diplomat-gate audit verify.

  • Review queue — REVIEW verdicts are auto-enqueued in a separate

SQLite database. Operators approve or reject from the CLI or programmatically. Pending → approved / rejected / expired lifecycle is enforced server-side.

  • Adapters for OpenAI tool calls, Anthropic tool_use blocks, and

LangChain-style tools — duck-typed, no SDK import required.

  • CLI (diplomat-gate audit verify | rebuild-chain,

diplomat-gate review list | show | approve | reject).

  • Sensitive field redaction by default in audit and review storage

(recipient, to, email, domain, amount, card_last4, phone).

  • 8 runnable examples under examples/, CI matrix across

Python 3.10–3.13 × Linux / Windows / macOS, microbenchmarks under benchmarks/.

See the full [CHANGELOG.md](CHANGELOG.md).

60-second setup

# gate.yaml
version: "1"

audit:
  enabled: true
  path: "./diplomat-audit.db"

review_queue:
  enabled: true
  path: "./diplomat-review.db"

payment:
  - id: payment.amount_limit
    max_amount: 10000
    on_fail: STOP

email:
  - id: email.domain_blocklist
    blocked: ["*.banque-*.fr", "*.gouv.fr"]
    on_fail: STOP
from diplomat_gate import Gate

gate = Gate.from_yaml("gate.yaml")

verdict = gate.evaluate({"action": "charge_card", "amount": 15_000})
# verdict.decision  -> Decision.STOP
# verdict.violations -> [Violation(policy_id="payment.amount_limit", ...)]
# verdict.latency_ms -> ~0.05

How it works

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor': '#FFFFFF',
  'primaryTextColor': '#0F172A',
  'primaryBorderColor': '#CBD5E1',
  'lineColor': '#64748B',
  'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
}}}%%

flowchart LR
    A["AI agentLangChain · OpenClawbrowser-use · OpenAI SDK"]
    B{{"diplomat-gatepolicy evaluation"}}
    C["Real-world actionStripe · SendGrid · DB"]
    D["Blockedexception raised"]
    E["Review queuehuman approval"]
    F[("SHA-256hash-chainedaudit trail")]

    A -->|"tool call"| B
    B -->|"CONTINUE"| C
    B -->|"STOP"| D
    B -->|"REVIEW"| E
    B -.->|"every verdict"| F

    style A fill:#F8FAFC,stroke:#CBD5E1,stroke-width:1.5px,color:#0F172A
    style B fill:#4F46E5,stroke:#312E81,stroke-width:2px,color:#FFFFFF
    style C fill:#10B981,stroke:#047857,stroke-width:1.5px,color:#FFFFFF
    style D fill:#EF4444,stroke:#991B1B,stroke-width:1.5px,color:#FFFFFF
    style E fill:#F59E0B,stroke:#B45309,stroke-width:1.5px,color:#FFFFFF
    style F fill:#F1F5F9,stroke:#64748B,stroke-width:1.5px,color:#334155

No LLM calls. No network requests. Pure deterministic evaluation. Each verdict produces a Receipt with a SHA-256 hash of the canonical tool call.

Decorator API

from diplomat_gate import Blocked, Gate, NeedsReview, configure, gate

configure(Gate.from_yaml("gate.yaml"))

@gate(action="charge_card")
def charge(amount: int, customer_id: str) -> dict:
    return stripe.charges.create(amount=amount, customer=customer_id)

charge(amount=500, customer_id="cus_123")          # CONTINUE -> normal return
charge(amount=50_000, customer_id="cus_123")       # STOP    -> raises Blocked
  • CONTINUE: function executes, returns its normal value.
  • STOP: raises Blocked with the full Verdict attached.
  • REVIEW: raises NeedsReview; if review_queue is enabled, the

call is also persisted for an operator to approve/reject.

Validate your gate.yaml

Catch typos and misconfigured policies before deployment:

diplomat-gate validate gate.yaml
# OK: 8 policies loaded, 0 errors, 0 warnings

Use --json for CI integration:

diplomat-gate validate gate.yaml --json --output report.json --quiet
echo $?  # 0 if valid, 1 if errors, 2 if I/O error

See [docs/cli.md](docs/cli.md) for full flag reference and the JSON schema.

Audit trail

Every verdict is recorded in an append-only SQLite log with a SHA-256 hash chain. The chain is tamper-resistant: accidental corruption and post-hoc row edits are detected by the verifier. Note that rebuild_chain() can recreate a valid chain over tampered data — an attacker with write access to the .db file is not stopped by the chain alone. For non-repudiation, ship records to a write-once store.

%%{init: {'theme':'base', 'themeVariables': {
  'primaryColor': '#FFFFFF',
  'primaryTextColor': '#0F172A',
  'primaryBorderColor': '#CBD5E1',
  'lineColor': '#64748B',
  'fontFamily': 'ui-sans-serif, system-ui, sans-serif'
}}}%%
flowchart LR
    V1["Verdict N-1hash: abc…"]
    V2["Verdict Nhash: SHA-256(canonical+prev)"]
    V3["Verdict N+1hash: def…"]
    DB[("SQLiteaudit.db")]

    V1 -->|"prev_hash"| V2
    V2 -->|"prev_hash"| V3
    V1 & V2 & V3 --> DB
    DB -->|"audit verify"| OK["✓ chain intact"]

    style V1 fill:#F1F5F9,stroke:#CBD5E1,color:#0F172A
    style V2 fill:#4F46E5,stroke:#312E81,color:#FFFFFF
    style V3 fill:#F1F5F9,stroke:#CBD5E1,color:#0F172A
    style DB fill:#F8FAFC,stroke:#94A3B8,color:#334155
    style OK fill:#D1FAE5,stroke:#6EE7B7,color:#065F46
diplomat-gate audit verify        --db ./diplomat-audit.db
diplomat-gate audit rebuild-chain --db ./diplomat-audit.db   # one-shot recovery

Sensitive parameters in violation contexts (recipient, to, email, domain, amount, card_last4, phone) are redacted to h: before persistence. See [docs/audit-trail.md](docs/audit-trail.md) for schema, threat model, and migration from 0.1.x.

Review queue (human-in-the-loop)

A REVIEW verdict is enqueued automatically in a separate SQLite database when review_queue.enabled is true.

diplomat-gate review list    --db ./diplomat-review.db
diplomat-gate review show    --db ./diplomat-review.db --id 
diplomat-gate review approve --db ./diplomat-review.db --id  --reviewer alice
diplomat-gate review reject  --db ./diplomat-review.db --id  --reviewer alice --note "..."

See [docs/review-queue.md](docs/review-queue.md).

Adapters

Bring-your-own LLM SDK. Adapters are duck-typed — installing the SDK is not required to use them.

from diplomat_gate.adapters.openai    import filter_allowed as openai_filter
from diplomat_gate.adapters.anthropic import filter_allowed as anthropic_filter
from diplomat_gate.adapters.langchain import gated_tool

# OpenAI
allowed, review, blocked = openai_filter(gate, response.choices[0].message.tool_calls)

# Anthropic
allowed, review, blocked = anthropic_filter(gate, response.content)

# LangChain
safe_tool = gated_tool(my_langchain_tool, gate)

See [docs/adapters.md](docs/adapters.md).

Payment policies

| Policy | What it checks | Config | |---|---|---| | payment.amount_limit | Single transaction cap | max_amount: 10000 | | payment.daily_limit | Cumulative daily spend | max_daily: 50000 | | payment.velocity | Max transactions per window | max_txn: 20, window: 1h | | payment.duplicate_detection | Same amount + recipient within window | window: 5m | | payment.recipient_blocklist | Block specific recipients (glob) | blocked: ["evil_*"] |

Email policies

| Policy | What it checks | Config | |---|---|---| | email.domain_blocklist | Restricted recipient domains | blocked: ["*.banque-*.fr"] | | email.rate_limit | Max emails per window | max: 50, window: 1h | | email.business_hours | Sends outside work hours | start: 9, end: 18, tz: Europe/Paris | | email.content_scan | Credit cards, SSNs, API keys, private keys in body | patterns: [credit_card, ssn] |

Every policy takes a severity (critical / high / medium / low) and an on_fail action (STOP or REVIEW).

Custom policies: see [docs/writing-policies.md](docs/writing-policies.md).

Performance

Microbenchmarks (python benchmarks/run.py, dev laptop, 5 000 iters):

| Scenario | mean | p95 | p99 | ops/s | | --------------------- | ------- | ------- | -------- | ------ | | simple_allow | ~18 µs | ~31 µs | ~41 µs | 54 000 | | simple_block | ~19 µs | ~33 µs | ~47 µs | 52 000 | | multi_policy (5) | ~496 µs | ~958 µs | ~1564 µs | 2 000 | | with_audit_sqlite | ~558 µs | ~625 µs | ~1925 µs | 1 800 |

These numbers reflect measurements on a mid-range 2024 Windows laptop under typical dev load. Values vary significantly with system state (CPU load, SQLite WAL cache, temperature) — we've observed 1.5x to 4x variation on the same machine across sessions. The benchmarks are meant to show order of magnitude, not exact latencies.

Audit numbers are dominated by fsync. The p99 long tail on with_audit_sqlite reflects SQLite fsync latency spikes — expect similar behavior on shared storage. Re-run python benchmarks/run.py on your hardware before quoting publicly.

Zero mandatory dependencies

diplomat-gate ships pure-stdlib. Optional extras:

| Extra | Brings in | Used for | | ------------ | -------------------- | ------------------------------------- | | [yaml] | pyyaml | Gate.from_yaml(...) | | [rich] | rich | colored CLI output | | [openai] | openai>=1.0 | optional, for typed adapter usage | | [anthropic]| anthropic>=0.20 | optional, for typed adapter usage | | [langchain]| langchain-core>=0.1| optional, for typed adapter usage | | [all] | all of the above | one-shot install |

pip install diplomat-gate          # core only
pip install "diplomat-gate[yaml]"  # for YAML policy files
pip install "diplomat-gate[all]"   # everything

Examples

Eight runnable examples — each works from the repo root and from inside examples/. None of them require an SDK install.

python examples/01_basic_gate.py
python examples/02_yaml_config.py
python examples/03_decorator.py
python examples/04_audit_trail.py
python examples/05_review_queue.py
python examples/06_openai_adapter.py
python examples/07_anthropic_adapter.py
python examples/08_langchain_adapter.py

See [examples/README.md](examples/README.md).

Use with diplomat-agent

diplomat-agent scans your codebase and reports every tool call with real-world side effects. diplomat-gate protects them.

Need centralized governance?

diplomat-gate is local-first and free. For teams that need a hosted control plane:

diplomat.run — immutable cross-tenant audit trail, real-time dashboard, managed approval routing, compliance export (EU AI Act Article 12).

Requirements

  • Python 3.10+
  • Zero mandatory dependencies (stdlib only)
  • Optional extras as listed above

How this was built

diplomat-gate is solo-maintained and AI-assisted: a large share of the commits are authored by me but committed through an AI coding agent. What matters for a security tool is not who typed the lines but whether the behavior is verifiable. The credibility anchors are the ones you can check yourself: 146 tests, a 14-step release gate (scripts/validate_release.py), a green CI matrix across Python 3.10–3.13 on Linux / Windows / macOS, and runnable examples that need no API key. Run python -m pytest tests/ -v and python benchmarks/run.py and judge the tool on its output.

Limitations

diplomat-gate is a syntactic enforcement layer, not a semantic one. It is designed to be one layer in a defense-in-depth strategy, not a silver bullet.

What diplomat-gate does well:

  • Exact-match, glob, regex, and amount-based policie

Source & license

This open-source MCP server is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.