SEP-1330: Compatibility with SEP-1034 (#1100)

Co-authored-by: cliffhall <cliff@futurescale.com>
This commit is contained in:
Konstantin Konstantinov 2025-11-11 19:03:28 +02:00 • committed by GitHub
commit 9f06b6b499
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -9,7 +9,7 @@
import { Client } from '../client/index.js';
import { InMemoryTransport } from '../inMemory.js';
import { ElicitRequestSchema } from '../types.js';
import { ElicitRequestParams, ElicitRequestSchema } from '../types.js';
import { AjvJsonSchemaValidator } from '../validation/ajv-provider.js';
import { CfWorkerJsonSchemaValidator } from '../validation/cfworker-provider.js';
import { Server } from './index.js';
@ -17,36 +17,58 @@ import { Server } from './index.js';
const ajvProvider = new AjvJsonSchemaValidator();
const cfWorkerProvider = new CfWorkerJsonSchemaValidator();
let server: Server;
let client: Client;
describe('Elicitation Flow', () => {
describe('with AJV validator', () => {
beforeEach(async () => {
server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: ajvProvider
}
);
client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
});
testElicitationFlow(ajvProvider, 'AJV');
});
describe('with CfWorker validator', () => {
beforeEach(async () => {
server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: cfWorkerProvider
}
);
client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
});
testElicitationFlow(cfWorkerProvider, 'CfWorker');
});
});
function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWorkerProvider, validatorName: string) {
test(`${validatorName}: should elicit simple object with string field`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { name: 'John Doe' }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'What is your name?',
requestedSchema: {
@ -65,24 +87,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should elicit object with integer field`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { age: 42 }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'What is your age?',
requestedSchema: {
@ -101,24 +110,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should elicit object with boolean field`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { agree: true }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Do you agree?',
requestedSchema: {
@ -137,16 +133,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should elicit complex object with multiple fields`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
const userData = {
name: 'Jane Smith',
email: 'jane@example.com',
@ -163,9 +149,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
content: userData
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
@ -192,16 +175,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should reject invalid object (missing required field)`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
@ -210,9 +183,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
}
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'Please provide your information',
@ -229,16 +199,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should reject invalid field type`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
@ -247,9 +207,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
}
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'Please provide your information',
@ -266,24 +223,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should reject invalid string (too short)`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { name: '' } // Too short
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'What is your name?',
@ -299,24 +243,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should reject invalid integer (out of range)`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { age: 200 } // Too high
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'What is your age?',
@ -332,24 +263,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should reject invalid pattern`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { zipCode: 'ABC123' } // Doesn't match pattern
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'Enter a 5-digit zip code',
@ -366,23 +284,10 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should allow decline action without validation`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'decline'
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
@ -400,23 +305,10 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should allow cancel action without validation`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'cancel'
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
@ -434,16 +326,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should handle multiple sequential elicitation requests`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
let requestCount = 0;
client.setRequestHandler(ElicitRequestSchema, request => {
requestCount++;
@ -457,9 +339,6 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
return { action: 'decline' };
});
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const nameResult = await server.elicitInput({
message: 'What is your name?',
requestedSchema: {
@ -500,24 +379,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should validate with optional fields present`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { name: 'John', nickname: 'Johnny' }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Enter your name',
requestedSchema: {
@ -537,24 +403,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should validate with optional fields absent`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { name: 'John' }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Enter your name',
requestedSchema: {
@ -574,24 +427,11 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
});
test(`${validatorName}: should validate email format`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { email: 'user@example.com' }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
const result = await server.elicitInput({
message: 'Enter your email',
requestedSchema: {
@ -629,18 +469,80 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
}
);
const testSchemaProperties: ElicitRequestParams['requestedSchema'] = {
type: 'object',
properties: {
subscribe: { type: 'boolean', default: true },
nickname: { type: 'string', default: 'Guest' },
age: { type: 'integer', minimum: 0, maximum: 150, default: 18 },
color: { type: 'string', enum: ['red', 'green'], default: 'green' },
untitledSingleSelectEnum: {
type: 'string',
title: 'Untitled Single Select Enum',
description: 'Choose your favorite color',
enum: ['red', 'green', 'blue'],
default: 'green'
},
untitledMultipleSelectEnum: {
type: 'array',
title: 'Untitled Multiple Select Enum',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: { type: 'string', enum: ['red', 'green', 'blue'] },
default: ['green', 'blue']
},
titledSingleSelectEnum: {
type: 'string',
title: 'Single Select Enum',
description: 'Choose your favorite color',
oneOf: [
{ const: 'red', title: 'Red' },
{ const: 'green', title: 'Green' },
{ const: 'blue', title: 'Blue' }
],
default: 'green'
},
titledMultipleSelectEnum: {
type: 'array',
title: 'Multiple Select Enum',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: {
anyOf: [
{ const: 'red', title: 'Red' },
{ const: 'green', title: 'Green' },
{ const: 'blue', title: 'Blue' }
]
},
default: ['green', 'blue']
},
legacyTitledEnum: {
type: 'string',
title: 'Legacy Titled Enum',
description: 'Choose your favorite color',
enum: ['red', 'green', 'blue'],
enumNames: ['Red', 'Green', 'Blue'],
default: 'green'
},
optionalWithADefault: { type: 'string', default: 'default value' }
},
required: [
'subscribe',
'nickname',
'age',
'color',
'titledSingleSelectEnum',
'titledMultipleSelectEnum',
'untitledSingleSelectEnum',
'untitledMultipleSelectEnum'
]
};
// Client returns no values; SDK should apply defaults automatically (and validate)
client.setRequestHandler(ElicitRequestSchema, request => {
expect(request.params.requestedSchema).toEqual({
type: 'object',
properties: {
subscribe: { type: 'boolean', default: true },
nickname: { type: 'string', default: 'Guest' },
age: { type: 'integer', minimum: 0, maximum: 150, default: 18 },
color: { type: 'string', enum: ['red', 'green'], default: 'green' }
},
required: ['subscribe', 'nickname', 'age', 'color']
});
expect(request.params.requestedSchema).toEqual(testSchemaProperties);
return {
action: 'accept',
content: {}
@ -652,16 +554,7 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
const result = await server.elicitInput({
message: 'Provide your preferences',
requestedSchema: {
type: 'object',
properties: {
subscribe: { type: 'boolean', default: true },
nickname: { type: 'string', default: 'Guest' },
age: { type: 'integer', minimum: 0, maximum: 150, default: 18 },
color: { type: 'string', enum: ['red', 'green'], default: 'green' }
},
required: ['subscribe', 'nickname', 'age', 'color']
}
requestedSchema: testSchemaProperties
});
expect(result).toEqual({
@ -670,30 +563,23 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
subscribe: true,
nickname: 'Guest',
age: 18,
color: 'green'
color: 'green',
untitledSingleSelectEnum: 'green',
untitledMultipleSelectEnum: ['green', 'blue'],
titledSingleSelectEnum: 'green',
titledMultipleSelectEnum: ['green', 'blue'],
legacyTitledEnum: 'green',
optionalWithADefault: 'default value'
}
});
});
test(`${validatorName}: should reject invalid email format`, async () => {
const server = new Server(
{ name: 'test-server', version: '1.0.0' },
{
capabilities: {},
jsonSchemaValidator: validatorProvider
}
);
const client = new Client({ name: 'test-client', version: '1.0.0' }, { capabilities: { elicitation: {} } });
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: { email: 'not-an-email' }
}));
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
await expect(
server.elicitInput({
message: 'Enter your email',
@ -707,4 +593,369 @@ function testElicitationFlow(validatorProvider: typeof ajvProvider | typeof cfWo
})
).rejects.toThrow(/does not match requested schema/);
});
// Enums - Valid - Single Select - Untitled / Titled
test(`${validatorName}: should succeed with valid selection in single-select untitled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: 'Red'
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
enum: ['Red', 'Green', 'Blue'],
default: 'Green'
}
},
required: ['color']
}
})
).resolves.toEqual({
action: 'accept',
content: {
color: 'Red'
}
});
});
test(`${validatorName}: should succeed with valid selection in single-select titled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: '#FF0000'
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
oneOf: [
{ const: '#FF0000', title: 'Red' },
{ const: '#00FF00', title: 'Green' },
{ const: '#0000FF', title: 'Blue' }
],
default: '#00FF00'
}
},
required: ['color']
}
})
).resolves.toEqual({
action: 'accept',
content: {
color: '#FF0000'
}
});
});
test(`${validatorName}: should succeed with valid selection in single-select titled legacy enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: '#FF0000'
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
enum: ['#FF0000', '#00FF00', '#0000FF'],
enumNames: ['Red', 'Green', 'Blue'],
default: '#00FF00'
}
},
required: ['color']
}
})
).resolves.toEqual({
action: 'accept',
content: {
color: '#FF0000'
}
});
});
// Enums - Valid - Multi Select - Untitled / Titled
test(`${validatorName}: should succeed with valid selection in multi-select untitled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
colors: ['Red', 'Blue']
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
colors: {
type: 'array',
title: 'Color Selection',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: {
type: 'string',
enum: ['Red', 'Green', 'Blue']
}
}
},
required: ['colors']
}
})
).resolves.toEqual({
action: 'accept',
content: {
colors: ['Red', 'Blue']
}
});
});
test(`${validatorName}: should succeed with valid selection in multi-select titled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
colors: ['#FF0000', '#0000FF']
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
colors: {
type: 'array',
title: 'Color Selection',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: {
anyOf: [
{ const: '#FF0000', title: 'Red' },
{ const: '#00FF00', title: 'Green' },
{ const: '#0000FF', title: 'Blue' }
]
}
}
},
required: ['colors']
}
})
).resolves.toEqual({
action: 'accept',
content: {
colors: ['#FF0000', '#0000FF']
}
});
});
// Enums - Invalid - Single Select - Untitled / Titled
test(`${validatorName}: should reject invalid selection in single-select untitled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: 'Black' // Color not in enum list
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
enum: ['Red', 'Green', 'Blue'],
default: 'Green'
}
},
required: ['color']
}
})
).rejects.toThrow(/^MCP error -32602: Elicitation response content does not match requested schema/);
});
test(`${validatorName}: should reject invalid selection in single-select titled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: 'Red' // Should be "#FF0000" (const not title)
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
oneOf: [
{ const: '#FF0000', title: 'Red' },
{ const: '#00FF00', title: 'Green' },
{ const: '#0000FF', title: 'Blue' }
],
default: '#00FF00'
}
},
required: ['color']
}
})
).rejects.toThrow(/^MCP error -32602: Elicitation response content does not match requested schema/);
});
test(`${validatorName}: should reject invalid selection in single-select titled legacy enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: 'Red' // Should be "#FF0000" (enum not enumNames)
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'string',
title: 'Color Selection',
description: 'Choose your favorite color',
enum: ['#FF0000', '#00FF00', '#0000FF'],
enumNames: ['Red', 'Green', 'Blue'],
default: '#00FF00'
}
},
required: ['color']
}
})
).rejects.toThrow(/^MCP error -32602: Elicitation response content does not match requested schema/);
});
// Enums - Invalid - Multi Select - Untitled / Titled
test(`${validatorName}: should reject invalid selection in multi-select untitled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
color: 'Red' // Should be array, not string
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
color: {
type: 'array',
title: 'Color Selection',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: {
type: 'string',
enum: ['Red', 'Green', 'Blue']
}
}
},
required: ['color']
}
})
).rejects.toThrow(/^MCP error -32602: Elicitation response content does not match requested schema/);
});
test(`${validatorName}: should reject invalid selection in multi-select titled enum`, async () => {
// Set up client to return valid response
client.setRequestHandler(ElicitRequestSchema, _request => ({
action: 'accept',
content: {
colors: ['Red', 'Blue'] // Should be ["#FF0000", "#0000FF"] (const not title)
}
}));
// Test with valid response
await expect(
server.elicitInput({
message: 'Please provide your information',
requestedSchema: {
type: 'object',
properties: {
colors: {
type: 'array',
title: 'Color Selection',
description: 'Choose your favorite colors',
minItems: 1,
maxItems: 3,
items: {
anyOf: [
{ const: '#FF0000', title: 'Red' },
{ const: '#00FF00', title: 'Green' },
{ const: '#0000FF', title: 'Blue' }
]
}
}
},
required: ['colors']
}
})
).rejects.toThrow(/^MCP error -32602: Elicitation response content does not match requested schema/);
});
}