[feat]: add support for WebMCP - #2178
Conversation
🦋 Changeset detectedLatest commit: 45239ef The changes in this PR will be included in the next version bump. This PR includes changesets to release 3 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
5d59d30 to
4809e8f
Compare
4809e8f to
8dba822
Compare
There was a problem hiding this comment.
1 issue found across 10 files
Confidence score: 3/5
- There is a concrete stability risk in
packages/core/lib/v3/understudy/page.ts: late responses from timed-out or abandoned WebMCP invocations are buffered indefinitely, which can grow memory usage over time on long-lived pages. - Given the clear user-impacting behavior (potential memory leak) and relatively high confidence, this sits above minor-risk territory even though it appears localized to one area.
- Pay close attention to
packages/core/lib/v3/understudy/page.ts- ensure buffered late responses are discarded or cleaned up to prevent unbounded memory growth.
Architecture diagram
sequenceDiagram
participant Client as Client Code
participant P as Page (Stagehand)
participant MainS as Main CDP Session
participant Chrome as Chrome Browser
participant WebMCP as WebMCP CDP Domain
Note over Client,WebMCP: WebMCP Tool Listing Flow
Client->>P: page.listWebMCPTools({ timeoutMs })
P->>P: ensureWebMCPEnabled() – register onWebMCPToolResponded listener
P->>MainS: WebMCP.enable
MainS->>Chrome: Send CDP "WebMCP.enable"
Chrome->>WebMCP: Enable WebMCP domain
WebMCP-->>Chrome: Emit "WebMCP.toolsAdded" events
Chrome-->>MainS: CDP events
MainS-->>P: onWebMCPToolResponded NOT triggered (wrong event type)
alt Tools added within timeout window
WebMCP->>WebMCP: Emit "WebMCP.toolsAdded" events with tool list
WebMCP-->>Chrome: "WebMCP.toolsAdded" event
Chrome-->>MainS: CDP event
MainS->>P: onToolsAdded callback invoked
P->>P: Strip debug fields (stackTrace), normalize tools
P->>P: Wait for quiet window (no new tools for up to 100ms)
else Timeout reached
P->>P: Force resolve after timeoutMs
end
P->>P: Cleanup: remove onToolsAdded listener
P-->>Client: Return WebMCPTool[] (current snapshot)
Note over Client,WebMCP: WebMCP Tool Invocation Flow
Client->>P: page.invokeWebMCPTool(toolName, input, { frameId?, timeoutMs? })
P->>P: ensureWebMCPEnabled() – return cached promise if already enabled
opt frameId not provided
P->>MainS: WebMCP.enable (if not already enabled)
MainS->>Chrome: Send CDP "WebMCP.enable"
Chrome-->>MainS: "WebMCP.toolsAdded" events
MainS-->>P: Collect tools to resolve frameId for toolName
alt Tool not found or ambiguous across frames
P-->>Client: Throw StagehandInvalidArgumentError
end
end
P->>MainS: WebMCP.invokeTool({ frameId, toolName, input })
MainS->>Chrome: Send CDP "WebMCP.invokeTool"
Chrome->>WebMCP: Start tool execution
WebMCP-->>Chrome: "WebMCP.invokeTool" response { invocationId }
Chrome-->>MainS: CDP response { invocationId }
MainS-->>P: Response { invocationId }
P->>P: Create deferred promise for invocation.result
P->>P: Start timeout timer
P-->>Client: Return WebMCPToolInvocation { invocationId, result, cancel() }
Note over Client,Chrome: Tool Execution & Result (async)
alt Tool completes normally
WebMCP-->>Chrome: "WebMCP.toolResponded" { invocationId, status: "Completed", output }
Chrome-->>MainS: CDP event
MainS->>P: onWebMCPToolResponded callback
P->>P: Clear timeout, resolve deferred with WebMCPToolResult
else Tool is canceled
Client->>P: invocation.cancel()
P->>MainS: WebMCP.cancelInvocation({ invocationId })
MainS->>Chrome: Send CDP "WebMCP.cancelInvocation"
Chrome-->>MainS: ACK
MainS-->>P: Success
WebMCP-->>Chrome: "WebMCP.toolResponded" { invocationId, status: "Canceled" }
Chrome-->>MainS: CDP event
MainS->>P: onWebMCPToolResponded callback
P->>P: Resolve deferred with Canceled status
else Tool errors
WebMCP-->>Chrome: "WebMCP.toolResponded" { invocationId, status: "Error", errorText, exception }
Chrome-->>MainS: CDP event
MainS->>P: onWebMCPToolResponded callback
P->>P: Resolve deferred with Error status
else Timeout expires
P->>P: Timer fires – reject deferred with timeout error
P->>P: Cleanup pending invocation entry
end
Note over P,Chrome: Early Response Handling (response before invokeTool returns)
alt Response arrives before invokeTool ACK
WebMCP-->>Chrome: "WebMCP.toolResponded" event
Chrome-->>MainS: CDP event
MainS->>P: onWebMCPToolResponded callback
P->>P: invocation not yet in pending map
P->>P: Buffer result in bufferedWebMCPResults
Chrome-->>MainS: "WebMCP.invokeTool" response { invocationId }
MainS-->>P: Response with invocationId
P->>P: Check buffer – find buffered result
P->>P: Resolve deferred immediately (clear timeout)
end
Note over P: Page Close / Cleanup
Client->>P: page.close()
P->>P: teardownWebMCP()
P->>P: Remove onWebMCPToolResponded listener
loop For each pending invocation
P->>P: Clear timeout, reject deferred with disposal error
end
P->>P: Clear buffered results and webMCPEnablePromise
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review |
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 10 files
Confidence score: 3/5
- There is a concrete regression risk in
packages/core/lib/v3/understudy/page.ts:invokeWebMCPTool()appears to map all CDP invocation failures to unsupported-browser-feature errors, which can hide real argument or state problems from users and developers. - Given the issue’s medium severity (6/10) and high confidence (9/10), this is more than a cosmetic concern and can impact debugging accuracy and runtime behavior, so merge risk is moderate.
- Pay close attention to
packages/core/lib/v3/understudy/page.ts- error classification ininvokeWebMCPTool()should preserve true CDP failure causes instead of collapsing them into a single unsupported-feature path.
Architecture diagram
sequenceDiagram
participant C as Client App
participant P as Stagehand Page
participant CDP as CDP Session (Browser)
participant CE as CDP Event Bus
participant W as WebMCP Runtime (Browser)
Note over C,P: Listing WebMCP Tools
C->>P: page.listWebMCPTools({ timeoutMs })
P->>CDP: WebMCP.enable
CDP->>CE: Emit WebMCP.toolsAdded
loop For each toolsAdded event
CE->>P: onToolsAdded(event)
P->>P: Collect tools in local map
P->>P: Reset quiet timer on change
end
alt Quiet window expires (no new tools)
P->>P: Resolve snapshot collection
else Timeout reached
P->>P: Force resolve snapshot
end
P->>CDP: WebMCP.disable
P->>P: Remove toolsAdded listener
P-->>C: Return WebMCPTool[]
Note over C,P: Invoking a WebMCP Tool
C->>P: page.invokeWebMCPTool(toolName, input, { frameId?, timeoutMs? })
alt frameId not provided
P->>P: Call listWebMCPTools (internal)
alt Tool found in single frame
P->>P: Use resolved frameId
else Tool found in multiple frames
P-->>C: Throw StagehandInvalidArgumentError
else Tool not found
P-->>C: Throw StagehandInvalidArgumentError
end
end
P->>CDP: WebMCP.invokeTool({ frameId, toolName, input })
CDP-->>P: Return { invocationId }
P->>P: Create deferred result promise
P->>P: Set timeout timer
Note over P: Invocation returned to client<br/>while tool executes
P-->>C: Return WebMCPToolInvocation { invocationId, result, cancel() }
alt Normal completion
CE->>P: WebMCP.toolResponded({ invocationId, status, output })
P->>P: Clear timeout, resolve deferred
C->>P: await invocation.result
P-->>C: Return WebMCPToolResult
else Early response (before invoke response)
CE->>P: WebMCP.toolResponded (arrives early)
P->>P: Buffer result in pendingWebMCPInvokeToolResponses window
CDP-->>P: Return { invocationId }
P->>P: Retrieve buffered result, resolve deferred
C->>P: await invocation.result
P-->>C: Return WebMCPToolResult
else Timeout
P->>P: Timer fires, reject deferred
C->>P: await invocation.result
P-->>C: Reject with timeout error
else Client calls cancel()
C->>P: invocation.cancel()
P->>CDP: WebMCP.cancelInvocation({ invocationId })
alt Tool responds before cancel
CE->>P: WebMCP.toolResponded (status: Canceled)
P->>P: Resolve deferred normally
end
P-->>C: void
C->>P: await invocation.result
P-->>C: Return WebMCPToolResult (status: Canceled)
end
Note over P: Cleanup & Browser Support
alt Unsupported browser (WebMCP.enable fails)
P->>P: Catch error, create StagehandUnsupportedBrowserFeatureError
P-->>C: Throw error with guidance message
end
opt Page close with pending invocations
C->>P: page.close()
P->>P: Reject all pending deferred promises
P->>P: Clear all timers and buffers
P->>CDP: Remove WebMCP.toolResponded listener
end
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review |
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 10 files
Confidence score: 3/5
- There is a concrete error-handling risk in
packages/core/lib/v3/understudy/page.ts:listWebMCPToolscurrently maps all failures to an unsupported-browser error, which can hide actual runtime or CDP failures. - Because this can mislead users and operators about the real root cause, it introduces moderate regression/debuggability risk even though it is localized to one function.
- Pay close attention to
packages/core/lib/v3/understudy/page.ts- error classification should preserve real failure types instead of collapsing them into unsupported-browser responses.
Architecture diagram
sequenceDiagram
participant Client as External Caller
participant Page as Stagehand Page
participant MainSession as Main CDP Session
participant Chrome as Chrome Browser (WebMCP)
Note over Client,Chrome: WebMCP API Flow
Client->>Page: page.listWebMCPTools({timeoutMs})
Page->>MainSession: ensureWebMCPEnabled() - check if already enabled
MainSession->>MainSession: Register listener for WebMCP.toolResponded
Page->>MainSession: WebMCP.enable (triggers browser snapshot)
MainSession-->>Page: WebMCP.toolsAdded (event with all tools)
MainSession->>MainSession: Listen for WebMCP.toolsAdded events
loop Collect tools during quiet window
MainSession-->>Page: WebMCP.toolsAdded (new/changed tools)
Page->>Page: Accumulate tools, update quiet timer
end
Page->>Page: Return collected WebMCPTool array (no stale cache)
Note over Client,Chrome: Tool Invocation
Client->>Page: page.invokeWebMCPTool(toolName, input, {frameId?, timeoutMs?})
alt FrameId not provided
Page->>Page: listWebMCPTools() to resolve frameId
alt Multiple frames have same tool name
Page-->>Client: StagehandInvalidArgumentError (ambiguous)
end
end
Page->>MainSession: WebMCP.invokeTool({frameId, toolName, input})
alt Unsupported browser
MainSession-->>Page: Error (code -32601 or "method not found")
Page-->>Client: StagehandUnsupportedBrowserFeatureError
end
MainSession-->>Page: {invocationId}
Page-->>Client: WebMCPToolInvocation (immediate, not waiting for tool result)
Note over Client,Chrome: Caller controls result/cancel lifecycle
alt Early response arrives before invokeTool returns
MainSession-->>Page: WebMCP.toolResponded (event arrives during send)
Page->>Page: Buffer result in bufferedWebMCPResults (pending response > 0)
end
par Caller waits for result
Client->>Page: await invocation.result
alt Result already buffered
Page-->>Client: Resolve from bufferedWebMCPResults
else Normal response
MainSession-->>Page: WebMCP.toolResponded (status, output, error)
Page->>Page: Clear timer, delete pending, resolve deferred
Page-->>Client: WebMCPToolResult
else Timeout reached
Page->>Page: Reject deferred with timeout error
Page-->>Client: StagehandInvalidArgumentError (timeout)
end
and Caller cancels
Client->>Page: await invocation.cancel()
Page->>MainSession: WebMCP.cancelInvocation({invocationId})
MainSession->>MainSession: WebMCP.toolResponded with Canceled status
Page->>Page: Clear timer, delete pending, resolve/reject deferred
end
alt Page closes
Page->>Page: teardownWebMCP()
Page->>MainSession: Remove WebMCP.toolResponded listener
loop All pending invocations
Page->>Page: Reject deferred with disposal error
end
Page->>Page: Clear pending maps and buffer
end
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review |
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
1 issue found across 12 files
Confidence score: 3/5
- There is a concrete API-contract risk in
packages/core/lib/v3/understudy/page.ts: success statuses from WebMCP may be returned as"Success"instead of the documented"Completed"value. - I’m scoring this as moderate merge risk because the issue is medium severity (6/10) with high confidence (8/10) and can directly affect client-side status handling.
- Pay close attention to
packages/core/lib/v3/understudy/page.ts- normalize WebMCP success states to the public API contract before or immediately after merge.
Architecture diagram
sequenceDiagram
participant Client as Caller Code
participant Page as Page (understudy/page.ts)
participant CDP as CDPSession
participant Browser as Chrome Browser (WebMCP)
participant Tools as Page WebMCP Tools
participant Timer as Timeout Timer
Note over Client,Browser: WebMCP Tool Listing Flow
Client->>Page: listWebMCPTools({ timeoutMs })
Page->>CDP: on("WebMCP.toolsAdded")
Page->>CDP: on("WebMCP.toolsRemoved")
Page->>CDP: send("WebMCP.enable")
CDP->>Browser: Enable WebMCP
Browser-->>CDP: OK
Browser-->>CDP: event "WebMCP.toolsAdded"
CDP-->>Page: onToolsAdded(event)
Page->>Page: Store tools in local Map
Browser-->>CDP: event "WebMCP.toolsRemoved"
CDP-->>Page: onToolsRemoved(event)
Page->>Page: Remove tools from local Map
Note over Page: Wait for quiet window or timeout
alt Tools settled within timeout
Page->>Page: Resolve with collected tools
else Timeout reached
Page->>Page: Resolve with current tools snapshot
end
Page->>CDP: off("WebMCP.toolsAdded")
Page->>CDP: off("WebMCP.toolsRemoved")
Page-->>Client: WebMCPTool[]
Note over Client,Browser: WebMCP Tool Invocation Flow
Client->>Page: invokeWebMCPTool(name, input, { frameId, timeoutMs })
alt frameId not provided
Page->>Page: Collect tool snapshot to resolve frame
alt Tool found in single frame
Page->>Page: Use that frameId
else Tool ambiguous across frames
Page-->>Client: Throw StagehandInvalidArgumentError
end
end
Page->>Page: Create deferred result promise
Page->>CDP: send("WebMCP.invokeTool", { toolName, input, frameId })
CDP->>Browser: Invoke tool
alt Browser does not support WebMCP
Browser-->>CDP: Error: method not found
CDP-->>Page: Error
Page->>Page: Check isWebMCPUnsupportedBrowserError
Page-->>Client: Throw StagehandUnsupportedBrowserFeatureError
else Browser accepts
Browser-->>CDP: { invocationId }
CDP-->>Page: { invocationId }
Page->>Page: Setup timeout timer
Page->>Page: Check buffered results for this invocationId
alt Response already buffered
Page->>Page: Resolve deferred with buffered result
end
Page-->>Client: WebMCPToolInvocation { invocationId, result, cancel }
alt Tool completes before timeout
Browser-->>CDP: event "WebMCP.toolResponded"
CDP-->>Page: onWebMCPToolResponded(event)
Page->>Timer: Clear timeout
Page->>Page: Resolve deferred with result
else Tool responds quickly before invokeTool returns
Browser-->>CDP: event "WebMCP.toolResponded"
CDP-->>Page: Buffer result temporarily
Page->>Page: On invokeTool response, consume buffer
else Timeout expires
Timer-->>Page: Timeout callback
Page->>Page: Reject deferred with timeout error
Page-->>Client: invocation.result rejects
end
end
Note over Client,Page: Cancel Flow (from returned invocation)
Client->>Page: invocation.cancel()
Page->>CDP: send("WebMCP.cancelInvocation", { invocationId })
CDP->>Browser: Cancel tool
Browser-->>CDP: OK
CDP-->>Page: OK
Page->>Page: Cleanup pending invocation state
Page-->>Client: void
Note over Client,Browser: Error Handling: Unsupported Browser
alt listWebMCPTools with unsupported browser
Client->>Page: listWebMCPTools()
Page->>CDP: send("WebMCP.enable")
CDP->>Browser: Enable WebMCP
Browser-->>CDP: Error: Method not found (code -32601)
CDP-->>Page: Error
Page->>Page: isWebMCPUnsupportedBrowserError()
Page-->>Client: Throw StagehandUnsupportedBrowserFeatureError
end
Note over Page: Cleanup on page close
Client->>Page: close()
Page->>Page: teardownWebMCP()
Page->>CDP: off("WebMCP.toolResponded")
Page->>Page: Reject all pending deferreds
Page->>Page: Clear pending map
Reply with feedback, questions, or to request a fix.
Fix all with cubic | Re-trigger cubic
|
@cubic-dev-ai review |
@seanmcguire12 I have started the AI code review. It will take a few minutes to complete. |
There was a problem hiding this comment.
No issues found across 12 files
Confidence score: 5/5
- Automated review surfaced no issues in the provided summaries.
- No files require special attention.
Architecture diagram
sequenceDiagram
participant Client as Caller Code
participant Page as Page (understudy/page.ts)
participant CDP as CDPSession
participant Browser as Chrome Browser (WebMCP)
participant Tools as Page WebMCP Tools
participant Timer as Timeout Timer
Note over Client,Browser: WebMCP Tool Listing Flow
Client->>Page: listWebMCPTools({ timeoutMs })
Page->>CDP: on("WebMCP.toolsAdded")
Page->>CDP: on("WebMCP.toolsRemoved")
Page->>CDP: send("WebMCP.enable")
CDP->>Browser: Enable WebMCP
Browser-->>CDP: OK
Browser-->>CDP: event "WebMCP.toolsAdded"
CDP-->>Page: onToolsAdded(event)
Page->>Page: Store tools in local Map
Browser-->>CDP: event "WebMCP.toolsRemoved"
CDP-->>Page: onToolsRemoved(event)
Page->>Page: Remove tools from local Map
Note over Page: Wait for quiet window or timeout
alt Tools settled within timeout
Page->>Page: Resolve with collected tools
else Timeout reached
Page->>Page: Resolve with current tools snapshot
end
Page->>CDP: off("WebMCP.toolsAdded")
Page->>CDP: off("WebMCP.toolsRemoved")
Page-->>Client: WebMCPTool[]
Note over Client,Browser: WebMCP Tool Invocation Flow
Client->>Page: invokeWebMCPTool(name, input, { frameId, timeoutMs })
alt frameId not provided
Page->>Page: Collect tool snapshot to resolve frame
alt Tool found in single frame
Page->>Page: Use that frameId
else Tool ambiguous across frames
Page-->>Client: Throw StagehandInvalidArgumentError
end
end
Page->>Page: Create deferred result promise
Page->>CDP: send("WebMCP.invokeTool", { toolName, input, frameId })
CDP->>Browser: Invoke tool
alt Browser does not support WebMCP
Browser-->>CDP: Error: method not found
CDP-->>Page: Error
Page->>Page: Check isWebMCPUnsupportedBrowserError
Page-->>Client: Throw StagehandUnsupportedBrowserFeatureError
else Browser accepts
Browser-->>CDP: { invocationId }
CDP-->>Page: { invocationId }
Page->>Page: Setup timeout timer
Page->>Page: Check buffered results for this invocationId
alt Response already buffered
Page->>Page: Resolve deferred with buffered result
end
Page-->>Client: WebMCPToolInvocation { invocationId, result, cancel }
alt Tool completes before timeout
Browser-->>CDP: event "WebMCP.toolResponded"
CDP-->>Page: onWebMCPToolResponded(event)
Page->>Timer: Clear timeout
Page->>Page: Resolve deferred with result
else Tool responds quickly before invokeTool returns
Browser-->>CDP: event "WebMCP.toolResponded"
CDP-->>Page: Buffer result temporarily
Page->>Page: On invokeTool response, consume buffer
else Timeout expires
Timer-->>Page: Timeout callback
Page->>Page: Reject deferred with timeout error
Page-->>Client: invocation.result rejects
end
end
Note over Client,Page: Cancel Flow (from returned invocation)
Client->>Page: invocation.cancel()
Page->>CDP: send("WebMCP.cancelInvocation", { invocationId })
CDP->>Browser: Cancel tool
Browser-->>CDP: OK
CDP-->>Page: OK
Page->>Page: Cleanup pending invocation state
Page-->>Client: void
Note over Client,Browser: Error Handling: Unsupported Browser
alt listWebMCPTools with unsupported browser
Client->>Page: listWebMCPTools()
Page->>CDP: send("WebMCP.enable")
CDP->>Browser: Enable WebMCP
Browser-->>CDP: Error: Method not found (code -32601)
CDP-->>Page: Error
Page->>Page: isWebMCPUnsupportedBrowserError()
Page-->>Client: Throw StagehandUnsupportedBrowserFeatureError
end
Note over Page: Cleanup on page close
Client->>Page: close()
Page->>Page: teardownWebMCP()
Page->>CDP: off("WebMCP.toolResponded")
Page->>Page: Reject all pending deferreds
Page->>Page: Clear pending map
You've manually re-run cubic several times on this PR. Each manual re-review checks the full PR again and counts toward your usage quota. To preserve your usage limits, we recommend letting cubic automatically review new commits.
Re-trigger cubic
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/stagehand@3.6.0 ### Minor Changes - [#2178](#2178) [`c49a3fc`](c49a3fc) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add support for WebMCP ### Patch Changes - [#2217](#2217) [`147e310`](147e310) Thanks [@monadoid](https://github.com/monadoid)! - Add Azure OpenAI Microsoft Entra ID model auth support. - [#2231](#2231) [`cf3603d`](cf3603d) Thanks [@miguelg719](https://github.com/miguelg719)! - Add claude-fable-5 support: native structured outputs via the @ai-sdk/anthropic bump, adaptive thinking (including the new "xhigh" effort) on the agent path, the API's built-in server-side refusal fallback to claude-opus-4-8, and auto tool choice for the final done call on models that reject forced tool use. - [#2233](#2233) [`8d7d414`](8d7d414) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Normalize URLs in `ActCache` key derivation by sorting query parameters before hashing. Semantically equivalent URLs that differ only in parameter order (e.g. `?utm_source=email&id=42` vs `?id=42&utm_source=email`) now hit the cache instead of silently missing. Fragments and duplicate keys are preserved. - [#2229](#2229) [`fd42e65`](fd42e65) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - launch local browser with --enable-features=WebMCPTesting,DevToolsWebMCPSupport by default - [#2220](#2220) [`a64c6b7`](a64c6b7) Thanks [@monadoid](https://github.com/monadoid)! - Fix Stagehand-generated shadow-root XPath resolution so deterministic actions can target elements inside web components. - [#2132](#2132) [`ed3e566`](ed3e566) Thanks [@miguelg719](https://github.com/miguelg719)! - Add canonical verifier evidence normalization for screenshots and text signals without requiring image dependencies in core installs. - [#2133](#2133) [`840aac8`](840aac8) Thanks [@miguelg719](https://github.com/miguelg719)! - Add the rubric-based verifier engine with normalized public rubric output and bounded failure-step parsing. ## @browserbasehq/stagehand-evals@2.0.3 ### Patch Changes - Updated dependencies \[[`147e310`](147e310), [`cf3603d`](cf3603d), [`8d7d414`](8d7d414), [`fd42e65`](fd42e65), [`a64c6b7`](a64c6b7), [`c49a3fc`](c49a3fc), [`ed3e566`](ed3e566), [`840aac8`](840aac8)]: - @browserbasehq/stagehand@3.6.0 ## @browserbasehq/stagehand-server-v3@3.7.1 ### Patch Changes - [#2217](#2217) [`147e310`](147e310) Thanks [@monadoid](https://github.com/monadoid)! - Add Azure OpenAI Microsoft Entra ID model auth support. - Updated dependencies \[[`147e310`](147e310), [`cf3603d`](cf3603d), [`8d7d414`](8d7d414), [`fd42e65`](fd42e65), [`a64c6b7`](a64c6b7), [`c49a3fc`](c49a3fc), [`ed3e566`](ed3e566), [`840aac8`](840aac8)]: - @browserbasehq/stagehand@3.6.0 Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
# why this PR introduces first class WebMCP support to Stagehand. this means callers can: - list tools exposed by a given page via `page.listWebMCPTools()` - invoke the given tools via `page.invokeWebMCPTool()` # what changed - added `page.listWebMCPTools()` to collect the current WebMCP tool snapshot from `WebMCP.toolsAdded` - added `page.invokeWebMCPTool()` with frame disambiguation, result promises, cancel support, timeouts, & early response handling - added WebMCP public types in `packages/core/lib/v3/types/public/page.ts` - added `StagehandUnsupportedBrowserFeatureError`, which gets thrown when using a version of chrome that doesn't support WebMCP - added flowlogger wrappers for `PageListWebMCPTools` & `PageInvokeWebMCPTool` - also added a WebMCP example in `packages/core/examples/webmcp.ts` ### behavioural notes: - `page.invokeWebMCPTool()` starts the tool invocation & returns a `WebMCPToolInvocation` once chrome accepts the request. **_it does not wait for the tool itself to finish_** - to wait for the actual tool execution, callers must do `await invocation.result` from the returned `WebMCPToolInvocation` - this keeps the returned invocation usable for follow up actions like `invocation.cancel()` while the tool is still running - this PR only implements WebMCP support for top level frames/pages. it does not currently handle WebMCP tools registered in OOPIFs # test plan - added tests in `packages/core/tests/unit/page-webmcp.test.ts` which cover `page.listWebMCPTools()` snapshot collection, no stale tool reuse, unsupported browser errors, `page.invokeWebMCPTool()` frame resolution, duplicate tool names, result routing, early responses, cancel, timeout, & close cleanup - updated `packages/core/tests/unit/helpers/mockCDPSession.ts` such that it now supports CDP event listeners so WebMCP events can be tested without a browser - public API tests cover the new WebMCP page types & `StagehandUnsupportedBrowserFeatureError`
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and the packages will be published to npm automatically. If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## @browserbasehq/stagehand@3.6.0 ### Minor Changes - [browserbase#2178](browserbase#2178) [`c49a3fc`](browserbase@c49a3fc) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - add support for WebMCP ### Patch Changes - [browserbase#2217](browserbase#2217) [`147e310`](browserbase@147e310) Thanks [@monadoid](https://github.com/monadoid)! - Add Azure OpenAI Microsoft Entra ID model auth support. - [browserbase#2231](browserbase#2231) [`cf3603d`](browserbase@cf3603d) Thanks [@miguelg719](https://github.com/miguelg719)! - Add claude-fable-5 support: native structured outputs via the @ai-sdk/anthropic bump, adaptive thinking (including the new "xhigh" effort) on the agent path, the API's built-in server-side refusal fallback to claude-opus-4-8, and auto tool choice for the final done call on models that reject forced tool use. - [browserbase#2233](browserbase#2233) [`8d7d414`](browserbase@8d7d414) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - Normalize URLs in `ActCache` key derivation by sorting query parameters before hashing. Semantically equivalent URLs that differ only in parameter order (e.g. `?utm_source=email&id=42` vs `?id=42&utm_source=email`) now hit the cache instead of silently missing. Fragments and duplicate keys are preserved. - [browserbase#2229](browserbase#2229) [`fd42e65`](browserbase@fd42e65) Thanks [@seanmcguire12](https://github.com/seanmcguire12)! - launch local browser with --enable-features=WebMCPTesting,DevToolsWebMCPSupport by default - [browserbase#2220](browserbase#2220) [`a64c6b7`](browserbase@a64c6b7) Thanks [@monadoid](https://github.com/monadoid)! - Fix Stagehand-generated shadow-root XPath resolution so deterministic actions can target elements inside web components. - [browserbase#2132](browserbase#2132) [`ed3e566`](browserbase@ed3e566) Thanks [@miguelg719](https://github.com/miguelg719)! - Add canonical verifier evidence normalization for screenshots and text signals without requiring image dependencies in core installs. - [browserbase#2133](browserbase#2133) [`840aac8`](browserbase@840aac8) Thanks [@miguelg719](https://github.com/miguelg719)! - Add the rubric-based verifier engine with normalized public rubric output and bounded failure-step parsing. ## @browserbasehq/stagehand-evals@2.0.3 ### Patch Changes - Updated dependencies \[[`147e310`](browserbase@147e310), [`cf3603d`](browserbase@cf3603d), [`8d7d414`](browserbase@8d7d414), [`fd42e65`](browserbase@fd42e65), [`a64c6b7`](browserbase@a64c6b7), [`c49a3fc`](browserbase@c49a3fc), [`ed3e566`](browserbase@ed3e566), [`840aac8`](browserbase@840aac8)]: - @browserbasehq/stagehand@3.6.0 ## @browserbasehq/stagehand-server-v3@3.7.1 ### Patch Changes - [browserbase#2217](browserbase#2217) [`147e310`](browserbase@147e310) Thanks [@monadoid](https://github.com/monadoid)! - Add Azure OpenAI Microsoft Entra ID model auth support. - Updated dependencies \[[`147e310`](browserbase@147e310), [`cf3603d`](browserbase@cf3603d), [`8d7d414`](browserbase@8d7d414), [`fd42e65`](browserbase@fd42e65), [`a64c6b7`](browserbase@a64c6b7), [`c49a3fc`](browserbase@c49a3fc), [`ed3e566`](browserbase@ed3e566), [`840aac8`](browserbase@840aac8)]: - @browserbasehq/stagehand@3.6.0
why
this PR introduces first class WebMCP support to Stagehand. this means callers can:
page.listWebMCPTools()page.invokeWebMCPTool()what changed
page.listWebMCPTools()to collect the current WebMCP tool snapshot fromWebMCP.toolsAddedpage.invokeWebMCPTool()with frame disambiguation, result promises, cancel support, timeouts, & early response handlingpackages/core/lib/v3/types/public/page.tsStagehandUnsupportedBrowserFeatureError, which gets thrown when using a version of chrome that doesn't support WebMCPPageListWebMCPTools&PageInvokeWebMCPToolpackages/core/examples/webmcp.tsbehavioural notes:
page.invokeWebMCPTool()starts the tool invocation & returns aWebMCPToolInvocationonce chrome accepts the request. it does not wait for the tool itself to finishawait invocation.resultfrom the returnedWebMCPToolInvocationinvocation.cancel()while the tool is still runningtest plan
packages/core/tests/unit/page-webmcp.test.tswhich coverpage.listWebMCPTools()snapshot collection, no stale tool reuse, unsupported browser errors,page.invokeWebMCPTool()frame resolution, duplicate tool names, result routing, early responses, cancel, timeout, & close cleanuppackages/core/tests/unit/helpers/mockCDPSession.tssuch that it now supports CDP event listeners so WebMCP events can be tested without a browserStagehandUnsupportedBrowserFeatureError