From 853a0aa3857856e0483d5898050f0a432055349b Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Fri, 17 Jul 2026 21:33:47 +0000
Subject: [PATCH 01/12] WebMCP: Spec executeTool() algorithm
This CL introduces the `executeTool()` method to the `ModelContext`
interface in the WebMCP specification. This aligns the spec with the C++
implementation in Blink.
Key changes:
- Adds `executeTool()` and `ExecuteToolOptions` IDL definitions.
- Specifies the `executeTool()` algorithm, detailing cross-document
tool lookup, origin visibility checks, and asynchronous invocation
in the target document`s realm.
- Clarifies the execution callback arguments and JSON serialization.
- Incorporates `AbortSignal` handling for tool cancellation,
dispatching a `toolcancel` event in the target document.
- Fires `toolactivated` event in the target document.
- Updates IDL member access syntax to use idiomatic Bikeshed style.
Small things
Fix some things
Don't serialize
Object argument enforcement
Add AbortSignal issue
Small things
---
index.bs | 183 +++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 172 insertions(+), 11 deletions(-)
diff --git a/index.bs b/index.bs
index 48c44a1..c7a42b9 100644
--- a/index.bs
+++ b/index.bs
@@ -164,10 +164,9 @@ A tool definition is a [=struct=] with the following [=struct/items=]
[[!JSON-SCHEMA]]
: execute steps
- :: a set of steps to invoke the tool.
+ :: an algorithm that takes both a [=string=] and an algorithm completionSteps that itself takes a [=string=]-or-null and a [=boolean=].
- Note: For tools registered imperatively, these steps will simply invoke the supplied
- {{ToolExecuteCallback}} callback. For tools registered
+ Note: For tools registered imperatively, these steps will simply invoke the [=imperative execute steps=]. For tools registered
[declaratively](https://github.com/webmachinelearning/webmcp/pull/76), this will be a set of
"internal" steps that have not been defined yet, that describe how to fill out a <{form}> and
its [=form-associated elements=].
@@ -257,6 +256,38 @@ a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |accessing origin
+
+The
imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Window}}
+|targetWindow|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows:
+
+1. [=Assert=]: these steps are running on |targetWindow|'s [=relevant agent=]'s [=agent/event
+ loop=].
+
+1. Let |inputObject| be the result of [=parse a JSON string to a JavaScript value=] given
+ |inputArguments| and |targetWindow|'s [=relevant realm=]. If
exception was thrown, then
+ run |completionSteps| given null and false, and abort these steps.
+
+ Issue: Support more granular errors; here we should return something that prompts the caller to reject its {{Promise}} with a "{{DataError}}" {{DOMException}}.
+
+1. If |inputObject| [=Object type|is not an Object=] is false, then run |completionSteps| given null and false, and abort these steps.
+
+ Issue(#146): Specify and fire the "
toolactivated" event.
+
+1. Let |toolPromise| be the result of [=invoke|invoking=] |tool|'s {{ModelContextTool/execute}} with
+ |inputObject|.
+
+1. [=promise/React=] to |toolPromise|:
+
+ - If |toolPromise| was fulfilled with value |v|:
+
+ 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON string=] given |v|. If this throws an exception, run |completionSteps| given null and false, and abort these steps.
+
+ 1. Run |completionSteps| given |serializedResult| and true.
+
+ - If |toolPromise| was rejected with reason
r, then run |completionSteps| given null and false.
+
+
+
To
unregister a tool given a {{ModelContext}} |modelContext| and a
[=string=] |tool name|, run these steps:
@@ -323,6 +354,7 @@ The {{ModelContext}} interface provides methods for web applications to register
interface ModelContext : EventTarget {
Promise
registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
Promise> getTools(optional ModelContextGetToolOptions options = {});
+ Promise executeTool(RegisteredTool tool, DOMString inputArguments, optional ExecuteToolOptions options = {});
attribute EventHandler ontoolchange;
};
@@ -348,6 +380,12 @@ is a [=model context=] [=struct=] created alongside the {{ModelContext}}.
agents written in JavaScript, and possibly living in <{iframe}>s. The [=user agent=]'s [=browser
agent=] uses a different internal mechanism to retrieve the tools exposed to it.
+
+ document.{{Document/modelContext}}.{{ModelContext/executeTool(tool, inputArguments, options)}}
+
+ Executes a tool on the document it was registered on. Returns a promise that resolves to the
+ result of the tool's execution.
+
@@ -451,7 +489,8 @@ The registerTool(tool, optionsregisterTool(tool, optionsgetTools(options) method steps
+
+The executeTool(tool, inputArguments,
+options) method steps are:
+
+1. Let |invokingDocument| be [=this=]'s [=relevant global object=]'s [=associated
+ Document|associated Document=].
+
+1. If |invokingDocument| is not [=Document/fully active=], then return [=a promise rejected with=]
+ an "{{InvalidStateError}}" {{DOMException}}.
+
+1. If this's [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and this's
+ [=relevant settings object=]'s [=environment settings object/origin=]'s [=origin/scheme=] is not
+ "file", then return [=a promise rejected with=] a "{{SecurityError}}"
+ {{DOMException}}.
+
+1. If |invokingDocument| is not [=allowed to use=] the "{{tools}}" feature, then return [=a promise
+ rejected with=] a "{{NotAllowedError}}" {{DOMException}}.
+
+1. Let |expectedTargetOriginURL| be the result of [=URL parser|parsing=] |tool|'s
+ {{RegisteredTool/origin}}.
+
+1. If |expectedTargetOriginURL| is failure, then return [=a promise rejected with=] a
+ "{{DataError}}" {{DOMException}}.
+
+1. Let |expectedTargetOrigin| be |expectedTargetOriginURL|'s [=url/origin=].
+
+1. [=Assert=]: |expectedTargetOrigin| is not an [=opaque origin=].
+
+1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=].
+
+1. Let |signal| be |options|'s {{ExecuteToolOptions/signal}} if it [=map/exists=]; otherwise null.
+
+1. If |signal| is not null, then:
+
+ 1. If |signal| is [=AbortSignal/aborted=], then return [=a promise rejected with=] |signal|'s
+ [=AbortSignal/abort reason=].
+
+ 1. [=AbortSignal/add|Add the following abort steps=] to |signal|:
+
+ 1. [=Reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
+
+ Issue(#48): Wire up |signal| to the tool execution callback in the tool host's document, so that tool abort is communicated all the way through.
+
+1. Let |targetWindow| be |tool|'s {{RegisteredTool/window}}.
+
+1. Let |targetDocument| be |targetWindow|'s [=associated Document|associated
+ Document=].
+
+1. Run the following steps [=in parallel=]:
+
+ 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=] is not
+ |invokingDocument|'s [=node navigable=]'s [=navigable/traversable navigable=], then [=queue a
+ global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global
+ object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these
+ steps.
+
+ Issue: TODO(domfarolino): File an issue about cross-frame-tree, same-browsing-context-group
+ tool execution.
+
+ 1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
+
+ 1. Let |callerOrigin| be |invokingDocument|'s [=Document/origin=].
+
+ 1. If |targetOrigin| is not [=same origin=] with |expectedTargetOrigin|, then [=queue a global
+ task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to
+ [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |targetToolMap| be |targetDocument|'s [=Document/associated ModelContext|associated
+ ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=].
+
+ 1. Let |toolName| be |tool|'s {{RegisteredTool/name}}.
+
+ 1. If |targetToolMap|[|toolName|] does not [=map/exist=], then [=queue a global task=] on the
+ [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=]
+ |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |tool definition| be |targetToolMap|[|toolName|].
+
+ 1. If [=tool is exposed to an origin=] given |targetOrigin|, |tool definition|'s [=tool
+ definition/exposed origins=], and |callerOrigin| is false, then [=queue a global task=] on the
+ [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=]
+ |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |completionSteps| be an algorithm that takes a [=string=]-or-null |result| and a [=boolean=] |success|, and runs the following steps:
+
+ 1. [=Assert=]: these steps are running [=in parallel=].
+
+ 1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|.
+
+ 1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}.
+
+ 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run |tool
+ definition|'s [=tool definition/execute steps=] given |inputArguments| and |completionSteps|.
+
+1. Return |promise|.
+
+
+
ModelContextTool Dictionary
The {{ModelContextTool}} dictionary describes a tool that can be invoked by [=agents=].
@@ -617,19 +760,21 @@ callback ToolExecuteCallback = Promise (object input);
tool["{{ModelContextTool/name}}"]
-
-
A unique identifier for the tool. This is used by [=agents=] to reference the tool when making tool calls.
+
A unique identifier for the tool. This is used by [=agents=] to reference the tool when
+ making tool calls.
tool["{{ModelContextTool/title}}"]
-
-
A label for the tool. This is used by the user agent to reference the tool in the user interface.
-
It is recommended that this string be localized to the user's
-{{NavigatorLanguage/language}}.
+
A label for the tool. This is used by the user agent to reference the tool in the user
+ interface.
It is recommended that this string be localized to the user's
+ {{NavigatorLanguage/language}}.
tool["{{ModelContextTool/description}}"]
-
-
A natural language description of the tool's functionality. This helps [=agents=] understand when and how to use the tool.
+
A natural language description of the tool's functionality. This helps [=agents=] understand
+ when and how to use the tool.
tool["{{ModelContextTool/inputSchema}}"]
@@ -700,6 +845,22 @@ dictionary ModelContextGetToolOptions {
same-origin documents.
+
+
+The {{ExecuteToolOptions}} dictionary allows web applications to pass options to
+{{ModelContext/executeTool()}}.
+
+
+dictionary ExecuteToolOptions {
+ AbortSignal signal;
+};
+
+
+
+ : options["{{ExecuteToolOptions/signal}}"]
+ :: An {{AbortSignal}} that can be used to cancel the execution of the tool.
+
+
The {{RegisteredTool}} dictionary represents a tool that has been registered and is available for
From 90ccdfa2db714dc610d2ce87f6b7393310b7b453 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Tue, 21 Jul 2026 19:59:20 -0400
Subject: [PATCH 02/12] Remove spec issue now that #227 is filed
---
index.bs | 3 ---
1 file changed, 3 deletions(-)
diff --git a/index.bs b/index.bs
index c7a42b9..13081bf 100644
--- a/index.bs
+++ b/index.bs
@@ -685,9 +685,6 @@ The executeTool(tool, inputArgument
object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these
steps.
- Issue: TODO(domfarolino): File an issue about cross-frame-tree, same-browsing-context-group
- tool execution.
-
1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
1. Let |callerOrigin| be |invokingDocument|'s [=Document/origin=].
From f9ef39efa61968296d800c711dd00c24b939bf44 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 09:12:08 -0400
Subject: [PATCH 03/12] Break tool execute steps out; perform on-loop existence
check, and document race
---
index.bs | 80 +++++++++++++++++++++++++++++++++++++++++++++++++-------
1 file changed, 71 insertions(+), 9 deletions(-)
diff --git a/index.bs b/index.bs
index 13081bf..cbdd980 100644
--- a/index.bs
+++ b/index.bs
@@ -257,15 +257,73 @@ a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |accessing origin
-The
imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Window}}
-|targetWindow|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows:
+The
tool execute steps, given a [=string=] |toolName|, a {{Document}} |targetDocument|,
+a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows. The
+|completionSteps| algorithm takes a [=string=]-or-null
result and a [=boolean=]
success.
+
+1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event loop=].
+
+1. Let |toolMap| be |targetDocument|'s [=Document/associated ModelContext|associated
+
ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=].
+
+1. If |toolMap|[|toolName|] does not [=map/exist=], then run |completionSteps| given null and false,
+ and abort these steps.
+
+ Issue: Support the plumbing of more granular errors back to the invoker; this should result in a
+ "{{NotFoundError}}" in the invoking document.
+
+
+
This protects us against a race between tool unregistration and execution. While tool
+ existence is protected from this race, tool unregistration followed by a quick
+ re-registration of a tool with the same |toolName| but input schema is not
+ protected against.
+
+
This might result in |inputArguments| for an old tool being applied to the
+ [=tool definition/input schema=] of a newer tool, and causing whatever error that might cause,
+ when issue #92 is
+ resolved.
+
+
+
+ // -- Tool owner document. --
+ const oldInputSchema = {...};
+ const newInputSchema = {...};
+ const ac = new AbortController();
+ document.modelContext.registerTool({..., inputSchema: oldInputSchema}, {signal: ac.signal});
+
+ // Unregister, and quickly re-register with an updated input schema.
+ ac.abort();
+ document.modelContext.registerTool({..., inputSchema: newInputSchema});
+
+
+ // -- Executing document. --
+ //
+ // This could target either the "old" tool, or the "new" one above,
+ // and the execution might encounter any requisite errors due to the mismatch.
+ const [tool] = await document.modelContext.getTools();
+ document.modelContext.executeTool(tool, "{a: 10}");
+
+
+
+
+1. Let |tool| be |toolMap|[|toolName|].
+
+1. Run |tool|'s [=tool definition/execute steps=] given |inputArguments| and |completionSteps|.
+
+ Note: This is the point where we branch into either the [=imperative execute steps=] or the [=declarative execute steps=].
+
+
-1. [=Assert=]: these steps are running on |targetWindow|'s [=relevant agent=]'s [=agent/event
+
+The
imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Document}} |targetDocument|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows:
+
+1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event
loop=].
1. Let |inputObject| be the result of [=parse a JSON string to a JavaScript value=] given
- |inputArguments| and |targetWindow|'s [=relevant realm=]. If
exception was thrown, then
- run |completionSteps| given null and false, and abort these steps.
+ |inputArguments| and |targetDocument|'s [=relevant realm=]. If
exception was thrown, then run |completionSteps| given null and false, and abort
+ these steps.
Issue: Support more granular errors; here we should return something that prompts the caller to reject its {{Promise}} with a "{{DataError}}" {{DOMException}}.
@@ -489,8 +547,7 @@ The
registerTool(tool, optionsexecuteTool(tool, inputArgument
1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}.
- 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run |tool
- definition|'s [=tool definition/execute steps=] given |inputArguments| and |completionSteps|.
+ 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|.
1. Return |promise|.
@@ -950,6 +1006,12 @@ The synthesize a declarative JSON Schema object algorithm, given a <{
}
+
+The declarative execute steps are as follows.
+
+domfarolino
+
+
Events
The following are the [=event handlers=] (and their corresponding [=event handler event types=])
From c968f7988cc5313f37ef6c209b13e7db4e1934a8 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 14:18:55 +0000
Subject: [PATCH 04/12] bf-cache note and cross-BCG note
---
index.bs | 11 +++++++++++
1 file changed, 11 insertions(+)
diff --git a/index.bs b/index.bs
index cbdd980..babb083 100644
--- a/index.bs
+++ b/index.bs
@@ -96,6 +96,9 @@ spec:html; type:dfn;
text:is origin-keyed
+spec: html; urlPrefix: https://html.spec.whatwg.org/C
+ type: dfn
+ text: browsing context group
spec: SECURE-CONTEXTS; urlPrefix: https://w3c.github.io/webappsec-secure-contexts/
type: abstract-op
text: is origin potentially trustworthy?; url: is-origin-trustworthy
@@ -742,6 +745,9 @@ The executeTool(tool, inputArgument
object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these
steps.
+ Issue(#227): Consider supporting tool execution across top-level documents in the same
+ [=browsing context group=].
+
1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
1. Let |callerOrigin| be |invokingDocument|'s [=Document/origin=].
@@ -782,6 +788,11 @@ The executeTool(tool, inputArgument
1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|.
+ Note: Because documents only process tasks on their event loops when [=Document/fully
+ active=], if |targetDocument| is not [=Document/fully active=], this will simply queue the
+ steps to execute the tool, to run when the document finally becomes active again (i.e., when
+ it leaves the bf-cache).
+
1. Return |promise|.
From 1d6b4bdaa289f38566759cf2d2bca9e9a9a76849 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 14:27:57 +0000
Subject: [PATCH 05/12] Default webidl spec
---
index.bs | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/index.bs b/index.bs
index babb083..991ad81 100644
--- a/index.bs
+++ b/index.bs
@@ -94,6 +94,8 @@ spec:html; type:dfn;
text:browsing context group set
text:unique internal value
text:is origin-keyed
+spec: webidl; type: dfn;
+ text: an exception was thrown
spec: html; urlPrefix: https://html.spec.whatwg.org/C
@@ -324,7 +326,7 @@ The imperative execute steps, given a {{ModelContextTool}} |tool|, a
loop=].
1. Let |inputObject| be the result of [=parse a JSON string to a JavaScript value=] given
- |inputArguments| and |targetDocument|'s [=relevant realm=]. If exception was thrown, then run |completionSteps| given null and false, and abort
these steps.
From fda9bde1a7c960a3644046d66dea3000623d7abb Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 15:13:06 +0000
Subject: [PATCH 06/12] Formalize this
---
index.bs | 17 ++++++++---------
1 file changed, 8 insertions(+), 9 deletions(-)
diff --git a/index.bs b/index.bs
index 991ad81..a0124d5 100644
--- a/index.bs
+++ b/index.bs
@@ -417,7 +417,7 @@ The {{ModelContext}} interface provides methods for web applications to register
interface ModelContext : EventTarget {
Promise registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
Promise> getTools(optional ModelContextGetToolOptions options = {});
- Promise executeTool(RegisteredTool tool, DOMString inputArguments, optional ExecuteToolOptions options = {});
+ Promise executeTool(RegisteredTool tool, DOMString inputArguments, optional ModelContextExecuteToolOptions options = {});
attribute EventHandler ontoolchange;
};
@@ -701,7 +701,7 @@ The executeTool(tool, inputArgument
1. If |invokingDocument| is not [=Document/fully active=], then return [=a promise rejected with=]
an "{{InvalidStateError}}" {{DOMException}}.
-1. If this's [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and this's
+1. If [=this=]'s [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and [=this=]'s
[=relevant settings object=]'s [=environment settings object/origin=]'s [=origin/scheme=] is not
"file", then return [=a promise rejected with=] a "{{SecurityError}}"
{{DOMException}}.
@@ -721,9 +721,9 @@ The executeTool(tool, inputArgument
1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=].
-1. Let |signal| be |options|'s {{ExecuteToolOptions/signal}} if it [=map/exists=]; otherwise null.
+1. If |options|'s {{ModelContextExecuteToolOptions/signal}} [=map/exists=], then:
-1. If |signal| is not null, then:
+ 1. Let |signal| be |options|'s {{ModelContextExecuteToolOptions/signal}}.
1. If |signal| is [=AbortSignal/aborted=], then return [=a promise rejected with=] |signal|'s
[=AbortSignal/abort reason=].
@@ -911,19 +911,18 @@ dictionary ModelContextGetToolOptions {
same-origin documents.
-
+ModelContextExecuteToolOptions Dictionary
-The {{ExecuteToolOptions}} dictionary allows web applications to pass options to
-{{ModelContext/executeTool()}}.
+The {{ModelContextExecuteToolOptions}} dictionary allows web applications to pass options to {{ModelContext/executeTool()}}.
-dictionary ExecuteToolOptions {
+dictionary ModelContextExecuteToolOptions {
AbortSignal signal;
};
- : options["{{ExecuteToolOptions/signal}}"]
+ : options["{{ModelContextExecuteToolOptions/signal}}"]
:: An {{AbortSignal}} that can be used to cancel the execution of the tool.
From e10a9f208ba9c41ff44d3912124698069c1f5779 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 15:39:44 +0000
Subject: [PATCH 07/12] Fix origin errors
---
index.bs | 16 +++++++++-------
1 file changed, 9 insertions(+), 7 deletions(-)
diff --git a/index.bs b/index.bs
index a0124d5..0c28b39 100644
--- a/index.bs
+++ b/index.bs
@@ -446,8 +446,7 @@ is a [=model context=] [=struct=] created alongside the {{ModelContext}}.
document.{{Document/modelContext}}.{{ModelContext/executeTool(tool, inputArguments, options)}}
- Executes a tool on the document it was registered on. Returns a promise that resolves to the
- result of the tool's execution.
+ Executes a tool on the document it was registered on. Returns a promise that resolves to the stringified result of the tool's execution.
@@ -712,8 +711,9 @@ The executeTool(tool, inputArgument
1. Let |expectedTargetOriginURL| be the result of [=URL parser|parsing=] |tool|'s
{{RegisteredTool/origin}}.
-1. If |expectedTargetOriginURL| is failure, then return [=a promise rejected with=] a
- "{{DataError}}" {{DOMException}}.
+1. If |expectedTargetOriginURL| is failure, or |expectedTargetOriginURL|'s [=url/origin=] is an
+ [=opaque origin=], then return [=a promise rejected with=] a "{{NotSupportedError}}"
+ {{DOMException}}.
1. Let |expectedTargetOrigin| be |expectedTargetOriginURL|'s [=url/origin=].
@@ -750,6 +750,8 @@ The executeTool(tool, inputArgument
Issue(#227): Consider supporting tool execution across top-level documents in the same
[=browsing context group=].
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
1. Let |callerOrigin| be |invokingDocument|'s [=Document/origin=].
@@ -774,9 +776,9 @@ The executeTool(tool, inputArgument
1. Let |tool definition| be |targetToolMap|[|toolName|].
1. If [=tool is exposed to an origin=] given |targetOrigin|, |tool definition|'s [=tool
- definition/exposed origins=], and |callerOrigin| is false, then [=queue a global task=] on the
- [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=]
- |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+ definition/exposed origins=], and |callerOrigin| returns false, then [=queue a global task=]
+ on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to
+ [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
From d6ce1bb30a59ce0c70a040c8099fa776204afa2c Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 11:57:59 -0400
Subject: [PATCH 08/12] Report a warning
---
index.bs | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)
diff --git a/index.bs b/index.bs
index 0c28b39..3907e76 100644
--- a/index.bs
+++ b/index.bs
@@ -343,11 +343,15 @@ The imperative execute steps, given a {{ModelContextTool}} |tool|, a
- If |toolPromise| was fulfilled with value |v|:
- 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON string=] given |v|. If this throws an exception, run |completionSteps| given null and false, and abort these steps.
+ 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON string=] given |v|. If this throws an exception, run |completionSteps| given null and false, and abort these steps.
- 1. Run |completionSteps| given |serializedResult| and true.
+ 1. Run |completionSteps| given |serializedResult| and true.
- - If |toolPromise| was rejected with reason r, then run |completionSteps| given null and false.
+ - If |toolPromise| was rejected with reason |r|, then:
+
+ 1. Optionally [=report a warning to the console=] describing |r|.
+
+ 1. Run |completionSteps| given null and false.
From 2f3688b4e9599738059fab56c38770a02f2ad646 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 14:00:16 -0400
Subject: [PATCH 09/12] Reference toolcanceled event
---
index.bs | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/index.bs b/index.bs
index 3907e76..46ca1a8 100644
--- a/index.bs
+++ b/index.bs
@@ -736,7 +736,10 @@ The executeTool(tool, inputArgument
1. [=Reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
- Issue(#48): Wire up |signal| to the tool execution callback in the tool host's document, so that tool abort is communicated all the way through.
+ Issue(#48): Wire up |signal| to the tool execution callback in the tool host's document, so
+ that tool abort is communicated all the way through. This should also fire the
+ "toolcanceled" event in the target document. See also issue #146.
1. Let |targetWindow| be |tool|'s {{RegisteredTool/window}}.
From 8828678d135b7cf9832f00c764f6202d344ab74f Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Mon, 27 Jul 2026 14:32:26 -0400
Subject: [PATCH 10/12] Don't let DOMString be null
---
index.bs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/index.bs b/index.bs
index 46ca1a8..12a59a4 100644
--- a/index.bs
+++ b/index.bs
@@ -421,7 +421,7 @@ The {{ModelContext}} interface provides methods for web applications to register
interface ModelContext : EventTarget {
Promise registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
Promise> getTools(optional ModelContextGetToolOptions options = {});
- Promise executeTool(RegisteredTool tool, DOMString inputArguments, optional ModelContextExecuteToolOptions options = {});
+ Promise executeTool(RegisteredTool tool, DOMString inputArguments, optional ModelContextExecuteToolOptions options = {});
attribute EventHandler ontoolchange;
};
From eea098a999a25657037793738acc8ab7f3780fea Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Tue, 28 Jul 2026 16:11:32 +0000
Subject: [PATCH 11/12] Spec the full pending execution cancellation
infrastructure
---
index.bs | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 152 insertions(+), 5 deletions(-)
diff --git a/index.bs b/index.bs
index 12a59a4..1abf191 100644
--- a/index.bs
+++ b/index.bs
@@ -193,6 +193,76 @@ An annotations is a [=struct=] with the following [=struct/items=]:
:: a [=boolean=], initially false.
+
+
+A pending tool execution is a [=struct=] with the following [=struct/items=]:
+
+
+ : caller document
+ :: a {{Document}}.
+
+ : target document
+ :: a {{Document}}.
+
+ : tool name
+ :: a [=string=].
+
+ : completion steps
+ :: an algorithm that takes a [=string=]-or-null and a [=boolean=].
+
+
+A [=traversable navigable=] has a pending tool executions
+map, which is a [=map=] whose keys are [=unique internal values=] and whose values are
+[=pending tool execution=] [=structs=]. It is initially empty.
+
+Note: This map is only ever mutated from steps [=in parallel=]. This simulates the single,
+authoritative "browser process" that most modern browsers implement, where execution tracking sits
+outside any individual Document process's event loop, and is accessed asynchronously via some
+inter-process communication mechanism.
+
+
+
+
+This specification's [=unloading document cleanup steps=], given a {{Document}} |document|, are as
+follows:
+
+1. Let |traversable| be |document|'s [=node navigable=]'s [=navigable/traversable navigable=].
+
+1. Run the following steps [=in parallel=]:
+
+ 1. Let |executionsToRemove| be an empty [=list=].
+
+ 1. [=map/For each=] |uuid| → |execution| of |traversable|'s [=traversable navigable/pending
+ tool executions map=]:
+
+ 1. If |document| is |execution|'s [=pending tool execution/target document=] or |document| is
+ |execution|'s [=pending tool execution/caller document=], then [=list/append=] |uuid| to
+ |executionsToRemove|.
+
+ 1. [=list/For each=] |uuid| of |executionsToRemove|:
+
+ 1. Let |execution| be |traversable|'s [=traversable navigable/pending tool executions
+ map=][|uuid|].
+
+ 1.
If |document| is |execution|'s [=pending tool
+ execution/target document=], then run |execution|'s [=pending tool execution/completion
+ steps=] given null and false.
+
+ Note: This removes |execution| from the [=traversable navigable/pending tool executions
+ map=].
+
+ 1. If |document| is |execution|'s [=pending tool execution/caller document=], then:
+
+ 1. [=map/Remove=] |traversable|'s [=traversable navigable/pending tool executions
+ map=][|uuid|].
+
+ 1. Issue(#48): Abort the `AbortSignal` in |execution|'s [=pending tool execution/target
+ document=], so the tool can abort early now that the caller is dead.
+
+ 1. [=Assert=]: |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|]
+ does not [=map/exist=].
+
+
@@ -288,7 +358,7 @@ a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follow
when issue #92 is
resolved.
-
+
// -- Tool owner document. --
const oldInputSchema = {...};
@@ -371,10 +441,33 @@ To unregister a tool given a {{ModelContext}} |mo
1. [=map/Remove=] |tool map|[|tool name|].
+1. Let |targetDocument| be |modelContext|'s [=relevant global object=]'s [=associated
+ Document|associated Document=].
+
+1. Let |traversable| be |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=].
+
1. Run the following steps [=in parallel=]:
- 1. [=Notify documents of a tool change=] given |modelContext|'s [=relevant global object=]'s
- [=associated Document|associated Document=] and |exposed origins|.
+ 1. Let |canceledExecutions| be an empty [=list=].
+
+ 1. [=map/For each=] |uuid| → |execution| of |traversable|'s [=traversable navigable/pending
+ tool executions map=]:
+
+ 1. If |execution|'s [=pending tool execution/target document=] is |targetDocument| and
+ |execution|'s [=pending tool execution/tool name=] is |tool name|, then [=list/append=]
+ |uuid| to |canceledExecutions|.
+
+ 1. [=list/For each=] |uuid| of |canceledExecutions|:
+
+ 1. Let |execution| be |traversable|'s [=traversable navigable/pending tool executions
+ map=][|uuid|].
+
+ 1. Run |execution|'s [=pending tool execution/completion steps=] given null and false.
+
+ Note: This removes |execution| from the [=traversable navigable/pending tool executions
+ map=].
+
+ 1. [=Notify documents of a tool change=] given |targetDocument| and |exposed origins|.
@@ -789,14 +882,67 @@ The
executeTool(tool, inputArgument
Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+ 1. Let |uuid| be a new [=unique internal value=].
+
1. Let |completionSteps| be an algorithm that takes a [=string=]-or-null |result| and a [=boolean=] |success|, and runs the following steps:
1. [=Assert=]: these steps are running [=in parallel=].
+ 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|] does not [=map/exist=], then return.
+
+
+
It is possible that a pending execution identified by |uuid| no longer exists. This can
+ happen due to a race between (a) tool cancellation when the caller document gets destroyed; and (b) tool promise resolution. Both
+ of these race to invoke |completionSteps|, and the first invocation will remove the
+ pending execution by its key |uuid|, this check protects subsequent racing invocations.
+
+
This can also happen due to a limitation with tool unregistration. Consider this
+ example:
+
+
+
+ const ac = new AbortController();
+ document.modelContext.registerTool({
+ name: "tool"
+ description: "a racy tool",
+ execute: async () => {
+ return new Promise(resolve => {
+ resolve("tool result");
+ ac.abort();
+ });
+ }
+ }, {signal: ac.signal});
+
+
+
In this case, tool unregistration happens after a tool Promise is resolved, but before
+ its fulfillment handler runs. Since both run |completionSteps|, this check protects the
+ second invocation from trying to remove the same pending execution twice.
+
+
+
+ 1. [=map/Remove=] |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|].
+
1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|.
1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}.
+ 1. Let |execution| be a new [=pending tool execution=], with the following [=struct/items=]:
+
+ : [=pending tool execution/caller document=]
+ :: |invokingDocument|
+
+ : [=pending tool execution/target document=]
+ :: |targetDocument|
+
+ : [=pending tool execution/tool name=]
+ :: |toolName|
+
+ : [=pending tool execution/completion steps=]
+ :: |completionSteps|
+
+ 1. Set |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|] to |execution|.
+
1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|.
Note: Because documents only process tasks on their event loops when [=Document/fully
@@ -1028,9 +1174,10 @@ The synthesize a declarative JSON Schema object algorithm, given a <{
-The declarative execute steps are as follows.
+The declarative execute steps are as follows:
+
+Issue: Spec the declarative execution steps, and their integration with form elements.
-domfarolino
Events
From 381b37654797159d09a46c93d42b26a686c4b996 Mon Sep 17 00:00:00 2001
From: Dominic Farolino
Date: Tue, 28 Jul 2026 12:15:05 -0400
Subject: [PATCH 12/12] specfmt
---
index.bs | 91 +++++++++++++++++++++++++++++++++++---------------------
1 file changed, 57 insertions(+), 34 deletions(-)
diff --git a/index.bs b/index.bs
index 1abf191..e884548 100644
--- a/index.bs
+++ b/index.bs
@@ -169,9 +169,11 @@ A tool definition is a [=struct=] with the following [=struct/items=]
[[!JSON-SCHEMA]]
: execute steps
- :: an algorithm that takes both a [=string=] and an algorithm completionSteps that itself takes a [=string=]-or-null and a [=boolean=].
+ :: an algorithm that takes both a [=string=] and an algorithm completionSteps
+ that itself takes a [=string=]-or-null and a [=boolean=].
- Note: For tools registered imperatively, these steps will simply invoke the [=imperative execute steps=]. For tools registered
+ Note: For tools registered imperatively, these steps will simply invoke the [=imperative
+ execute steps=]. For tools registered
[declaratively](https://github.com/webmachinelearning/webmcp/pull/76), this will be a set of
"internal" steps that have not been defined yet, that describe how to fill out a <{form}> and
its [=form-associated elements=].
@@ -332,11 +334,13 @@ a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |accessing origin
-The
tool execute steps, given a [=string=] |toolName|, a {{Document}} |targetDocument|,
-a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows. The
-|completionSteps| algorithm takes a [=string=]-or-null
result and a [=boolean=]
success.
+The
tool execute steps, given a [=string=] |toolName|, a {{Document}} |targetDocument|, a
+[=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows. The
+|completionSteps| algorithm takes a [=string=]-or-null
result and a [=boolean=]
+
success.
-1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event loop=].
+1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event
+ loop=].
1. Let |toolMap| be |targetDocument|'s [=Document/associated ModelContext|associated
ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=].
@@ -350,13 +354,12 @@ a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follow
This protects us against a race between tool unregistration and execution. While tool
existence is protected from this race, tool unregistration followed by a quick
- re-registration of a tool with the same |toolName| but input schema is not
- protected against.
+ re-registration of a tool with the same |toolName| but input schema is
not protected
+ against.
-
This might result in |inputArguments| for an old tool being applied to the
- [=tool definition/input schema=] of a newer tool, and causing whatever error that might cause,
- when issue #92 is
- resolved.
+
This might result in |inputArguments| for an old tool being applied to the [=tool
+ definition/input schema=] of a newer tool, and causing whatever error that might cause, when issue #92 is resolved.
@@ -385,24 +388,28 @@ a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follow
1. Run |tool|'s [=tool definition/execute steps=] given |inputArguments| and |completionSteps|.
- Note: This is the point where we branch into either the [=imperative execute steps=] or the [=declarative execute steps=].
+ Note: This is the point where we branch into either the [=imperative execute steps=] or the
+ [=declarative execute steps=].
-The
imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Document}} |targetDocument|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows:
+The
imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Document}}
+|targetDocument|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows:
1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event
loop=].
1. Let |inputObject| be the result of [=parse a JSON string to a JavaScript value=] given
- |inputArguments| and |targetDocument|'s [=relevant realm=]. If
exception was thrown, then run |completionSteps| given null and false, and abort
+ |inputArguments| and |targetDocument|'s [=relevant realm=]. If
exception was thrown, then run |completionSteps| given null and false, and abort
these steps.
- Issue: Support more granular errors; here we should return something that prompts the caller to reject its {{Promise}} with a "{{DataError}}" {{DOMException}}.
+ Issue: Support more granular errors; here we should return something that prompts the caller to
+ reject its {{Promise}} with a "{{DataError}}" {{DOMException}}.
-1. If |inputObject| [=Object type|is not an Object=] is false, then run |completionSteps| given null and false, and abort these steps.
+1. If |inputObject| [=Object type|is not an Object=] is false, then run |completionSteps| given null
+ and false, and abort these steps.
Issue(#146): Specify and fire the "
toolactivated" event.
@@ -413,7 +420,9 @@ The
imperative execute steps, given a {{ModelContextTool}} |tool|, a
- If |toolPromise| was fulfilled with value |v|:
- 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON string=] given |v|. If this throws an exception, run |completionSteps| given null and false, and abort these steps.
+ 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON
+ string=] given |v|. If this throws an exception, run |completionSteps| given null and
+ false, and abort these steps.
1. Run |completionSteps| given |serializedResult| and true.
@@ -543,7 +552,8 @@ is a [=model context=] [=struct=] created alongside the {{ModelContext}}.
document.{{Document/modelContext}}.{{ModelContext/executeTool(tool, inputArguments, options)}}
- Executes a tool on the document it was registered on. Returns a promise that resolves to the stringified result of the tool's execution.
+ Executes a tool on the document it was registered on. Returns a promise that resolves to the
+ stringified result of the tool's execution.
@@ -648,7 +658,9 @@ The
registerTool(tool, optionsexecuteTool(tool, inputArgument
1. If |invokingDocument| is not [=Document/fully active=], then return [=a promise rejected with=]
an "{{InvalidStateError}}" {{DOMException}}.
-1. If [=this=]'s [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and [=this=]'s
- [=relevant settings object=]'s [=environment settings object/origin=]'s [=origin/scheme=] is not
- "file", then return [=a promise rejected with=] a "{{SecurityError}}"
- {{DOMException}}.
+1. If [=this=]'s [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and
+ [=this=]'s [=relevant settings object=]'s [=environment settings object/origin=]'s
+ [=origin/scheme=] is not "file", then return [=a promise rejected with=] a
+ "{{SecurityError}}" {{DOMException}}.
1. If |invokingDocument| is not [=allowed to use=] the "{{tools}}" feature, then return [=a promise
rejected with=] a "{{NotAllowedError}}" {{DOMException}}.
@@ -884,18 +896,22 @@ The executeTool(tool, inputArgument
1. Let |uuid| be a new [=unique internal value=].
- 1. Let |completionSteps| be an algorithm that takes a [=string=]-or-null |result| and a [=boolean=] |success|, and runs the following steps:
+ 1. Let |completionSteps| be an algorithm that takes a [=string=]-or-null |result| and a
+ [=boolean=] |success|, and runs the following steps:
1. [=Assert=]: these steps are running [=in parallel=].
- 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|] does not [=map/exist=], then return.
+ 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s
+ [=traversable navigable/pending tool executions map=][|uuid|] does not [=map/exist=], then
+ return.
It is possible that a pending execution identified by |uuid| no longer exists. This can
happen due to a race between (a) tool cancellation when the caller document gets destroyed; and (b) tool promise resolution. Both
of these race to invoke |completionSteps|, and the first invocation will remove the
- pending execution by its key |uuid|, this check protects subsequent racing invocations.
+ pending execution by its key |uuid|, this check protects subsequent racing
+ invocations.
This can also happen due to a limitation with tool unregistration. Consider this
example:
@@ -921,11 +937,15 @@ The
executeTool(tool, inputArgument
- 1. [=map/Remove=] |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|].
+ 1. [=map/Remove=] |targetDocument|'s [=node navigable=]'s [=navigable/traversable
+ navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|].
- 1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|.
+ 1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given
+ |invokingDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|.
- 1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}.
+ 1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |invokingDocument|'s
+ [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}"
+ {{DOMException}}.
1. Let |execution| be a new [=pending tool execution=], with the following [=struct/items=]:
@@ -941,9 +961,11 @@ The
executeTool(tool, inputArgument
: [=pending tool execution/completion steps=]
:: |completionSteps|
- 1. Set |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|] to |execution|.
+ 1. Set |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s
+ [=traversable navigable/pending tool executions map=][|uuid|] to |execution|.
- 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|.
+ 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool
+ execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|.
Note: Because documents only process tasks on their event loops when [=Document/fully
active=], if |targetDocument| is not [=Document/fully active=], this will simply queue the
@@ -1068,7 +1090,8 @@ dictionary ModelContextGetToolOptions {
ModelContextExecuteToolOptions Dictionary
-The {{ModelContextExecuteToolOptions}} dictionary allows web applications to pass options to {{ModelContext/executeTool()}}.
+The {{ModelContextExecuteToolOptions}} dictionary allows web applications to pass options to
+{{ModelContext/executeTool()}}.
dictionary ModelContextExecuteToolOptions {