WebMCP는 웹 애플리케이션이 브라우저 내부에서 직접 실행되는 AI 에이전트에 구조화된 도구를 직접 제공할 수 있게 하는 제안된 웹 표준입니다. 애플리케이션이 정의한 도구를 사용하면 AI 어시스턴트가 애플리케이션과 직접 상호작용할 수 있으므로, 에이전트의 기능을 확장하고 DOM 상호작용의 필요성을 줄일 수 있습니다.
예를 들어 신규 사용자 등록 애플리케이션은 브라우저의 AI 에이전트가 DOM 상호작용으로 복잡한 마법사 UI를 거치는 대신, 사용자를 직접 생성하도록 WebMCP 도구를 제공할 수 있습니다.
Angular는 WebMCP를 실험적으로 지원합니다. 애플리케이션의 의존성 주입 라이프사이클와 연결된 도구를 쉽게 등록하고, 시그널 폼을 AI가 사용할 수 있는 도구로 자동 변환할 수 있습니다.
중요: 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.',
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}) => {
// `query`의 타입은 `string`으로 추론합니다.
// `maxResults`의 타입은 `number | undefined`로 추론합니다.
// 입력이 스키마에 맞는지 검증되지 않을 수 있으므로 실행 중 검증하는 것을 고려하세요.
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}".`}],
};
},
},
]),
],
});
팁: 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.',
execute: () => ({
content: [{type: 'text', text: 'Dashboard export successfully triggered.'}],
}),
},
]),
],
},
];
참고: 특정 라우팅 규칙에 도구를 등록할 때는 사용자가 해당 라우팅 규칙을 벗어나면 도구의 등록이 자동으로 해제 되도록 라우터에 withAutoCleanupInjectors를 설정하는 것을 고려하세요.
이 옵션을 사용하지 않으면 사용자가 다른 라우팅 규칙으로 이동한 뒤에도 AI 에이전트가 이전 라우팅 규칙에 선언된 WebMCP 도구에 접근할 수 있습니다.
app.config.ts
import {ApplicationConfig} from '@angular/core';
import {provideRouter, withAutoCleanupInjectors} from '@angular/router';
import {routes} from './routes';
export const appConfig: ApplicationConfig = {
providers: [provideRouter(routes, withAutoCleanupInjectors())],
};
서비스 내부에서 도구 제공하기
동적인 활용 사례에서는 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.',
execute: () => ({
content: [{type: 'text', text: `The count is: ${this.count()}.`}],
}),
});
}
}
declareExperimentalWebMcpTool은 어떤 주입 컨텍스트에서든 동작하지만, 이름 충돌에 주의하고 루트 서비스에서 사용하는 것이 좋습니다.
시그널 폼의 암시적 도구
최소한의 설정만으로 기존 Angular 시그널 폼에서 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()],
});
시그널 폼에 적용하기
다음으로 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.'});
},
{
// `model`에서 파생한 매개변수로 `registerUser`라는 WebMCP 도구를 암시적으로 등록합니다.
experimentalWebMcpTool: {
name: 'registerUser',
description: 'Registers a new user.',
},
submission: {
action: async (formValue) => {
console.log('Submitting user:', formValue().value());
// ...
},
},
},
);
}
이 예제에서 Angular는 다음과 같은 JSON 스키마를 갖는 WebMCP 도구를 생성합니다:
model시그널의 초기값에서 추론한firstName,lastName,age,hobbies를 매개변수로 포함합니다.required유효성 검사기를 바탕으로firstName과lastName을 필수 필드로 정의합니다.hobbies를 문자열 배열로 정의해 에이전트가 취미를 원하는 개수만큼 제공할 수 있게 합니다.
Angular는 입력 스키마를 추론하는 것 외에도 WebMCP 도구를 폼의 유효성 검사 로직과 제출 핸들러에 연결합니다. 따라서 에이전트는 입력으로 발생한 유효성 검사 오류나 제출 중 발생한 실패를 확인하고, 스스로 수정한 뒤 재시도할 수 있습니다.
참고: 기본적으로 비동기 유효성 검사기는 제출을 막지 않습니다. 검사가 끝나기 전에 제출 작업이 실행되므로 검사 오류가 에이전트에 전달되지 않습니다. 이러한 검사는 제출 작업에서 처리하세요.
제약 조건
Angular는 폼 모델의 초기값으로 WebMCP 스키마를 추론합니다. 그러려면 다음 조건을 충족해야 합니다:
- 구체적인 초기값(
'',0,false): Angular는null이나undefined로 데이터 타입을 추론할 수 없습니다. - 비어 있지 않은 배열(
['Hello!']): Angular는 빈 배열로 데이터 타입을 추론할 수 없으므로 초기값이 하나 이상 필요합니다.
모범 사례
다음 모범 사례를 참고하세요:
이름 충돌
WebMCP에서는 모든 도구의 이름이 고유해야 하며, 같은 이름을 여러 번 등록하면 오류가 발생합니다.
따라서 컴포넌트 생성자처럼 여러 번 등록될 수 있는 컨텍스트에서 declareExperimentalWebMcpTool이나 provideExperimentalWebMcpTools를 호출하면 실행 중 오류가 발생할 수 있습니다.
가능하면 애플리케이션 프로바이더, 라우팅 규칙 프로바이더, 루트 서비스에 도구를 배치하세요. 시그널 폼의 암시적 도구를 포함해 컴포넌트에 도구를 배치한다면, 어느 시점이든 해당 컴포넌트가 페이지에 최대 한 번 만 렌더링되도록 해야 합니다.
도구 입력값 검증하기
Angular는 에이전트가 제공한 입력이 정의된 JSON 스키마와 실제로 일치하는지 암시적으로 검증하지 않습니다.
안정성을 확보하려면 execute 함수의 인자를 사용하기 전에 명시적으로 검증하는 것을 고려하세요.
테스트
도구를 효과적으로 단위 테스트하려면 @mcp-b/webmcp-polyfill과 같은 WebMCP 목업 구현을 사용하는 것을 고려하세요.