diff --git a/README.md b/README.md
index 8049f51..f3e0cb6 100644
--- a/README.md
+++ b/README.md
@@ -1,17 +1,9 @@
-# Typescript Type Definitions for WebMCP
+# TypeScript definitions for WebMCP
-This package defines Typescript types (`.d.ts`) for the [WebMCP specification](https://webmachinelearning.github.io/webmcp).
+This package augments TypeScript's DOM types with definitions for the [WebMCP specification](https://webmachinelearning.github.io/webmcp/).
+It also adds `agentInvoked` and `respondWith()` to `SubmitEvent` from the [declarative API explainer](https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md), which the draft does not specify yet.
-Use this package to augment the ambient [`"dom"`](https://www.typescriptlang.org/docs/handbook/compiler-options.html#compiler-options) type definitions with the new definitions for WebMCP.
-
-## What are declaration files?
-
-See the [TypeScript handbook](http://www.typescriptlang.org/docs/handbook/declaration-files/introduction.html).
-
-
-## How can I use them?
-
-### Install
+## Install
- npm: `npm install --save-dev webmcp-types`
- yarn: `yarn add --dev webmcp-types`
@@ -19,61 +11,31 @@ See the [TypeScript handbook](http://www.typescriptlang.org/docs/handbook/declar
This package requires TypeScript 5.0 or newer.
-### Configure
+## Configure
-Since this package is outside DefinitelyTyped, the dependency won't be picked up automatically.
-There are several ways to add a additional TypeScript type definition dependencies to your TypeScript project:
+Add `"webmcp-types"` to [`compilerOptions.types`](https://www.typescriptlang.org/tsconfig/types.html) in your `tsconfig.json`, keeping any entries you already have:
-#### TypeScript `tsc` and `tsc`-based bundlers
-
-In `tsconfig.json`:
-
-```js
+```json
{
- // ...
"compilerOptions": {
- // ...
"types": ["webmcp-types"]
}
}
```
-Or you can use `typeRoots`:
-
-```js
-{
- // ...
- "compilerOptions": {
- // ...
- "typeRoots": ["./node_modules/webmcp-types", "./node_modules/@types"]
- }
-}
-```
-
-#### Inline in TypeScript
-
-This may work better if your toolchain doesn't read `tsconfig.json`.
+Alternatively, add a [type reference](https://www.typescriptlang.org/docs/handbook/triple-slash-directives.html#-reference-types-) at the top of a `.d.ts` file included in your project:
```ts
///
```
-#### Webpack
-
-If you use Webpack and the options above aren't sufficient (this has not been verified),
-you may need the following in `webpack.config.js`:
-
-```js
-"types": ["webmcp-types"]
-```
-
-### Run the type tests
+## Run the type tests
- `npm install`
- `npm test`
The tests in `index.test-d.ts` are statically checked with [Vitest typecheck mode](https://vitest.dev/guide/testing-types) against `tsconfig.json`; they are never executed.
-### Publish a new npm package version
+## Publish
[Release Please](https://github.com/googleapis/release-please) keeps a release pull request open with the next version and changelog, based on [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) on `main`. Merging it creates the GitHub release and publishes to npm through [trusted publishing](https://docs.npmjs.com/trusted-publishers/); no npm token or local publish step is needed.
diff --git a/index.d.ts b/index.d.ts
index b743902..476d60c 100644
--- a/index.d.ts
+++ b/index.d.ts
@@ -1,11 +1,10 @@
-export {};
-
type IsUnion = T extends TWhole ? [TWhole] extends [T] ? false : true : never;
type NonUnionTupleElements = {
[TIndex in keyof TTuple]: true extends IsUnion ? never : TTuple[TIndex];
}[number];
type Simplify = { [TKey in keyof T]: T[TKey] } & {};
+// Only literal names in a literal tuple are required; widened or union shapes are runtime choices.
type JsonSchemaRequiredKeys = TSchema extends {
readonly required: infer TRequired extends readonly string[];
} ? number extends TRequired["length"]
@@ -283,6 +282,7 @@ interface Document {
interface ToolActivatedEventInit extends EventInit {
/**
* The name of the tool whose execution has started.
+ * @default ""
*/
toolName?: string;
}
@@ -305,6 +305,7 @@ var ToolActivatedEvent: {
interface ToolCancelEventInit extends EventInit {
/**
* The name of the tool whose execution was cancelled.
+ * @default ""
*/
toolName?: string;
}
@@ -323,6 +324,24 @@ var ToolCancelEvent: {
prototype: ToolCancelEvent;
new(type: string, eventInitDict?: ToolCancelEventInit): ToolCancelEvent;
};
+
+interface SubmitEvent {
+ /**
+ * Whether an agent caused this submission by invoking the form's declarative tool.
+ * Absent in browsers without declarative tools, so check it before calling `respondWith()`.
+ *
+ * @see https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md#events
+ */
+ readonly agentInvoked: boolean;
+ /**
+ * Responds to the agent that invoked the form's declarative tool instead of letting the form navigate.
+ * Call during submit event dispatch, after calling `preventDefault()`.
+ *
+ * @param agentResponse A promise that resolves to the response the agent will consume.
+ * @see https://github.com/webmachinelearning/webmcp/blob/main/declarative-api-explainer.md#events
+ */
+ respondWith(agentResponse: PromiseLike): void;
+}
}
export type { WebMCP };
diff --git a/index.test-d.ts b/index.test-d.ts
index 56d0505..32dd057 100644
--- a/index.test-d.ts
+++ b/index.test-d.ts
@@ -194,7 +194,8 @@ test('supports the toolchange, toolactivated, and toolcancel events', () => {
expectTypeOf(event).toEqualTypeOf();
});
- document.modelContext?.addEventListener('toolactivated', (event) => {
+ document.modelContext?.addEventListener('toolactivated', function (event) {
+ expectTypeOf(this).toEqualTypeOf();
expectTypeOf(event).toEqualTypeOf();
expectTypeOf(event.toolName).toEqualTypeOf();
});
@@ -203,12 +204,14 @@ test('supports the toolchange, toolactivated, and toolcancel events', () => {
expectTypeOf(event).toEqualTypeOf();
expectTypeOf(event.toolName).toEqualTypeOf();
});
+ window.addEventListener('toolactivated', (event) => expectTypeOf(event).toEqualTypeOf());
if (document.modelContext) {
document.modelContext.ontoolchange = (event) => {
expectTypeOf(event).toEqualTypeOf();
};
- document.modelContext.ontoolactivated = (event) => {
+ document.modelContext.ontoolactivated = function (event) {
+ expectTypeOf(this).toEqualTypeOf();
expectTypeOf(event).toEqualTypeOf();
};
document.modelContext.ontoolcancel = (event) => {
@@ -217,12 +220,29 @@ test('supports the toolchange, toolactivated, and toolcancel events', () => {
document.modelContext.ontoolchange = null;
document.modelContext.ontoolactivated = null;
document.modelContext.ontoolcancel = null;
+ // @ts-expect-error toolchange dispatches a plain Event.
+ document.modelContext.ontoolchange = (event: ToolActivatedEvent) => event.toolName;
}
- expectTypeOf(new ToolActivatedEvent('toolactivated')).toEqualTypeOf();
+ const activated = new ToolActivatedEvent('toolactivated');
+ expectTypeOf(activated).toEqualTypeOf();
expectTypeOf(new ToolActivatedEvent('toolactivated', { toolName: 'search', bubbles: true }))
.toEqualTypeOf();
- expectTypeOf(new ToolCancelEvent('toolcancel', { toolName: 'search' })).toEqualTypeOf();
+ expectTypeOf(new ToolCancelEvent('toolcancel', { toolName: 'search', bubbles: true })).toEqualTypeOf();
+
+ expectTypeOf<{}>().toExtend();
+ expectTypeOf<{}>().toExtend();
+ const event = new Event('toolactivated');
+ if (event instanceof ToolActivatedEvent) {
+ expectTypeOf(event).toEqualTypeOf();
+ }
+
+ // @ts-expect-error The event type is required.
+ new ToolActivatedEvent();
+ // @ts-expect-error toolName is a string.
+ new ToolCancelEvent('toolcancel', { toolName: 1 });
+ // @ts-expect-error toolName is read-only.
+ activated.toolName = 'other';
expectTypeOf().toExtend();
expectTypeOf().toExtend();
@@ -233,3 +253,27 @@ test('supports the toolchange, toolactivated, and toolcancel events', () => {
expectTypeOf().toEqualTypeOf();
expectTypeOf().toEqualTypeOf();
});
+
+test("adds the declarative explainer's SubmitEvent members", () => {
+ document.forms[0].addEventListener('submit', (event) => {
+ expectTypeOf(event.agentInvoked).toEqualTypeOf();
+ expectTypeOf(event.respondWith).parameters
+ .toEqualTypeOf<[agentResponse: PromiseLike]>();
+ expectTypeOf(event.respondWith).returns.toEqualTypeOf();
+
+ if (event.agentInvoked) {
+ event.preventDefault();
+ event.respondWith(fetch('/search').then((response) => response.json()));
+ }
+
+ // @ts-expect-error agentInvoked is read-only.
+ event.agentInvoked = true;
+ // @ts-expect-error respondWith() requires a response.
+ event.respondWith();
+ // @ts-expect-error Pass the response promise, not a function that creates it.
+ event.respondWith(async () => 'done');
+ });
+
+ // @ts-expect-error SubmitEventInit has no agentInvoked member; only Chromium adds one.
+ new SubmitEvent('submit', { agentInvoked: true });
+});
diff --git a/package.json b/package.json
index f720de0..957f2ec 100644
--- a/package.json
+++ b/package.json
@@ -8,7 +8,6 @@
"homepage": "https://github.com/webmachinelearning/webmcp-types",
"bugs": "https://github.com/webmachinelearning/webmcp-types/issues",
"description": "TypeScript type definitions for WebMCP",
- "main": "",
"types": "index.d.ts",
"files": [
"index.d.ts"