Not as a bolt-on Gateway. Not as a separate Semantic Filter service. As an organic, built-in architectural property of the agent itself — through context isolation, subagent orchestration, and on-demand resource loading.
Ampcode, brought to you by Engineering Software Lab (ESL)
Liu et al. (arXiv 2023 / TACL 2024) showed that long-context models often retrieve best from the beginning or end of the prompt, while information placed in the middle is used less reliably.
In Liu et al.'s experiments, relevant information near the beginning was often retrieved more reliably.
The paper's key result is a pronounced dip when the answer-bearing document or key sits in the middle.
Many models rebound when the same information is moved near the end, producing the familiar U-shaped curve.
(1) Static tool-definition tax: Public GitHub MCP reports have shown dozens of tools and roughly tens of thousands of tokens depending on version and configuration. Add Slack, Jira, Sonar, and the starting context can get crowded fast.
(2) Dynamic execution noise: Every grep output, stack trace, and large file read accumulates. Long debugging sessions can end up dominated by history that is no longer useful.
The "MCP Gateway" architecture (LiteLLM, AWS AgentCore, etc.) addresses static tool bloat by exposing only N relevant tools per session. Necessary — but not sufficient.
Insight: Filtering inputs is reactive. The real fix is preventing context from accumulating in the first place — through architectural isolation. That is the design Amp ships organically.
Instead of one giant context window stuffed with everything, Amp orchestrates N disposable child contexts that return only their summary to the parent. The main thread stays lean by construction.
// Everything lives in one window main_context = [ system_prompt, all_tool_defs, AGENTS.md, read_file_1..N, grep_output_1..M, oracle_reasoning, user_turns, ] // Search results, raw reads, and reasoning all accumulate in one place
main_context = [ system_prompt, builtin_tools, AGENTS.md (scoped), user_turns + summaries ] // delegates large work to focused tools/subagents Task(...) oracle(...) finder(...) librarian(...) // Main thread stays smaller because children return distilled results
The flagship primitive. Spawns a child agent with its own context window, its own tool budget, and its own model invocation loop. Returns only a final summary to the parent.
A child agent can search, read, edit, and verify in its own window while the parent keeps only the task brief and the outcome.
Parallel fan-out lets Amp do independent work in parallel without stuffing every intermediate grep, file read, and failed attempt into the main thread.
// Real invocation pattern from Amp's tool schema Task({ description: "Convert all CSS to Tailwind", prompt: `Convert these 12 CSS files to Tailwind classes: [list]. Conventions in AGENTS.md. Run \`npm test\` to verify. Return: list of changed files + any failures.` }) // → spawns isolated agent, returns summary only
A second-opinion advisor backed by GPT-5.4. It can read files, search code, and browse the web, then return focused advice instead of raw investigative output.
Oracle is useful for architecture review, code review, and tricky bugs that benefit from a stronger reasoning pass.
It keeps deep analysis off the main interaction path, so the parent thread gets the recommendation rather than the whole reasoning journey.
Tradeoff: Oracle is intentionally slower and more expensive than Amp's everyday coding path, so it is best used where the extra reasoning matters.
oracle({ task: "Review the authentication architecture and find race conditions", files: ["src/auth/index.ts", "src/auth/jwt.ts", "src/auth/session.ts"], context: "We're seeing intermittent 401s under load" }) // Oracle reads files in its own window, grep-searches related code, // runs deep CoT reasoning, returns 1-2 page actionable analysis.
"Where do we validate JWT headers?" no longer has to mean a long grep/read/grep chain in the main thread. Amp can delegate that search to a dedicated search subagent and bring back just the useful pointers.
Grep("jwt") Grep("verify") Grep("validateToken") Read(file_a) Read(file_b) Grep("middleware") // The main thread accumulates search noise and dead ends
finder({ query: "Find every place we verify JWT auth headers. Return file paths + line numbers." }) // → search subagent runs the investigation internally // → returns: // src/auth/jwt.ts:42-58 // src/middleware/auth.ts:15 // src/api/routes.ts:233 // Main thread sees the answer, not the full search trail
Search is one of the easiest ways to flood a thread with low-value text. By making search itself a subagent, Amp can keep bulky intermediate results out of the main thread unless they are explicitly needed.
Reads public GitHub, private GitHub, Bitbucket Enterprise. Cross-repo research is one of the worst Lost-in-the-Middle offenders — Librarian quarantines it in its own subagent.
Cross-repo research can easily swamp a coding thread with code that is not local to the task at hand.
Librarian keeps that investigation off to the side and brings back a focused explanation instead of raw repository spelunking.
Skills are SKILL.md files registered with the agent. Only the name + description stay visible by default. The full body - workflows, scripts, references - loads only when invoked.
~/.agents/skills/cve-vulnerability-lookup/ ├── SKILL.md # body (lazy-loaded) ├── mcp.json # bundled MCP server ├── scripts/ │ └── lookup.py └── templates/ └── report.md.j2
Frontmatter exposes name and description only. Everything else is dormant until skill("cve-vulnerability-lookup") is called.
An MCP server bundled inside a skill (mcp.json) starts at launch but its tools stay hidden until the skill is loaded. This is Amp's organic answer to Gal's "Semantic Filter" — without a Gateway service.
This lines up with the broader idea of pre-filtering tool exposure before the model sees a large catalog.
Project context lives in nested AGENTS.md files. The key feature is glob-scoped @-mentions: a referenced file only enters context if Amp has read a matching file.
# AGENTS.md (project root) @docs/typescript-conventions.md globs: ["**/*.ts", "**/*.tsx"] # Only loaded if Amp reads a TypeScript file @docs/backend-rules.md globs: ["server/**", "api/**"] # Only loaded for backend work @docs/test-conventions.md globs: ["*.test.ts", "__tests__/*"] # Only loaded during testing tasks
Without scoping, unrelated project guidance stays active all the time even when it has nothing to do with the task in front of the model.
Working on a Python script? TypeScript rules don't load. Editing tests? API conventions stay dormant. Context size scales with relevance, not with documentation volume.
Review prompts live in .agents/checks/*.md at the module level. Each check runs as its own subagent in parallel with its own context. Findings are aggregated.
repo/ ├── .agents/checks/ │ └── api-standards.md # global ├── payments/ │ └── .agents/checks/ │ └── pci-compliance.md # module ├── auth/ │ └── .agents/checks/ │ └── security.md # module └── frontend/ └── .agents/checks/ └── a11y.md # module
When a thread accumulates noise (failed attempts, dead-end traces), handoff analyzes the thread, drafts a prompt for a new one, and carries forward the relevant files for the next task.
Long thread
dead ends + stale history
Fresh thread
drafted prompt + relevant files
handoff({ goal: "Continue refactoring the payments module — focus on extracting the Stripe adapter.", follow: true })
Handoff replaces summary-stacking with a user-reviewable fresh start. Amp drafts the next-thread prompt, shows it to you, and lets you edit it before sending.
Why it helps: you leave behind irrelevant history instead of carrying it forever in the same conversation.
Replace MCP servers with simple executables in $AMP_TOOLBOX. Each script self-describes via stdout key-value pairs.
Why it helps: No JSON-RPC overhead, no boilerplate tool schemas. A 5-line bash script = a tool. Drastically smaller tool definitions.
Amp's built-in tools (Read, Grep, glob, Bash, Task, oracle, finder, librarian, ...) cover common coding workflows without needing an MCP server for everything.
The official guidance: "Use built-ins. Add MCP only when truly needed. Bundle MCPs in skills."
Tool-level allow/deny lists per workspace. Untrusted MCPs and skills can be sandboxed. amp permissions list.
Governance at the tool boundary is built into Amp instead of requiring a separate gateway layer.
Every Amp tool follows the same design rule: if a task can produce big output, give it its own context. The agent's job is to orchestrate small summaries, not swim in raw data.
Everything piles into one window.
The danger is not one specific number. It is that search trails, raw reads, and intermediate reasoning all compete with the actual task.
Main thread stays closer to the actual goal.
Search, deep analysis, and remote-repo research can happen in dedicated tools/subagents, so the parent sees the result instead of every intermediate step.
Amp does not magically eliminate context limits. It changes where bulky work happens, so the main conversation can stay more focused while child contexts do the noisy investigation.
| Concern | External Gateway + Semantic Filter | Amp (organic, built-in) |
|---|---|---|
| Static tool bloat | ✓ Filter to N relevant tools | ✓ Skills hide MCP tools until loaded |
| Tool output pollution | △ Not addressed by filtering alone | ✓ Task / finder / oracle in own contexts |
| Long search chains | △ Depends on the main agent's search behavior | ✓ finder subagent returns summaries |
| Heavy reasoning passes | △ Depends on the main agent's design | ✓ oracle offloads deep analysis |
| Cross-repo research | △ Can still expand the main context if done inline | ✓ librarian quarantines it |
| Per-module guidance | △ Not inherent to gateway/filter design | ✓ Nested AGENTS.md + glob-gated @-refs |
| Code-review at scale | △ Not inherent to gateway/filter design | ✓ Per-module .agents/checks/*.md in parallel subagents |
| Session-level decay | △ Does not reset conversation history by itself | ✓ handoff resets with curated context |
| Infrastructure required | Gateway service + Registry + Semantic index | None — ships in the agent binary |
| Per-call latency | Often adds an extra network/policy layer | In-process |
One useful way to think about the difference: filtering decides what the model sees, while orchestration also decides where bulky work happens.
Reactive. Operates on the static catalog. Once tools are loaded, you're back in the same window.
Proactive. Operates on the execution dimension. Big work moves to disposable contexts, not the main one.
Amp does both. Skills + mcp.json filter what loads. Subagents control where work runs. In practice, that addresses more of the context problem than filtering alone.
// Gateway/Filter approach context(t) = system + filter(tools, query) + history(t) // → history(t) grows monotonically → eventually saturates // Amp orchestrated approach main(t) = system + builtins + scoped_AGENTS + summaries(t) where summaries(t) = Σ subagent_i.summary and Σ |summary| ≪ Σ |subagent_i.full_context| // → main stays bounded; work scales horizontally
If tokens are cheap and quality is paramount, the right move is to spend more tokens in parallel disposable contexts, not to cram them into one window.
Different subagents can use different models. Oracle uses GPT-5 reasoning; main agent uses fast frontier coder. Each tool, the right model.
Don't try to RAG/compress your way around context limits — give the model a clean, focused window every time.
Longer context windows do not automatically fix middle-retrieval problems. As models scale, orchestration can still matter because it keeps work focused instead of merely making the window larger.
The principles directly imply the design. Context isolation isn't a feature Amp added — it's the consequence of taking these principles seriously.
A Gateway treats Lost-in-the-Middle as an input filtering problem.
Amp treats it as an execution architecture problem.
The second framing addresses a broader part of the problem and ships inside the agent itself.
Your agent isn't dumb. It's just lost in the middle.
Amp's solution: never let it get there in the first place.
Ampcode, brought to you by Engineering Software Lab (ESL)