Guidance influences what an agent does. Enforcement determines what it can execute.
These are separate mechanisms. This repo proves it.
Built by an AAIF Ambassador — agentgateway and MCP are Linux Foundation AAIF projects.
A repository says:
Deployment work targets staging.
The runtime exposes:
deploy(environment)
├─ staging
└─ production
One instruction. Broader authority than it describes. No model failure required — the architecture already contains it.
Guidance shapes intent. Enforcement determines what executes. These need to be two separate, independently tested mechanisms.
Prerequisites: Docker Desktop · make · Node.js 22+
git clone https://github.com/Darshitpandya/two-layer-agent-contract.git
cd two-layer-agent-contract
make up
make session
make show-visibilityThree containers. One enforcement boundary.
AGENTS.md → Agent decision → agentgateway → MCP server
(intent) (candidate) (enforcement) (capability)
Diagram by Archify
| Layer | Component | What it does |
|---|---|---|
| Guidance | AGENTS.md |
Shapes agent intent — commands, conventions, preferred behaviour |
| Enforcement | agentgateway |
Filters discovery surface, authorizes every tool call |
| Enforcement | ExtMCP policy |
Argument-sensitive rules — same tool, different outcome |
make show-visibility # without vs through agentgateway — 3 tools → 2 visible
make show-allow # deploy(staging) → ALLOW
make show-deny # deploy(production) → DENY — same tool, different authority
make show-hidden-denied # production_admin direct call → DENY
make show-backend-log # backend receipt — only allowed calls arrived
make show-fail-closed # policy down → failClosed → nothing forwards→ Start here:
ADOPTING.md
Four steps:
- Copy
gateway/config.yamlandgateway/extmcp-policy/policy.tsinto your repo - Update tool names and argument rules to match your MCP server
- Wire your identity provider (API key or OIDC)
- Run the contract tests — prove the boundary holds
The lab, Docker Compose, and Makefile are for running this demo. ADOPTING.md tells you exactly what to take and what to leave behind.
34 tests prove runtime behaviour — not model compliance.
| Suite | What it proves |
|---|---|
allowed.test.ts |
Staging deploy reaches the backend |
hidden.test.ts |
production_admin absent from agent discovery |
denied.test.ts |
Direct invocation rejected, backend non-receipt confirmed |
arg-sensitive.test.ts |
Same tool name, different argument, different outcome |
fail-closed.test.ts |
Policy unavailable → request not forwarded |
make verify # full suite: topology + unit + contract + fail-closed
make test # contract tests only (stack must be running)This pattern addresses one problem: behavioural instructions should not carry authorization responsibility.
It does not claim to solve:
- Model alignment or prompt injection
- Credential lifecycle or secrets management
- Host sandboxing or MCP server correctness
- Non-MCP paths to the same backend
| Component | Version | |
|---|---|---|
| agentgateway (AAIF project) | v1.5.0 | MCP authz docs |
| Model Context Protocol (AAIF) | 2025-03-26 | Spec |
| MCP SDK | @modelcontextprotocol/sdk@1.10.2 | npm |
| Node.js | 22+ | nodejs.org |
AAIF — Agentic AI Foundation, a Linux Foundation directed fund governing open agentic AI infrastructure.
ADOPTING.md |
How to apply this pattern to your project or org |
docs/architecture.md |
Two-layer contract diagrams |
docs/contract-explained.md |
Pattern deep-dive |
docs/production-hardening.md |
Production adaptation guide |
CONTRIBUTING.md · SECURITY.md · Apache 2.0 LICENSE
Intent is not policy. Context can remain flexible. Runtime authority must remain explicit.

