@@ -78,6 +78,13 @@ p + dl.props { margin-top: -0.5em; }
7878[data-algorithm] [data-algorithm] {
7979 margin: 1em 0;
8080}
81+
82+ /* Table styling definitions from https://resources.whatwg.org/standard.css */
83+ table { border-collapse: collapse; border-style: hidden hidden none hidden; margin: 1.25em 0; }
84+ table thead, table tbody { border-bottom: solid; }
85+ table tbody th { text-align: left; }
86+ table tbody th:first-child { border-left: solid; }
87+ table td, table th { border-left: solid; border-right: solid; border-bottom: solid thin; vertical-align: top; padding: 0.2em; }
8188</style>
8289
8390<pre class="link-defaults">
@@ -94,6 +101,9 @@ spec: html; type: dfn; urlPrefix: https://html.spec.whatwg.org/C
94101 # `url`.
95102 text:is initial about:blank; url: is-initial-about:blank
96103 text: associated Navigator
104+ spec: SECURE-CONTEXTS; urlPrefix: https://w3c.github.io/webappsec-secure-contexts/
105+ type: abstract-op
106+ text: is origin potentially trustworthy?; url: is-origin-trustworthy
97107</pre>
98108
99109<h2 id="intro">Introduction</h2>
@@ -168,18 +178,95 @@ A <dfn>tool definition</dfn> is a [=struct=] with the following [=struct/items=]
168178
169179 : <dfn>untrusted content hint</dfn>
170180 :: a [=boolean=] , initially false.
181+
182+ : <dfn>exposed origins</dfn>
183+ :: a [=list=] or [=origins=] , initially [=list/empty=] .
171184</dl>
172185
186+ <hr>
187+
188+ <div algorithm>
189+ To <dfn>notify documents of a tool change</dfn> given a {{Document}} |tool owner| and a [=list=] of
190+ [=origins=] |exposed origins|, run these steps:
191+
192+ 1. [=Assert=] : these steps are running on |tool owner|'s [=relevant agent=]' s [=agent/event loop=] .
193+
194+ 1. Let |navigablesToNotify| be |tool owner|'s [=node navigable=]' s [=navigable/traversable
195+ navigable=] 's [=Document/descendant navigables=] .
196+
197+ 1. [=list/For each=] |navigable| of |navigablesToNotify|:
198+
199+ 1. Let |targetDocument| be |navigable|'s [=navigable/active document=] .
200+
201+ 1. If |targetDocument| is not [=allowed to use=] the "{{tools}} " feature, then
202+ [=iteration/continue=] .
203+
204+ 1. If [=tool is visible to an origin=] given |tool owner|'s [=Document/origin=] , |exposed
205+ origins|, and |targetDocument|'s [=Document/origin=] , then [=queue a global task=] on the
206+ [=webmcp task source=] given |targetDocument|'s [=relevant global object=] to [=fire an
207+ event=] named {{ModelContext/toolchange}} at |targetDocument|'s [=relevant global object=]' s
208+ [=associated Navigator|associated <code>Navigator</code>=] 's [=Navigator/associated
209+ ModelContext|associated <code>ModelContext</code>=] .
210+
211+ <div class=example id=notify-task-ordering>
212+ <p> This algorithm's use of the [=webmcp task source=] means that the timing between firing the
213+ {{ModelContext/toolchange}} event, and other tasks queued after this algorithm, cannot be relied
214+ upon. For example:</p>
215+
216+ <xmp class=language-js>
217+ navigator.modelContext.ontoolchange = e => console.log('Parent toolchange' );
218+ iframe.contentWindow.navigator.modelContext.ontoolchange = e => console.log('Child toolchange' );
219+
220+ // Queues a task to fire `toolchange`, on the `webmcp task source`.
221+ navigator.modelContext.registerTool({
222+ name: "tool_name",
223+ description: "tool_desc",
224+ execute: async () => {}
225+ });
226+
227+ // Queues a task on the `timer task source`.
228+ setTimeout(() => console.log('Post-register task' ));
229+
230+ // `Parent toolchange` will always log before `Child toolchange`.
231+ // But `Post-register task` can log before, in between, or after both.
232+ </xmp>
233+ </div>
234+
235+ </div>
236+
173237<div algorithm>
174- To <dfn for="model context">unregister a tool </dfn> for a [=model context =] given a [=string=]
175- |tool name |, run these steps:
238+ To determine if a <dfn>tool is visible to an origin </dfn> given an [=origin =] |tool owner origin|,
239+ a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |target origin |, run these steps:
176240
177- 1. Let |tool map| be the associated [=model context=] 's [=model context/tool map=] .
241+ 1. If |tool owner origin| is [=same origin=] with |target origin|, then return true.
242+
243+ 1. [=list/For each=] |origin| of |exposed origins|:
244+
245+ 1. If |target origin| is [=same origin=] with |origin|, then return true.
246+
247+ 1. Return false.
248+
249+ </div>
250+
251+ <div algorithm>
252+ To <dfn for="model context">unregister a tool</dfn> given a {{ModelContext}} |modelContext| and a
253+ [=string=] |tool name|, run these steps:
254+
255+ 1. [=Assert=] : these steps are running on |modelContext|'s [=relevant agent=]' s [=agent/event
256+ loop=] .
257+
258+ 1. Let |tool map| be |modelContext|'s [=ModelContext/internal context=]' s [=model context/tool
259+ map=] .
178260
1792611. [=Assert=] |tool map|[|tool name|] [=map/exists=] .
180262
263+ 1. Let |exposed origins| be |tool map|[|tool name|] 's [=tool definition/exposed origins=] .
264+
1812651. [=map/Remove=] |tool map|[|tool name|] .
182266
267+ 1. Run [=notify documents of a tool change=] given |modelContext|'s [=relevant global object=]' s
268+ [=associated Document|associated <code>Document</code>=] and |exposed origins|.
269+
183270</div>
184271
185272
@@ -242,8 +329,10 @@ The {{ModelContext}} interface provides methods for web applications to register
242329
243330<xmp class="idl">
244331[Exposed=Window, SecureContext]
245- interface ModelContext {
332+ interface ModelContext : EventTarget {
246333 undefined registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
334+
335+ attribute EventHandler ontoolchange;
247336};
248337</xmp>
249338
@@ -265,6 +354,15 @@ is a [=model context=] [=struct=] created alongside the {{ModelContext}}.
265354<div algorithm>
266355The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var>)</dfn> method steps are:
267356
357+ 1. Let |tool owner| be [=this=] 's [=relevant global object=]' s [=associated Document|associated
358+ <code>Document</code>=] .
359+
360+ 1. If |tool owner| is not [=Document/fully active=] , then [=exception/throw=] an
361+ "{{InvalidStateError}} " {{DOMException}} .
362+
363+ 1. If |tool owner| is not [=allowed to use=] the "{{tools}} " feature, then [=exception/throw=] a
364+ "{{NotAllowedError}} " {{DOMException}} .
365+
2683661. Let |tool map| be [=this=] 's [=ModelContext/internal context=]' s [=model context/tool map=] .
269367
2703681. Let |tool name| be |tool|'s {{ModelContextTool/name}} .
@@ -316,7 +414,21 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
316414 [=AbortSignal/aborted=] , and return.
317415
318416 1. [=AbortSignal/add|Add an abort algorithm=] to |signal| that [=model context/unregisters a
319- tool=] from [=this=] [=ModelContext/internal context=] given |tool name|.
417+ tool=] given [=this=] and |tool name|.
418+
419+ 1. Let |exposed origins| be an empty [=list=] of [=origins=] .
420+
421+ 1. If |options|'s {{ModelContextRegisterToolOptions/exposedTo}} [=map/exists=] , then:
422+
423+ 1. [=list/For each=] |origin| of |options|'s {{ModelContextRegisterToolOptions/exposedTo}} :
424+
425+ 1. Let |parsedURL| be the result of running the [=URL parser=] on |origin|.
426+
427+ 1. If |parsedURL| is failure or its [=url/origin=] is not [$is origin potentially
428+ trustworthy?|potentially trustworthy$], then [=exception/throw=] an "{{SecurityError}} "
429+ {{DOMException}} .
430+
431+ 1. [=list/Append=] |parsedURL|'s [=url/origin=] to |exposed origins|.
320432
3214331. Let |tool definition| be a new [=tool definition=] , with the following [=struct/items=] :
322434
@@ -341,8 +453,13 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
341453 : [=tool definition/untrusted content hint=]
342454 :: |untrusted content hint|
343455
456+ : [=tool definition/exposed origins=]
457+ :: |exposed origins|
458+
3444591. Set [=this=] 's [=ModelContext/internal context=] [|tool name|] to |tool definition|.
345460
461+ 1. Run [=notify documents of a tool change=] given |tool owner| and |exposed origins|.
462+
346463</div>
347464
348465<h4 id="model-context-tool">ModelContextTool Dictionary</h4>
@@ -425,6 +542,7 @@ definition itself.
425542<xmp class="idl">
426543dictionary ModelContextRegisterToolOptions {
427544 AbortSignal signal;
545+ sequence<USVString> exposedTo;
428546};
429547</xmp>
430548
@@ -433,6 +551,12 @@ dictionary ModelContextRegisterToolOptions {
433551 <dd>
434552 <p> An {{AbortSignal}} that unregisters the tool when aborted.
435553 </dd>
554+
555+ <dt><code><var ignore> exposedTo</var> ["{{ModelContextRegisterToolOptions/exposedTo}}"] </code></dt>
556+ <dd>
557+ <p> An array of origins that control which documents this tool is exposed to, in the current
558+ document's tree.
559+ </dd>
436560</dl>
437561
438562
@@ -493,6 +617,28 @@ The <dfn>synthesize a declarative JSON Schema object algorithm</dfn>, given a <{
493617}
494618</pre>
495619
620+ <h3 id="events">Events</h3>
621+
622+ The following are the [=event handlers=] (and their corresponding [=event handler event types=] )
623+ that must be supported, as [=event handler IDL attributes=] , by all {{ModelContext}} objects:
624+
625+ <table>
626+ <thead>
627+ <tr>
628+ <th> [=Event handler=]
629+ <th> [=Event handler event type=]
630+ <tbody>
631+ <tr>
632+ <td> <dfn attribute for="ModelContext">ontoolchange</dfn>
633+ <td> <dfn event for="ModelContext">toolchange</dfn>
634+ </table>
635+
636+ <h3 id="permissions-policy">Permissions policy integration</h3>
637+
638+ Access to the APIs in this specification is gated behind the [=policy-controlled feature=] "<dfn
639+ permission> tools</dfn> ", which has a [=policy-controlled feature/default allowlist=] of
640+ <code> [=default allowlist/'self'=] </code> .
641+
496642<h2 id="interaction-with-agents">Interaction with agents</h2>
497643
498644<h3 id="event-loop">Event loop integration</h3>
@@ -506,7 +652,7 @@ queued on its <dfn>AI agent queue</dfn>, which is the result of [=starting a new
506652
507653Conversely, steps queued *from* the [=browser agent=] onto the [=event loop=] of a given
508654{{ModelContext}} object (i.e., the "main thread" where JavaScript runs) are queued on its [=relevant
509- global object=] 's <dfn noexport>tool calling task source</dfn> .
655+ global object=] 's <dfn noexport>webmcp task source</dfn> .
510656
511657<h3 id="observations">Page observations</h3>
512658
0 commit comments