Skip to content

MCP Gateway SecurityAvailable

Every MCP tool call, checked before it runs.

Aegis is a gateway between your AI agents and your MCP servers that authenticates, authorizes and scans every tool call, for teams connecting agents to systems where a wrong call has consequences.

The source is shared during an evaluation — the gateway core is MIT-licensed.

A refused call never reaches your serverPlan of Aegis, an MCP gateway, drawn inside your perimeter. Three agents, each an MCP client, sit outside; the perimeter has one opening, the identity gate, where every call must carry a valid JWT. Inside, three nested fences are later and later checks: 02 policy, meaning the kill-switch and role-based access control; 03 the approval hold; 04 schema validation and the threat gate for injection patterns, egress DLP and tool pins. Your MCP servers sit in the core and stay where they are. Five calls enter. The first has no valid token and stops at the gate with HTTP 401; it is not written to the audit log. The second has no matching allow policy and stops at fence 02 with −32003. The third matches an approve policy and is held at fence 03 with −32061 until an operator decides. The fourth carries an injection pattern and is blocked at fence 04 with −32051. The fifth passes every check and reaches your servers, allow, code 0; responses are scanned on return and secrets and PII are redacted as [REDACTED:KIND]. Each decision after the gate is written as one row of the audit log, an HMAC-SHA256 chain in which every row's tag is computed from the key, the previous tag and the row, so an edited row breaks the chain. Cheap checks run first and the forward runs last, so a refused call never reaches your server. The drawing is simplified: 5 of the 16 pipeline stages are shown, and rate-limit and quota refusals are not audited.your servers stay where they areAegis — one gateway in front of your servers02 POLICY · KILL-SWITCH · RBAC03 APPROVAL · OPERATOR HOLD04 SCHEMA · INJECTION · EGRESS DLP · PINSYOUR MCP SERVERSMCP serverMCP serverMCP serverRESPONSES SCANNED ON RETURNsecrets, PII → [REDACTED:KIND]1simplified: 5 of 16 stages shownYOUR PERIMETER01 IDENTITY · JWTagentMCP CLIENTagentMCP CLIENTagentMCP CLIENT401no valid token ·not written tothe audit log−32003no matchingallow−32061held · awaitsan operator−32051injectionpatternallow · 0AUDIT LOG · HMAC-SHA256 CHAINtag = HMAC(key, previous tag ‖ row)2rate-limit and quota refusals: not auditedseq nallow0seq n+1block−32051seq n+2deny−32061seq n+3deny−32003nextA refused call never reaches your serverPlan of Aegis, an MCP gateway, drawn inside your perimeter. Three agents, each an MCP client, sit outside; the perimeter has one opening, the identity gate, where every call must carry a valid JWT. Inside, three nested fences are later and later checks: 02 policy, meaning the kill-switch and role-based access control; 03 the approval hold; 04 schema validation and the threat gate for injection patterns, egress DLP and tool pins. Your MCP servers sit in the core and stay where they are. Five calls enter. The first has no valid token and stops at the gate with HTTP 401; it is not written to the audit log. The second has no matching allow policy and stops at fence 02 with −32003. The third matches an approve policy and is held at fence 03 with −32061 until an operator decides. The fourth carries an injection pattern and is blocked at fence 04 with −32051. The fifth passes every check and reaches your servers, allow, code 0; responses are scanned on return and secrets and PII are redacted as [REDACTED:KIND]. Each decision after the gate is written as one row of the audit log, an HMAC-SHA256 chain in which every row's tag is computed from the key, the previous tag and the row, so an edited row breaks the chain. Cheap checks run first and the forward runs last, so a refused call never reaches your server. The drawing is simplified: 5 of the 16 pipeline stages are shown, and rate-limit and quota refusals are not audited.agentMCP CLIENTagentMCP CLIENTagentMCP CLIENT02 POLICY · RBAC03 APPROVAL HOLD04 SCHEMA · THREATYOUR MCP SERVERSMCP serverMCP serverMCP serverRESPONSES SCANNED:secrets, PII →[REDACTED:KIND]01 IDENTITY · JWTYOUR PERIMETER401no valid token ·not in the audit log−32003no matching allow−32061held · awaitsan operator−32051injection patternallow · 0AUDIT LOG ·HMAC-SHA256 CHAINtag = HMAC(key,previous tag ‖ row)seq nallow0seq n+1block−32051seq n+2deny−32061seq n+3deny−32003next1simplified: 5 of16 stages shown2rate-limit, quotarefusals: not audited
Policy
Allow, deny or approve per tool
Tool definitions
Pinned — changes quarantined
Audit log
Hash-chained
Core
MIT, source on evaluation

Documented: 43 injection rules · 13 PII and secret classes · see the docs

A refused call never reaches your serverPlan of Aegis, an MCP gateway, drawn inside your perimeter. Three agents, each an MCP client, sit outside; the perimeter has one opening, the identity gate, where every call must carry a valid JWT. Inside, three nested fences are later and later checks: 02 policy, meaning the kill-switch and role-based access control; 03 the approval hold; 04 schema validation and the threat gate for injection patterns, egress DLP and tool pins. Your MCP servers sit in the core and stay where they are. Five calls enter. The first has no valid token and stops at the gate with HTTP 401; it is not written to the audit log. The second has no matching allow policy and stops at fence 02 with −32003. The third matches an approve policy and is held at fence 03 with −32061 until an operator decides. The fourth carries an injection pattern and is blocked at fence 04 with −32051. The fifth passes every check and reaches your servers, allow, code 0; responses are scanned on return and secrets and PII are redacted as [REDACTED:KIND]. Each decision after the gate is written as one row of the audit log, an HMAC-SHA256 chain in which every row's tag is computed from the key, the previous tag and the row, so an edited row breaks the chain. Cheap checks run first and the forward runs last, so a refused call never reaches your server. The drawing is simplified: 5 of the 16 pipeline stages are shown, and rate-limit and quota refusals are not audited.your servers stay where they areAegis — one gateway in front of your servers02 POLICY · KILL-SWITCH · RBAC03 APPROVAL · OPERATOR HOLD04 SCHEMA · INJECTION · EGRESS DLP · PINSYOUR MCP SERVERSMCP serverMCP serverMCP serverRESPONSES SCANNED ON RETURNsecrets, PII → [REDACTED:KIND]1simplified: 5 of 16 stages shownYOUR PERIMETER01 IDENTITY · JWTagentMCP CLIENTagentMCP CLIENTagentMCP CLIENT401no valid token ·not written tothe audit log−32003no matchingallow−32061held · awaitsan operator−32051injectionpatternallow · 0AUDIT LOG · HMAC-SHA256 CHAINtag = HMAC(key, previous tag ‖ row)2rate-limit and quota refusals: not auditedseq nallow0seq n+1block−32051seq n+2deny−32061seq n+3deny−32003next

The problem

An agent with tools is a new kind of caller.

  1. Any tool, any argument

    Connect an agent to an MCP server and it can call every tool that server exposes, with whatever arguments a prompt steers it towards. The server rarely knows who is asking.

  2. Definitions change underneath you

    A tool you reviewed last week can ship a new description or schema today, and a lookalike server can register a name that shadows a trusted one.

  3. Results carry things back

    A tool response can return keys, tokens or personal data straight into the model's context, and from there into logs and transcripts nobody meant to keep.

How it works

One checkpoint, in a fixed order.

  1. Identify the caller

    Once you configure a verifier, every request must carry a bearer JWT, checked against your identity provider's JWKS. The tenant comes from a claim, and every layer after this is scoped to it.

  2. Decide before forwarding

    Kill-switch, per-tool policy, approval hold, schema validation, and injection and egress scanning run in order. A refused call never reaches your server, and each refusal carries its own JSON-RPC code.

  3. Check the result, then record it

    The response is scanned for secrets and personal data, which are redacted by default. The decision is appended to a hash-chained audit log keyed by a secret the database never sees.

Held, approved, admitted oncePlan detail of the approval hold and the kill-switch, inside your perimeter. The approval check works like an airlock with two doors in front of your MCP server. The first send of a call, add(a, b), matches an approve policy and stops at door 1 with −32061: it is held, an approval_id is returned, and the server is not called. An operator in the same tenant, who is not the requester, decides it with POST /admin/v1/approvals/:id; deciding your own request returns 404. The operator sees argument names and payload size, never values. After approval, the identical call from the same caller with the same arguments passes both doors once and reaches the server, allow, code 0. The decision counts for the approval window, one hour by default, measured from the first hold. A third send is held again at door 1 with −32061 under a new approval_id. If the approvals store is unreachable, calls that need approval are refused with −32061 and status unavailable: the hold fails closed. The kill-switch can be set on a tenant, a server or a tool. A call to a switched tool passes the tenant and server scopes and stops at the tool with −32062, before RBAC, approvals and threat checks run. Only default-tenant operators can set or lift a switch. The replica that sets it applies it at once; the other replicas pick it up on their next refresh, every 2 seconds by default.operatorPOST /admin/v1/approvals/:idsame tenant,not the requester2own request → 404sees argument namesand size, never values03 APPROVALDOOR 1DOOR 2your MCP serverapproval_idapproval window · 1 h defaultcounted from the first holdYOUR PERIMETERcall · add(a, b)−32061held · approval_id returnedthe server was not calledidentical callsame caller, same argumentsallow · 0third send−32061held again · new approval_idKILL-SWITCHTENANTSERVERTOOLa switched tool−32062kill-switch · checked beforeRBAC, approvals and threat checksset or lift: default-tenant operators onlyGATEWAY REPLICASR1R2R3applies at oncenext refresh · 2 s default1approvals store unreachable→ −32061, status unavailable(fails closed)Held, approved, admitted oncePlan detail of the approval hold and the kill-switch, inside your perimeter. The approval check works like an airlock with two doors in front of your MCP server. The first send of a call, add(a, b), matches an approve policy and stops at door 1 with −32061: it is held, an approval_id is returned, and the server is not called. An operator in the same tenant, who is not the requester, decides it with POST /admin/v1/approvals/:id; deciding your own request returns 404. The operator sees argument names and payload size, never values. After approval, the identical call from the same caller with the same arguments passes both doors once and reaches the server, allow, code 0. The decision counts for the approval window, one hour by default, measured from the first hold. A third send is held again at door 1 with −32061 under a new approval_id. If the approvals store is unreachable, calls that need approval are refused with −32061 and status unavailable: the hold fails closed. The kill-switch can be set on a tenant, a server or a tool. A call to a switched tool passes the tenant and server scopes and stops at the tool with −32062, before RBAC, approvals and threat checks run. Only default-tenant operators can set or lift a switch. The replica that sets it applies it at once; the other replicas pick it up on their next refresh, every 2 seconds by default.operatorSAME TENANT03 APPROVALyour MCP serverapproval_id2own request → 404APPROVAL WINDOW1 h default, fromthe first holdYOUR PERIMETER1st and 3rd send−32061held · a newapproval_ideach time2nd send,afterapprovalallow · 0KILL-SWITCHset or lift:default-tenant operatorsTENANTSERVERTOOL−32062checked before RBAC,approvals, threat checks1approvals store unreachable →−32061 unavailable (fails closed)REPLICASR1R2R3at oncenext refresh, 2 s

Stations in the drawing

The order every tools/call runs in.

  1. S1Authenticate

    Verifies the bearer JWT. A missing or invalid token is refused before anything else runs, and is not written to the audit log.

    HTTP 401aegis/how-it-works

  2. S2Route and kill-switch

    Resolves the owning server once, for both the check and the forward. A switch on the tenant, server or tool refuses the call and overrides RBAC.

    −32062aegis/approvals-and-kill-switch

  3. S3RBAC

    A call with no matching allow, or any matching deny, is refused. Deny wins over approve, and approve wins over allow.

    HTTP 403 · −32003aegis/how-it-works

  4. S4Approval hold

    An approve policy holds the call and returns an approval_id. Once an operator approves it, the same caller can run the identical call once.

    −32061 · 1 h window by defaultaegis/approvals-and-kill-switch

  5. S5Schema and threat gate

    Arguments are checked against the tool's inputSchema, then the whole params object is scanned for injection patterns and outbound secrets.

    −32602 · −32051 · −32053aegis/threat-detection

  6. S6Forward and response scan

    The call reaches your server only now. Secrets and personal data in the response are redacted by default.

    [REDACTED:KIND]aegis/threat-detection

  7. S7Record

    The decision is appended to the HMAC-SHA256 chained audit log in the audit store. Rate-limit and quota refusals are counted in metrics, not audited.

    GET /admin/v1/audit/verifyaegis/audit-and-observability

Capabilities

The controls tool use actually needs.

The chain, and how it's checkedElevation of the Aegis audit log. On the left, the gateway holds the audit key. The key never reaches the audit store: the line from the key stops at the fence around it. The gateway appends each decision as one row. Rows are drawn as plates stacked bottom to top, row k−2 up to row k+3, with the next row not yet written above them. Each row carries the decision's fields and a tag. Each tag is HMAC-SHA256 over the key, the previous row's tag and the row, so the tags form one global chain. A correlation id is stored but not covered by the tag. In row k the row's fields have been edited. GET /admin/v1/audit/verify recomputes every tag in order; the check climbs the chain from the bottom and stops at the connector into row k, reporting first_break_seq = k. Exhibit A below shows both responses. Verify catches edited, inserted, deleted and reordered rows, and a truncated tail while the gateway runs. Known limits: rate-limited and quota refusals are not chained; rows cut from the end while the gateway is down are not caught after restart; and a full 4,096-event queue drops new events and logs an error.gatewayAUDIT KEYAUDIT STOREthe key never reachesthe audit storenext#k+3row fieldstagk+2k+1keditedk−1k−2first_break_seq = kHMAC-SHA256(key, prev tag ‖ row)correlation id — stored, not covered by the tagGET /admin/v1/audit/verifyrecomputes every tag in orderEXHIBIT A ↓3a full 4,096-event queuedrops new events1rate-limit and quotarefusals are not chained2tail rows cut while thegateway is down: notcaught after restartYOUR PERIMETERThe chain, and how it's checkedElevation of the Aegis audit log. On the left, the gateway holds the audit key. The key never reaches the audit store: the line from the key stops at the fence around it. The gateway appends each decision as one row. Rows are drawn as plates stacked bottom to top, row k−2 up to row k+3, with the next row not yet written above them. Each row carries the decision's fields and a tag. Each tag is HMAC-SHA256 over the key, the previous row's tag and the row, so the tags form one global chain. A correlation id is stored but not covered by the tag. In row k the row's fields have been edited. GET /admin/v1/audit/verify recomputes every tag in order; the check climbs the chain from the bottom and stops at the connector into row k, reporting first_break_seq = k. Exhibit A below shows both responses. Verify catches edited, inserted, deleted and reordered rows, and a truncated tail while the gateway runs. Known limits: rate-limited and quota refusals are not chained; rows cut from the end while the gateway is down are not caught after restart; and a full 4,096-event queue drops new events and logs an error.gatewayAUDIT KEYAUDIT STOREthe key neverreaches theaudit storerow fields, then the tagnextk+3k+2k+1keditedk−1k−2first_break_seq = kHMAC-SHA256(key, prev tag ‖ row)correlation id: stored,not covered by the tagGET /admin/v1/audit/verifyrecomputes every tag in orderEXHIBIT A ↓1rate-limit, quota refusals: not chained2tail cut while down: not caught3full 4,096-event queue drops eventsYOUR PERIMETER
EXHIBIT AGET /admin/v1/audit/verify

Chain intact

{
  "ok": true,
  "rows": n,
  "first_break_seq": null
}

Row k edited

{
  "ok": false,
  "rows": n,
  "first_break_seq": k
}

Catches edited, inserted, deleted and reordered rows, and a truncated tail while the gateway runs. Needs catalog:read on aegis-admin in the default tenant.

  1. Per-tool policy

    Allow, deny or approve per role, server and tool pattern. Deny wins, a call that matches no rule is denied, and tools/list hides what a caller cannot reach.

  2. Approval holds

    An approve policy holds the call. The operator sees argument names and size, never values. An approval admits that exact call once, it expires, and nobody can approve their own request.

  3. Kill-switch

    Stop traffic for a tenant, a server or a single tool without waiting for a policy refresh. Every replica picks it up within one refresh interval.

  4. Tool pinning

    Each tool's name, description and schema are hashed on first sight. A later change quarantines the tool until an operator re-pins it. Pins are held per replica and reset on restart, and pinning is trust-on-first-use, so review a server before you register it.

  5. Argument and response scanning

    Pattern rules check every string in a call's parameters, keys included, for shell and prompt injection. Credentials, card numbers and SSNs are refused on the way out and redacted on the way back.

  6. A verifiable audit chain

    Each allow, deny, block and error is sealed with an HMAC over the previous row. The verify endpoint recomputes the chain and names the first row that breaks.

Outcomes

What changes with Aegis.

Agents get the tools they need, not all of them

Policy is per tool, so an agent can read tickets without being able to close them, and a tool no rule allows is denied.

Risky actions have a person behind them

Mark a tool such as a branch delete or a refund for approval, and each call is held and runs only when the caller re-sends it after an operator approves that one call with those exact arguments.

An audit trail you can check, not just read

Editing, inserting, deleting or reordering rows breaks the chain while the gateway runs, and an operator can run the check on demand.

Compared

Aegis and generic API gateways

Each line on our side is sourced from the docs. The other column describes the general approach, not any one vendor.

Unit of control
AegisThe MCP tool call: which server, which tool, which arguments
Generic API gatewaysThe HTTP route and method
Protocol awareness
AegisSpeaks MCP — filters tools/list per caller and routes each call to the server that owns the tool
Generic API gatewaysSees JSON-RPC as an opaque request body unless extended with custom plugins
Tool integrity
AegisPins tool definitions and quarantines changes
Generic API gatewaysNo notion of a tool definition
Human approval
AegisBuilt-in hold for a specific call, single-use and time-bound
Generic API gatewaysNot usually part of the request path
Scope and maturity
AegisTool traffic only, pre-1.0 — MCP resources and prompts are not proxied
Generic API gatewaysMature, general-purpose and suited to any HTTP traffic

Rollout

How teams roll out Aegis.

  1. Rev A · Evaluate

    Run it locally

    Start the Compose stack with its mock MCP servers and send a first tool call through the gateway to see each check and refusal code.

  2. Rev B · Observe

    Front one server

    Put Aegis in front of one server with a narrow allow policy, and run the threat checks in monitor mode to see what they would refuse.

  3. Rev C · Enforce

    Turn the controls on

    Configure your identity provider's JWKS, Postgres for policy and ClickHouse with a strong audit key, then enforce threat checks and add approval holds for risky tools.

  4. Rev D · Scale

    Deploy with Helm

    Move to the hardened Helm chart, enable NetworkPolicy and confirm your CNI enforces it, and schedule audit-chain verification.

Who it's for

Built for the teams who have to answer for it.

  • Platform engineering

    One place to put agent traffic under policy.

    A reverse proxy your clients point at. MCP servers stay where they are, and the Helm chart ships hardened defaults.

  • Security

    Know what agents did, and be able to prove it.

    Every decision lands in a hash-chained log with the control that made it, and the chain can be verified.

  • AI and agent teams

    Ship agents without a review meeting for every tool.

    Allow the safe tools, hold the few risky ones for approval, and handle refusals by their distinct error codes.

FAQ

Questions teams ask about Aegis.

The full FAQ in the docs

Not if your servers speak MCP over Streamable HTTP. Point your client at the gateway's /mcp endpoint and have it send a bearer token. Your servers stay where they are, behind the gateway, and you register them in the server catalog. Aegis builds each upstream request itself and never passes your client's headers through. It does not launch stdio servers.

The tools surface. Aegis answers initialize and ping itself, serves tools/list from the tools it discovered, filtered to what each caller may reach, and forwards tools/call to the server that owns the tool. Notifications are acknowledged. MCP resources and prompts are not proxied today, and any other method returns a method-not-found error.

Aegis is open core. The gateway core is MIT-licensed and runs on its own: the proxy, authentication, policy, threat detection, the audit log, approvals, the kill-switch and the Compose and Helm deployments. Billing, SSO and SCIM, the SIEM and alert connectors, the credential broker and per-tenant namespace guardrails are commercial. The source is not in a public repository today — we share it during an evaluation.

We do not publish a latency or throughput figure. Internal load tests exist, but they are not an independent benchmark and do not run in CI, and tail latency depends heavily on the host. Checks run cheapest first, and audit writes never block a call. Measure on your own hardware during an evaluation — the docs list every stage a call passes through.

Each has a defined posture. Redis is required: the gateway will not start without it, and while it is unreachable tool calls fail with HTTP 500 — kill-switches already in force stay in force, and rate limiting fails open. If Postgres drops, approval-gated calls fail closed and policy keeps its last snapshot. The audit store never blocks traffic — if ClickHouse is unreachable at startup, auditing stays off until restart, and a later failed write is dropped and logged.

It reduces the risk — it does not end it. Injection detection uses pattern rules over every string in a call's parameters, including keys, and refuses matches before the call is forwarded. It does not scan tool responses or tool descriptions for injection, and no pattern set is a complete defense. Pair it with per-tool policy and approval holds for tools that change state.

Yes. The tenant comes from a JWT claim, and routing, tools/list, policy, circuit breakers and the audit log are all scoped to it, so one tenant cannot tell another exists. You can restrict an instance to named tenants, and the Helm chart has an isolated mode that installs one release per tenant. Backing stores are still yours to separate.

Sources

Read the source of every drawing.

Each schematic on this page is drawn from the Aegis documentation, not from a product screen. The pages below are where every label comes from.

All Aegis docs

See Aegis against your architecture.