Skip to content

Commit 7908e11

Browse files
committed
fix(server): tolerate elicitation annotation metadata
1 parent 81e79b1 commit 7908e11

3 files changed

Lines changed: 48 additions & 1 deletion

File tree

packages/core-internal/src/shared/elicitation.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,12 @@ function isRedundantFormatPattern(original: Record<string, unknown>, parsed: Rec
8282
return ZOD_REDUNDANT_FORMAT_PATTERNS.get(parsed.format)?.has(original.pattern) === true;
8383
}
8484

85+
const ANNOTATION_ONLY_JSON_SCHEMA_KEYWORDS = new Set(['$comment', 'deprecated', 'examples', 'readOnly', 'writeOnly']);
86+
87+
function isAnnotationOnlyJsonSchemaKeyword(key: string): boolean {
88+
return ANNOTATION_ONLY_JSON_SCHEMA_KEYWORDS.has(key) || key.startsWith('x-');
89+
}
90+
8591
function findStrippedJsonSchemaPaths(original: unknown, parsed: unknown, path = ''): string[] {
8692
if (Array.isArray(original) && Array.isArray(parsed)) {
8793
return original.flatMap((item, index) => findStrippedJsonSchemaPaths(item, parsed[index], `${path}[${index}]`));
@@ -94,7 +100,7 @@ function findStrippedJsonSchemaPaths(original: unknown, parsed: unknown, path =
94100
return Object.entries(original).flatMap(([key, value]) => {
95101
const childPath = path ? `${path}.${key}` : key;
96102
if (!Object.prototype.hasOwnProperty.call(parsed, key)) {
97-
if (isRedundantFormatPattern(original, parsed, key)) {
103+
if (isRedundantFormatPattern(original, parsed, key) || isAnnotationOnlyJsonSchemaKeyword(key)) {
98104
return [];
99105
}
100106
return [childPath];

packages/core-internal/test/shared/inputRequired.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,35 @@ describe('inputRequired() builder', () => {
7474
});
7575
});
7676

77+
test('elicit drops annotation-only metadata from Standard Schema properties', () => {
78+
const request = inputRequired.elicit({
79+
message: 'Name?',
80+
requestedSchema: z.object({
81+
name: z.string().meta({
82+
title: 'Name',
83+
examples: ['Ada Lovelace'],
84+
deprecated: false,
85+
readOnly: true,
86+
'x-ui-order': 1
87+
})
88+
})
89+
});
90+
91+
expect(request).toEqual({
92+
method: 'elicitation/create',
93+
params: {
94+
mode: 'form',
95+
message: 'Name?',
96+
requestedSchema: {
97+
$schema: 'https://json-schema.org/draft/2020-12/schema',
98+
type: 'object',
99+
properties: { name: { type: 'string', title: 'Name' } },
100+
required: ['name']
101+
}
102+
}
103+
});
104+
});
105+
77106
test('elicit rejects non-object Standard Schema roots as invalid elicitation params', () => {
78107
expect(() =>
79108
inputRequired.elicit({

packages/server/src/server/server.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1136,6 +1136,18 @@ export class Server extends Protocol<ServerContext> {
11361136
params: ElicitInputFormParams<Schema>,
11371137
options?: RequestOptions
11381138
): Promise<ElicitInputResult<Schema>>;
1139+
/**
1140+
* Creates an elicitation request for the given parameters.
1141+
* For backwards compatibility, `mode` may be omitted for form requests and will default to `"form"`.
1142+
* @param params The parameters for the elicitation request.
1143+
* @param options Optional request options.
1144+
* @returns The result of the elicitation request.
1145+
*
1146+
* @deprecated Throws on a 2026-07-28-era request — use {@link index.inputRequired | inputRequired} (multi-round-trip)
1147+
* instead. The 2025 push-style server-to-client request model is replaced by input_required
1148+
* results in the 2026-07-28 protocol. If your factory serves both eras, this only works on the
1149+
* legacy path.
1150+
*/
11391151
async elicitInput(params: ElicitRequestFormParams | ElicitRequestURLParams, options?: RequestOptions): Promise<ElicitResult>;
11401152
async elicitInput(
11411153
params: ElicitRequestFormParams | ElicitRequestURLParams | ElicitInputFormParams<StandardSchemaWithJSON>,

0 commit comments

Comments
 (0)