MCPcopy Create free account
hub / github.com/angular/angular / declareExperimentalWebMcpTool

Function declareExperimentalWebMcpTool

packages/core/src/webmcp/declare_tool.ts:32–82  ·  view source on GitHub ↗
(tool: ToolDescriptor<InputSchema>, injector?: Injector)

Source from the content-addressed store, hash-verified

30 * @experimental
31 */
32export async function declareExperimentalWebMcpTool<
33 const InputSchema extends JsonSchemaForInference,
34>(tool: ToolDescriptor<InputSchema>, injector?: Injector): Promise<void> {
35 // SSR may not have a document yet, so we abort before checking it.
36 if (typeof ngServerMode !== 'undefined' && ngServerMode) return;
37
38 // modelContext was moved from `navigator` to `document` in the spec, but we check both for compatibility with different environments.
39 const modelContext =
40 (globalThis.document as {modelContext?: ModelContext}).modelContext ??
41 (globalThis.navigator as unknown as {modelContext?: ModelContext}).modelContext;
42
43 // Verify WebMCP is supported in this client. The typeof check guards against
44 // DOM clobbering (e.g. <form id="modelContext">), which would produce a truthy
45 // Element instead of a ModelContext object.
46 if (!modelContext || typeof modelContext.registerTool !== 'function') return;
47
48 if (typeof ngDevMode !== 'undefined' && ngDevMode) {
49 if (!injector) assertInInjectionContext(declareExperimentalWebMcpTool);
50 }
51
52 // Inject the `DestroyRef` and `Injector` immediately, so if it fails we abort
53 // before causing any side effects.
54 const currentInjector = injector ?? inject(Injector);
55 const destroyRef = currentInjector.get(DestroyRef);
56
57 // Wrap the input tool with an `AbortSignal` which aborts when the
58 // injection context is destroyed.
59 const abortCtrl = new AbortController();
60 const wrappedTool: ToolDescriptor<InputSchema> = {
61 ...tool,
62 execute: (args, client) => {
63 // TODO: `@mcp-b/webmcp-polyfill` currently lacks `AbortSignal` in its mock client.
64 // Remove the optional chaining when it is updated to match Chrome 153 spec.
65 const signal = client?.signal
66 ? AbortSignal.any([abortCtrl.signal, client.signal])
67 : abortCtrl.signal;
68
69 return runInInjectionContext(currentInjector, () =>
70 tool.execute(args, {
71 ...client,
72 signal,
73 }),
74 );
75 },
76 };
77
78 // Unregister when the associated `Injector` is destroyed.
79 destroyRef.onDestroy(() => void abortCtrl.abort());
80
81 await modelContext.registerTool(wrappedTool, {signal: abortCtrl.signal});
82}

Callers 3

initWebMcpFormFunction · 0.90

Calls 9

assertInInjectionContextFunction · 0.90
injectFunction · 0.90
runInInjectionContextFunction · 0.90
anyMethod · 0.80
registerToolMethod · 0.80
getMethod · 0.65
executeMethod · 0.65
onDestroyMethod · 0.65
abortMethod · 0.65

Tested by

no test coverage detected