Skip to main content

Toolgate

The capability control plane for embedded AI agents.

CI License: Apache-2.0 Python 3.12+

Portal & live simulation: maglionejm.github.io/toolgate — try the gate and tamper with a real hash chain in your browser.

New in 0.5: per-user OAuth brokering (agents call SaaS tools with the user's own connection — Toolgate custodies the tokens), approval push channels (signed webhooks, Slack with operator-bound decisions, email magic links), transparency-log anchoring with offline divergence detection + WORM retention exports, KMS envelope encryption for the vault (GCP/AWS), a Postgres store with multi-instance exactly-once guarantees, a live LLM demo (toolgate demo --live) with a prompt-injection containment act, token-endpoint brute-force protections, and a red-team suite that runs in CI.

0.4 shipped: MCP surface, operator identities + roles, console at /console, body-bound PoP proofs, key rotation with in-chain lineage, Merkle-checkpointed audit, taint-tracking policies (lethal-trifecta defense), framework adapters, async SDK, usage reports, Docker image (toolgate up).

Agents should never hold credentials — not the user's OAuth token, not a tenant API key, not anything. Toolgate sits between agents and the tools they call:

  • the agent authenticates with its own Ed25519 key and a delegation grant from a human;
  • it receives short-lived capability tokens (sub = the human, act.sub = the agent, RFC 8693 semantics) that are useless if stolen (proof-of-possession bound);
  • every tool call is policy-checked (allow / deny / require human approval), metered against the grant's budget, and executed by the gate, which injects the real credential server-side;
  • every decision — including denials — lands in a hash-chained, Ed25519-signed audit trail.
Agent (no secrets) ── token + one-time proof ──▶ GATE ── real credential ──▶ Upstream API
                                                  │
                                    verify → decide → budget → inject → execute → audit

Try it

uvx --from toolgate-io toolgate demo    # zero-install, straight from PyPI
uvx --from toolgate-io toolgate up      # run the real thing in Docker (console at /console)

or from source:

uv sync
uv run toolgate demo

The demo boots Toolgate plus two credential-guarded mock APIs and runs a six-act scenario: an allowed CRM read (the upstream rejects anything without its live key — proving injection), a policy denial, an external email parked for human approval and executed against the approved args only, budget exhaustion, revocation that kills a live token instantly, and audit chain verification with the full decision trace.

With an Anthropic API key you can watch a real model drive the same gate — including a seventh act where a hostile web page instructs the agent to exfiltrate data through an allowed email tool, and the taint policy parks the attempt no matter what the model decides:

pip install 'toolgate-io[demo]'
ANTHROPIC_API_KEY=... toolgate demo --live
[OK      ] read_contact executed -> {'contact': {...}}
[DENIED  ] TG_DENIED: matched deny rule never-delete
[PARKED  ] approval apr_... pending — agent is blocked, not trusted
[HUMAN   ] Sam approved the exact parked arguments (args are hash-bound)
[OK      ] send_email executed after approval
[BUDGET  ] blocked: delegation grant budget exhausted
[REVOKED ] TG_REVOKED: live token died with the grant, no TTL wait
[AUDIT   ] chain of 10 records — verification: VALID

How it works

  1. Register a tenant, its users, agents (public keys only), and upstreams. Upstream credentials are sealed into the vault (AES-256-GCM) and never leave the server.
  2. Delegate: a user grants an agent bounded authority — which upstreams/tools (RFC 9396-style authorization_details), what budget (cost units), which policy, until when.
  3. Exchange: the agent presents a signed client assertion (RFC 7523 style) and receives a capability token — TTL ~2 minutes with jitter, audience-bound, sender-constrained via cnf.jkt.
  4. Call: each gate call carries the token plus a one-time DPoP-style proof signed by the agent key (bound to method, URL, and token hash; replays rejected).
  5. Enforce: token bounds → policy rules (first match wins, glob matching, dot-path argument constraints, cost ceilings) → default deny → atomic budget charge.
  6. Approve: require_approval parks the call; a human decides on the exact argument set (hash-bound — no post-approval swaps); the agent polls and executes.
  7. Audit: every decision appends to a hash chain signed by the gate key. GET /v1/control/audit/verify proves nothing was edited, removed, or reordered.

CLI

Everything an operator does is a toolgate command (full reference):

pip install toolgate-io
toolgate init                                        # profile + connectivity check
toolgate keys generate --out agent-key.json          # agent identity (private key stays local)
toolgate grants create -t tnt_... --user usr_... --agent agt_... \
    --policy pol_... --budget 100 --authz "crm:*"    # bounded delegation
toolgate approvals watch -t tnt_... --by usr_...     # interactive human-in-the-loop inbox
toolgate audit export --out audit.json && toolgate audit verify --file audit.json   # verify an exported chain
toolgate dev call crm read_contact --grant grt_... --key agent-key.json             # act as the agent

Offline-verification caveat. audit verify --file is genuinely offline/third-party only when you supply the gate's public key out-of-band via --jwk. Without --jwk, the verifier fetches the key from GET /v1/keys on the very server being audited — so a server that forged the chain could also serve a matching key. For independent verification, pass --jwk with a key you obtained separately.

Agent-side SDK

from toolgate.sdk import ToolgateClient, PendingApproval, generate_ed25519_key_pair

client = ToolgateClient(
    base_url=base_url,
    agent_id=agent_id,
    agent_private_jwk=agent_private_jwk,  # the only secret an agent ever holds
    grant_id=grant_id,
)

result = client.call("crm", "read_contact", {"contactId": "c-001"})
if isinstance(result, PendingApproval):
    result = client.wait_for_approval(result.approval_id)
# Denials, budget exhaustion, and revocation raise typed ToolgateCallError
# (TG_DENIED / TG_BUDGET_EXCEEDED / TG_REVOKED / TG_PROOF_INVALID / ...).

Layout

Module Purpose
toolgate.core Capability tokens, client assertions + PoP proofs, policy engine, audit chain + anchoring verification
toolgate.server Control plane (registry, grants, token endpoint, approvals, revocation, audit) + gate (enforcement pipeline), vault (KMS envelope), notifier (push channels), OAuth broker, Rekor anchoring, SQLite + Postgres stores
toolgate.sdk Agent-side client: token exchange, signed calls, approval flow, typed errors
toolgate.integrations Framework adapters: anthropic_tools, openai_tools, langchain_tools (pip install 'toolgate-io[langchain]')
toolgate.console Operator console (approvals inbox, audit explorer, grants, simulator, reports) served at /console
toolgate.demo End-to-end scenario (uv run toolgate-demo); toolgate demo --live drives it with a real Claude model (pip install 'toolgate-io[demo]')

MCP & framework adapters

Any MCP client can consume a grant's tools — paste the server URL and a capability token:

POST /v1/mcp        Authorization: Bearer <capability token>
tools/list -> crm__read_contact, email__send_email   (bounded by the delegation)
tools/call -> runs the full policy/budget/audit pipeline; approvals surface as
              retryable errors carrying the approval id

Or stay in your framework:

from toolgate.integrations import anthropic_tools, openai_tools
tools, dispatch = anthropic_tools(client)   # Anthropic Messages API tools + gate-routed dispatch
tools, dispatch = openai_tools(client)      # OpenAI tools format + gate-routed dispatch
# pip install 'toolgate-io[langchain]' -> langchain_tools(client)

Policies can now reason about task history — the lethal-trifecta defense:

{ "effect": "require_approval", "match": {"tool": "send_email"},
  "when": {"txnTouchedUntrusted": true} }

Documentation

Full suite in docs/: Quickstart · API Reference · Token Spec · Security Model · Deployment · Operations

Design

  • docs/ARCHITECTURE.md — components, token design, threat model
  • docs/adr/0001 — delegation, never user-credential impersonation
  • docs/adr/0002 — JWT on OAuth rails (RFC 8693/9396/7800) over Biscuit/Macaroon/UCAN
  • docs/adr/0003 — original TS runtime decision (superseded by 0005)
  • docs/adr/0004 — approvals bound to args hashes; hash-chained signed audit
  • docs/adr/0005 — Python as the reference implementation

The 0.4–0.5 roadmap is fully shipped (MCP surface, OAuth brokering, transparency-log anchoring, approval push channels, KMS vault, Postgres scale-out, live LLM demo, red-team suite). Next themes live in the issue tracker as they open: TypeScript SDK rebuild, dashboard, field-level taint.

Development

uv sync
uv run ruff check src tests
uv run pytest tests/ -q     # 166 tests: unit + integration + adversarial red-team + fake-provider E2E
uv run toolgate-demo        # the six-act scenario
uv run toolgate-server      # standalone server (logs only the admin-key fingerprint at boot; set TOOLGATE_ADMIN_KEY explicitly)

Production env vars: TOOLGATE_MASTER_KEY, TOOLGATE_ADMIN_KEY, TOOLGATE_PUBLIC_URL, TOOLGATE_DB, PORT.

Contributing & security

Contributions welcome — see CONTRIBUTING.md. Security findings go through private vulnerability reporting, never public issues.

License

Apache License 2.0 © 2026 Juan Martin Maglione. Co-authored with Marc Puig — see AUTHORS.md. Toolgate is early-stage software (pre-1.0): the wire format is a compatibility surface we take seriously, but expect movement before 1.0.

Release files for toolgate-io 0.5.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for toolgate-io 0.5.0
File Size Uploaded
toolgate_io-0.5.0.tar.gz 368.6 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for toolgate-io 0.5.0
File Interpreter ABI Platform
toolgate_io-0.5.0-py3-none-any.whl Python 3 none any Details

Total release size: 504.8 kB

Release files / toolgate_io-0.5.0.tar.gz

Download URL toolgate_io-0.5.0.tar.gz
Size 368.6 kB
Tags Source
SHA-256 checksum
How to use checksums
bdfd4848b3381679fe6557ad223d5a1ca3be5e8147d58abe6ee3003052561138
BLAKE2b-256 checksum
How to use checksums
66591effd8e8bcc2e18f16eacf42103b2a46677e9419cf2a2ff636db0ad77ff6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.

Transparency log

Release files / toolgate_io-0.5.0-py3-none-any.whl

Download URL toolgate_io-0.5.0-py3-none-any.whl
Size 136.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
348e37f07246871fa18c2e4b3ddc2ab19ce0a44520d5b97411f3a9c104c826a2
BLAKE2b-256 checksum
How to use checksums
de4b5aeba5d96a6b578693f4d4628a000455e4769d709f4bd35b2197422f8b0e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.1

2 release files

0.3.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page