Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
The provideExperimentalWebMcpTools function:
- is defined as generic to leverage TypeScript's type inference in the descriptor object (i.e. type safety and consistency between the
inputSchema definition and the execute callback;
- accepts multiple descriptors as an array.
declare function provideExperimentalWebMcpTools<const InputSchema extends JsonSchemaForInference>(
tools: ToolDescriptor<InputSchema>[]
): EnvironmentProviders;
This definition unfortunately breaks type inference when using multiple tools with heterogeneous input parameters (that work individually):
provideExperimentalWebMcpTools([
{
name: "tool1",
description: "Tool #1",
inputSchema: {
type: "object",
properties: {
arg1: { type: "number" }
},
required: ["arg1"]
},
execute: (args) => {
const arg1 = args.arg1; // Type inference fails
return "arg1 number value is " + arg1.toFixed(); // Type ERROR here
}
},
{
name: "tool2",
description: "Tool #2",
inputSchema: {
type: "object",
properties: {
arg2: { type: "string" }
},
required: ["arg2"]
},
execute: (args) => {
const arg2 = args.arg2; // Type inference fails
return "arg2 string value is " + arg2.trim(); // Type ERROR here
}
}
])
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
TypeScript is unable to infer useful types for both `execute` arguments:
- `arg1` is resolved as `number | { [x: string]: unknown; }` instead of `number`
- `arg2` is resolved as `string | { [x: string]: unknown; }` instead of `string`
Please provide the environment you discovered this bug in (run ng version)
angular/core 22.1.1
TypeScript 6.0.3
Anything else?
This is a well known limitation of TypeScript's generics, with no concept of existential types that could relax the overly restrictive constraint of the ToolDescriptor<InputSchema>[] argument (the tools do not need to have homogeneous input types in practice, they are only iterated over in the implementation).
Workarounds
- Disable type inference on the method, loose consistency and re-cast arguments: 🥲
// eslint-disable-next-line @typescript-eslint/no-explicit-any
provideExperimentalWebMcpTools<any>([
{
inputSchema: { ... }, // Tool #1 with arg1 number param
execute: (args) => {
const arg1 = args["arg1"] as number;
...
}
},
{
inputSchema: { ... }, // Tool #2 with arg2 string param
execute: (args) => {
const arg2 = args["arg2"] as string;
...
}
}
])
- Give up
provideExperimentalWebMcpTools and replace it with its current implementation, with chained calls to declareExperimentalWebMcpTool:
makeEnvironmentProviders([
provideEnvironmentInitializer(() => {
void declareExperimentalWebMcpTool({
inputSchema: { ... }, // Tool #1 with arg1 number param
execute: (args) => { args.arg1 /* Inference works */ }
});
void declareExperimentalWebMcpTool({
inputSchema: { ... }, // Tool #2 with arg2 string param
execute: (args) => { args.arg2 /* Inference works */ }
});
})
])
Possible solutions
Individual type inference for MCP descriptors AND collection support are hard to mix in the same call.
Based on the previous workaround 2, a non-generic provideExperimentalWebMcpTools method could simply offer a callback to call multiple times for descriptor registration:
export function provideExperimentalWebMcpTools(
toolsDeclaration: (declare: typeof declareExperimentalWebMcpTool) => Promise<void>
): EnvironmentProviders {
return makeEnvironmentProviders([
provideEnvironmentInitializer(() => {
void toolsDeclaration(declareExperimentalWebMcpTool);
})
]);
}
Usage:
provideExperimentalWebMcpTools(async declare => {
await declare({ /* Tool 1 */);
await declare({ /* Tool 2 */);
})
(note that declareExperimentalWebMcpTool promise is unhandled in the current implementation)
Which @angular/* package(s) are the source of the bug?
core
Is this a regression?
No
Description
The
provideExperimentalWebMcpToolsfunction:inputSchemadefinition and theexecutecallback;This definition unfortunately breaks type inference when using multiple tools with heterogeneous input parameters (that work individually):
Please provide a link to a minimal reproduction of the bug
No response
Please provide the exception or error you saw
Please provide the environment you discovered this bug in (run
ng version)Anything else?
This is a well known limitation of TypeScript's generics, with no concept of existential types that could relax the overly restrictive constraint of the
ToolDescriptor<InputSchema>[]argument (the tools do not need to have homogeneous input types in practice, they are only iterated over in the implementation).Workarounds
provideExperimentalWebMcpToolsand replace it with its current implementation, with chained calls todeclareExperimentalWebMcpTool:Possible solutions
Individual type inference for MCP descriptors AND collection support are hard to mix in the same call.
Based on the previous workaround 2, a non-generic
provideExperimentalWebMcpToolsmethod could simply offer a callback to call multiple times for descriptor registration:Usage:
(note that
declareExperimentalWebMcpToolpromise is unhandled in the current implementation)