FrootAI MCP becomes a router
For most of the last year, every team that wanted AI agents to talk to Azure, GitHub, Playwright, and library docs had to install four separate MCP servers, wire them into four separate config files, learn four separate auth flows, and pray that the four subprocesses didn't fight over a port.
Today we're shipping the change that ends all four problems. The FrootAI MCP server stops being one tool surface and becomes a federation router — one MCP connection that exposes every Tier-1 tool from every Tier-1 publisher, gated by a single trust manifest you control.
The v6 lockstep release shipped five surfaces in the same hour: [email protected] on npm + PyPI, the [email protected] CLI, the [email protected] extension, the frootai/frootai@v6 GitHub Action, and v2.0.0 of the Foundry hosted prompt agent. Every one of them speaks the same federation protocol. Every one of them reads the same trust manifest. Every one of them lands on latest/stable at the same time.
What "becomes a router" means in practice
Here's the before:
``
Your agent ──▶ Azure MCP server (port 1)
──▶ GitHub MCP server (port 2)
──▶ Playwright MCP server (port 3)
──▶ MS Learn MCP server (port 4)
`
Four processes. Four configs. Four sets of credentials in scope. Four cold starts.
Here's the after:
`
Your agent ──▶ FrootAI MCP (one process)
│
├──▶ azure.* tools
├──▶ github.* tools
├──▶ playwright.* tools
├──▶ markitdown.* tools
├──▶ ms-learn.* tools
├──▶ context7.* tools
└──▶ chrome-devtools.* tools
`
One process. One config. Every tool prefixed by its area, so azure.deploy_resource is unambiguously the Azure MCP's deploy_resource and never gets confused with anyone else's. The router handles spawn, lifecycle, trust evaluation, and timeout for each area transparently.
The two declarations that make it work
Federation isn't magic. It's two declarations:
On the agent side, you add mcpAttachments to the agent's frontmatter:
`yaml
`
name: "Azure Architect"
mcpAttachments:
required: ["azure"]
optional: ["github", "playwright"]
This tells the engine: "I need Azure, I can use GitHub and Playwright if they're available, please attach them before my first turn."
On the play side — for solution plays that wire up multi-agent workflows — you add mcp_scope:
`yaml
`
mcp_scope:
attached: ["azure", "github", "playwright"]
This tells the engine: "This entire solution requires these three areas. Refuse to start if any of them can't be attached." The router enforces the union: anything the play needs, anything any agent in the play needs, anything any skill in the play needs.
Two declarations. The router does everything else.
The merge semantics nobody asks about until they need them
Suppose a user activates a play. The play declares mcp_scope.attached: ["azure", "context7"]. The play's primary agent declares mcpAttachments.required: ["azure"] and mcpAttachments.optional: ["github"]. One of the skills the agent might invoke declares requiresMcp: ["azure"].
The engine doesn't just pick one source and ignore the others. frootai/engine/context-resolver.js reads all three, computes the union, and hands the result to frootai/engine/mcp-bridge.js:
`
required (UNION): ["azure", "context7"] (play attached + agent required)
optional (UNION): ["github"] (agent optional, not elsewhere)
`
The bridge then calls fai_attach_mcp for each area in the required set, gracefully degrades anything in the optional set that fails, and exposes the merged tool registry to the LLM. Skill pre-flight runs after attach completes: if a skill's requiresMcp isn't satisfied, the skill aborts with a named error before its first token. No silent half-execution.
If you flip mcp_scope.router_config.detach_on_finish: true on the play, the engine tears down attached areas at session end so the next play doesn't inherit them. That option matters most when you're running heterogeneous workflows back-to-back on the same long-running engine instance — say, a CI job that processes ten plays in a loop.
Router vs gateway — why the word matters
We deliberately picked "router" instead of "gateway" because it ships a different contract.
A gateway is a chokepoint: every request goes through it, the gateway transforms the request, then forwards. The semantic model is HTTP middleware. Latency stacks.
A router is a switchboard: the client makes one connection, the router opens N back-end connections, the router forwards JSON-RPC calls to the right back-end based on the area prefix. The semantic model is process-tree composition. Latency is dominated by the slowest area, not by the router itself — measured edge-to-edge at sub-10 ms on the hot path.
A concrete walk-through
Say you want to attach an internal MCP server that exposes your team's vector database. You publish it on a private npm registry as @my-org/mcp-vector-db. You add a corresponding entry to frootai/data/mcp-servers-seed.json with trust: "community". The M10.5 validator now accepts the slug; the M10.6 validator blocks attach unless someone declares an override.
You open a PR that adds:
`yaml
mcpAttachments:
required: ["my-vector-db"]
trustOverrides:
my-vector-db: "SEC-892 reviewed 2026-06-10 by @bob, pinned v0.4.0"
`
The PR reviewer sees SEC-892 and pulls up the ticket. The ticket has the supply-chain review, the dependency scan, the scope statement ("only approved for the ai-research-bot agent in our staging cluster"). The reviewer either approves or pushes back with a Jira link to where the review is incomplete. The trust override is now part of git history; revocation is just a PR that deletes it.
Compare that to the world where you publish a @my-org/mcp-vector-db@latest and any agent that types it correctly can attach it. This is why federation needed a year to ship.
The practical consequence: if Azure's MCP server has a 2-second cold start and GitHub's has a 200 ms cold start, your github.* tool calls aren't paying Azure's tax. They route directly.
Trust, the part nobody else does
The other reason this took a year to ship is that the easy version of federation is irresponsible. If the router will attach any MCP server anyone publishes, then a typo in a requires field becomes a supply-chain attack vector.
So we built a trust manifest. Every MCP server has a trust tier:
- first-party-ms — published by Microsoft, auto-attached. Azure, GitHub, Playwright, MarkItDown, MS Learn.
- verified-publisher — published by an org that passed marketplace verification, auto-attached. Context7 (Upstash), Chrome DevTools (Google).
- community — published by anyone. The router refuses to attach unless the agent or play declares an explicit trust_overrides entry with a review reference.
- untrusted — flagged or revoked. The router refuses to attach, regardless of overrides.
The trust evaluator is evaluate() in frootai-core/npm-mcp/src/federation/trust.ts. It runs on every single attach. There is no bypass path. There is no "silent attach" doctrine — every attach passes through the gate. We audit this with a CI rule (validate-mcp-trust-policy.js) and we audit the audit (the M11.4 security review).
If you want to attach a community server you reviewed yourself, you add a trust_overrides entry that names the review ticket:
`yaml
mcpAttachments:
optional: ["my-internal-tool"]
trustOverrides:
my-internal-tool: "SEC-4421 reviewed 2026-06-15 by @alice, pinned v2.1.0"
`
The override is a free-form string. The CI doesn't validate the ticket; that's your security team's job. But every override gets surfaced in PR review, so nobody quietly bypasses the policy.
Backward compatibility — the boring promise
The most important thing about a "the X is now a router" announcement is that the X you've been using for the last year still works.
[email protected] consumer code runs unchanged on @6.0.0 when you set FROOTAI_FEDERATION=off. All 52 v5 tools are still registered. The bin/frootai-mcp entry point still resolves. knowledge.json and search-index.json still parse the same. The npx frootai-mcp cold start is 150 ms at p95 (down from a 3-second budget we'd reserved).
The M11.2 backward-compat gauntlet runs 20 checks across 5 sample consumer-project scenarios on every PR and exits zero. The day you upgrade your CI pipeline from @5.x to @6.0.0 without setting any new env var, nothing breaks. The day you flip FROOTAI_FEDERATION=on, the router takes over.
Cold start, with proof
The other thing about routers is that they're famously slow. People assume that a process spawning four subprocesses to talk to four areas must take seven seconds to boot.
We measured. Cold start with no pre-attach: 150 ms p95. Cold start with four Tier-1 areas pre-attached (azure, github, playwright, markitdown): 5.15 seconds p95, against an 8-second budget.
The budget is honest. Spawning four npx-cached MCP server subprocesses takes time. We use a synthetic harness in frootai-core/benchmarks/launch-perf.json to measure it deterministically across CI runs. If a future ship breaks the budget, the CI gate fails and the launch blocks.
What ships with this release
All on latest/stable at the same time:
| Surface | Version | Channel |
|---------|---------|---------|
| frootai-mcp (npm + PyPI) | 6.0.0 | latest |
| frootai (CLI) | 6.1.0 | latest |
| frootai-vscode (extension) | 6.1.0 | Marketplace stable |
| frootai/frootai (Action) | v6.0.0 (and v6 major branch) | GitHub Marketplace |
| Foundry agent frootai-enterprise-rag | 2.0.0 | Production slot |
| Hosted MCP at mcp.frootai.dev | GA | Operational — Stable |
Six surfaces. One protocol. Same hour.
Federation content already wired
Twelve MCP-specialist agents declare mcpAttachments. Ten high-traffic solution plays declare mcp_scope.attached. Ten language MCP plugins declare requiresMcp: ["context7"] for docs grounding. Five representative skills declare requiresMcp to exercise the field across the cookbook. Forty-six total slug references across the corpus, every one resolved against the canonical seven-server roster, every one passing the trust policy.
If you install today and run npx frootai mcp list, you'll see the seven Tier-1 areas. If you run npx frootai agent run fai-azure-architect --prompt "Design a hub-spoke network", the engine reads the agent's declaration, the router attaches Azure, and the agent's first turn has all 200-something Azure tools in scope. No extra config.
What's next
The 14-day post-launch window starts now. Telemetry watch on federation_attach_completed / ..._failed ratio. Per-day adoption tracking: % of v6 installs that successfully complete one fai_attach_mcp within 24 hours. NPS survey at +7 days. P0/P1 hotpatch template pre-staged for any regression caught in the first week.
After that: cross-server tool composition (call azure.list_resource_groups and pipe straight into github.create_issue without round-tripping to the LLM) and signed bundles for the marketplace. Both are scoped in the next-arc planning kickoff that lands at M11.30 close.
For now: pick a config, point an MCP client at the router, and tell us what you build.
Links
- npm: [email protected]
- PyPI: frootai-mcp==6.0.0
- CLI: [email protected]
- VS Code: [email protected]
- GitHub Action: frootai/frootai@v6
- Hosted MCP: https://mcp.frootai.dev/mcp
- Status: status.frootai.dev`
- Cookbook (federation): Recipe 19 · Recipe 20 · Recipe 21 · Recipe 22 · Recipe 23