@@ -4765,6 +4765,201 @@ describe.each(zodTestMatrix)('$zodVersionLabel', (entry: ZodMatrixEntry) => {
47654765 } ) ;
47664766 } ) ;
47674767
4768+ describe ( 'Tools with transformation schemas' , ( ) => {
4769+ test ( 'should support z.preprocess() schemas' , async ( ) => {
4770+ const server = new McpServer ( {
4771+ name : 'test' ,
4772+ version : '1.0.0'
4773+ } ) ;
4774+
4775+ const client = new Client ( {
4776+ name : 'test-client' ,
4777+ version : '1.0.0'
4778+ } ) ;
4779+
4780+ // z.preprocess() allows transforming input before validation
4781+ const preprocessSchema = z . preprocess (
4782+ input => {
4783+ // Normalize input by trimming strings
4784+ if ( typeof input === 'object' && input !== null ) {
4785+ const obj = input as Record < string , unknown > ;
4786+ if ( typeof obj . name === 'string' ) {
4787+ return { ...obj , name : obj . name . trim ( ) } ;
4788+ }
4789+ }
4790+ return input ;
4791+ } ,
4792+ z . object ( { name : z . string ( ) } )
4793+ ) ;
4794+
4795+ server . registerTool ( 'preprocess-test' , { inputSchema : preprocessSchema } , async args => {
4796+ return {
4797+ content : [ { type : 'text' as const , text : `Hello, ${ args . name } !` } ]
4798+ } ;
4799+ } ) ;
4800+
4801+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
4802+ await server . connect ( serverTransport ) ;
4803+ await client . connect ( clientTransport ) ;
4804+
4805+ // Test with input that has leading/trailing whitespace
4806+ const result = await client . callTool ( {
4807+ name : 'preprocess-test' ,
4808+ arguments : { name : ' World ' }
4809+ } ) ;
4810+
4811+ expect ( result . content ) . toEqual ( [
4812+ {
4813+ type : 'text' ,
4814+ text : 'Hello, World!'
4815+ }
4816+ ] ) ;
4817+ } ) ;
4818+
4819+ test ( 'should support z.transform() schemas' , async ( ) => {
4820+ const server = new McpServer ( {
4821+ name : 'test' ,
4822+ version : '1.0.0'
4823+ } ) ;
4824+
4825+ const client = new Client ( {
4826+ name : 'test-client' ,
4827+ version : '1.0.0'
4828+ } ) ;
4829+
4830+ // z.transform() allows transforming validated output
4831+ const transformSchema = z
4832+ . object ( {
4833+ firstName : z . string ( ) ,
4834+ lastName : z . string ( )
4835+ } )
4836+ . transform ( data => ( {
4837+ ...data ,
4838+ fullName : `${ data . firstName } ${ data . lastName } `
4839+ } ) ) ;
4840+
4841+ server . registerTool ( 'transform-test' , { inputSchema : transformSchema } , async args => {
4842+ return {
4843+ content : [ { type : 'text' as const , text : `Full name: ${ args . fullName } ` } ]
4844+ } ;
4845+ } ) ;
4846+
4847+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
4848+ await server . connect ( serverTransport ) ;
4849+ await client . connect ( clientTransport ) ;
4850+
4851+ const result = await client . callTool ( {
4852+ name : 'transform-test' ,
4853+ arguments : { firstName : 'John' , lastName : 'Doe' }
4854+ } ) ;
4855+
4856+ expect ( result . content ) . toEqual ( [
4857+ {
4858+ type : 'text' ,
4859+ text : 'Full name: John Doe'
4860+ }
4861+ ] ) ;
4862+ } ) ;
4863+
4864+ test ( 'should support z.pipe() schemas' , async ( ) => {
4865+ const server = new McpServer ( {
4866+ name : 'test' ,
4867+ version : '1.0.0'
4868+ } ) ;
4869+
4870+ const client = new Client ( {
4871+ name : 'test-client' ,
4872+ version : '1.0.0'
4873+ } ) ;
4874+
4875+ // z.pipe() chains multiple schemas together
4876+ const pipeSchema = z
4877+ . object ( { value : z . string ( ) } )
4878+ . transform ( data => ( { ...data , processed : true } ) )
4879+ . pipe ( z . object ( { value : z . string ( ) , processed : z . boolean ( ) } ) ) ;
4880+
4881+ server . registerTool ( 'pipe-test' , { inputSchema : pipeSchema } , async args => {
4882+ return {
4883+ content : [ { type : 'text' as const , text : `Value: ${ args . value } , Processed: ${ args . processed } ` } ]
4884+ } ;
4885+ } ) ;
4886+
4887+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
4888+ await server . connect ( serverTransport ) ;
4889+ await client . connect ( clientTransport ) ;
4890+
4891+ const result = await client . callTool ( {
4892+ name : 'pipe-test' ,
4893+ arguments : { value : 'test' }
4894+ } ) ;
4895+
4896+ expect ( result . content ) . toEqual ( [
4897+ {
4898+ type : 'text' ,
4899+ text : 'Value: test, Processed: true'
4900+ }
4901+ ] ) ;
4902+ } ) ;
4903+
4904+ test ( 'should support nested transformation schemas' , async ( ) => {
4905+ const server = new McpServer ( {
4906+ name : 'test' ,
4907+ version : '1.0.0'
4908+ } ) ;
4909+
4910+ const client = new Client ( {
4911+ name : 'test-client' ,
4912+ version : '1.0.0'
4913+ } ) ;
4914+
4915+ // Complex schema with both preprocess and transform
4916+ const complexSchema = z . preprocess (
4917+ input => {
4918+ if ( typeof input === 'object' && input !== null ) {
4919+ const obj = input as Record < string , unknown > ;
4920+ // Convert string numbers to actual numbers
4921+ if ( typeof obj . count === 'string' ) {
4922+ return { ...obj , count : parseInt ( obj . count , 10 ) } ;
4923+ }
4924+ }
4925+ return input ;
4926+ } ,
4927+ z
4928+ . object ( {
4929+ name : z . string ( ) ,
4930+ count : z . number ( )
4931+ } )
4932+ . transform ( data => ( {
4933+ ...data ,
4934+ doubled : data . count * 2
4935+ } ) )
4936+ ) ;
4937+
4938+ server . registerTool ( 'complex-transform' , { inputSchema : complexSchema } , async args => {
4939+ return {
4940+ content : [ { type : 'text' as const , text : `${ args . name } : ${ args . count } -> ${ args . doubled } ` } ]
4941+ } ;
4942+ } ) ;
4943+
4944+ const [ clientTransport , serverTransport ] = InMemoryTransport . createLinkedPair ( ) ;
4945+ await server . connect ( serverTransport ) ;
4946+ await client . connect ( clientTransport ) ;
4947+
4948+ // Pass count as string, preprocess will convert it
4949+ const result = await client . callTool ( {
4950+ name : 'complex-transform' ,
4951+ arguments : { name : 'items' , count : '5' }
4952+ } ) ;
4953+
4954+ expect ( result . content ) . toEqual ( [
4955+ {
4956+ type : 'text' ,
4957+ text : 'items: 5 -> 10'
4958+ }
4959+ ] ) ;
4960+ } ) ;
4961+ } ) ;
4962+
47684963 describe ( 'resource()' , ( ) => {
47694964 /***
47704965 * Test: Resource Registration with URI and Read Callback
0 commit comments