From 533e8c7e1ddad907306a032e38e8e6ea90db8970 Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Thu, 26 Feb 2026 12:24:02 -0500 Subject: [PATCH 1/2] Add some basic tool infra and tool registration --- index.bs | 141 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 9 deletions(-) diff --git a/index.bs b/index.bs index dcc00cd..8d05c3b 100644 --- a/index.bs +++ b/index.bs @@ -52,7 +52,8 @@ Die On: warning .XXX { color: #D50606; - background: white; + /* The value #111 is what WHATWG uses in dark mode: `--xxx-bg: #111;`. */ + background: light-dark(white, #111); border: solid #D50606; } @@ -79,6 +80,11 @@ p + dl.props { margin-top: -0.5em; } } + +

Introduction

WebMCP API is a new JavaScript interface that allows web developers to expose their web application functionality as “tools” - JavaScript functions with natural language descriptions and structured schemas that can be invoked by [=agents=], [=browser's agents=], and [=assistive technologies=]. Web pages that use WebMCP can be thought of as Model Context Protocol [[!MCP]] servers that implement tools in client-side script instead of on the backend. WebMCP enables collaborative workflows where users and agents work together within the same web interface, leveraging existing application logic while maintaining shared context and user control. @@ -95,14 +101,49 @@ A browser’s agent is an [=agent=] provided by or through the browse An AI platform is a provider of agentic assistants such as OpenAI’s ChatGPT, Anthropic’s Claude, or Google’s Gemini. -

Security and privacy considerations

+

Supporting concepts

- +A model context is a [=struct=] with the following [=struct/items=]: -

Accessibility considerations

+
+ : tool map + :: a [=map=] whose [=map/keys=] are [=strings=] and whose [=map/values=] are [=tool data=] + [=structs=]. +
+ +A tool data is a [=struct=] with the following [=struct/items=]: + +
+ : name + :: a [=string=] uniquely identifying a tool registered within a [=model context=]'s [=model + context/tool map=]; it is the same as the [=map/key=] identifying this object. + + : description + :: a [=string=]. + + : input schema + :: a [=string=]. + + Note: For tools registered by the imperative form of this API (i.e., + {{ModelContext/registerTool()}}), this is the stringified representation of + {{ModelContextTool/inputSchema}}. For tools registered + [declaratively](https://github.com/webmachinelearning/webmcp/pull/76), this will be a + stringified JSON Schema object created by the + [=synthesize a declarative JSON Schema object algorithm=]. + [[!JSON-SCHEMA]] + + : execute steps + :: a set of steps to invoke the tool. + + Note: For tools registered imperatively, these steps will simply invoke the supplied + {{ToolExecuteCallback}} callback. 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=]. + + : read-only hint + :: a [=boolean=], initially false. +

API

@@ -143,6 +184,10 @@ interface ModelContext { }; +Each {{ModelContext}} object has an associated internal context, which +is a [=model context=] [=struct=] created alongside the {{ModelContext}}. + +
navigator.{{Navigator/modelContext}}.{{ModelContext/provideContext(options)}}
@@ -181,9 +226,64 @@ The clearContext() method steps are:
-The registerTool(tool) method steps are: +The registerTool(tool) method steps are: -1. TODO: fill this out. +1. Let |tool map| be [=this=]'s [=ModelContext/internal context=]'s [=model context/tool map=]. + +1. Let |tool name| be |tool|'s {{ModelContextTool/name}}. + +1. If |tool map|[|tool name|] [=map/exists=], then [=exception/throw=] an {{InvalidStateError}} + {{DOMException}}. + +1. If either |tool name| or {{ModelContextTool/description}} is the empty string, then + [=exception/throw=] an {{InvalidStateError}} {{DOMException}}. + +1. Let |stringified input schema| be the empty string. + +1. If |tool|'s {{ModelContextTool/inputSchema}} [=map/exists=], then set |stringified input schema| + to the result of [=serializing a JavaScript value to a JSON string=], given |tool|'s + {{ModelContextTool/inputSchema}}. + +
+

The serialization algorithm above throws exceptions in the following cases:

+ +
    +
  1. Throws a new {{TypeError}} when the backing "JSON.stringify()" + yields undefined, e.g., + "inputSchema: { toJSON() {return HTMLDivElement;}}", or + "innputSchema: { toJSON() {return undefined;}}".

  2. + +
  3. Re-throws exceptions thrown by "JSON.stringify()". For example, + "inputSchema: objectWithCircularReference", etc.

  4. +
+ +

Currently, the only implementation of this spec (Chromium) does not throw + exceptions in the first case above. And for the second case, Chromium throws new {{TypeError}} + exceptions, as opposed to re-throwing the original exception. We should reconcile this difference.

+
+ +1. Let |read-only hint| be true if |tool|'s {{ModelContextTool/annotations}} [=map/exists=], and if + its {{ToolAnnotations/readOnlyHint}} [=map/exists=] and is true. Otherwise, let it be false. + +1. Let |tool data| be a new [=tool data=], with the following [=struct/items=]: + + : [=tool data/name=] + :: |tool name| + + : [=tool data/description=] + :: |tool|'s {{ModelContextTool/description}} + + : [=tool data/input schema=] + :: |stringified input schema| + + : [=tool data/execute steps=] + :: steps that invoke |tool|'s {{ModelContextTool/execute}} + + : [=tool data/read-only hint=] + :: |read-only hint| + +1. Set [=this=]'s [=ModelContext/internal context=][|tool name|] to |tool data|.
@@ -296,6 +396,19 @@ The requestUserInteraction(callba +

Declarative WebMCP

+ +This section is entirely a TODO. For now, refer to the [explainer draft](https://github.com/webmachinelearning/webmcp/pull/76). + +
+The synthesize a declarative JSON Schema object algorithm, given a <{form}> element +|form|, runs the following steps. They return a [=map=] representing a JSON Schema object. +[[!JSON-SCHEMA]] + +1. TODO: Derive a conformant JSON Schema object from |form| and its [=form-associated elements=]. + +
+
 {
   "mcp": {
@@ -311,6 +424,16 @@ The requestUserInteraction(callba
 }
 
+

Security and privacy considerations

+ + + +

Accessibility considerations

+ +

Acknowledgements

Thanks to From 625d494ba9fe82e580d26290dc2102c00514036a Mon Sep 17 00:00:00 2001 From: Dominic Farolino Date: Fri, 27 Feb 2026 16:26:29 -0500 Subject: [PATCH 2/2] Add unregisterTool() --- index.bs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/index.bs b/index.bs index 8d05c3b..c896150 100644 --- a/index.bs +++ b/index.bs @@ -253,8 +253,8 @@ The registerTool(tool) method step "inputSchema: { toJSON() {return HTMLDivElement;}}", or "innputSchema: { toJSON() {return undefined;}}".

-
  • Re-throws exceptions thrown by "JSON.stringify()". For example, - "inputSchema: objectWithCircularReference", etc.

  • +
  • Re-throws exceptions thrown by "JSON.stringify()", e.g., when + "inputSchema" is an object with a circular reference, etc.

  • Currently, the only implementation of this spec (Chromium) does not throw @@ -288,9 +288,14 @@ The registerTool(tool) method step

    -The unregisterTool(name) method steps are: +The unregisterTool(name) method steps are: -1. TODO: fill this out. +1. Let |tool map| be [=this=]'s [=ModelContext/internal context=]'s [=model context/tool map=]. + +1. If |tool map|[|name|] does not [=map/exist=], then [=exception/throw=] an {{InvalidStateError}} + {{DOMException}}. + +1. [=map/Remove=] |tool map|[|name|].