MCP Gateway — Two-Meta-Tool Architecture
Overview
Writer's production enterprise MCP gateway pattern. RAG-MCP retrieves top-K
tools and injects descriptions into the prompt. The Gateway pattern keeps
descriptions entirely outside the prompt — the agent sees exactly two
tools regardless of how many the gateway manages.
Agent ─── search(query) ───▶ [tool_name_1, tool_name_2, ...]
└── execute(name) ───▶ {tool, params, result}
The book cites Writer running this at "hundreds of connectors and thousands
of possible tool calls" without prompt bloat. Phil Fersht's research (HFS /
Cognizant) found 73% of organizations deploying agentic AI run multi-agent
systems averaging 12 agents per system, with governance frameworks failing
once the count exceeds five. The gateway is the enforcement point where
tool access policies scale with agent proliferation.
When to Use
- Tool registry exceeds ~30 tools and even RAG-MCP top-K injection bloats
- Multi-tenant agents where each customer should see only their tools
- Per-agent access policies (CS agent sees CRM, finance agent sees reports)
- Compliance domains where the prompt must not name unauthorized tools
- Migrating from individual MCP servers to one gateway endpoint
When NOT to Use
- Tool registry has fewer than 30 tools — use
rag-mcp-tool-selection instead
- One-off scripts with hardcoded tool flow
- As the only security layer — gateway is enforcement, IAM/RBAC is source-of-truth
- For multi-tool workflow planning — see Baidu COLT (Ch6) for tool-graph retrieval
Process
| Step |
Input |
Action |
Output |
Verification |
| 1 |
Tool registry JSON |
Gateway.from_registry_file(path) |
Gateway instance |
len(gateway.registry) > 0 |
| 2 |
(Optional) Role-based access policy |
Gateway(registry=..., access_filter=fn) or example_access_filter_devops |
Gateway scoped to one role |
len(gateway._visible()) < len(gateway.registry) for restricted roles |
| 3 |
Backend bindings (boto3 / HTTP / MCP forward) |
gateway.register_executor("tool_name", fn) per tool |
Wired gateway |
Each execute call returns real backend results, not dry-run |
| 4 |
User query |
gateway.search(query, top_k=5) |
List of {name, score} — descriptions NOT included |
Result is JSON-serializable; no tool descriptions leaked |
| 5 |
Selected tool name + parameters |
gateway.execute(tool_name, **params) |
{tool, params, result} |
Access filter raises PermissionError for out-of-scope tools |
| 6 |
Constant-prompt budget check |
gateway.agent_prompt(query) |
Prompt string |
len(prompt) does NOT scale with registry size |
Rationalizations
| Agent rationalization |
Documented rebuttal |
| "RAG-MCP top-K injection is good enough." |
True at 30-100 tools. Falls over at thousands per the chapter's Writer / HFS anchors. Gateway is the next architectural step, not a competing option. |
| "The agent needs to see descriptions to decide." |
The agent calls search first, gets ranked names, then can inspect one via a discovery tool if needed. Net cost is two round-trips for ambiguous cases vs one bloated prompt always. |
| "Access policies should live in the agent's system prompt." |
System-prompt rules degrade under adversarial input (the chapter cites IBM AI Engineer talk + Microsoft Core AI Platform research). Gateway-enforced policies are mechanical, not prompted. |
| "Per-tenant separation isn't worth the complexity." |
At scale every multi-tenant SaaS hits this — see Writer's anchor. Defer at small scale, but design the gateway so the access_filter seam exists from day one. |
Red Flags
Gateway.search returns full tool descriptions — leakage breaks the constant-prompt invariant
execute succeeds for tools that access_filter should have rejected — IAM/gateway-policy mismatch
- Agent prompt grows with registry size — gateway is being bypassed somewhere
- One tool wins every
search call — index has degenerate semantic-hooks; fix the registry, not the retriever
Non-Negotiable Verification
- Constant-prompt invariant.
len(gateway.agent_prompt(q)) must be identical for a 30-tool registry and a 300-tool registry. If it grows, the gateway is leaking descriptions.
- Access enforcement. With
access_filter=example_access_filter_devops, calling gateway.execute("secrets_manager_get_secret_value", ...) MUST raise PermissionError — secrets are not in the devops topic set.
- Search returns names not descriptions. Inspect
gateway.search(q) output — every element is {name, score}, no description key present.
- CLI
--help exits 0. Multi-harness invariant: the skill must be runnable from any CLI harness.
Security Posture
- Prompt injection. Tool descriptions never enter the prompt — major reduction in injection surface vs RAG-MCP. The
search call still uses the description-text for retrieval, so a malicious description biases retrieval but never reaches the LLM verbatim.
- Data exfiltration.
execute calls user-registered executors with **params. Validate params at the executor boundary (this skill does not — it's a substrate primitive).
- Privilege escalation. Access filter is the enforcement seam. Wire it to your IAM/RBAC source-of-truth. Do not trust the agent to enforce its own permissions.
Composition
- Pairs with
rag-mcp-tool-selection (sibling Ch6 skill) — gateway uses the same retriever internally (lib.score_tool swap point) but exposes a different API shape to the agent.
- Pairs with AAuth identity primitives (Posta canonical) for per-agent access tokens flowing through
access_filter.
- Inputs can be outputs of
enhance_tool_representation (Toolshed pattern, Ch6) — richer per-tool descriptions improve search retrieval without affecting agent prompt cost.
Source Attribution
Distilled from Agentic GraphRAG (O'Reilly), Chapter 6 — Tool Orchestration. Specific references:
- Writer's production enterprise MCP gateway (three-layer architecture: OpenAPI/Postman ingestion + Palmyra X5 description refinement + two-meta-tool surface)
- Phil Fersht's HFS / Cognizant research on multi-agent governance failure above 5 agents
- Microsoft Core AI Platform research on prompt bloat at MCP scale
1---2name: mcp-gateway-two-meta-tools3description: Build a gateway that exposes any-size tool registry through just two meta-tools: search(query) and execute(tool_name, **params). Tool descriptions stay outside the prompt entirely — the agent's prompt cost is constant regardless of registry size. Use when you need per-tenant tool segmentation, per-agent access policies, or are scaling to hundreds-of-tools where even top-k injection (rag-mcp- tool-selection) bloats prompts. NOT for cases under 30 tools where RAG-MCP top-K injection is simpler. NOT a security layer on its own — bring an IAM/RBAC source-of-truth for the access_filter.4---56# MCP Gateway — Two-Meta-Tool Architecture78## Overview910Writer's production enterprise MCP gateway pattern. RAG-MCP retrieves top-K11tools and injects descriptions into the prompt. The Gateway pattern keeps12descriptions entirely *outside* the prompt — the agent sees exactly two13tools regardless of how many the gateway manages.1415```16Agent ─── search(query) ───▶ [tool_name_1, tool_name_2, ...]17 └── execute(name) ───▶ {tool, params, result}18```1920The book cites Writer running this at "hundreds of connectors and thousands21of possible tool calls" without prompt bloat. Phil Fersht's research (HFS /22Cognizant) found 73% of organizations deploying agentic AI run multi-agent23systems averaging 12 agents per system, with governance frameworks failing24once the count exceeds five. The gateway is the enforcement point where25tool access policies scale with agent proliferation.2627## When to Use2829- Tool registry exceeds ~30 tools and even RAG-MCP top-K injection bloats30- Multi-tenant agents where each customer should see only their tools31- Per-agent access policies (CS agent sees CRM, finance agent sees reports)32- Compliance domains where the prompt must not name unauthorized tools33- Migrating from individual MCP servers to one gateway endpoint3435## When NOT to Use3637- Tool registry has fewer than 30 tools — use `rag-mcp-tool-selection` instead38- One-off scripts with hardcoded tool flow39- As the only security layer — gateway is *enforcement*, IAM/RBAC is *source-of-truth*40- For multi-tool workflow planning — see Baidu COLT (Ch6) for tool-graph retrieval4142## Process4344| Step | Input | Action | Output | Verification |45|------|-------|--------|--------|--------------|46| 1 | Tool registry JSON | `Gateway.from_registry_file(path)` | Gateway instance | `len(gateway.registry) > 0` |47| 2 | (Optional) Role-based access policy | `Gateway(registry=..., access_filter=fn)` or `example_access_filter_devops` | Gateway scoped to one role | `len(gateway._visible()) < len(gateway.registry)` for restricted roles |48| 3 | Backend bindings (boto3 / HTTP / MCP forward) | `gateway.register_executor("tool_name", fn)` per tool | Wired gateway | Each `execute` call returns real backend results, not dry-run |49| 4 | User query | `gateway.search(query, top_k=5)` | List of `{name, score}` — descriptions NOT included | Result is JSON-serializable; no tool descriptions leaked |50| 5 | Selected tool name + parameters | `gateway.execute(tool_name, **params)` | `{tool, params, result}` | Access filter raises `PermissionError` for out-of-scope tools |51| 6 | Constant-prompt budget check | `gateway.agent_prompt(query)` | Prompt string | `len(prompt)` does NOT scale with registry size |5253## Rationalizations5455| Agent rationalization | Documented rebuttal |56|-----------------------|---------------------|57| "RAG-MCP top-K injection is good enough." | True at 30-100 tools. Falls over at thousands per the chapter's Writer / HFS anchors. Gateway is the next architectural step, not a competing option. |58| "The agent needs to see descriptions to decide." | The agent calls `search` first, gets ranked names, then can inspect one via a discovery tool if needed. Net cost is two round-trips for ambiguous cases vs one bloated prompt always. |59| "Access policies should live in the agent's system prompt." | System-prompt rules degrade under adversarial input (the chapter cites IBM AI Engineer talk + Microsoft Core AI Platform research). Gateway-enforced policies are mechanical, not prompted. |60| "Per-tenant separation isn't worth the complexity." | At scale every multi-tenant SaaS hits this — see Writer's anchor. Defer at small scale, but design the gateway so the access_filter seam exists from day one. |6162## Red Flags6364- `Gateway.search` returns full tool descriptions — leakage breaks the constant-prompt invariant65- `execute` succeeds for tools that `access_filter` should have rejected — IAM/gateway-policy mismatch66- Agent prompt grows with registry size — gateway is being bypassed somewhere67- One tool wins every `search` call — index has degenerate semantic-hooks; fix the registry, not the retriever6869## Non-Negotiable Verification70711. **Constant-prompt invariant.** `len(gateway.agent_prompt(q))` must be identical for a 30-tool registry and a 300-tool registry. If it grows, the gateway is leaking descriptions.722. **Access enforcement.** With `access_filter=example_access_filter_devops`, calling `gateway.execute("secrets_manager_get_secret_value", ...)` MUST raise `PermissionError` — secrets are not in the devops topic set.733. **Search returns names not descriptions.** Inspect `gateway.search(q)` output — every element is `{name, score}`, no `description` key present.744. **CLI `--help` exits 0.** Multi-harness invariant: the skill must be runnable from any CLI harness.7576## Security Posture7778- **Prompt injection.** Tool descriptions never enter the prompt — major reduction in injection surface vs RAG-MCP. The `search` call still uses the description-text for retrieval, so a malicious description biases retrieval but never reaches the LLM verbatim.79- **Data exfiltration.** `execute` calls user-registered executors with `**params`. Validate params at the executor boundary (this skill does not — it's a substrate primitive).80- **Privilege escalation.** Access filter is the enforcement seam. Wire it to your IAM/RBAC source-of-truth. Do not trust the agent to enforce its own permissions.8182## Composition8384- **Pairs with** `rag-mcp-tool-selection` (sibling Ch6 skill) — gateway uses the same retriever internally (`lib.score_tool` swap point) but exposes a different API shape to the agent.85- **Pairs with** AAuth identity primitives (Posta canonical) for per-agent access tokens flowing through `access_filter`.86- **Inputs** can be **outputs** of `enhance_tool_representation` (Toolshed pattern, Ch6) — richer per-tool descriptions improve `search` retrieval without affecting agent prompt cost.8788## Source Attribution8990Distilled from *Agentic GraphRAG* (O'Reilly), Chapter 6 — Tool Orchestration. Specific references:9192- Writer's production enterprise MCP gateway (three-layer architecture: OpenAPI/Postman ingestion + Palmyra X5 description refinement + two-meta-tool surface)93- Phil Fersht's HFS / Cognizant research on multi-agent governance failure above 5 agents94- Microsoft Core AI Platform research on prompt bloat at MCP scale