Skip to content

Commit b057aac

Browse files
committed
Bonus tests: form elicitation schema validation safety
1 parent 1a02a6f commit b057aac

File tree

1 file changed

+108
-0
lines changed

1 file changed

+108
-0
lines changed

src/server/index.test.ts

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
SUPPORTED_PROTOCOL_VERSIONS
2121
} from '../types.js';
2222
import { Server } from './index.js';
23+
import type { JsonSchemaType, JsonSchemaValidator, jsonSchemaValidator } from '../validation/types.js';
2324

2425
test('should accept latest protocol version', async () => {
2526
let sendPromiseResolve: (value: unknown) => void;
@@ -555,6 +556,113 @@ test('should include url mode when sending elicitation URL requests', async () =
555556
expect(receivedIds).toEqual(['elicitation-xyz']);
556557
});
557558

559+
test('should reject elicitFormInput when client response violates requested schema', async () => {
560+
const server = new Server(
561+
{
562+
name: 'test server',
563+
version: '1.0'
564+
},
565+
{
566+
capabilities: {}
567+
}
568+
);
569+
570+
const client = new Client(
571+
{
572+
name: 'test client',
573+
version: '1.0'
574+
},
575+
{
576+
capabilities: {
577+
elicitation: {
578+
form: {}
579+
}
580+
}
581+
}
582+
);
583+
584+
client.setRequestHandler(ElicitRequestSchema, () => ({
585+
action: 'accept',
586+
587+
// Bad response: missing required field `username`
588+
content: {}
589+
}));
590+
591+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
592+
593+
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
594+
595+
await expect(
596+
server.elicitFormInput({
597+
message: 'Please provide your username',
598+
requestedSchema: {
599+
type: 'object',
600+
properties: {
601+
username: {
602+
type: 'string'
603+
}
604+
},
605+
required: ['username']
606+
}
607+
})
608+
).rejects.toThrow('Elicitation response content does not match requested schema');
609+
});
610+
611+
test('should wrap unexpected validator errors during elicitFormInput', async () => {
612+
class ThrowingValidator implements jsonSchemaValidator {
613+
getValidator<T>(_schema: JsonSchemaType): JsonSchemaValidator<T> {
614+
throw new Error('boom - validator exploded');
615+
}
616+
}
617+
618+
const server = new Server(
619+
{
620+
name: 'test server',
621+
version: '1.0'
622+
},
623+
{
624+
capabilities: {},
625+
jsonSchemaValidator: new ThrowingValidator()
626+
}
627+
);
628+
629+
const client = new Client(
630+
{
631+
name: 'test client',
632+
version: '1.0'
633+
},
634+
{
635+
capabilities: {
636+
elicitation: {
637+
form: {}
638+
}
639+
}
640+
}
641+
);
642+
643+
client.setRequestHandler(ElicitRequestSchema, () => ({
644+
action: 'accept',
645+
content: {
646+
username: 'ignored'
647+
}
648+
}));
649+
650+
const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair();
651+
652+
await Promise.all([client.connect(clientTransport), server.connect(serverTransport)]);
653+
654+
await expect(
655+
server.elicitFormInput({
656+
message: 'Provide any data',
657+
requestedSchema: {
658+
type: 'object',
659+
properties: {},
660+
required: []
661+
}
662+
})
663+
).rejects.toThrow('MCP error -32603: Error validating elicitation response: boom - validator exploded');
664+
});
665+
558666
test('should forward notification options when using elicitation completion notifier', async () => {
559667
const server = new Server(
560668
{

0 commit comments

Comments
 (0)