Skip to content

Signal tool stability with waitForToolsUntil() for user agent observations - #373

Open
khushalsagar wants to merge 4 commits into
mainfrom
wait-for-tools-until
Open

khushalsagar wants to merge 4 commits into
mainfrom
wait-for-tools-until

Conversation

@khushalsagar

@khushalsagar khushalsagar commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Implement the resolution from #328 (minutes) and #328 (comment):

  • Add the IDL for waitForToolsUntil and toolsReady, and state tracking for the tools-ready state based on pending promises.
  • Wait for tools-ready (up to an implementation-defined timeout) in perform an observation.
  • Dispatch a toolchange event (ToolChangeEvent with ready) when tools become not ready and suppress dispatch of this event until tools become ready again.

Wiring the wait for tools-ready to getTools() and changing dispatch of toolchange events is deferred for a follow up PR.

Note: There's still an open question for the Document lifecycle until which tools are presumed to be not ready.

Part of #328.


Preview | Diff

@khushalsagar

khushalsagar commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator Author

A tangential thing worth taking up in a separate PR: the toolChange notification from register/unregister isn't coalesced well right now. It's triggered for every register/unregister call. Seems like it would be better to do it in a separate task (or microtask?) since in a task the state will be updated by multiple calls and we should dispatch a single event for all those mutations.

@khushalsagar
khushalsagar force-pushed the wait-for-tools-until branch 2 times, most recently from 94b382b to 9f62142 Compare November 3, 2026 20:26

@markafoltz markafoltz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall structure looks good, it this PR has several features (toolchange event, modelContext.toolsReady, blocking getTools()) not yet landed; are you thinking of adjusting this once the implementations land?

Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs Resolved
Comment thread index.bs
Comment on lines +1077 to +1079
1. [=set/Append=] |promise| to |context|'s [=model context/pending tool readiness promises=].

1. [=Update the tools-ready state=] given |document|.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Web IDL wraps every Promise<any> argument in a new promise, so even waitForToolsUntil(Promise.resolve()) or waitForToolsUntil(42) synchronously sets tools-ready to false and fires toolchange{ready:false} at every document. A microtask later it fires toolchange{ready:true}.

Chromium returns early when the promise has already settled, so it fires nothing. The waitForToolsUntil(42) case in the WPT would behave differently from the spec once ToolChangeEvent ships.

Suggest coalescing: have update the tools-ready state queue a task that compares the current state with the last state we notified about, and notify only when they differ. This fits with your PR comment about coalescing the register/unregister notifications.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the spec to match the WPTs and ignore already settled promises, nice catch!

Deferred the ToolChangeEvent dispatch changes to a follow-up PR, will take care of toolchange event batching there. Also, my comment was about an existing issue with no batching for toolchange in the register/unregister case tracked in #374.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an Issue(374) note in the spec about the toolchange coalescing (if there isn't one already)?

Comment thread index.bs Resolved
Comment thread index.bs Resolved
…omments

- Defer ToolChangeEvent/toolchange readiness dispatch and the getTools() wait
  to a follow-up PR.
- wait for tools to be ready: capture the top-level active document up front,
  drop the null check, and return "ready" or "navigated".
- perform an observation: return early on "navigated".
- waitForToolsUntil(): ignore already-settled promises, split React into
  fulfilled/rejected branches, and always update tools-ready on settle.
- Move tools-ready below the ModelContext creation paragraph, rename the
  example variable, and fix in-parallel indentation in registerTool().
The navigable can also be destroyed while waiting, so "navigated" was too
narrow. Update the note in perform an observation to cover both cases.
meziantou added a commit to meziantou/Meziantou.Framework that referenced this pull request Nov 12, 2026
* WebPlatform: implement WebMCP (document.modelContext)

- document.modelContext and the ModelContext interface of the WebMCP
  specification: registerTool() (name/description validation, schema
  serialization, exposedTo, signal), getTools() (frame tree, same-origin
  and fromOrigins/exposedTo filtering, sorted by name), executeTool()
  (pending executions per traversable, cancellation, toolcancel and
  toolactivated events, unloading document cleanup steps), toolchange
- the Tool interface of webmachinelearning/webmcp#348 (what Chromium and
  the WPT implement) instead of the RegisteredTool dictionary, and the
  outputSchema member
- waitForToolsUntil() and toolsReady of webmachinelearning/webmcp#373,
  marked [Experimental("WEBPLATEXP0018")]
- the "tools" permissions policy feature and WebPlatformFeatures.WebMcpEnabled
- a host API for the application, as the browser agent:
  BrowsingContext.GetModelContextTools(), ExecuteModelContextToolAsync()
  and ModelContextToolsReady
- ScriptEngine JSON hooks (JSON.stringify/JSON.parse of the realm)
- WPT: run webmcp; the declarative API (explainer only) is skipped, and
  the subtests that expect unquoted string results are TEST BUGs
  (webmachinelearning/webmcp#361)

* WebPlatform: address the review of WebMCP

- the settlement of a tool and of a waitForToolsUntil() promise is handled by a task of the event loop (a task of the application can complete on another thread)
- the realms capture JSON.parse() and JSON.stringify() when they are created, before the page can replace them
- executeTool() checks the caller again after serializing the arguments (toJSON() can detach it)
- Tool.Document is the document that registered the tool
@khushalsagar khushalsagar changed the title Signal tool stability with waitForToolsUntil() and ToolChangeEvent Signal tool stability with waitForToolsUntil() for user agent observations Nov 13, 2026

@markafoltz markafoltz left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM % adding Issues for things to follow up on.

Comment thread index.bs
disconnected and is no longer [=Document/fully active=]. Returning true here immediately
unblocks the wait so the caller can check [=Document/fully active=] and abort.

1. [=list/For each=] [=navigable=] |navigable| of |activeDocument|'s [=Document/inclusive

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see a note about permissions policy near the tools-ready definition, where I expected to find it.

Comment thread index.bs
Comment on lines +1077 to +1079
1. [=set/Append=] |promise| to |context|'s [=model context/pending tool readiness promises=].

1. [=Update the tools-ready state=] given |document|.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add an Issue(374) note in the spec about the toolchange coalescing (if there isn't one already)?

Comment thread index.bs

1. Let |activeDocument| be |traversable|'s [=navigable/active document=].

1. [=Assert=]: |activeDocument| is [=Document/fully active=].

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is checked before these steps are run, but can this be asserted if this is running in parallel, or do we need to abort if it's no longer the case?

Comment thread index.bs Outdated

<dl class="domintro" dfn-type=attribute dfn-for=ToolChangeEvent>
: <dfn>ready</dfn>
:: Returns true if the tools are ready to be read, or false if tool updates are in progress.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a Issue(236) note here to update it later?

Comment thread index.bs Outdated

1. Run the following steps [=in parallel=]:

1. [=Wait for tools to be ready=] given |toolRequestor|'s [=node navigable=]'s

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Issue(328) here noting the deadlock issue?

Comment thread index.bs Resolved
Comment thread index.bs Outdated
1. If |toolsReady| is false, then return.

1. [=In parallel=], [=notify documents of a tool change=] given |targetDocument|, |exposed
origins|, and |toolsReady|.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't feel strongly, it just looks a little odd. It should be a minor change on this PR?

Comment thread index.bs Outdated

1. [=Notify documents of a tool change=] given |tool owner| and |exposed origins|.
1. If |toolsReady| is true, then [=notify documents of a tool change=] given |tool owner|,
|exposed origins|, and |toolsReady|.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants