feat: eagerly expose in-page tools to browser agents - #376
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
61555b9 to
7c2ad90
Compare
72abe22 to
efa762e
Compare
- exposeState registry, in-page channel with agent tools (list/get/set/patch) - node devframe definition with help tool, plain HTML config page, connect.js - Vite plugin, Next handler, Nuxt module - local devframe build (PR devframes/devframe#376) vendored as tarballs
Rebase onto the 0.10 main and drop this branch's own eager client-script implementation, which now lives in main via #387. Keep the browser-to-node agent bridge that turns in-page channel functions into MCP tools. An in-page channel function may now carry `agent` metadata (requiring `jsonSerializable: true`). Page-script and panel endpoints register those as browser-agent tools, mirror the manifest to node over an RPC bridge, and a node-side tool provider registers them on the agent host so they surface over MCP. Invocations round-trip back into the browser through `devframe:agent:invoke-client-tool`. Adapt to the 0.10 MCP move: the discovery-metadata improvement now lands in `@devframes/agentic`'s connect surface, the browser-safe JSON-schema and positional-arg helpers replace the pre-move paths, and the new diagnostic is renumbered to DF0080 to avoid the DF0078 agentic collision. Co-authored-by: agent <agent@opencode>
fee41f5 to
b85af55
Compare
…dupe safety Align the in-page `agent` option with #379: setting `agent` now implies `jsonSerializable: true` instead of requiring it, and DF0080 only fires on an explicit `jsonSerializable: false`. Collapse the three copies of the agent safety inference (WebMCP, the MCP host, and the in-page bridge) into one shared `resolveAgentSafety` helper, and drop the redundant id de-duplication in the browser-agent registry since the node-side tool provider already dedupes. Co-authored-by: agent <agent@opencode>
Groundwork for #394. Each browser tab mints a stable client id (`sessionStorage`-backed nanoid that survives reloads and RPC reconnects) and tags its `devframe:agent:sync-client-tools` payload with it, so the node-side client-agent registry records which tab each session belongs to. This is the identifier only: no per-tab listing or routing yet. Tab duplication copies `sessionStorage`, so duplicated tabs can briefly share an id until the wider tab-metadata work adds disambiguation. Co-authored-by: agent <agent@opencode>
The client-tool bridge only matters when the node exposes an MCP endpoint, so gate it on `connectionMeta.mcp` (already forwarded in `__connection.json`) and pull it in through a dynamic `import()`. Its code — the bridge, the browser tool registry, and the client-id helper — now lands in its own chunk that a non-MCP connection never downloads or runs. Co-authored-by: agent <agent@opencode>
◈ PR Lens
Architecture 10 components touched across 3 lanes. Inside the changed components — 2 viewsComponent view — Browser Client Bridge In-page channel agent registration, browser tool registry, and conditional RPC bridge. Component view — Devframe Agent Host & MCP Client agent session tracking, agent tool provider aggregation, and MCP gateway integration. Data flow
The other flows — 1 sequence
View
Tip Untick Architecture lens or Data flow lens under View to hide a diagram, or tick Expand every detail to open every section. The comment redraws in a few seconds. 🪧 More tips
Thanks for using PR Lens! It's built by Coldtea, free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. |
Update summaryRebased this PR onto the 0.10 1.
2.
3.
4.
Full validation passes on each step: build (incl. the client-dist chunk-reachability guard), This update was prepared with the help of an agent. |
There was a problem hiding this comment.
🟡 Changes recommended
Unresolved issues remain in MCP mounting, synchronization retries, codec handling, output metadata, and session reconciliation.
Get a fresh assessment by requesting another Copilot review.
Pull request overview
Adds eager exposure of agent-enabled in-page functions as browser-backed MCP tools, with browser-to-node synchronization and RPC invocation.
Changes:
- Adds browser tool registration, lifecycle management, synchronization, and invocation.
- Projects browser tools into node-side MCP agents with shared safety and metadata handling.
- Updates tests, API snapshots, diagnostics, and documentation.
File summaries
| File | Summary |
|---|---|
tests/__snapshots__/tsnapi/devframe/index.snapshot.d.ts |
Updates generated API declarations. |
packages/devframe/src/types/rpc-augments.ts |
Declares browser-agent RPC methods. |
packages/devframe/src/node/rpc/index.ts |
Registers manifest synchronization RPC. |
packages/devframe/src/node/rpc/agent-sync-client-tools.ts |
Handles browser tool manifest synchronization. |
packages/devframe/src/node/host-functions.ts |
Removes disconnected sessions. |
packages/devframe/src/node/host-agent.ts |
Uses shared safety resolution. |
packages/devframe/src/node/client-agent.ts |
Aggregates and routes browser tools. |
packages/devframe/src/node/__tests__/client-agent.test.ts |
Tests browser tool projection and invocation. |
packages/devframe/src/in-page-channel/types.ts |
Adds in-page agent metadata. |
packages/devframe/src/in-page-channel/panel.ts |
Manages panel tool registration. |
packages/devframe/src/in-page-channel/page-script.ts |
Manages page-script tool registration. |
packages/devframe/src/in-page-channel/internal.ts |
Implements tool registration and invocation. |
packages/devframe/src/in-page-channel/diagnostics.ts |
Adds the DF0080 diagnostic. |
packages/devframe/src/in-page-channel/agent.test.ts |
Tests in-page agent behavior. |
packages/devframe/src/client/webmcp.ts |
Reuses shared safety resolution. |
packages/devframe/src/client/rpc.ts |
Loads the browser-agent bridge. |
packages/devframe/src/client/rpc-auth-gate.test.ts |
Tests conditional bridge loading. |
packages/devframe/src/client/client-id.ts |
Provides stable tab identifiers. |
packages/devframe/src/client/browser-agent.ts |
Maintains the browser tool registry. |
packages/devframe/src/client/browser-agent-rpc.ts |
Synchronizes and invokes browser tools. |
packages/devframe/src/client/browser-agent-rpc.test.ts |
Tests browser-agent RPC behavior. |
packages/devframe/src/agent/safety.ts |
Centralizes safety inference. |
packages/agentic/src/connect/index.ts |
Preserves MCP tool metadata. |
docs/content/6.errors/DF0080.md |
Documents the new diagnostic. |
docs/content/1.guide/12.in-page-channel.md |
Documents in-page agent exposure. |
Review details
Suppressed comments (4)
packages/devframe/src/client/browser-agent-rpc.ts:62
lastSyncedCountis updated before the sync result is known. If an empty-manifest sync fails while the RPC session stays alive, a laterconnectedevent seesmanifests.length === 0 && lastSyncedCount === 0and skips retrying, leaving the server's old tools exposed. Update the count only aftercallOptionalsucceeds.
lastSyncedCount = manifests.length
await rpc.callOptional('devframe:agent:sync-client-tools', resolveClientId(), manifests).catch(() => {})
packages/devframe/src/client/browser-agent-rpc.ts:67
connection:statusis the canonical client event already defined inDEVFRAME_EVENTS.client.connectionStatus, and the transport code uses that map. Referencing the raw string here violates the repository's event-map contract and can let this listener drift if the event is renamed.
const stopConnection = rpc.events.on('connection:status', (status) => {
packages/devframe/src/in-page-channel/internal.ts:290
registry.resolve()is the MessagePort receive pipeline: it applies the endpoint'sdeserializehook to arguments andserializehook to the result. Browser-agent calls arrive as already-JSON tool inputs and return directly to the MCP bridge, so a channel using a custom codec will transform the agent payload/result unexpectedly. Invoke through a local agent path that keeps JSON/schema validation but bypasses channel codecs.
invoke: (args) => {
const positional = toolInputToRpcArgs(args, definition.args?.length)
return registry.resolve(channelMethod(definition.type, definition.name))!(...positional)
packages/devframe/src/in-page-channel/internal.ts:287
- The in-page function's
returnsschema is dropped from the browser manifest here. The node agent projection usestool.outputSchemato advertise structured output and populate MCPstructuredContent, so an object-returning in-page tool is exposed as text-only despite declaringreturns. Include the converted return schema in the manifest/provider projection.
safety: resolveAgentSafety(definition.type, agent),
tags: agent.tags,
inputSchema: argsToJsonSchema(definition.args),
- Files reviewed: 24/25 changed files
- Comments generated: 2
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| if (connectionMeta.mcp) { | ||
| void import('./browser-agent-rpc') | ||
| .then(({ setupBrowserAgentRpcBridge }) => { | ||
| if (!closed) | ||
| disposeBrowserAgentBridge = setupBrowserAgentRpcBridge(rpc) |
| const state = getState(context) | ||
| state.sessions.set(session.meta, { session, clientId, tools }) | ||
| state.notifyChanged() |
agentsis added to in-page functions