From 8bf19657ac05790ff2d280d27b14917371eeb909 Mon Sep 17 00:00:00 2001 From: "mark a. foltz" Date: Tue, 6 Oct 2026 21:24:56 -0700 Subject: [PATCH 1/3] Fix concurrency, detached document, and permissions checks in algorithms --- index.bs | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/index.bs b/index.bs index da8b99a..82779ad 100644 --- a/index.bs +++ b/index.bs @@ -744,8 +744,7 @@ The registerTool(tool, optionsgetTools(options) method steps 1. Run the following steps [=in parallel=]: - 1. Let |tools| be an empty [=list=] of {{RegisteredTool}} dictionaries. + 1. Let |discoveredTools| be an empty [=list=]. 1. Let |navigables| be |toolRequestor|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=navigable/active document=]'s [=Document/inclusive descendant navigables=]. @@ -868,6 +867,17 @@ The getTools(options) method steps definition/exposed origins=], and |callerOrigin| returns false, then [=iteration/continue=]. + 1. [=list/Append=] (|tool definition|, |targetDocument|, |targetOrigin|) to + |discoveredTools|. + + 1. [=Queue a global task=] on the [=webmcp task source=] given |global| to run the following + steps: + + 1. Let |tools| be an empty [=list=] of {{RegisteredTool}} dictionaries. + + 1. [=list/For each=] (|tool definition|, |targetDocument|, |targetOrigin|) of + |discoveredTools|: + 1. Let |registeredTool| be a new {{RegisteredTool}} dictionary, with the following fields: : {{RegisteredTool/name}} @@ -885,8 +895,9 @@ The getTools(options) method steps : {{RegisteredTool/inputSchema}} :: the result of [=parse a JSON string to a JavaScript value=] given |tool - definition|'s [=tool definition/input schema=], if |tool definition|'s [=tool - definition/input schema=] is not the empty string; otherwise undefined. + definition|'s [=tool definition/input schema=] and [=this=]'s [=relevant realm=], if + |tool definition|'s [=tool definition/input schema=] is not the empty string; + otherwise undefined. Note: This will never throw an exception, because the string stored in the tool definition is always a valid JSON string. @@ -910,11 +921,10 @@ The getTools(options) method steps 1. [=list/Append=] |registeredTool| to |tools|. - 1. [=list/Sort in ascending order=] |tools|, with |a| being less than |b| if - |a|["{{RegisteredTool/name}}"] is [=code unit less than=] |b|["{{RegisteredTool/name}}"]. + 1. [=list/Sort in ascending order=] |tools|, with |a| being less than |b| if + |a|["{{RegisteredTool/name}}"] is [=code unit less than=] |b|["{{RegisteredTool/name}}"]. - 1. [=Queue a global task=] on the [=webmcp task source=] given |global| to [=resolve=] |promise| - with |tools|. + 1. [=Resolve=] |promise| with |tools|. 1. Return |promise|. @@ -959,6 +969,9 @@ The executeTool(tool, inputObjectDocument=]. +1. If |targetDocument|'s [=node navigable=] is null, then return [=a promise rejected with=] an + "{{InvalidStateError}}" {{DOMException}}. + 1. Let |uuid| be a new [=unique internal value=]. 1. If |options|'s {{ModelContextExecuteToolOptions/signal}} [=map/exists=], then: @@ -1476,12 +1489,20 @@ steps: 1. Let |document| be |descendant|'s [=navigable/active document=]. + 1. If |document| is not [=allowed to use=] the "{{tools}}" feature, then + [=iteration/continue=]. + 1. Let |id| be |document|'s [=Document/unique ID=]. 1. Let |tools| be |document|'s [=Document/associated ModelContext|associated ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=]'s [=map/values=], which are [=tool definitions=]. + Note: Although these steps run on the [=AI agent queue=] [=in parallel=] with each + {{Document}}'s [=relevant agent=]'s [=agent/event loop=], implementations synchronize access + to registered [=tool definitions=] via inter-process communication or locking so that + partial updates are never observed. + 1. Let |observedToolCollection| be a new [=observed tool collection=] with the following [=struct/items=]: : [=observed tool collection/origin=] From 4cbd163413309b230d4f44ec5b7b00d17435da5c Mon Sep 17 00:00:00 2001 From: "mark a. foltz" Date: Thu, 8 Oct 2026 15:38:47 -0700 Subject: [PATCH 2/3] Respond to comments from Dom --- index.bs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/index.bs b/index.bs index 82779ad..8677099 100644 --- a/index.bs +++ b/index.bs @@ -895,9 +895,8 @@ The getTools(options) method steps : {{RegisteredTool/inputSchema}} :: the result of [=parse a JSON string to a JavaScript value=] given |tool - definition|'s [=tool definition/input schema=] and [=this=]'s [=relevant realm=], if - |tool definition|'s [=tool definition/input schema=] is not the empty string; - otherwise undefined. + definition|'s [=tool definition/input schema=], if |tool definition|'s + [=tool definition/input schema=] is not the empty string; otherwise undefined. Note: This will never throw an exception, because the string stored in the tool definition is always a valid JSON string. @@ -1499,8 +1498,8 @@ steps: map=]'s [=map/values=], which are [=tool definitions=]. Note: Although these steps run on the [=AI agent queue=] [=in parallel=] with each - {{Document}}'s [=relevant agent=]'s [=agent/event loop=], implementations synchronize access - to registered [=tool definitions=] via inter-process communication or locking so that + {{Document}}'s [=relevant agent=]'s [=agent/event loop=], implementations synchronize + access to each [=model context/tool map=] via inter-process communication or locking so that partial updates are never observed. 1. Let |observedToolCollection| be a new [=observed tool collection=] with the following [=struct/items=]: From 6b098ef0f4fe0f3d6d0e2fc14dc83194f52a0699 Mon Sep 17 00:00:00 2001 From: "mark a. foltz" Date: Tue, 13 Oct 2026 10:16:49 -0700 Subject: [PATCH 3/3] Respond to comments from Dom --- index.bs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/index.bs b/index.bs index e5aa797..8669a88 100644 --- a/index.bs +++ b/index.bs @@ -916,16 +916,14 @@ The getTools(options) method steps definition/exposed origins=], and |callerOrigin| returns false, then [=iteration/continue=]. - 1. [=list/Append=] (|tool definition|, |targetDocument|, |targetOrigin|) to - |discoveredTools|. + 1. [=list/Append=] (|tool definition|, |targetDocument|) to |discoveredTools|. 1. [=Queue a global task=] on the [=webmcp task source=] given |global| to run the following steps: 1. Let |tools| be an empty [=list=] of {{RegisteredTool}} dictionaries. - 1. [=list/For each=] (|tool definition|, |targetDocument|, |targetOrigin|) of - |discoveredTools|: + 1. [=list/For each=] (|tool definition|, |targetDocument|) of |discoveredTools|: 1. Let |registeredTool| be a new {{RegisteredTool}} dictionary, with the following fields: @@ -954,7 +952,7 @@ The getTools(options) method steps :: |targetDocument|'s [=relevant global object=] : {{RegisteredTool/origin}} - :: |targetOrigin|, [=serialization of an origin|serialized=]. + :: [=serialization of an origin|serialized=] |targetDocument|'s [=Document/origin=]. : {{RegisteredTool/annotations}} :: if |tool definition|'s [=tool definition/annotations=] is not null, a @@ -1496,9 +1494,6 @@ steps: 1. Let |document| be |descendant|'s [=navigable/active document=]. - 1. If |document| is not [=allowed to use=] the "{{tools}}" feature, then - [=iteration/continue=]. - 1. Let |id| be |document|'s [=Document/unique ID=]. 1. Let |tools| be |document|'s [=Document/associated ModelContext|associated