refactor(forms): expose consequentialHint annotation

Unlike `readOnlyHint` and `untrustedContentHint`, Angular cannot infer any reasonable default value for `consequentialHint` and simply exposes it to users to specify as appropriate.

See: https://groups.google.com/a/chromium.org/g/chrome-ai-dev-preview/c/uHu5kfclbKw/m/dgorjfHPDgAJ
This commit is contained in:
Doug Parker
2026-09-09 14:53:44 -07:00
committed by Andrew Scott
parent 96dbae53bb
commit 8ca879e2d8
4 changed files with 22 additions and 2 deletions
@@ -213,6 +213,9 @@ export interface FormOptions<TModel> {
experimentalWebMcpTool?: {
name: string;
description: string;
annotations?: {
consequentialHint?: boolean;
};
};
injector?: Injector;
name?: string;
@@ -69,6 +69,20 @@ export interface FormOptions<TModel> {
/** A description of the tool's purpose and usage information. */
description: string;
/**
* Set of configuration values extending the core tool behavior.
*/
annotations?: {
// `readOnlyHint` and `untrustedContentHint` are intentionally omitted as they have
// hard-coded values.
/**
* A hint that the tool will have consequential effects like mutating state or interacting
* with the external world.
*/
consequentialHint?: boolean;
};
};
/** Options that define how to handle form submission. */
@@ -232,6 +246,7 @@ export function form<TModel>(...args: any[]): FieldTree<TModel> {
registerWebMcpForm(fieldRoot.fieldTree, {
name: experimentalWebMcpTool.name,
description: experimentalWebMcpTool.description,
annotations: experimentalWebMcpTool.annotations,
}),
);
} else {
@@ -37,7 +37,7 @@ const registerWebMcpForm: RegisterWebMcpForm = (formTree, options) => {
async function initWebMcpForm(
formTree: FieldTree<unknown>,
options: {name: string; description: string},
options: {name: string; description: string; annotations?: {consequentialHint?: boolean}},
injector: Injector,
) {
const node = formTree() as FieldNode;
@@ -56,6 +56,8 @@ async function initWebMcpForm(
description: options.description,
inputSchema,
annotations: {
...options.annotations,
// Forms are assumed to implicitly mutate the DOM (otherwise how would a user interact with them?)
// and therefore are _never_ read-only.
readOnlyHint: false,
+1 -1
View File
@@ -22,5 +22,5 @@ export const REGISTER_WEBMCP_FORM = new InjectionToken<RegisterWebMcpForm>(
*/
export type RegisterWebMcpForm = (
form: FieldTree<unknown>,
options: {name: string; description: string},
options: {name: string; description: string; annotations?: {consequentialHint?: boolean}},
) => Promise<void>;