55
66import { Client } from '@modelcontextprotocol/client' ;
77import type { TextContent } from '@modelcontextprotocol/core' ;
8- import { InMemoryTransport } from '@modelcontextprotocol/core' ;
8+ import { InMemoryTransport , ProtocolErrorCode } from '@modelcontextprotocol/core' ;
99import { completable , fromJsonSchema as serverFromJsonSchema , McpServer } from '@modelcontextprotocol/server' ;
1010import { toStandardJsonSchema } from '@valibot/to-json-schema' ;
1111import { type } from 'arktype' ;
@@ -33,6 +33,18 @@ describe('Standard Schema Support', () => {
3333 await Promise . all ( [ client . connect ( clientTransport ) , mcpServer . connect ( serverTransport ) ] ) ;
3434 }
3535
36+ async function expectToolInputValidationError ( params : { name : string ; arguments : Record < string , unknown > } ) {
37+ try {
38+ await client . request ( { method : 'tools/call' , params } ) ;
39+ } catch ( error ) {
40+ expect ( error ) . toMatchObject ( { code : ProtocolErrorCode . InvalidParams } ) ;
41+ const message = error instanceof Error ? error . message : String ( error ) ;
42+ expect ( message ) . toContain ( 'Input validation error' ) ;
43+ return message ;
44+ }
45+ throw new Error ( 'Expected tool input validation to reject with InvalidParams' ) ;
46+ }
47+
3648 describe ( 'ArkType schemas' , ( ) => {
3749 describe ( 'tool registration' , ( ) => {
3850 test ( 'should register tool with ArkType input schema' , async ( ) => {
@@ -130,14 +142,7 @@ describe('Standard Schema Support', () => {
130142
131143 await connectClientAndServer ( ) ;
132144
133- const result = await client . request ( {
134- method : 'tools/call' ,
135- params : { name : 'double' , arguments : { value : 'not a number' } }
136- } ) ;
137-
138- expect ( result . isError ) . toBe ( true ) ;
139- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
140- expect ( errorText ) . toContain ( 'Input validation error' ) ;
145+ const errorText = await expectToolInputValidationError ( { name : 'double' , arguments : { value : 'not a number' } } ) ;
141146 expect ( errorText ) . toContain ( 'value' ) ;
142147 expect ( errorText ) . toContain ( 'number' ) ;
143148 } ) ;
@@ -153,14 +158,7 @@ describe('Standard Schema Support', () => {
153158
154159 await connectClientAndServer ( ) ;
155160
156- const result = await client . request ( {
157- method : 'tools/call' ,
158- params : { name : 'calculate' , arguments : { operation : 'divide' } }
159- } ) ;
160-
161- expect ( result . isError ) . toBe ( true ) ;
162- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
163- expect ( errorText ) . toContain ( 'Input validation error' ) ;
161+ const errorText = await expectToolInputValidationError ( { name : 'calculate' , arguments : { operation : 'divide' } } ) ;
164162 expect ( errorText ) . toMatch ( / a d d | s u b t r a c t | m u l t i p l y / ) ;
165163 } ) ;
166164
@@ -173,14 +171,7 @@ describe('Standard Schema Support', () => {
173171
174172 await connectClientAndServer ( ) ;
175173
176- const result = await client . request ( {
177- method : 'tools/call' ,
178- params : { name : 'greet' , arguments : { name : 'Alice' } }
179- } ) ;
180-
181- expect ( result . isError ) . toBe ( true ) ;
182- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
183- expect ( errorText ) . toContain ( 'Input validation error' ) ;
174+ const errorText = await expectToolInputValidationError ( { name : 'greet' , arguments : { name : 'Alice' } } ) ;
184175 expect ( errorText ) . toContain ( 'age' ) ;
185176 } ) ;
186177 } ) ;
@@ -273,14 +264,7 @@ describe('Standard Schema Support', () => {
273264
274265 await connectClientAndServer ( ) ;
275266
276- const result = await client . request ( {
277- method : 'tools/call' ,
278- params : { name : 'double' , arguments : { value : 'not a number' } }
279- } ) ;
280-
281- expect ( result . isError ) . toBe ( true ) ;
282- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
283- expect ( errorText ) . toContain ( 'Input validation error' ) ;
267+ const errorText = await expectToolInputValidationError ( { name : 'double' , arguments : { value : 'not a number' } } ) ;
284268 expect ( errorText ) . toContain ( 'number' ) ;
285269 } ) ;
286270
@@ -297,14 +281,7 @@ describe('Standard Schema Support', () => {
297281
298282 await connectClientAndServer ( ) ;
299283
300- const result = await client . request ( {
301- method : 'tools/call' ,
302- params : { name : 'calculate' , arguments : { operation : 'divide' } }
303- } ) ;
304-
305- expect ( result . isError ) . toBe ( true ) ;
306- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
307- expect ( errorText ) . toContain ( 'Input validation error' ) ;
284+ await expectToolInputValidationError ( { name : 'calculate' , arguments : { operation : 'divide' } } ) ;
308285 } ) ;
309286
310287 test ( 'should validate min/max constraints' , async ( ) => {
@@ -328,13 +305,7 @@ describe('Standard Schema Support', () => {
328305 expect ( validResult . isError ) . toBeFalsy ( ) ;
329306
330307 // Invalid value (too high)
331- const invalidResult = await client . request ( {
332- method : 'tools/call' ,
333- params : { name : 'setPercentage' , arguments : { percentage : 150 } }
334- } ) ;
335- expect ( invalidResult . isError ) . toBe ( true ) ;
336- const errorText = ( invalidResult . content [ 0 ] as TextContent ) . text ;
337- expect ( errorText ) . toContain ( 'Input validation error' ) ;
308+ await expectToolInputValidationError ( { name : 'setPercentage' , arguments : { percentage : 150 } } ) ;
338309 } ) ;
339310 } ) ;
340311 } ) ;
@@ -420,11 +391,7 @@ describe('Standard Schema Support', () => {
420391
421392 await connectClientAndServer ( ) ;
422393
423- const result = await client . request ( { method : 'tools/call' , params : { name : 'double' , arguments : { count : 'not a number' } } } ) ;
424-
425- expect ( result . isError ) . toBe ( true ) ;
426- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
427- expect ( errorText ) . toContain ( 'Input validation error' ) ;
394+ await expectToolInputValidationError ( { name : 'double' , arguments : { count : 'not a number' } } ) ;
428395 } ) ;
429396 } ) ;
430397
@@ -557,21 +524,15 @@ describe('Standard Schema Support', () => {
557524
558525 await connectClientAndServer ( ) ;
559526
560- const result = await client . request ( {
561- method : 'tools/call' ,
562- params : {
563- name : 'test' ,
564- arguments : {
565- email : 123 ,
566- age : 'not a number' ,
567- status : 'unknown'
568- }
527+ const errorText = await expectToolInputValidationError ( {
528+ name : 'test' ,
529+ arguments : {
530+ email : 123 ,
531+ age : 'not a number' ,
532+ status : 'unknown'
569533 }
570534 } ) ;
571535
572- expect ( result . isError ) . toBe ( true ) ;
573- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
574-
575536 // Check that error mentions the specific issues
576537 expect ( errorText ) . toContain ( 'Input validation error' ) ;
577538 // ArkType should mention type mismatches
@@ -593,21 +554,15 @@ describe('Standard Schema Support', () => {
593554
594555 await connectClientAndServer ( ) ;
595556
596- const result = await client . request ( {
597- method : 'tools/call' ,
598- params : {
599- name : 'test' ,
600- arguments : {
601- email : 123 ,
602- age : 'not a number' ,
603- status : 'unknown'
604- }
557+ const errorText = await expectToolInputValidationError ( {
558+ name : 'test' ,
559+ arguments : {
560+ email : 123 ,
561+ age : 'not a number' ,
562+ status : 'unknown'
605563 }
606564 } ) ;
607565
608- expect ( result . isError ) . toBe ( true ) ;
609- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
610-
611566 // Check that error mentions the specific issues
612567 expect ( errorText ) . toContain ( 'Input validation error' ) ;
613568 // Valibot should provide "Invalid type" messages
@@ -627,21 +582,15 @@ describe('Standard Schema Support', () => {
627582
628583 await connectClientAndServer ( ) ;
629584
630- const result = await client . request ( {
631- method : 'tools/call' ,
632- params : {
633- name : 'test' ,
634- arguments : {
635- email : 123 ,
636- age : 'not a number' ,
637- status : 'unknown'
638- }
585+ const errorText = await expectToolInputValidationError ( {
586+ name : 'test' ,
587+ arguments : {
588+ email : 123 ,
589+ age : 'not a number' ,
590+ status : 'unknown'
639591 }
640592 } ) ;
641593
642- expect ( result . isError ) . toBe ( true ) ;
643- const errorText = ( result . content [ 0 ] as TextContent ) . text ;
644-
645594 // Check that error mentions the specific issues
646595 expect ( errorText ) . toContain ( 'Input validation error' ) ;
647596 } ) ;
0 commit comments