You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(agent): list connected browser tabs and route client tool calls per tab
Forward agent-flagged in-page channel functions to the node MCP endpoint
(ports the bridge from #376 onto 0.10), and make each connected browser
tab visible and addressable: a built-in devframe:agent:list-clients tool,
a reserved client_id argument on forwarded tools, and focus-aware default
routing so an unaddressed call follows the tab the user looked at last.
devframe connect nests the tab list under mcp.clients per instance.
Closes#394
Copy file name to clipboardExpand all lines: docs/content/1.guide/12.in-page-channel.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -64,7 +64,7 @@ Channel names are namespaced with the devframe id, like RPC ids. Function names
64
64
65
65
## The page script endpoint
66
66
67
-
The required `functions` option and optional `events` option declare every incoming name on the endpoint's protocol side; use `{}` for an empty direction. Functions require a `handler`. Events accept an optional `handler`, and `{}` registers an event for runtime subscriptions through `on()`. Handlers are contextually typed from the shared protocol and support Standard-Schema argument validation and `jsonSerializable` metadata. A function with `agent` metadata is available to coding agents through MCP; the field implicitly enables strict JSON serialization (an explicit `jsonSerializable: false` conflicts). `defineChannelFunction` retains the named definition shape for lower-level authoring.
67
+
The required `functions` option and optional `events` option declare every incoming name on the endpoint's protocol side; use `{}` for an empty direction. Functions require a `handler`. Events accept an optional `handler`, and `{}` registers an event for runtime subscriptions through `on()`. Handlers are contextually typed from the shared protocol and support Standard-Schema argument validation and `jsonSerializable` metadata. A function with `agent` metadata is forwarded to coding agents [over the node's MCP endpoint](/guide/agent-native#in-page-tools-over-mcp); the field implicitly enables strict JSON serialization (an explicit `jsonSerializable: false` conflicts). `defineChannelFunction` retains the named definition shape for lower-level authoring.
68
68
69
69
`call()` accepts names from `functions`, including actions returning `void` or `Promise<void>`: callers can await completion and catch errors or timeouts. `emit()` and `on()` use the names declared in `events`. Function and event names have separate namespaces.
Copy file name to clipboardExpand all lines: docs/content/1.guide/15.agent-native.md
+21Lines changed: 21 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -160,6 +160,27 @@ rpc.client.register({
160
160
> [!WARNING]
161
161
> WebMCP is an experimental proposal; `registerWebMcpTools` tracks the current draft (`AbortSignal`-based unregistration) and earlier handle-returning drafts, but the browser API may still change.
162
162
163
+
## In-page tools over MCP
164
+
165
+
An [in-page channel](/guide/in-page-channel) function carrying an `agent` field is forwarded to the node side over the page's RPC connection and served from the same MCP endpoint as node-side tools, under the id `<channel name>:<function name>`. The page keeps executing the handler; the node relays the call and the result.
166
+
167
+
```ts
168
+
const channel =createPageScriptChannel({
169
+
name: 'my-plugin',
170
+
functions: {
171
+
'selected-node': {
172
+
type: 'query',
173
+
agent: { description: 'Return the node the user selected in the page. Call it before proposing an edit.' },
174
+
handler: () =>getSelectedNode(),
175
+
},
176
+
},
177
+
})
178
+
```
179
+
180
+
Every browser tab exposing such tools syncs its own manifest, so the same app open in several tabs exposes each tool once, plus a built-in `devframe:agent:list-clients` tool (wire name `devframe_agent_list-clients`) listing the connected tabs: a per-tab `id` (stable across reloads), `url`, `title`, `visible`/`focused`, `connectedAt`, and the tool ids that tab exposes. `devframe connect` nests the same list under `mcp.clients` for each instance.
181
+
182
+
A forwarded tool accepts a reserved `client_id` argument to run on one tab. Without it, the call goes to the most recently focused visible tab, falling back to the tab that synced last; an unknown `client_id` fails with [DF0081](/errors/DF0081), which lists the live ids. Tabs re-sync on focus and visibility changes, so an unaddressed call follows the tab the user looked at last.
183
+
163
184
## Writing descriptions agents act on
164
185
165
186
Describe *when* to use a tool, not just its return:
description: 'Tool "{tool}" was addressed to client "{clientId}", but no connected browser tab has that id and the tool.'
4
+
---
5
+
6
+
## Message
7
+
8
+
> Tool "`{tool}`" was addressed to client "`{clientId}`", but no connected browser tab has that id and the tool. Connected clients: `{live}`.
9
+
10
+
## Cause
11
+
12
+
A forwarded in-page tool was called with a `client_id` that matches none of the browser tabs currently connected to this devframe (or the tab with that id does not expose the tool). Tab ids survive reloads but not closing the tab, so an id an agent listed earlier may have gone away since.
13
+
14
+
## Fix
15
+
16
+
Call `devframe:agent:list-clients` (wire name `devframe_agent_list-clients`; `mcp.clients` in `devframe connect`'s `list-instances`) to get the live ids and retry with one of them, or omit `client_id` to target the most recently focused tab.
17
+
18
+
## Source
19
+
20
+
-[`packages/devframe/src/node/client-agent.ts`](https://github.com/devframes/devframe/blob/main/packages/devframe/src/node/client-agent.ts): the forwarded tool's handler throws this when no connected session matches the requested `client_id`.
description: 'Discover every running devframe dev server on this machine and list each one\'s MCP tools. Call this FIRST, before assuming which devtools are available; the result names the instance (id, project root, origin) and the port to pass to the call tool. Safe to call freely.',
107
+
description: 'Discover every running devframe dev server on this machine and list each one\'s MCP tools, plus the browser tabs connected to it (`mcp.clients`). Call this FIRST, before assuming which devtools are available; the result names the instance (id, project root, origin) and the port to pass to the call tool. Safe to call freely.',
port: {type: 'number',description: 'The instance\'s port, from the list-instances tool.'},
114
119
tool: {type: 'string',description: 'Tool name, from the instance\'s tool list.'},
115
-
args: {type: 'object',description: 'Arguments object for the tool. Omit for zero-argument tools.'},
120
+
args: {type: 'object',description: 'Arguments object for the tool. Omit for zero-argument tools. Tools forwarded from a browser tab accept `client_id` (from `mcp.clients` in list-instances) to target one tab; omitted, the most recently focused tab runs it.'},
116
121
},
117
122
required: ['port','tool'],
118
123
additionalProperties: false,
@@ -187,7 +192,7 @@ async function index(options: ConnectServerOptions): Promise<unknown> {
0 commit comments