@mastra/mcp 0.10.10 → 0.10.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +31 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/index.cjs +26 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +26 -4
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +4 -4
- package/src/client/client.ts +5 -7
- package/src/server/server.test.ts +286 -1
- package/src/server/server.ts +27 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAE5D,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,KAAK,EACV,eAAe,EACf,UAAU,EACV,gBAAgB,EAChB,aAAa,EACb,uBAAuB,EACvB,mBAAmB,EACnB,WAAW,EACZ,MAAM,kBAAkB,CAAC;AAE1B,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,OAAO,KAAK,EAAE,oCAAoC,EAAE,MAAM,oDAAoD,CAAC;AAuB/G,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAE7D,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,KAAK,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,kBAAkB,EAAW,MAAM,SAAS,CAAC;AACjG,qBAAa,SAAU,SAAQ,aAAa;IAC1C,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,cAAc,CAAC,CAAuB;IAC9C,OAAO,CAAC,YAAY,CAAC,CAAqB;IAC1C,OAAO,CAAC,iBAAiB,CAA4B;IACrD,OAAO,CAAC,wBAAwB,CAAyD;IAEzF,OAAO,CAAC,mBAAmB,CAAkC;IAE7D,OAAO,CAAC,gBAAgB,CAAC,CAAa;IACtC,OAAO,CAAC,wBAAwB,CAAC,CAAqB;IACtD,OAAO,CAAC,eAAe,CAAC,CAAqB;IAC7C,OAAO,CAAC,cAAc,CAAC,CAAW;IAClC,OAAO,CAAC,aAAa,CAAC,CAAmB;IACzC,OAAO,CAAC,aAAa,CAA0B;IAC/C,SAAgB,SAAS,EAAE,qBAAqB,CAAC;IACjD,SAAgB,OAAO,EAAE,mBAAmB,CAAC;IAC7C,SAAgB,WAAW,EAAE,kBAAkB,CAAC;IAEhD;;OAEG;IACI,iBAAiB,IAAI,oBAAoB,GAAG,SAAS;IAI5D;;OAEG;IACI,eAAe,IAAI,kBAAkB,GAAG,SAAS;IAIxD;;OAEG;IACI,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;IAIvE;;OAEG;IACI,SAAS,IAAI,MAAM;IAI1B;;;OAGG;gBACS,IAAI,EAAE,eAAe,GAAG;QAAE,SAAS,CAAC,EAAE,kBAAkB,CAAC;QAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;KAAE;IAyDlG;;;;;;;OAOG;YACW,wBAAwB;IActC;;;OAGG;IACH,OAAO,CAAC,oBAAoB;IAuB5B;;;OAGG;IACH,OAAO,CAAC,wBAAwB;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/server/server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/mcp",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.11",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"vitest": "^3.2.4",
|
|
52
52
|
"zod": "^3.25.67",
|
|
53
53
|
"zod-to-json-schema": "^3.24.5",
|
|
54
|
-
"@internal/
|
|
55
|
-
"@mastra/core": "0.13.
|
|
56
|
-
"@internal/
|
|
54
|
+
"@internal/lint": "0.0.29",
|
|
55
|
+
"@mastra/core": "0.13.2",
|
|
56
|
+
"@internal/types-builder": "0.0.4"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "tsup --silent --config tsup.config.ts",
|
package/src/client/client.ts
CHANGED
|
@@ -169,7 +169,6 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
169
169
|
// Set up log message capturing
|
|
170
170
|
this.setupLogging();
|
|
171
171
|
|
|
172
|
-
|
|
173
172
|
this.resources = new ResourceClientActions({ client: this, logger: this.logger });
|
|
174
173
|
this.prompts = new PromptClientActions({ client: this, logger: this.logger });
|
|
175
174
|
this.elicitation = new ElicitationClientActions({ client: this, logger: this.logger });
|
|
@@ -318,7 +317,6 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
318
317
|
originalOnClose();
|
|
319
318
|
}
|
|
320
319
|
};
|
|
321
|
-
|
|
322
320
|
} catch (e) {
|
|
323
321
|
this.isConnected = null;
|
|
324
322
|
reject(e);
|
|
@@ -466,12 +464,12 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
466
464
|
|
|
467
465
|
setElicitationRequestHandler(handler: ElicitationHandler): void {
|
|
468
466
|
this.log('debug', 'Setting elicitation request handler');
|
|
469
|
-
this.client.setRequestHandler(ElicitRequestSchema, async
|
|
467
|
+
this.client.setRequestHandler(ElicitRequestSchema, async request => {
|
|
470
468
|
this.log('debug', `Received elicitation request: ${request.params.message}`);
|
|
471
469
|
return handler(request.params);
|
|
472
470
|
});
|
|
473
471
|
}
|
|
474
|
-
|
|
472
|
+
|
|
475
473
|
private async convertInputSchema(
|
|
476
474
|
inputSchema: Awaited<ReturnType<Client['listTools']>>['tools'][0]['inputSchema'] | JSONSchema,
|
|
477
475
|
): Promise<z.ZodType> {
|
|
@@ -480,7 +478,7 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
480
478
|
}
|
|
481
479
|
|
|
482
480
|
try {
|
|
483
|
-
await $RefParser.dereference(inputSchema)
|
|
481
|
+
await $RefParser.dereference(inputSchema);
|
|
484
482
|
return convertJsonSchemaToZod(inputSchema as JSONSchema);
|
|
485
483
|
} catch (error: unknown) {
|
|
486
484
|
let errorDetails: string | undefined;
|
|
@@ -511,13 +509,13 @@ export class InternalMastraMCPClient extends MastraBase {
|
|
|
511
509
|
private async convertOutputSchema(
|
|
512
510
|
outputSchema: Awaited<ReturnType<Client['listTools']>>['tools'][0]['outputSchema'] | JSONSchema,
|
|
513
511
|
): Promise<z.ZodType | undefined> {
|
|
514
|
-
if (!outputSchema) return
|
|
512
|
+
if (!outputSchema) return;
|
|
515
513
|
if (isZodType(outputSchema)) {
|
|
516
514
|
return outputSchema;
|
|
517
515
|
}
|
|
518
516
|
|
|
519
517
|
try {
|
|
520
|
-
await $RefParser.dereference(outputSchema)
|
|
518
|
+
await $RefParser.dereference(outputSchema);
|
|
521
519
|
return convertJsonSchemaToZod(outputSchema as JSONSchema);
|
|
522
520
|
} catch (error: unknown) {
|
|
523
521
|
let errorDetails: string | undefined;
|
|
@@ -1347,7 +1347,7 @@ describe('MCPServer - Workflow to Tool Conversion', () => {
|
|
|
1347
1347
|
});
|
|
1348
1348
|
|
|
1349
1349
|
it('should call workflow.createRun().start() when the derived tool is executed', async () => {
|
|
1350
|
-
const testWorkflow = createMockWorkflow('MyExecWorkflow', 'Executable workflow');
|
|
1350
|
+
const testWorkflow = createMockWorkflow('MyExecWorkflow', 'Executable workflow', z.object({ data: z.string() }));
|
|
1351
1351
|
const step = createStep({
|
|
1352
1352
|
id: 'my-step',
|
|
1353
1353
|
description: 'My step description',
|
|
@@ -1855,3 +1855,288 @@ describe('MCPServer with Tool Output Schema', () => {
|
|
|
1855
1855
|
expect(JSON.parse(result.content[0].text)).toEqual(result.structuredContent);
|
|
1856
1856
|
});
|
|
1857
1857
|
});
|
|
1858
|
+
|
|
1859
|
+
describe('MCPServer - Tool Input Validation', () => {
|
|
1860
|
+
let validationServer: MCPServer;
|
|
1861
|
+
let validationClient: InternalMastraMCPClient;
|
|
1862
|
+
let httpValidationServer: ServerType;
|
|
1863
|
+
let tools: Record<string, any>;
|
|
1864
|
+
const VALIDATION_PORT = 9700 + Math.floor(Math.random() * 100);
|
|
1865
|
+
|
|
1866
|
+
const toolsWithValidation: ToolsInput = {
|
|
1867
|
+
stringTool: {
|
|
1868
|
+
description: 'Tool that requires a string input',
|
|
1869
|
+
parameters: z.object({
|
|
1870
|
+
message: z.string().min(3, 'Message must be at least 3 characters'),
|
|
1871
|
+
optional: z.string().optional(),
|
|
1872
|
+
}),
|
|
1873
|
+
execute: async args => ({
|
|
1874
|
+
result: `Received: ${args.message}`,
|
|
1875
|
+
}),
|
|
1876
|
+
},
|
|
1877
|
+
numberTool: {
|
|
1878
|
+
description: 'Tool that requires number inputs',
|
|
1879
|
+
parameters: z.object({
|
|
1880
|
+
age: z.number().min(0).max(150),
|
|
1881
|
+
score: z.number().optional(),
|
|
1882
|
+
}),
|
|
1883
|
+
execute: async args => ({
|
|
1884
|
+
result: `Age: ${args.age}, Score: ${args.score ?? 'N/A'}`,
|
|
1885
|
+
}),
|
|
1886
|
+
},
|
|
1887
|
+
complexTool: {
|
|
1888
|
+
description: 'Tool with complex validation',
|
|
1889
|
+
parameters: z.object({
|
|
1890
|
+
email: z.string().email('Invalid email format'),
|
|
1891
|
+
tags: z.array(z.string()).min(1, 'At least one tag required'),
|
|
1892
|
+
metadata: z.object({
|
|
1893
|
+
priority: z.enum(['low', 'medium', 'high']),
|
|
1894
|
+
deadline: z.string().datetime().optional(),
|
|
1895
|
+
}),
|
|
1896
|
+
}),
|
|
1897
|
+
execute: async args => ({
|
|
1898
|
+
result: `Processing ${args.email} with ${args.tags.length} tags`,
|
|
1899
|
+
}),
|
|
1900
|
+
},
|
|
1901
|
+
};
|
|
1902
|
+
|
|
1903
|
+
beforeAll(async () => {
|
|
1904
|
+
const app = new Hono();
|
|
1905
|
+
validationServer = new MCPServer({
|
|
1906
|
+
name: 'ValidationTestServer',
|
|
1907
|
+
version: '1.0.0',
|
|
1908
|
+
description: 'Server for testing tool validation',
|
|
1909
|
+
tools: toolsWithValidation,
|
|
1910
|
+
});
|
|
1911
|
+
|
|
1912
|
+
app.get('/sse', async c => {
|
|
1913
|
+
const url = new URL(c.req.url, `http://localhost:${VALIDATION_PORT}`);
|
|
1914
|
+
return await validationServer.startHonoSSE({
|
|
1915
|
+
url,
|
|
1916
|
+
ssePath: '/sse',
|
|
1917
|
+
messagePath: '/message',
|
|
1918
|
+
context: c,
|
|
1919
|
+
});
|
|
1920
|
+
});
|
|
1921
|
+
|
|
1922
|
+
app.post('/message', async c => {
|
|
1923
|
+
const url = new URL(c.req.url, `http://localhost:${VALIDATION_PORT}`);
|
|
1924
|
+
return await validationServer.startHonoSSE({
|
|
1925
|
+
url,
|
|
1926
|
+
ssePath: '/sse',
|
|
1927
|
+
messagePath: '/message',
|
|
1928
|
+
context: c,
|
|
1929
|
+
});
|
|
1930
|
+
});
|
|
1931
|
+
|
|
1932
|
+
httpValidationServer = serve({
|
|
1933
|
+
fetch: app.fetch,
|
|
1934
|
+
port: VALIDATION_PORT,
|
|
1935
|
+
});
|
|
1936
|
+
|
|
1937
|
+
validationClient = new InternalMastraMCPClient({
|
|
1938
|
+
name: 'validation-test-client',
|
|
1939
|
+
server: { url: new URL(`http://localhost:${VALIDATION_PORT}/sse`) },
|
|
1940
|
+
});
|
|
1941
|
+
|
|
1942
|
+
await validationClient.connect();
|
|
1943
|
+
tools = await validationClient.tools();
|
|
1944
|
+
});
|
|
1945
|
+
|
|
1946
|
+
afterAll(async () => {
|
|
1947
|
+
await validationClient.disconnect();
|
|
1948
|
+
httpValidationServer.close();
|
|
1949
|
+
});
|
|
1950
|
+
|
|
1951
|
+
it('should successfully execute tool with valid inputs', async () => {
|
|
1952
|
+
const stringTool = tools['stringTool'];
|
|
1953
|
+
expect(stringTool).toBeDefined();
|
|
1954
|
+
|
|
1955
|
+
const result = await stringTool.execute({
|
|
1956
|
+
context: {
|
|
1957
|
+
message: 'Hello world',
|
|
1958
|
+
optional: 'optional value',
|
|
1959
|
+
},
|
|
1960
|
+
});
|
|
1961
|
+
|
|
1962
|
+
expect(result).toBeDefined();
|
|
1963
|
+
expect(result.content[0].text).toContain('Received: Hello world');
|
|
1964
|
+
});
|
|
1965
|
+
|
|
1966
|
+
it('should return validation error for missing required parameters', async () => {
|
|
1967
|
+
const stringTool = tools['stringTool'];
|
|
1968
|
+
const result = await stringTool.execute({
|
|
1969
|
+
context: {},
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
expect(result).toBeDefined();
|
|
1973
|
+
// Handle both client-side and server-side error formats
|
|
1974
|
+
if (result.error) {
|
|
1975
|
+
expect(result.error).toBe(true);
|
|
1976
|
+
expect(result.message).toContain('Tool validation failed');
|
|
1977
|
+
expect(result.message).toContain('Please fix the following errors');
|
|
1978
|
+
} else {
|
|
1979
|
+
expect(result.isError).toBe(true);
|
|
1980
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
1981
|
+
expect(result.content[0].text).toContain('Please fix the following errors');
|
|
1982
|
+
}
|
|
1983
|
+
});
|
|
1984
|
+
|
|
1985
|
+
it('should return validation error for invalid string length', async () => {
|
|
1986
|
+
const stringTool = tools['stringTool'];
|
|
1987
|
+
const result = await stringTool.execute({
|
|
1988
|
+
context: {
|
|
1989
|
+
message: 'Hi', // Too short, min is 3
|
|
1990
|
+
},
|
|
1991
|
+
});
|
|
1992
|
+
|
|
1993
|
+
expect(result).toBeDefined();
|
|
1994
|
+
// Handle both client-side and server-side error formats
|
|
1995
|
+
if (result.error) {
|
|
1996
|
+
expect(result.error).toBe(true);
|
|
1997
|
+
expect(result.message).toContain('Tool validation failed');
|
|
1998
|
+
expect(result.message).toContain('String must contain at least 3 character(s)');
|
|
1999
|
+
} else {
|
|
2000
|
+
expect(result.isError).toBe(true);
|
|
2001
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
2002
|
+
expect(result.content[0].text).toContain('Message must be at least 3 characters');
|
|
2003
|
+
}
|
|
2004
|
+
});
|
|
2005
|
+
|
|
2006
|
+
it('should return validation error for invalid number range', async () => {
|
|
2007
|
+
const numberTool = tools['numberTool'];
|
|
2008
|
+
const result = await numberTool.execute({
|
|
2009
|
+
context: {
|
|
2010
|
+
age: -5, // Negative age not allowed
|
|
2011
|
+
},
|
|
2012
|
+
});
|
|
2013
|
+
|
|
2014
|
+
expect(result).toBeDefined();
|
|
2015
|
+
// Handle both client-side and server-side error formats
|
|
2016
|
+
if (result.error) {
|
|
2017
|
+
expect(result.error).toBe(true);
|
|
2018
|
+
expect(result.message).toContain('Tool validation failed');
|
|
2019
|
+
} else {
|
|
2020
|
+
expect(result.isError).toBe(true);
|
|
2021
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
|
|
2025
|
+
it('should return validation error for invalid email format', async () => {
|
|
2026
|
+
const complexTool = tools['complexTool'];
|
|
2027
|
+
const result = await complexTool.execute({
|
|
2028
|
+
context: {
|
|
2029
|
+
email: 'not-an-email',
|
|
2030
|
+
tags: ['tag1'],
|
|
2031
|
+
metadata: {
|
|
2032
|
+
priority: 'medium',
|
|
2033
|
+
},
|
|
2034
|
+
},
|
|
2035
|
+
});
|
|
2036
|
+
|
|
2037
|
+
expect(result).toBeDefined();
|
|
2038
|
+
expect(result.isError).toBe(true);
|
|
2039
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
2040
|
+
expect(result.content[0].text).toContain('Invalid email format');
|
|
2041
|
+
});
|
|
2042
|
+
|
|
2043
|
+
it('should return validation error for empty array when minimum required', async () => {
|
|
2044
|
+
const complexTool = tools['complexTool'];
|
|
2045
|
+
const result = await complexTool.execute({
|
|
2046
|
+
context: {
|
|
2047
|
+
email: 'test@example.com',
|
|
2048
|
+
tags: [], // Empty array, min 1 required
|
|
2049
|
+
metadata: {
|
|
2050
|
+
priority: 'low',
|
|
2051
|
+
},
|
|
2052
|
+
},
|
|
2053
|
+
});
|
|
2054
|
+
|
|
2055
|
+
expect(result).toBeDefined();
|
|
2056
|
+
// Handle both client-side and server-side error formats
|
|
2057
|
+
if (result.error) {
|
|
2058
|
+
expect(result.error).toBe(true);
|
|
2059
|
+
expect(result.message).toContain('Tool validation failed');
|
|
2060
|
+
expect(result.message).toContain('Array must contain at least 1 element(s)');
|
|
2061
|
+
} else {
|
|
2062
|
+
expect(result.isError).toBe(true);
|
|
2063
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
2064
|
+
expect(result.content[0].text).toContain('Array must contain at least 1 element(s)');
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
|
|
2068
|
+
it('should return validation error for invalid enum value', async () => {
|
|
2069
|
+
const complexTool = tools['complexTool'];
|
|
2070
|
+
const result = await complexTool.execute({
|
|
2071
|
+
context: {
|
|
2072
|
+
email: 'test@example.com',
|
|
2073
|
+
tags: ['tag1'],
|
|
2074
|
+
metadata: {
|
|
2075
|
+
priority: 'urgent', // Not in enum ['low', 'medium', 'high']
|
|
2076
|
+
},
|
|
2077
|
+
},
|
|
2078
|
+
});
|
|
2079
|
+
|
|
2080
|
+
expect(result).toBeDefined();
|
|
2081
|
+
// Handle both client-side and server-side error formats
|
|
2082
|
+
if (result.error) {
|
|
2083
|
+
expect(result.error).toBe(true);
|
|
2084
|
+
expect(result.message).toContain('Tool validation failed');
|
|
2085
|
+
} else {
|
|
2086
|
+
expect(result.isError).toBe(true);
|
|
2087
|
+
expect(result.content[0].text).toContain('Tool validation failed');
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
|
|
2091
|
+
it('should handle multiple validation errors', async () => {
|
|
2092
|
+
const complexTool = tools['complexTool'];
|
|
2093
|
+
const result = await complexTool.execute({
|
|
2094
|
+
context: {
|
|
2095
|
+
email: 'invalid-email',
|
|
2096
|
+
tags: [],
|
|
2097
|
+
metadata: {
|
|
2098
|
+
priority: 'invalid',
|
|
2099
|
+
},
|
|
2100
|
+
},
|
|
2101
|
+
});
|
|
2102
|
+
|
|
2103
|
+
expect(result).toBeDefined();
|
|
2104
|
+
// Handle both client-side and server-side error formats
|
|
2105
|
+
if (result.error) {
|
|
2106
|
+
expect(result.error).toBe(true);
|
|
2107
|
+
const errorText = result.message;
|
|
2108
|
+
expect(errorText).toContain('Tool validation failed');
|
|
2109
|
+
// Should contain multiple validation errors
|
|
2110
|
+
// Note: Some validations might not trigger when there are other errors
|
|
2111
|
+
expect(errorText).toContain('- tags: Array must contain at least 1 element(s)');
|
|
2112
|
+
expect(errorText).toContain('Provided arguments:');
|
|
2113
|
+
} else {
|
|
2114
|
+
expect(result.isError).toBe(true);
|
|
2115
|
+
const errorText = result.content[0].text;
|
|
2116
|
+
expect(errorText).toContain('Tool validation failed');
|
|
2117
|
+
// Should contain multiple validation errors
|
|
2118
|
+
// Note: Some validations might not trigger when there are other errors
|
|
2119
|
+
expect(errorText).toContain('- tags: Array must contain at least 1 element(s)');
|
|
2120
|
+
expect(errorText).toContain('Provided arguments:');
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
|
|
2124
|
+
it('should work with executeTool method directly', async () => {
|
|
2125
|
+
// Test valid input
|
|
2126
|
+
const validResult = await validationServer.executeTool('stringTool', {
|
|
2127
|
+
message: 'Valid message',
|
|
2128
|
+
});
|
|
2129
|
+
// executeTool returns result directly, not in MCP format
|
|
2130
|
+
expect(validResult.result).toBe('Received: Valid message');
|
|
2131
|
+
|
|
2132
|
+
// Test invalid input - should return validation error (not throw)
|
|
2133
|
+
const invalidResult = await validationServer.executeTool('stringTool', {
|
|
2134
|
+
message: 'No', // Too short
|
|
2135
|
+
});
|
|
2136
|
+
|
|
2137
|
+
// executeTool returns client-side validation format
|
|
2138
|
+
expect(invalidResult.error).toBe(true);
|
|
2139
|
+
expect(invalidResult.message).toContain('Tool validation failed');
|
|
2140
|
+
expect(invalidResult.message).toContain('Message must be at least 3 characters');
|
|
2141
|
+
});
|
|
2142
|
+
});
|
package/src/server/server.ts
CHANGED
|
@@ -248,9 +248,25 @@ export class MCPServer extends MCPServerBase {
|
|
|
248
248
|
this.logger.warn(`CallTool: Invalid tool arguments for '${request.params.name}'`, {
|
|
249
249
|
errors: validation.error,
|
|
250
250
|
});
|
|
251
|
+
|
|
252
|
+
// Format validation errors for agent understanding
|
|
253
|
+
let errorMessages = 'Validation failed';
|
|
254
|
+
if ('errors' in validation.error && Array.isArray(validation.error.errors)) {
|
|
255
|
+
errorMessages = validation.error.errors
|
|
256
|
+
.map((e: any) => `- ${e.path?.join('.') || 'root'}: ${e.message}`)
|
|
257
|
+
.join('\n');
|
|
258
|
+
} else if (validation.error instanceof Error) {
|
|
259
|
+
errorMessages = validation.error.message;
|
|
260
|
+
}
|
|
261
|
+
|
|
251
262
|
return {
|
|
252
|
-
content: [
|
|
253
|
-
|
|
263
|
+
content: [
|
|
264
|
+
{
|
|
265
|
+
type: 'text',
|
|
266
|
+
text: `Tool validation failed. Please fix the following errors and try again:\n${errorMessages}\n\nProvided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`,
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
isError: true, // Set to true so the LLM sees the error and can self-correct
|
|
254
270
|
};
|
|
255
271
|
}
|
|
256
272
|
if (!tool.execute) {
|
|
@@ -268,7 +284,7 @@ export class MCPServer extends MCPServerBase {
|
|
|
268
284
|
},
|
|
269
285
|
};
|
|
270
286
|
|
|
271
|
-
const result = await tool.execute(validation?.value, {
|
|
287
|
+
const result = await tool.execute(validation?.value ?? request.params.arguments ?? {}, {
|
|
272
288
|
messages: [],
|
|
273
289
|
toolCallId: '',
|
|
274
290
|
elicitation: sessionElicitation,
|
|
@@ -1356,12 +1372,17 @@ export class MCPServer extends MCPServerBase {
|
|
|
1356
1372
|
const validation = tool.parameters.safeParse(args ?? {});
|
|
1357
1373
|
if (!validation.success) {
|
|
1358
1374
|
const errorMessages = validation.error.errors
|
|
1359
|
-
.map((e: z.ZodIssue) =>
|
|
1360
|
-
.join('
|
|
1375
|
+
.map((e: z.ZodIssue) => `- ${e.path?.join('.') || 'root'}: ${e.message}`)
|
|
1376
|
+
.join('\n');
|
|
1361
1377
|
this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
|
|
1362
1378
|
errors: validation.error.format(),
|
|
1363
1379
|
});
|
|
1364
|
-
|
|
1380
|
+
// Return validation error as a result instead of throwing
|
|
1381
|
+
return {
|
|
1382
|
+
error: true,
|
|
1383
|
+
message: `Tool validation failed. Please fix the following errors and try again:\n${errorMessages}\n\nProvided arguments: ${JSON.stringify(args, null, 2)}`,
|
|
1384
|
+
validationErrors: validation.error.format(),
|
|
1385
|
+
};
|
|
1365
1386
|
}
|
|
1366
1387
|
validatedArgs = validation.data;
|
|
1367
1388
|
} else {
|