Web Model Context Protocol (WebMCP)は、Webアプリケーションがブラウザ内でネイティブに実行されるAIエージェントに構造化されたツールを直接公開できるようにする新興のWeb標準です。アプリケーションによって定義されたツールにより、AIアシスタントはアプリケーションと直接対話できるようになり、エージェントに追加の機能を提供し、DOM操作の必要性を減らします。
例えば、新規ユーザーを登録するアプリケーションは、エージェントがDOM操作を通じて複雑なウィザードUIを操作することを要求する代わりに、ブラウザのAIエージェントがユーザーを直接作成するためのWebMCPツールを提供する場合があります。
AngularはWebMCPの実験的サポートを提供しており、アプリケーションの依存性の注入ライフサイクルに結びついたツールを簡単に登録し、シグナルフォームを自動的にAI対応ツールに変換できます。
IMPORTANT: WebMCP仕様はライフサイクルの非常に初期の段階にあり、頻繁に変更されています。そのため、AngularにおけるWebMCPサポートは現在実験的です。APIはメジャーバージョン以外でも変更される可能性があります。
アプリケーションにツールを提供する
アプリケーション設定でprovideExperimentalWebMcpToolsを使用して、アプリケーションのライフサイクル全体にわたってツールを登録します。この方法で提供されたツールは、アプリケーションの初期化時に自動的に登録され、アプリケーションの破棄時に登録解除されます。
executeコールバックは関連付けられたInjectorの注入コンテキストで呼び出されるため、サービスを直接injectできます。
main.ts
import {Service, inject, provideExperimentalWebMcpTools} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';
import {AppRoot} from './app-root';
@Service()
class Greeter {
sayHello(): string {
return 'Hello agent!';
}
}
bootstrapApplication(AppRoot, {
providers: [
provideExperimentalWebMcpTools([
{
name: 'greet',
description: 'Greets the agent.',
inputSchema: {type: 'object', properties: {}},
execute: () => {
const greeter = inject(Greeter);
return {content: [{type: 'text', text: greeter.sayHello()}]};
},
},
]),
],
});
ツールパラメータを定義する
ツールがAIアシスタントからの入力を必要とする場合、JSON Schema構文を使用してinputSchema内に期待される引数を定義します。Angularはスキーマ定義に基づいて、executeコールバックに渡されるパラメータの型を自動的に推論します。
main.ts
import {provideExperimentalWebMcpTools} from '@angular/core';
import {bootstrapApplication} from '@angular/platform-browser';
import {AppRoot} from './app-root';
bootstrapApplication(AppRoot, {
providers: [
provideExperimentalWebMcpTools([
{
name: 'searchCatalog',
description: 'Searches the store catalog for products matching a query.',
inputSchema: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'The search keywords.',
},
maxResults: {
type: 'number',
description: 'Maximum number of results to return.',
},
},
required: ['query'],
additionalProperties: false,
},
execute: ({query, maxResults}) => {
// Type of `query` is inferred as `string`.
// Type of `maxResults` is inferred as `number | undefined`.
// Consider validating this at runtime, since inputs may not be validated to match the schema.
if (typeof query !== 'string') throw new Error(`Bad query: ${query}`);
if (typeof maxResults !== 'number' && maxResults !== undefined)
throw new Error(`Bad maxResults: ${maxResults}`);
const limit = maxResults ?? 5;
return {
content: [{type: 'text', text: `Returning up to ${limit} results for "${query}".`}],
};
},
},
]),
],
});
TIP: required: ['param1', 'param2', ...]を使用してそれらのパラメータの型からundefinedを削除し、additionalProperties: falseを使用して引数オブジェクトの型をこれらのパラメータのみに制限します。
ルートにツールを提供する
複雑なアプリケーションを構築する場合、ユーザーが特定のルートを表示しているときにのみ特定のツールを利用できるようにしたい場合があります。これは、ルート定義で直接ツールを提供することで実現できます。
routes.ts
import {provideExperimentalWebMcpTools} from '@angular/core';
import {Routes} from '@angular/router';
export const routes: Routes = [
{
path: 'dashboard',
loadComponent: () => import('./dashboard').then((m) => m.Dashboard),
providers: [
provideExperimentalWebMcpTools([
{
name: 'exportDashboardReports',
description: 'Exports the current dashboard analytics.',
inputSchema: {type: 'object', properties: {}},
execute: () => ({
content: [{type: 'text', text: 'Dashboard export successfully triggered.'}],
}),
},
]),
],
},
];
NOTE: 特定のルートにツールを登録する場合、ユーザーがルートから移動したときにツールが自動的に_登録解除_されるように、ルーターを構成してwithExperimentalAutoCleanupInjectorsを使用することを検討してください。このオプションがない場合、ルートで宣言されたWebMCPツールは、ユーザーが別のルートに移動した後でもAIエージェントからアクセス可能なままになります。
app.config.ts
import {ApplicationConfig} from '@angular/core';
import {provideRouter, withExperimentalAutoCleanupInjectors} from '@angular/router';
import {routes} from './routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withExperimentalAutoCleanupInjectors())],
};
サービス内でのツールの提供
動的なユースケースの場合、declareExperimentalWebMcpTool関数は注入コンテキスト内にツールを直接登録し、そのコンテキストが破棄されたときに自動的に登録を解除します。
counter.ts
import {Service, declareExperimentalWebMcpTool, signal, inject} from '@angular/core';
@Service()
export class Counter {
readonly count = signal(0);
constructor() {
declareExperimentalWebMcpTool({
name: 'getCounter',
description: 'Reads the global counter.',
inputSchema: {type: 'object', properties: {}},
execute: () => ({
content: [{type: 'text', text: `The count is: ${this.count()}.`}],
}),
});
}
}
declareExperimentalWebMcpToolは任意の注入コンテキストで機能しますが、名前の衝突に注意し、ルートサービスで使用することを推奨します。
シグナルフォームの暗黙的ツール
最小限の設定で、既存のAngularのSignal Formから暗黙的にWebMCPツールを作成できます。AngularはフォームモデルをリッチなWebMCPツールに変換し、JSONスキーマやイベントハンドラーを手動で記述することなく、高度に動的なフォームを効果的にサポートします。
WebMCPフォーム機能の有効化
まず、ルートアプリケーションのプロバイダーにprovideExperimentalWebMcpFormsを追加します:
main.ts
import {bootstrapApplication} from '@angular/platform-browser';
import {provideExperimentalWebMcpForms} from '@angular/forms/signals';
import {AppRoot} from './app-root';
bootstrapApplication(AppRoot, {
providers: [provideExperimentalWebMcpForms()],
});
Signal Formのオプトイン
次に、formを使用してSignal Formを定義する際、experimentalWebMcpTool設定オプションを渡して暗黙的なWebMCPツールにオプトインします。Angularはフォームのデータモデルを検査し、接続されたAIエージェント用のJSONスキーマを自動的に生成します。
user-registration.ts
import {Component, signal} from '@angular/core';
import {form, required, minLength} from '@angular/forms/signals';
@Component({
selector: 'app-user-registration',
templateUrl: './user-registration.html',
})
export class UserRegistration {
private readonly model = signal({
firstName: '',
lastName: '',
age: 0,
hobbies: ['Web Development'],
});
readonly userForm = form(
this.model,
(f) => {
required(f.firstName, {message: 'First name is mandatory.'});
required(f.lastName, {message: 'Last name is mandatory.'});
},
{
// Implicitly registers a WebMCP tool named `registerUser` with parameters derived from `model`.
experimentalWebMcpTool: {
name: 'registerUser',
description: 'Registers a new user.',
},
submission: {
action: async (formValue) => {
console.log('Submitting user:', formValue);
// ...
},
},
},
);
}
この例では、Angularは以下のJSONスキーマを持つWebMCPツールを生成します:
modelシグナルの初期値から推論されたパラメーターとして、firstName、lastName、age、およびhobbiesを含みます。requiredバリデーターから推論された_必須_フィールドとして、firstNameとlastNameを定義します。hobbiesを文字列の配列として定義し、エージェントが任意の数の趣味を提供できるようにします。
入力スキーマの推論にとどまらず、AngularはWebMCPツールをフォームの検証ロジックと送信ハンドラーにも接続します。これにより、エージェントは自身の入力によってトリガーされた検証エラーや送信中に発生した失敗を監視し、自己修正して再試行できるようになります。
NOTE: 非同期バリデーターはトリガーされ_ない_ため、送信アクションで処理する必要があります。
制約事項
Angularはフォームモデルの初期値からWebMCPスキーマを推論します。これには以下が必要です:
- 具体的な初期値(
''、0、false): Angularはnullやundefinedからデータ型を推論できません。 - 空ではない配列(
['Hello!']): Angularは空の配列からデータ型を推論できず、少なくとも1つの初期値を必要とします。
ベストプラクティス
以下のベストプラクティスを念頭に置いてください:
名前の衝突
WebMCPは各ツールが一意の名前を持つことを要求し、同じツール名が複数回登録された場合はエラーをスローします。これは、複数回登録される可能性のあるコンテキスト(コンポーネントのコンストラクターなど)でdeclareExperimentalWebMcpToolやprovideExperimentalWebMcpToolsを呼び出すと、実行時にエラーが発生する可能性があることを意味します。
可能な限り、ツールはアプリケーションプロバイダー、ルートプロバイダー、またはルートサービスに配置することを推奨します。Signal Formsの暗黙的ツールを含め、コンポーネントにツールを配置する場合は、そのコンポーネントが常にページ上で最大_1回_しかレンダリングされないことを確認してください。
ツール入力の検証
Angularは、エージェントによって提供された入力が定義されたJSONスキーマと実際に一致するかどうかの暗黙的な検証を提供しません。信頼性を確保するために、使用する前にexecute関数への引数を明示的に検証することを検討してください。
テスト
ツールを効果的にユニットテストするために、@mcp-b/webmcp-polyfillのようなモックWebMCP実装を使用することを検討してください。