Skip to content

Commit 38fd436

Browse files
antfubotagent
andcommitted
perf: load the browser-agent bridge lazily, only when MCP is enabled
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>
1 parent 08122d8 commit 38fd436

2 files changed

Lines changed: 38 additions & 2 deletions

File tree

‎packages/devframe/src/client/rpc-auth-gate.test.ts‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,4 +145,29 @@ describe('getDevframeRpcClient: auth bootstrap gates outbound calls', () => {
145145
// `close?:` exists to keep working.
146146
expect(() => rpc.close?.()).not.toThrow()
147147
})
148+
149+
const INVOKE_CLIENT_TOOL = 'devframe:agent:invoke-client-tool'
150+
151+
it('loads the browser-agent bridge only when the node advertises MCP', async () => {
152+
const { getDevframeRpcClient } = await import('./rpc')
153+
const rpc = await getDevframeRpcClient({
154+
connectionMeta: { ...connectionMeta, mcp: { path: '__mcp' } },
155+
otpParam: false,
156+
simpleAuth: false,
157+
})
158+
// The bridge lives in its own chunk and registers this handler once loaded.
159+
await vi.waitFor(() => expect(rpc.client.definitions.has(INVOKE_CLIENT_TOOL)).toBe(true))
160+
})
161+
162+
it('leaves the browser-agent bridge unloaded without MCP', async () => {
163+
const { getDevframeRpcClient } = await import('./rpc')
164+
const rpc = await getDevframeRpcClient({
165+
connectionMeta,
166+
otpParam: false,
167+
simpleAuth: false,
168+
})
169+
// Give any stray dynamic import time to resolve; it must not.
170+
await new Promise(resolve => setTimeout(resolve, 20))
171+
expect(rpc.client.definitions.has(INVOKE_CLIENT_TOOL)).toBe(false)
172+
})
148173
})

‎packages/devframe/src/client/rpc.ts‎

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import { DEVFRAME_OTP_URL_PARAM } from 'devframe/constants'
1111
import { RpcCacheManager, RpcFunctionsCollectorBase } from 'devframe/rpc'
1212
import { createEventEmitter } from 'devframe/utils/events'
1313
import { withBase } from 'devframe/utils/url'
14-
import { setupBrowserAgentRpcBridge } from './browser-agent-rpc'
1514
import { setupDevframeConnection } from './connection'
1615
import { storeAuthToken } from './connection-storage'
1716
import { authenticateWithUrlOtp } from './otp'
@@ -358,6 +357,7 @@ export async function getDevframeRpcClient(
358357
// No-op when the browser provides no WebMCP model context.
359358
const disposeWebMcp = options.webmcp === false ? undefined : registerWebMcpTools(clientRpc)
360359
let disposeBrowserAgentBridge: (() => void) | undefined
360+
let closed = false
361361

362362
async function fetchJsonFromBases(path: string): Promise<any> {
363363
const candidates = [
@@ -449,6 +449,7 @@ export async function getDevframeRpcClient(
449449

450450
/** Release authentication and transport resources even if another disposer fails. */
451451
function closeRpcClient(): void {
452+
closed = true
452453
try {
453454
disposeBrowserAgentBridge?.()
454455
disposeWebMcp?.()
@@ -599,7 +600,17 @@ export async function getDevframeRpcClient(
599600
() => { bootstrapAuthSettled = true },
600601
)
601602

602-
disposeBrowserAgentBridge = setupBrowserAgentRpcBridge(rpc)
603+
// Only when the node advertises an MCP endpoint (`connectionMeta.mcp`) is the
604+
// browser-agent bridge useful, so load it from its own chunk on demand and
605+
// keep it out of the main client bundle for every non-MCP connection.
606+
if (connectionMeta.mcp) {
607+
void import('./browser-agent-rpc')
608+
.then(({ setupBrowserAgentRpcBridge }) => {
609+
if (!closed)
610+
disposeBrowserAgentBridge = setupBrowserAgentRpcBridge(rpc)
611+
})
612+
.catch(() => {})
613+
}
603614

604615
// Listen for auth updates from other tabs (e.g., the auth page, or another
605616
// tab that just completed a code exchange).

0 commit comments

Comments
 (0)