Skip to content

docs(specs): WebMCP host — the SPA as an agent-callable surface - #1004

Open
philmerrell wants to merge 1 commit into
developfrom
feature/webmcp-host-spec
Open

docs(specs): WebMCP host — the SPA as an agent-callable surface#1004
philmerrell wants to merge 1 commit into
developfrom
feature/webmcp-host-spec

Conversation

@philmerrell

Copy link
Copy Markdown
Contributor

What

Spec only — no code. docs/specs/webmcp-host-spa-tools.md.

Started as "how do we build a browser agent so we can use WebMCP later?" and the research inverted the ordering.

The reframe

WebMCP yields two different products, and only one is worth building now:

  • Outbound — our agent drives other people's sites through a headless cloud browser. WebMCP helps only where the target adopted it; Chrome's origin trial opened May 2026 and external adoption is ~zero. So it has to justify itself on DOM driving alone, where it's an expensive commodity: per-session billing on top of tokens, unbounded per-step payloads, bot-blocking, and no story for anything behind a login.
  • Inboundour platform becomes agent-callable. Our agent drives the UI the user is looking at, in their authenticated session. Later, external agents (Claude for Chrome, Gemini) can drive the platform through the same declarations.

Inbound pays off immediately, depends on nobody else's adoption curve, and needs no AgentCore Browser, no Playwright, no headless anything. It's also strictly cheaper: outbound produces unbounded payloads by construction, while an inbound call returns a bounded structured result — the page already knows its own state, so there's nothing to scrape.

We already ship most of a WebMCP host

It's just pointed at iframes:

  • mcp-app-bridge.ts:186/:648 — host→View JSON-RPC requests with promise correlation already exist (used for resource teardown)
  • app_tool_dispatch.py — page→agent tools/call with full auth/OAuth/consent, no model turn, echoed into the live stream
  • app_context_dispatch.py:79/:155 — a prompt-cache-safe way to get page state into a turn
  • session_lease.py — a proven owner-scoped side channel into a running turn (Stop, mid-turn steering)

What's missing: any notion of the top-level SPA as a provider, and the transport from the inference container out to the browser mid-turn.

The decision that shapes everything (§D3)

Page tools are route-scoped, so they churn on navigation. All tool sources converge at _build_filtered_tools() (base_agent.py:448) into toolConfig — the cached prefix. Putting page tools there rewrites a 30k–150k-token prefix at the cache-write premium every time the user navigates.

So toolConfig gains exactly one stable entry, page_action(name, arguments), and the list of available tools rides the per-turn context mechanism MCP Apps PR-6 already built (stashed on agent.state, rendered into that turn's prompt only, sorted by name). Same lever as tool-search-token-bloat-strategy.md, except here it's mandatory rather than advisable.

PR sequence

  1. SPA tool registry + declarations (frontend only, nothing exposed)
  2. The agent→page call path — page_action, page_tool_call SSE event, lease-row side channel. Carries the real risk.
  3. Per-turn tool listing + a toolConfig byte-stability test across a navigation
  4. document.modelContext projection for third-party agents

PR-1–3 deliver the capability. PR-4 is the standards bet, and the only piece coupled to an unstable external spec — §D1 keeps our own registry as the primitive so Angular's provideExperimental* churn is a one-file adapter change.

Worth arguing about

  • R1 (latency). If the SSE-out/lease-row-in round trip is slow, this feels worse than just calling an app-api endpoint. Spec says measure in PR-2 before building PR-3 on it.
  • Open question 1. Declarations alone tell the model nothing about what's on screen. A state snapshot would help and costs per-turn tokens — deferred pending a measured budget.
  • Open question 3. Is a page tool a fifth bindable primitive for the Agent Designer? Worth deciding before the registry shape ossifies.
  • Appendix B. Outbound is unbundled, not cancelled. The already-deployed CfnBrowserCustom stays idle — that's optionality, not sunk cost.

🤖 Generated with Claude Code

Spec for inbound WebMCP: our Angular SPA declares tools that the agent can
call in the user's own authenticated session. Inverts the "browser agent
first" framing — WebMCP's payoff for us is inbound, and inbound needs no
AgentCore Browser, Playwright, or headless anything.

Shaping constraint (§D3): page tools are route-scoped, so they churn on
navigation. All tool sources converge at `_build_filtered_tools()` into
`toolConfig`, which is the cached prefix — putting them there would rewrite
a 30k-150k-token prefix on every navigation. So `toolConfig` gains exactly
one stable `page_action(name, arguments)` entry, and the available-tool list
rides the per-turn context mechanism MCP Apps PR-6 already built.

Own-registry-first (§D1) hedges both moving specs: Angular's API is
`provideExperimental*`, and W3C renamed the global and replaced
provideContext() with registerTool() within months. PR-1..3 deliver the
capability without depending on WebMCP; PR-4's document.modelContext
projection is the only piece coupled to the unstable part.

Outbound browsing is unbundled, not cancelled (Appendix B).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant