Skip to content

Commit b81bfb9

Browse files
Merge branch 'main' into fweinberger/sep-1699
2 parents 0b1898b + b59a2bd commit b81bfb9

File tree

6 files changed

+91
-42
lines changed

6 files changed

+91
-42
lines changed

src/client/index.test.ts

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
CallToolRequestSchema,
1515
CreateMessageRequestSchema,
1616
ElicitRequestSchema,
17+
ElicitResultSchema,
1718
ListRootsRequestSchema,
1819
ErrorCode
1920
} from '../types.js';
@@ -917,6 +918,64 @@ test('should reject form-mode elicitation when client only supports URL mode', a
917918
await client.close();
918919
});
919920

921+
test('should reject missing-mode elicitation when client only supports URL mode', async () => {
922+
const server = new Server(
923+
{
924+
name: 'test server',
925+
version: '1.0'
926+
},
927+
{
928+
capabilities: {}
929+
}
930+
);
931+
932+
const client = new Client(
933+
{
934+
name: 'test client',
935+
version: '1.0'
936+
},
937+
{
938+
capabilities: {
939+
elicitation: {
940+
url: {}
941+
}
942+
}
943+
}
944+
);
945+
946+
const handler = vi.fn().mockResolvedValue({
947+
action: 'cancel'
948+
});
949+
client.setRequestHandler(ElicitRequestSchema, handler);
950+
951+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
952+
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
953+
954+
await expect(
955+
server.request(
956+
{
957+
method: 'elicitation/create',
958+
params: {
959+
message: 'Please provide data',
960+
requestedSchema: {
961+
type: 'object',
962+
properties: {
963+
username: {
964+
type: 'string'
965+
}
966+
}
967+
}
968+
}
969+
},
970+
ElicitResultSchema
971+
)
972+
).rejects.toThrow('Client does not support form-mode elicitation requests');
973+
974+
expect(handler).not.toHaveBeenCalled();
975+
976+
await Promise.all([client.close(), server.close()]);
977+
});
978+
920979
test('should reject URL-mode elicitation when client only supports form mode', async () => {
921980
const client = new Client(
922981
{

src/client/index.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -267,13 +267,14 @@ export class Client<
267267
}
268268

269269
const { params } = validatedRequest.data;
270+
const mode = params.mode ?? 'form';
270271
const { supportsFormMode, supportsUrlMode } = getSupportedElicitationModes(this._capabilities.elicitation);
271272

272-
if (params.mode === 'form' && !supportsFormMode) {
273+
if (mode === 'form' && !supportsFormMode) {
273274
throw new McpError(ErrorCode.InvalidParams, 'Client does not support form-mode elicitation requests');
274275
}
275276

276-
if (params.mode === 'url' && !supportsUrlMode) {
277+
if (mode === 'url' && !supportsUrlMode) {
277278
throw new McpError(ErrorCode.InvalidParams, 'Client does not support URL-mode elicitation requests');
278279
}
279280

@@ -288,9 +289,9 @@ export class Client<
288289
}
289290

290291
const validatedResult = validationResult.data;
291-
const requestedSchema = params.mode === 'form' ? (params.requestedSchema as JsonSchemaType) : undefined;
292+
const requestedSchema = mode === 'form' ? (params.requestedSchema as JsonSchemaType) : undefined;
292293

293-
if (params.mode === 'form' && validatedResult.action === 'accept' && validatedResult.content && requestedSchema) {
294+
if (mode === 'form' && validatedResult.action === 'accept' && validatedResult.content && requestedSchema) {
294295
if (this._capabilities.elicitation?.form?.applyDefaults) {
295296
try {
296297
applyElicitationDefaults(requestedSchema, validatedResult.content);

src/server/index.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -700,7 +700,7 @@ test('should include form mode when sending elicitation form requests', async ()
700700

701701
const receivedModes: string[] = [];
702702
client.setRequestHandler(ElicitRequestSchema, request => {
703-
receivedModes.push(request.params.mode);
703+
receivedModes.push(request.params.mode ?? '');
704704
return {
705705
action: 'accept',
706706
content: {
@@ -764,7 +764,7 @@ test('should include url mode when sending elicitation URL requests', async () =
764764
const receivedModes: string[] = [];
765765
const receivedIds: string[] = [];
766766
client.setRequestHandler(ElicitRequestSchema, request => {
767-
receivedModes.push(request.params.mode);
767+
receivedModes.push(request.params.mode ?? '');
768768
if (request.params.mode === 'url') {
769769
receivedIds.push(request.params.elicitationId);
770770
}

src/server/index.ts

Lines changed: 6 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,6 @@ import {
3737
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
3838
import type { JsonSchemaType, jsonSchemaValidator } from '../validation/types.js';
3939

40-
type LegacyElicitRequestFormParams = Omit<ElicitRequestFormParams, 'mode'>;
41-
4240
export type ServerOptions = ProtocolOptions & {
4341
/**
4442
* Capabilities to advertise as being supported by this server.
@@ -375,33 +373,13 @@ export class Server<
375373

376374
/**
377375
* Creates an elicitation request for the given parameters.
378-
* @param params The parameters for the form elicitation request (explicit mode: 'form').
376+
* For backwards compatibility, `mode` may be omitted for form requests and will default to `'form'`.
377+
* @param params The parameters for the elicitation request.
379378
* @param options Optional request options.
380379
* @returns The result of the elicitation request.
381380
*/
382-
async elicitInput(params: ElicitRequestFormParams, options?: RequestOptions): Promise<ElicitResult>;
383-
/**
384-
* Creates an elicitation request for the given parameters.
385-
* @param params The parameters for the URL elicitation request (with url and elicitationId).
386-
* @param options Optional request options.
387-
* @returns The result of the elicitation request.
388-
*/
389-
async elicitInput(params: ElicitRequestURLParams, options?: RequestOptions): Promise<ElicitResult>;
390-
/**
391-
* Creates an elicitation request for the given parameters.
392-
* @deprecated Use the overloads with explicit `mode: 'form' | 'url'` instead.
393-
* @param params The parameters for the form elicitation request (legacy signature without mode).
394-
* @param options Optional request options.
395-
* @returns The result of the elicitation request.
396-
*/
397-
async elicitInput(params: LegacyElicitRequestFormParams, options?: RequestOptions): Promise<ElicitResult>;
398-
399-
// Implementation (not visible to callers)
400-
async elicitInput(
401-
params: LegacyElicitRequestFormParams | ElicitRequestFormParams | ElicitRequestURLParams,
402-
options?: RequestOptions
403-
): Promise<ElicitResult> {
404-
const mode = ('mode' in params ? params.mode : 'form') as 'form' | 'url';
381+
async elicitInput(params: ElicitRequestFormParams | ElicitRequestURLParams, options?: RequestOptions): Promise<ElicitResult> {
382+
const mode = (params.mode ?? 'form') as 'form' | 'url';
405383

406384
switch (mode) {
407385
case 'url': {
@@ -416,10 +394,9 @@ export class Server<
416394
if (!this._clientCapabilities?.elicitation?.form) {
417395
throw new Error('Client does not support form elicitation.');
418396
}
397+
419398
const formParams: ElicitRequestFormParams =
420-
'mode' in params
421-
? (params as ElicitRequestFormParams)
422-
: ({ ...(params as LegacyElicitRequestFormParams), mode: 'form' } as ElicitRequestFormParams);
399+
params.mode === 'form' ? (params as ElicitRequestFormParams) : { ...(params as ElicitRequestFormParams), mode: 'form' };
423400

424401
const result = await this.request({ method: 'elicitation/create', params: formParams }, ElicitResultSchema, options);
425402

src/spec.types.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Source: https://github.com/modelcontextprotocol/modelcontextprotocol
55
* Pulled from: https://raw.githubusercontent.com/modelcontextprotocol/modelcontextprotocol/main/schema/draft/schema.ts
6-
* Last updated from commit: 4528444698f76e6d0337e58d2941d5d3485d779d
6+
* Last updated from commit: 7dcdd69262bd488ddec071bf4eefedabf1742023
77
*
88
* DO NOT EDIT THIS FILE MANUALLY. Changes will be overwritten by automated updates.
99
* To update this file, run: npm run fetch:spec-types
@@ -469,6 +469,15 @@ export interface BaseMetadata {
469469
export interface Implementation extends BaseMetadata, Icons {
470470
version: string;
471471

472+
/**
473+
* An optional human-readable description of what this implementation does.
474+
*
475+
* This can be used by clients or servers to provide context about their purpose
476+
* and capabilities. For example, a server might describe the types of resources
477+
* or tools it provides, while a client might describe its intended use case.
478+
*/
479+
description?: string;
480+
472481
/**
473482
* An optional URL of the website for this implementation.
474483
*
@@ -613,7 +622,7 @@ export interface ResourceRequestParams extends RequestParams {
613622
* @category `resources/read`
614623
*/
615624
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
616-
export interface ReadResourceRequestParams extends ResourceRequestParams {}
625+
export interface ReadResourceRequestParams extends ResourceRequestParams { }
617626

618627
/**
619628
* Sent from the client to the server, to read a specific resource URI.
@@ -650,7 +659,7 @@ export interface ResourceListChangedNotification extends JSONRPCNotification {
650659
* @category `resources/subscribe`
651660
*/
652661
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
653-
export interface SubscribeRequestParams extends ResourceRequestParams {}
662+
export interface SubscribeRequestParams extends ResourceRequestParams { }
654663

655664
/**
656665
* Sent from the client to request resources/updated notifications from the server whenever a particular resource changes.
@@ -668,7 +677,7 @@ export interface SubscribeRequest extends JSONRPCRequest {
668677
* @category `resources/unsubscribe`
669678
*/
670679
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
671-
export interface UnsubscribeRequestParams extends ResourceRequestParams {}
680+
export interface UnsubscribeRequestParams extends ResourceRequestParams { }
672681

673682
/**
674683
* Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request.
@@ -1377,7 +1386,7 @@ export type SamplingMessageContentBlock =
13771386
*/
13781387
export interface Annotations {
13791388
/**
1380-
* Describes who the intended customer of this object or data is.
1389+
* Describes who the intended audience of this object or data is.
13811390
*
13821391
* It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`).
13831392
*/
@@ -1833,7 +1842,7 @@ export interface ElicitRequestFormParams extends RequestParams {
18331842
/**
18341843
* The elicitation mode.
18351844
*/
1836-
mode: "form";
1845+
mode?: "form";
18371846

18381847
/**
18391848
* The message to present to the user describing what information is being requested.
@@ -1845,6 +1854,7 @@ export interface ElicitRequestFormParams extends RequestParams {
18451854
* Only top-level properties are allowed, without nesting.
18461855
*/
18471856
requestedSchema: {
1857+
$schema?: string;
18481858
type: "object";
18491859
properties: {
18501860
[key: string]: PrimitiveSchemaDefinition;

src/types.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1479,8 +1479,10 @@ export const PrimitiveSchemaDefinitionSchema = z.union([EnumSchemaSchema, Boolea
14791479
export const ElicitRequestFormParamsSchema = BaseRequestParamsSchema.extend({
14801480
/**
14811481
* The elicitation mode.
1482+
*
1483+
* Optional for backward compatibility. Clients MUST treat missing mode as "form".
14821484
*/
1483-
mode: z.literal('form'),
1485+
mode: z.literal('form').optional(),
14841486
/**
14851487
* The message to present to the user describing what information is being requested.
14861488
*/

0 commit comments

Comments
 (0)