An experiment comparing three ways browser-based agents can cooperate:
- Direct WebMCP: the extension agent calls page tools itself.
- Agent as a WebMCP tool: the extension delegates reasoning to the on-page agent.
- A2A: the extension talks to the on-page or backend agent using A2A messages and task lifecycles.
Warning
This repository is for demonstration and educational purposes only. It does not represent a real product or production-ready service. The authentication, credential storage, persistence, security controls, agent behavior, and protocol coverage are deliberately simplified to make the examples easier to understand.
Do not use production API keys, personal information, confidential content, or other sensitive data with this demo, and do not expose it directly to the public internet. References to A2A compatibility describe only the tested subset documented below; they are not a security review, compliance claim, or certification.
Available under the MIT License.
| Component | Runs in | Role |
|---|---|---|
| On-page agent | Demo tab | Manages the notes board; exposed through WebMCP and experimental A2A-over-postMessage |
| Extension agent | Chrome side panel | Discovers page tools and chooses direct WebMCP, delegation, or A2A |
| Backend agent | Node server | A2A/HTTP agent with caller-scoped memos, meetings, contexts, and tasks |
The page and extension use the same dependency-free multi-provider agent loop. The backend keeps provider keys server-side and cannot see the page DOM.
The demo supports Anthropic, OpenAI, and Google Gemini. The site and extension each
have a collapsed provider/model/key control. Keys are remembered per provider in
browser storage; the backend reads its key from environment variables or .env.
Requirements:
- Node.js 20+
- Chromium with
chrome://flags/#enable-webmcp-testingenabled - A provider key for each agent you intend to run
Install dependencies:
npm installCreate .env, set one provider key, and optionally select the backend model:
ANTHROPIC_API_KEY=sk-ant-...
# OPENAI_API_KEY=sk-...
# GEMINI_API_KEY=AIza...
# AGENT_MODEL=claude-haiku-4-5Start the server:
npm startThen:
- Open
http://localhost:8787and configure the on-page model. - In
chrome://extensions, enable Developer mode and loadextension/unpacked. - Open the extension side panel and configure its model.
- Discovery runs automatically. A retry button appears only when discovery fails.
After extension changes, click Reload in chrome://extensions.
Use the side panel channel selector or scenario buttons:
- Direct tools: summarize or edit notes through native WebMCP tools.
- Agent as tool: delegate a request to
ask_page_agentthrough WebMCP. - A2A to page: reach the same on-page agent with A2A v1-shaped JSON-RPC carried by
the custom
postMessagebridge. - A2A to backend: reach the independent backend agent over HTTP/SSE.
- Input required: request a launch meeting without a day, then answer the backend's
question against the same
taskId.
The launch meeting is an invented demonstration scenario, not an official A2A example. Meetings and tasks exist only in backend memory.
The extension's Caller selector uses illustrative bearer tokens:
- Alice:
demo-alice - Bob:
demo-bob
Backend contexts, tasks, memos, and meetings are bound to the authenticated caller. Alice cannot retrieve, resume, or cancel Bob's tasks. These tokens demonstrate the authorization boundary; they are not production credentials.
The backend can be called without the website:
node examples/a2a-client.mjs "What memos do you have?"
node examples/a2a-client.mjs "Book a meeting about the launch" --stream
node examples/a2a-client.mjs "Friday" --task <taskId>Use --token demo-bob to switch callers.
The backend implements a focused JSON-RPC/SSE subset of the released A2A v1.0.1 specification:
- Agent Card discovery through
/.well-known/agent-card.json SendMessageSendStreamingMessageGetTaskCancelTaskinput-requiredtask resumption
Agent interfaces and the A2A-Version header use wire version 1.0, as required for
the v1 protocol line. Automated interoperability tests use the official JavaScript v1
SDK, currently published as @a2a-js/sdk@1.0.0-beta.0.
This verifies the named subset; it is not certification of every A2A operation or
transport. The browser-side postMessage pipe reuses A2A data and operation shapes but
is not a standard A2A transport binding.
npm run buildThe build checks generated extension copies and runs the model-free protocol and official-SDK interoperability tests.
When editing shared runtime files, synchronize their extension copies:
npm run sync:extension- Browser keys are stored locally and sent directly to provider APIs.
- Backend state is in memory and disappears when the process stops.
- The
postMessagecapability hardens routing but cannot authenticate against another script already running in the same window. - Built-in callers and bearer tokens are illustrative only.
- Production use would require real authentication, persistent storage, quotas, rate limits, broader protocol coverage, and operational hardening.
| Problem | Fix |
|---|---|
| WebMCP unavailable | Enable chrome://flags/#enable-webmcp-testing and restart Chromium |
| No bridge in active tab | Focus the localhost:8787 tab and use Retry discovery |
| Missing provider key | Configure the selected provider in the relevant site, extension, or backend host |
| Extension behaves stale | Run npm run sync:extension, reload it in chrome://extensions, and reopen the panel |
| Port 8787 is occupied | Stop the existing process or configure a different server port consistently |