Skip to content

Commit 5801678

Browse files
authored
Spec the getTools() API (#223)
1 parent d29b8d7 commit 5801678

1 file changed

Lines changed: 192 additions & 1 deletion

File tree

index.bs

Lines changed: 192 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,7 @@ The {{ModelContext}} interface provides methods for web applications to register
322322
[Exposed=Window, SecureContext]
323323
interface ModelContext : EventTarget {
324324
Promise<undefined> registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
325+
Promise<sequence<RegisteredTool>> getTools(optional ModelContextGetToolOptions options = {});
325326

326327
attribute EventHandler ontoolchange;
327328
};
@@ -339,6 +340,14 @@ is a [=model context=] [=struct=] created alongside the {{ModelContext}}.
339340
{{ModelContextTool/description}} are empty strings, or if the {{ModelContextTool/inputSchema}}
340341
is invalid.</p>
341342
</dd>
343+
344+
<dt><code><var ignore>document</var>.{{Document/modelContext}}.{{ModelContext/getTools(options)}}</code></dt>
345+
<dd>
346+
<p>Returns a promise that resolves to a list of registered tools from this document and its
347+
descendants that are exposed to this document. This API is designed for so-called "in-page"
348+
agents written in JavaScript, and possibly living in <{iframe}>s. The [=user agent=]'s [=browser
349+
agent=] uses a different internal mechanism to retrieve the tools exposed to it.</p>
350+
</dd>
342351
</dl>
343352

344353

@@ -470,6 +479,117 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
470479

471480
</div>
472481

482+
<div algorithm>
483+
The <dfn method for=ModelContext>getTools(<var>options</var>)</dfn> method steps are:
484+
485+
1. Let |global| be [=this=]'s [=relevant global object=].
486+
487+
1. Let |toolRequestor| be |global|'s [=associated Document|associated <code>Document</code>=].
488+
489+
1. If |toolRequestor| is not [=Document/fully active=], then return [=a promise rejected with=] an
490+
"{{InvalidStateError}}" {{DOMException}}.
491+
492+
1. If this's [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and this's
493+
[=relevant settings object=]'s [=environment settings object/origin=]'s [=origin/scheme=] is not
494+
<code>"file"</code>, then return [=a promise rejected with=] a "{{SecurityError}}"
495+
{{DOMException}}.
496+
497+
1. If |toolRequestor| is not [=allowed to use=] the "{{tools}}" feature, then return [=a promise
498+
rejected with=] a "{{NotAllowedError}}" {{DOMException}}.
499+
500+
1. Let |from origins| be an empty [=list=] of [=origins=].
501+
502+
1. If |options|'s {{ModelContextGetToolOptions/fromOrigins}} [=map/exists=], then:
503+
504+
1. [=list/For each=] |origin| of |options|'s {{ModelContextGetToolOptions/fromOrigins}}:
505+
506+
1. Let |parsedURL| be the result of running the [=URL parser=] on |origin|.
507+
508+
1. If |parsedURL| is failure or its [=url/origin=] is not [$is origin potentially
509+
trustworthy?|potentially trustworthy$], then return [=a promise rejected with=] a
510+
"{{SecurityError}}" {{DOMException}}.
511+
512+
1. [=list/Append=] |parsedURL|'s [=url/origin=] to |from origins|.
513+
514+
1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=].
515+
516+
1. Run the following steps [=in parallel=]:
517+
518+
1. Let |tools| be an empty [=list=] of {{RegisteredTool}} dictionaries.
519+
520+
1. Let |navigables| be |toolRequestor|'s [=node navigable=]'s [=navigable/traversable
521+
navigable=]'s [=Document/inclusive descendant navigables=].
522+
523+
1. [=list/For each=] |navigable| of |navigables|:
524+
525+
1. Let |targetDocument| be |navigable|'s [=navigable/active document=].
526+
527+
1. If |targetDocument| is not [=allowed to use=] the "{{tools}}" feature, then
528+
[=iteration/continue=].
529+
530+
1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
531+
532+
1. Let |callerOrigin| be |toolRequestor|'s [=Document/origin=].
533+
534+
1. If |toolOwnerIsRequested| be true if |targetOrigin| is [=same origin=] with |callerOrigin|,
535+
or if |from origins| [=list/contains=] |targetOrigin|; otherwise, false.
536+
537+
1. If |toolOwnerIsRequested| is false, then [=iteration/continue=].
538+
539+
1. Let |targetToolMap| be |targetDocument|'s [=Document/associated ModelContext|associated
540+
<code>ModelContext</code>=]'s [=ModelContext/internal context=]'s [=model context/tool
541+
map=].
542+
543+
1. [=map/For each=] <var ignore>tool name</var> → |tool definition| of |targetToolMap|:
544+
545+
1. If [=tool is exposed to an origin=] given |targetOrigin|, |tool definition|'s [=tool
546+
definition/exposed origins=], and |callerOrigin| returns false, then
547+
[=iteration/continue=].
548+
549+
1. Let |registeredTool| be a new {{RegisteredTool}} dictionary, with the following fields:
550+
551+
: {{RegisteredTool/name}}
552+
:: |tool definition|'s [=tool definition/name=]
553+
554+
: {{RegisteredTool/title}}
555+
:: |tool definition|'s [=tool definition/title=] if it is non-null; otherwise the empty
556+
string.
557+
558+
Issue(#224): Consider not defaulting to the empty string, and just excluding this
559+
member, which will result in {{undefined}}.
560+
561+
: {{RegisteredTool/description}}
562+
:: |tool definition|'s [=tool definition/description=]
563+
564+
: {{RegisteredTool/inputSchema}}
565+
:: |tool definition|'s [=tool definition/input schema=] if it is not the empty string;
566+
otherwise undefined.
567+
568+
: {{RegisteredTool/window}}
569+
:: |targetDocument|'s [=relevant global object=]
570+
571+
: {{RegisteredTool/origin}}
572+
:: |targetOrigin|, [=serialization of an origin|serialized=].
573+
574+
: {{RegisteredTool/annotations}}
575+
:: if |tool definition|'s [=tool definition/annotations=] is not null, a
576+
{{ToolAnnotations}} dictionary whose {{ToolAnnotations/readOnlyHint}} is |tool
577+
definition|'s [=tool definition/annotations=]'s [=annotations/read-only hint=] and
578+
{{ToolAnnotations/untrustedContentHint}} is |tool definition|'s [=tool
579+
definition/annotations=]'s [=annotations/untrusted content hint=].
580+
581+
1. [=list/Append=] |registeredTool| to |tools|.
582+
583+
1. [=list/Sort in ascending order=] |tools|, with |a| being less than |b| if
584+
|a|["{{RegisteredTool/name}}"] is [=code unit less than=] |b|["{{RegisteredTool/name}}"].
585+
586+
1. [=Queue a global task=] on the [=webmcp task source=] given |global| to [=resolve=] |promise|
587+
with |tools|.
588+
589+
1. Return |promise|.
590+
591+
</div>
592+
473593
<h4 id="model-context-tool">ModelContextTool Dictionary</h4>
474594

475595
The {{ModelContextTool}} dictionary describes a tool that can be invoked by [=agents=].
@@ -514,7 +634,7 @@ callback ToolExecuteCallback = Promise<any> (object input);
514634

515635
<dt><code><var ignore>tool</var>["{{ModelContextTool/inputSchema}}"]</code></dt>
516636
<dd>
517-
<p>A JSON Schema [[!JSON-SCHEMA]] object describing the expected input parameters for the tool.
637+
<p>A JSON Schema object describing the expected input parameters for the tool [[!JSON-SCHEMA]].
518638
</dd>
519639

520640
<dt><code><var ignore>tool</var>["{{ModelContextTool/execute}}"]</code></dt>
@@ -562,6 +682,77 @@ dictionary ModelContextRegisterToolOptions {
562682
:: <p>An array of origins that control which documents this tool is exposed to, in the current document's tree.
563683
</dl>
564684

685+
<h4 id="model-context-get-tool-options">ModelContextGetToolOptions Dictionary</h4>
686+
687+
The {{ModelContextGetToolOptions}} dictionary allows web applications to filter the tools returned
688+
by {{ModelContext/getTools()}}.
689+
690+
<xmp class="idl">
691+
dictionary ModelContextGetToolOptions {
692+
sequence<USVString> fromOrigins;
693+
};
694+
</xmp>
695+
696+
<dl class="domintro">
697+
: <code><var ignore>options</var>["{{ModelContextGetToolOptions/fromOrigins}}"]</code>
698+
:: An array of origins from which to query tools. Documents whose origin appears in this list, or
699+
are same-origin with the caller, have their tools queried. An empty list only includes
700+
same-origin documents.
701+
</dl>
702+
703+
<h4 id="registered-tool">RegisteredTool Dictionary</h4>
704+
705+
The {{RegisteredTool}} dictionary represents a tool that has been registered and is available for
706+
execution.
707+
708+
<xmp class="idl">
709+
dictionary RegisteredTool {
710+
required DOMString name;
711+
// `title` can be exposed as a `DOMString` since it was taken in by a
712+
// `USVString`, meaning all unmatched surrogate processing has already been
713+
// done, and there's no need to do it again on tool exposure.
714+
DOMString title;
715+
required DOMString description;
716+
DOMString inputSchema;
717+
required Window window;
718+
required USVString origin;
719+
ToolAnnotations annotations;
720+
};
721+
</xmp>
722+
723+
<dl class="domintro">
724+
: <code><var ignore>tool</var>["{{RegisteredTool/name}}"]</code>
725+
:: A unique identifier for the tool. It is the same value provided at tool registration, via
726+
{{ModelContextTool/name}}.
727+
728+
: <code><var ignore>tool</var>["{{RegisteredTool/title}}"]</code>
729+
:: A human-readable label for the tool. It is the same value provided at tool registration, via
730+
{{ModelContextTool/title}}.
731+
732+
: <code><var ignore>tool</var>["{{RegisteredTool/description}}"]</code>
733+
:: A natural language description of the tool's functionality. It is the same value provided at
734+
tool registration, via {{ModelContextTool/description}}.
735+
736+
: <code><var ignore>tool</var>["{{RegisteredTool/inputSchema}}"]</code>
737+
:: A stringified JSON Schema object describing the expected input parameters for the tool
738+
[[!JSON-SCHEMA]]. It is the string derived from {{ModelContextTool/inputSchema}} during
739+
registration.
740+
741+
: <code><var ignore>tool</var>["{{RegisteredTool/window}}"]</code>
742+
:: The {{Window}} of the document that registered the tool.
743+
744+
: <code><var ignore>tool</var>["{{RegisteredTool/origin}}"]</code>
745+
:: The origin of the document that registered the tool. This member is only meaningful when the
746+
tool is cross-origin, and the consumer of a tool cannot otherwise get the tool's origin from
747+
its {{RegisteredTool/window}}. For same-origin tools, this is the same as the tool's
748+
{{RegisteredTool/window}}'s {{WindowOrWorkerGlobalScope/origin}}, and the caller's own
749+
{{Window}}.{{WindowOrWorkerGlobalScope/origin}}.
750+
751+
: <code><var ignore>tool</var>["{{RegisteredTool/annotations}}"]</code>
752+
:: Optional annotations providing metadata about the tool. It matches
753+
{{ModelContextTool/annotations}}.
754+
</dl>
755+
565756

566757
<h3 id="declarative-api">Declarative WebMCP</h3>
567758

0 commit comments

Comments
 (0)