Skip to content

MCP: list every connected browser tab and route client tool calls per tab #394

Description

@antfubot

Problem

With several browser tabs of the same app open (same port), the MCP surface acts as if there were one client:

  • devframe_connect_list-instances lists node instances keyed per (port, basePath) (packages/devframe/src/node/instance-registry.ts), so tabs are invisible by construction — N tabs share one dev-server process.
  • The client-tool bridge from feat: eagerly expose in-page tools to browser agents #376 (devframe:agent:sync-client-toolsnode/client-agent.ts) tracks one session entry per tab, but tool ids collide across tabs and the provider loop lets the last-synced session silently own every tool. medula currently ships this as an uncommitted vendored patch and documents it as "the tab you focused last" — there is no actual focus tracking.

A coding agent cannot see which tabs are connected, nor act on a specific one.

Proposal

Make connected RPC clients (browser tabs) first-class on the MCP surface: list them per instance, and let a tool call target one.

1. Stable tab identity (devframe core, browser side)

  • Each tab mints a sessionStorage-backed tab id (same pattern as the hub's frame-nav tabId, packages/hub/src/client/frame-nav.ts), so it survives reloads and reconnects. Caveat to handle: browser tab duplication copies sessionStorage — follow the frame-nav dedupe approach.
  • The RPC client reports tab metadata to the node: tab id, URL, title, visibility/focus state, plus the client tool manifest it already syncs.
  • Freshness: metadata is (re)sent on connect, on focus/visibilitychange, and with every client-tool re-sync. No history-API patching; URL may lag briefly after an SPA navigation until the next event.

2. Per-tab session registry (devframe core, node side)

3. Listing (agentic)

  • New built-in agent tool devframe:agent:list-clients on each instance's own MCP endpoint: returns the connected tabs with id, URL, title, visibility, connectedAt, and available client tool names.
  • devframe_connect_list-instances (packages/agentic/src/connect/index.ts) calls it per live instance and nests a clients array in each instance entry — no new gateway tool.

4. Per-tab tool routing (agentic + core)

  • tools/list stays deduped: one entry per tool id regardless of tab count (no per-tab suffixing, no list churn).
  • Client-forwarded tools accept an optional reserved client_id argument, injected into their published inputSchema and stripped before forwarding. Works with any MCP client; devframe_connect_call-tool passes it through unchanged.
  • client_id given → route to that tab's session. Unknown/gone id → fail fast with an error that includes the current live client ids so the agent can re-target in one step.
  • client_id omitted → focus-aware default: the most-recently-focused visible tab wins, falling back to last-synced. This upstreams medula's vendored patch and makes its "tab you focused last" behavior actually true; stray headless tabs stop competing (a known medula pain point).

Dependencies / sequencing

Out of scope


This issue was created with the help of an agent.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions