Skip to content

Commit e7bd697

Browse files
Merge branch 'main' of github.com:webmachinelearning/webmcp into consequentialHint
2 parents 073712c + 5801678 commit e7bd697

2 files changed

Lines changed: 224 additions & 25 deletions

File tree

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
WebMCP lets developers expose web application functionality—either JavaScript functions or HTML `<form>` elements—as "tools" with natural language descriptions and structured schemas, designed for AI agent ingestion. These tools can be invoked by AI agents, including those built into the browser, hosted in iframes, or running in extensions to actuate web content that was traditionally designed for human interaction.
44

5+
TypeScript type definitions for WebMCP are available in the [`webmcp-types`](https://www.npmjs.com/package/webmcp-types) npm package.
6+
57

68
## Background and Motivation
79

index.bs

Lines changed: 222 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Default Biblio Status: current
1717
Boilerplate: omit conformance
1818
Indent: 2
1919
Die On: warning
20+
Test Suite: https://wpt.fyi/results/webmcp
2021
</pre>
2122

2223
<style>
@@ -171,6 +172,16 @@ A <dfn>tool definition</dfn> is a [=struct=] with the following [=struct/items=]
171172
"internal" steps that have not been defined yet, that describe how to fill out a <{form}> and
172173
its [=form-associated elements=].
173174

175+
: <dfn>annotations</dfn>
176+
:: an [=annotations=]-or-null.
177+
178+
: <dfn>exposed origins</dfn>
179+
:: a [=list=] or [=origins=], initially [=list/empty=].
180+
</dl>
181+
182+
An <dfn>annotations</dfn> is a [=struct=] with the following [=struct/items=]:
183+
184+
<dl dfn-for="annotations">
174185
: <dfn>read-only hint</dfn>
175186
:: a [=boolean=], initially false.
176187

@@ -179,11 +190,9 @@ A <dfn>tool definition</dfn> is a [=struct=] with the following [=struct/items=]
179190

180191
: <dfn>consequential hint</dfn>
181192
:: a [=boolean=], initially false.
182-
183-
: <dfn>exposed origins</dfn>
184-
:: a [=list=] or [=origins=], initially [=list/empty=].
185193
</dl>
186194

195+
187196
<hr>
188197

189198
<div algorithm>
@@ -202,7 +211,7 @@ To <dfn>notify documents of a tool change</dfn> given a {{Document}} |tool owner
202211
1. If |targetDocument| is not [=allowed to use=] the "{{tools}}" feature, then
203212
[=iteration/continue=].
204213

205-
1. If [=tool is visible to an origin=] given |tool owner|'s [=Document/origin=], |exposed
214+
1. If [=tool is exposed to an origin=] given |tool owner|'s [=Document/origin=], |exposed
206215
origins|, and |targetDocument|'s [=Document/origin=], then [=queue a global task=] on the
207216
[=webmcp task source=] given |targetDocument|'s [=relevant global object=] to [=fire an
208217
event=] named {{ModelContext/toolchange}} at |targetDocument|'s [=Document/associated
@@ -238,14 +247,14 @@ To <dfn>notify documents of a tool change</dfn> given a {{Document}} |tool owner
238247
</div>
239248

240249
<div algorithm>
241-
To determine if a <dfn>tool is visible to an origin</dfn> given an [=origin=] |tool owner origin|,
242-
a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |target origin|, run these steps:
250+
To determine if a <dfn>tool is exposed to an origin</dfn> given an [=origin=] |tool owner origin|,
251+
a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |accessing origin|, run these steps:
243252

244-
1. If |tool owner origin| is [=same origin=] with |target origin|, then return true.
253+
1. If |tool owner origin| is [=same origin=] with |accessing origin|, then return true.
245254

246-
1. [=list/For each=] |origin| of |exposed origins|:
255+
1. [=list/For each=] |allowed origin| of |exposed origins|:
247256

248-
1. If |target origin| is [=same origin=] with |origin|, then return true.
257+
1. If |accessing origin| is [=same origin=] with |allowed origin|, then return true.
249258

250259
1. Return false.
251260

@@ -316,6 +325,7 @@ The {{ModelContext}} interface provides methods for web applications to register
316325
[Exposed=Window, SecureContext]
317326
interface ModelContext : EventTarget {
318327
Promise<undefined> registerTool(ModelContextTool tool, optional ModelContextRegisterToolOptions options = {});
328+
Promise<sequence<RegisteredTool>> getTools(optional ModelContextGetToolOptions options = {});
319329

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

338356

@@ -392,15 +410,6 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
392410
</ol>
393411
</div>
394412

395-
1. Let |read-only hint| be true if |tool|'s {{ModelContextTool/annotations}} [=map/exists=] and
396-
its {{ToolAnnotations/readOnlyHint}} is true. Otherwise, let it be false.
397-
398-
1. Let |untrusted content hint| be true if |tool|'s {{ModelContextTool/annotations}} [=map/exists=] and
399-
its {{ToolAnnotations/untrustedContentHint}} is true. Otherwise, let it be false.
400-
401-
1. Let |consequential hint| be true if |tool|'s {{ModelContextTool/annotations}} [=map/exists=] and
402-
its {{ToolAnnotations/consequentialHint}} is true. Otherwise, let it be false.
403-
404413
1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=].
405414

406415
1. Let |signal| be |options|'s {{ModelContextRegisterToolOptions/signal}}.
@@ -447,14 +456,18 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
447456
: [=tool definition/execute steps=]
448457
:: steps that invoke |tool|'s {{ModelContextTool/execute}}
449458

450-
: [=tool definition/read-only hint=]
451-
:: |read-only hint|
459+
: [=tool definition/annotations=]
460+
:: null if |tool|'s {{ModelContextTool/annotations}} does not [=map/exist=]. Otherwise, an
461+
[=annotations=] with the following [=struct/items=]:
462+
463+
: [=annotations/read-only hint=]
464+
:: |tool|'s {{ModelContextTool/annotations}}'s {{ToolAnnotations/readOnlyHint}}
452465

453-
: [=tool definition/untrusted content hint=]
454-
:: |untrusted content hint|
466+
: [=annotations/untrusted content hint=]
467+
:: |tool|'s {{ModelContextTool/annotations}}'s {{ToolAnnotations/untrustedContentHint}}
455468

456-
: [=tool definition/consequential hint=]
457-
:: |consequential hint|
469+
: [=annotations/consequential hint=]
470+
:: |tool|'s {{ModelContextTool/annotations}}'s {{ToolAnnotations/consequentialHint}}
458471

459472
: [=tool definition/exposed origins=]
460473
:: |exposed origins|
@@ -472,6 +485,119 @@ The <dfn method for=ModelContext>registerTool(<var>tool</var>, <var>options</var
472485

473486
</div>
474487

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

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

518644
<dt><code><var ignore>tool</var>["{{ModelContextTool/inputSchema}}"]</code></dt>
519645
<dd>
520-
<p>A JSON Schema [[!JSON-SCHEMA]] object describing the expected input parameters for the tool.
646+
<p>A JSON Schema object describing the expected input parameters for the tool [[!JSON-SCHEMA]].
521647
</dd>
522648

523649
<dt><code><var ignore>tool</var>["{{ModelContextTool/execute}}"]</code></dt>
@@ -568,6 +694,77 @@ dictionary ModelContextRegisterToolOptions {
568694
:: <p>An array of origins that control which documents this tool is exposed to, in the current document's tree.
569695
</dl>
570696

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

572769
<h3 id="declarative-api">Declarative WebMCP</h3>
573770

0 commit comments

Comments
 (0)