WebMCP Tools Support
Angular 22 introduces experimental WebMCP support, enabling AI agents to invoke tools registered in your Angular app via the WebMCP protocol.
1. ToolDescriptor Interface
Every WebMCP tool is defined by a ToolDescriptor with a name, description, a JSON Schema inputSchema, and an execute callback. The Client provides an AbortSignal for cancellation.
nameUnique identifier the agent uses to call this tooldescriptionHelps the agent decide when to use the toolinputSchemaJSON Schema โ TypeScript infers arg types from itexecuteCallback that runs in an injection contextclient.signalAbortSignal for cancelling long operations// The ToolDescriptor interface:
interface ToolDescriptor<InputSchema> {
/** Unique name the agent uses to invoke this tool */
name: string;
/** Description helping the agent decide when to use this tool */
description: string;
/** JSON Schema defining the expected arguments */
inputSchema: InputSchema;
/** The function that runs when the agent calls this tool */
execute: (
args: InferArgsFromInputSchema<InputSchema>,
client: Client
) => unknown;
}
// The Client interface:
interface Client {
/** AbortSignal โ cancel long-running operations */
signal: AbortSignal;
}2. provideExperimentalWebMcpTools()
Register tools at the application level in app.config.ts. Tools are available for the entire lifetime of the app. The execute callback runs in the application's injection context, so you can use inject().
provideExperimentalWebMcpTools()execute function and returns the result// app.config.ts โ register tools at the application level
import { provideExperimentalWebMcpTools } from '@angular/core';
export const appConfig: ApplicationConfig = {
providers: [
provideExperimentalWebMcpTools([
{
name: 'get_user_profile',
description: 'Fetches the current user profile data',
inputSchema: {
type: 'object',
properties: {
includeAvatar: {
type: 'boolean',
description: 'Whether to include the avatar URL',
},
},
},
execute: async (args, client) => {
const userService = inject(UserService);
const user = await userService.getCurrentUser();
return JSON.stringify({
name: user.name,
email: user.email,
avatar: args.includeAvatar ? user.avatarUrl : undefined,
});
},
},
]),
],
};3. declareExperimentalWebMcpTool()
Register a tool dynamically inside a component or service. The tool is tied to the injection context โ it's automatically unregistered when the component is destroyed. This is ideal for context-specific tools that should only be available while a certain view is active.
// product-list.component.ts โ register a tool dynamically
import { declareExperimentalWebMcpTool, inject } from '@angular/core';
@Component({ ... })
export class ProductListComponent {
private readonly productService = inject(ProductService);
private readonly cart = inject(CartService);
constructor() {
// Tool is registered when component is created,
// and automatically unregistered when destroyed.
declareExperimentalWebMcpTool({
name: 'add_to_cart',
description: 'Adds a product to the shopping cart by product ID',
inputSchema: {
type: 'object',
properties: {
productId: {
type: 'string',
description: 'The unique product identifier',
},
quantity: {
type: 'number',
description: 'Number of items to add (default: 1)',
},
},
required: ['productId'],
},
execute: async (args, client) => {
// Runs in the component's injection context
const product = await this.productService.getById(args.productId);
if (!product) return 'Product not found';
// Respect the abort signal for cancellation
if (client.signal.aborted) return 'Operation cancelled';
this.cart.add(product, args.quantity ?? 1);
return `Added ${args.quantity ?? 1}x "${product.name}" to cart`;
},
});
}
}4. Route-Level Tools
Since provideExperimentalWebMcpTools() returns EnvironmentProviders, you can scope tools to specific routes. The tools are only available when the route is active โ perfect for admin panels, dashboards, or feature-specific AI capabilities.
// Register tools for specific routes only
const routes: Routes = [
{
path: 'admin',
loadComponent: () => import('./admin.component'),
providers: [
provideExperimentalWebMcpTools([
{
name: 'list_users',
description: 'Lists all registered users (admin only)',
inputSchema: {
type: 'object',
properties: {
role: { type: 'string', description: 'Filter by role' },
limit: { type: 'number', description: 'Max results' },
},
},
execute: async (args) => {
const userService = inject(UserService);
const users = await userService.list({
role: args.role,
limit: args.limit ?? 50,
});
return JSON.stringify(users);
},
},
]),
],
},
];
// Tool is available only when the /admin route is active!Key Points
- Experimental โ APIs are prefixed with
Experimentaland may change. - Type-safe โ TypeScript infers
executeargument types from theinputSchemaJSON Schema viaInferArgsFromInputSchema. - Injection context โ
executeruns in the associated injector's context, soinject()works inside it. - Automatic cleanup โ
declareExperimentalWebMcpToolunregisters the tool when the injection context is destroyed. - Cancellation โ The
Clientprovides anAbortSignalto cancel long-running operations. - Scoped registration โ Use at the app level, route level, or component level depending on when the tool should be available.