v2.27.5 · open source · MIT
Every MCP tool call ends in a verdict.
Hangar is the runtime security and governance layer between your agents and your MCP servers: identity, policy, approvals and an audit record for every call. Runs on a laptop, a VM, or Kubernetes.
Deterministic — no ML. Self-hosted — no SaaS. MIT.
| time | verdict | call · reasonaudit record |
|---|---|---|
| 15:15:02 | allow | github.get_issuetenant_id=acme |
| 15:15:04 | hold | github.create_issuegate=approval state=pending |
| 15:15:07 | deny | github.delete_repositorygate=tool_access reason=tool_not_in_access_policy |
| 15:15:09 | deny | fetch.getl7_verdict=deny l7_mode=enforce policy_id=sha256:9f2c41e0 |
01 · the path
How a call is decided
A tool call passes 13 gates, in an order core fixes, before it reaches your server. The first one that says no is the verdict. Nothing on the verdict path is scored or learned.
- CancelledruntimeWas the batch cancelled before this call ran?
cancelled - TimeoutruntimeIs the batch's time budget already spent?
batch_timeout - RouteruntimeWhich server answers: this one, or a member of its group?
server_not_found · no_available_member - Tool accessIs this tool exposed to this caller at all?
tool_not_in_access_policy - WithdrawalHas the tool been withdrawn for this tenant?
tool_withdrawn - Digest pinIs the tool still the one you approved, byte for byte?
digest_mismatch - HealthruntimeIs the server failing often enough to stop sending it calls?
circuit_open - ValidatorsDo the interceptor validators pass? They fail closed.
validator_denied - Tenant budgetDoes the caller's tenant have a budget, a free slot, and rate left?
no_budget · concurrency · rate - ApprovalDoes a person have to say yes first? The call holds until they do.
approval_denied · approval_timeout - Cold startruntimeStart a stopped server once, however many calls are waiting.
start_failed - Pin, after startThe pin check a cold start made possible.
digest_mismatch - CancelledruntimeWas the batch cancelled while the server started?
cancelled
then, and not gates
- Rate limitHas this caller spent its command-bus budget?
RateLimitExceeded - Egress policy (L7)Inside the invocation, just before any outbound I/O: may this call, with these arguments and headers, leave?
l7_verdict=deny · l7_verdict=require_approval - Response sizeIs the result over the cap? It is cut, and the cut is marked, with a continuation to fetch the rest.
truncated
Generated from _GATES in core v2.24.0. The path with the cluster plane around it is on the request path.
this config
mcp_servers:
github:
tools:
allow_list: ["get_*", "list_*"]
approval_list: ["create_issue"]produces this record
| time | verdict | call · reason |
|---|---|---|
| 15:15:02 | allow | github.get_issuetenant_id=acme |
| 15:15:03 | allow | github.list_issuestenant_id=acme |
| 15:15:04 | hold | github.create_issuegate=approval state=pending |
| 15:15:07 | deny | github.delete_repositorygate=tool_access reason=tool_not_in_access_policy |
| 15:15:11 | deny | github.list_issuesgate=tenant_budget reason=rate |
02 · the record
What you can prove afterwards
Every call is written down with the caller attached — allowed and denied alike. A verdict nobody can attribute proves nothing, so identity travels with the record rather than being reconstructed afterwards.
- Audit trail
- Each allow, deny and hold, with the caller, the tenant, the gate and the bounded reason. Codes only, never the text a gate was handed.
- SIEM export
- CEF, LEEF 2.0, JSON lines and RFC 5424 syslog, the same refusal fields in all four.
- Traces
- OTLP spans per call, a
hangar.gate.decisionevent per gate, andhangar.refusal.gateon the call that stopped.
the refused delete_repository, as your SIEM receives it
LEEF 2.0
LEEF:2.0|MCP Hangar|MCP Hangar|0.15.0|103| devTime=Oct 05 2026 15:15:07 proto=tool usrName=svc:ci sessID=sess-4f1a tenantID=acme action=delete_repository duration=0.41 gate=tool_access gateReason=tool_not_in_access_policy src=github routeBackend=githubJSON lines
{"timestamp": "2026-10-05T15:15:07.204000+00:00", "event_type": "ToolInvocationDenied", "provider_id": "github", "route_backend": "github", "tool_name": "delete_repository", "status": "denied", "duration_ms": 0.41, "user_id": "svc:ci", "session_id": "sess-4f1a", "tenant_id": "acme", "gate": "tool_access", "gate_reason": "tool_not_in_access_policy"}ToolCallRefused event, not typed for this page. Configure the exporters.03 · start
Install it, understand it, or look it up
Install the Python core, put a policy in front of one MCP server, and watch a call get denied on purpose.
Quickstart: five minutes, start to refusal- Docs25 recipesConfiguration, CLI, REST API and MCP tool reference, plus production recipes.
- Learn22 pagesWhat an enforcement plane is, how a request travels it, and where it stops.
- Securityadvisories · OWASPThe published advisories, the CVE ledger, and the OWASP MCP Top 10 mapped to what Hangar does and does not cover.