@mastra/mcp 0.5.0 → 0.5.1-alpha.0

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.
@@ -1,23 +1,23 @@
1
1
 
2
- > @mastra/mcp@0.5.0-alpha.6 build /home/runner/work/mastra/mastra/packages/mcp
2
+ > @mastra/mcp@0.5.1-alpha.0 build /home/runner/work/mastra/mastra/packages/mcp
3
3
  > tsup src/index.ts --format esm,cjs --experimental-dts --clean --treeshake=smallest --splitting
4
4
 
5
5
  CLI Building entry: src/index.ts
6
6
  CLI Using tsconfig: tsconfig.json
7
7
  CLI tsup v8.4.0
8
8
  TSC Build start
9
- TSC ⚡️ Build success in 17099ms
9
+ TSC ⚡️ Build success in 19696ms
10
10
  DTS Build start
11
11
  CLI Target: es2022
12
12
  Analysis will use the bundled TypeScript version 5.8.3
13
13
  Writing package typings: /home/runner/work/mastra/mastra/packages/mcp/dist/_tsup-dts-rollup.d.ts
14
14
  Analysis will use the bundled TypeScript version 5.8.3
15
15
  Writing package typings: /home/runner/work/mastra/mastra/packages/mcp/dist/_tsup-dts-rollup.d.cts
16
- DTS ⚡️ Build success in 12447ms
16
+ DTS ⚡️ Build success in 14884ms
17
17
  CLI Cleaning output folder
18
18
  ESM Build start
19
19
  CJS Build start
20
- CJS dist/index.cjs 32.75 KB
21
- CJS ⚡️ Build success in 1410ms
22
- ESM dist/index.js 32.45 KB
23
- ESM ⚡️ Build success in 1416ms
20
+ ESM dist/index.js 36.70 KB
21
+ ESM ⚡️ Build success in 1126ms
22
+ CJS dist/index.cjs 37.02 KB
23
+ CJS ⚡️ Build success in 1130ms
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @mastra/mcp
2
2
 
3
+ ## 0.5.1-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - 23f258c: Add new list and get routes for mcp servers. Changed route make-up for more consistency with existing API routes. Lastly, added in a lot of extra detail that can be optionally passed to the mcp server per the mcp spec.
8
+ - 2672a05: Add MCP servers and tool call execution to playground
9
+ - Updated dependencies [f53a6ac]
10
+ - Updated dependencies [eabdcd9]
11
+ - Updated dependencies [90be034]
12
+ - Updated dependencies [99f050a]
13
+ - Updated dependencies [d0ee3c6]
14
+ - Updated dependencies [23f258c]
15
+ - Updated dependencies [2672a05]
16
+ - @mastra/core@0.9.5-alpha.0
17
+
3
18
  ## 0.5.0
4
19
 
5
20
  ### Minor Changes
package/README.md CHANGED
@@ -330,12 +330,12 @@ console.log(Object.keys(resources)); // ['weather', 'dataService']
330
330
  if (resources.weather) {
331
331
  // Access resources from the weather server
332
332
  const weatherResources = resources.weather;
333
-
333
+
334
334
  // Each resource has uri, name, description, and mimeType
335
335
  weatherResources.forEach(resource => {
336
336
  console.log(`${resource.uri}: ${resource.name} (${resource.mimeType})`);
337
337
  });
338
-
338
+
339
339
  // Find a specific resource by URI
340
340
  const forecast = weatherResources.find(r => r.uri === 'weather://forecast');
341
341
  if (forecast) {
@@ -4,11 +4,14 @@ import type * as http from 'node:http';
4
4
  import { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { MastraBase } from '@mastra/core/base';
6
6
  import { MCPServerBase } from '@mastra/core/mcp';
7
+ import type { MCPServerConfig } from '@mastra/core/mcp';
7
8
  import type { MCPServerHonoSSEOptions } from '@mastra/core/mcp';
8
9
  import type { MCPServerSSEOptions } from '@mastra/core/mcp';
9
10
  import type { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';
10
11
  import type { RuntimeContext } from '@mastra/core/di';
11
12
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
13
+ import type { ServerDetailInfo } from '@mastra/core/mcp';
14
+ import type { ServerInfo } from '@mastra/core/mcp';
12
15
  import type { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
13
16
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
14
17
  import type { SSEStreamingApi } from 'hono/streaming';
@@ -218,15 +221,9 @@ declare class MCPServer extends MCPServerBase {
218
221
  getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined;
219
222
  /**
220
223
  * Construct a new MCPServer instance.
221
- * @param opts.name - Server name
222
- * @param opts.version - Server version
223
- * @param opts.tools - Tool definitions to register
224
+ * @param opts - Configuration options for the server, including registry metadata.
224
225
  */
225
- constructor({ name, version, tools }: {
226
- name: string;
227
- version: string;
228
- tools: ToolsInput;
229
- });
226
+ constructor(opts: MCPServerConfig);
230
227
  /**
231
228
  * Convert and validate all provided tools, logging registration status.
232
229
  * @param tools Tool definitions
@@ -295,6 +292,50 @@ declare class MCPServer extends MCPServerBase {
295
292
  * Close the MCP server and all its connections
296
293
  */
297
294
  close(): Promise<void>;
295
+ /**
296
+ * Gets the basic information about the server, conforming to the Server schema.
297
+ * @returns ServerInfo object.
298
+ */
299
+ getServerInfo(): ServerInfo;
300
+ /**
301
+ * Gets detailed information about the server, conforming to the ServerDetail schema.
302
+ * @returns ServerDetailInfo object.
303
+ */
304
+ getServerDetail(): ServerDetailInfo;
305
+ /**
306
+ * Gets a list of tools provided by this MCP server, including their schemas.
307
+ * This leverages the same tool information used by the internal ListTools MCP request.
308
+ * @returns An object containing an array of tool information.
309
+ */
310
+ getToolListInfo(): {
311
+ tools: Array<{
312
+ name: string;
313
+ description?: string;
314
+ inputSchema: any;
315
+ }>;
316
+ };
317
+ /**
318
+ * Gets information for a specific tool provided by this MCP server.
319
+ * @param toolId The ID/name of the tool to retrieve.
320
+ * @returns Tool information (name, description, inputSchema) or undefined if not found.
321
+ */
322
+ getToolInfo(toolId: string): {
323
+ name: string;
324
+ description?: string;
325
+ inputSchema: any;
326
+ } | undefined;
327
+ /**
328
+ * Executes a specific tool provided by this MCP server.
329
+ * @param toolId The ID/name of the tool to execute.
330
+ * @param args The arguments to pass to the tool's execute function.
331
+ * @param executionContext Optional context for the tool execution.
332
+ * @returns A promise that resolves to the result of the tool execution.
333
+ * @throws Error if the tool is not found, validation fails, or execution fails.
334
+ */
335
+ executeTool(toolId: string, args: any, executionContext?: {
336
+ messages?: any[];
337
+ toolCallId?: string;
338
+ }): Promise<any>;
298
339
  }
299
340
  export { MCPServer }
300
341
  export { MCPServer as MCPServer_alias_1 }
@@ -4,11 +4,14 @@ import type * as http from 'node:http';
4
4
  import { LoggingLevel } from '@modelcontextprotocol/sdk/types.js';
5
5
  import { MastraBase } from '@mastra/core/base';
6
6
  import { MCPServerBase } from '@mastra/core/mcp';
7
+ import type { MCPServerConfig } from '@mastra/core/mcp';
7
8
  import type { MCPServerHonoSSEOptions } from '@mastra/core/mcp';
8
9
  import type { MCPServerSSEOptions } from '@mastra/core/mcp';
9
10
  import type { Protocol } from '@modelcontextprotocol/sdk/shared/protocol.js';
10
11
  import type { RuntimeContext } from '@mastra/core/di';
11
12
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
13
+ import type { ServerDetailInfo } from '@mastra/core/mcp';
14
+ import type { ServerInfo } from '@mastra/core/mcp';
12
15
  import type { SSEClientTransportOptions } from '@modelcontextprotocol/sdk/client/sse.js';
13
16
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
14
17
  import type { SSEStreamingApi } from 'hono/streaming';
@@ -218,15 +221,9 @@ declare class MCPServer extends MCPServerBase {
218
221
  getStreamableHTTPTransport(): StreamableHTTPServerTransport | undefined;
219
222
  /**
220
223
  * Construct a new MCPServer instance.
221
- * @param opts.name - Server name
222
- * @param opts.version - Server version
223
- * @param opts.tools - Tool definitions to register
224
+ * @param opts - Configuration options for the server, including registry metadata.
224
225
  */
225
- constructor({ name, version, tools }: {
226
- name: string;
227
- version: string;
228
- tools: ToolsInput;
229
- });
226
+ constructor(opts: MCPServerConfig);
230
227
  /**
231
228
  * Convert and validate all provided tools, logging registration status.
232
229
  * @param tools Tool definitions
@@ -295,6 +292,50 @@ declare class MCPServer extends MCPServerBase {
295
292
  * Close the MCP server and all its connections
296
293
  */
297
294
  close(): Promise<void>;
295
+ /**
296
+ * Gets the basic information about the server, conforming to the Server schema.
297
+ * @returns ServerInfo object.
298
+ */
299
+ getServerInfo(): ServerInfo;
300
+ /**
301
+ * Gets detailed information about the server, conforming to the ServerDetail schema.
302
+ * @returns ServerDetailInfo object.
303
+ */
304
+ getServerDetail(): ServerDetailInfo;
305
+ /**
306
+ * Gets a list of tools provided by this MCP server, including their schemas.
307
+ * This leverages the same tool information used by the internal ListTools MCP request.
308
+ * @returns An object containing an array of tool information.
309
+ */
310
+ getToolListInfo(): {
311
+ tools: Array<{
312
+ name: string;
313
+ description?: string;
314
+ inputSchema: any;
315
+ }>;
316
+ };
317
+ /**
318
+ * Gets information for a specific tool provided by this MCP server.
319
+ * @param toolId The ID/name of the tool to retrieve.
320
+ * @returns Tool information (name, description, inputSchema) or undefined if not found.
321
+ */
322
+ getToolInfo(toolId: string): {
323
+ name: string;
324
+ description?: string;
325
+ inputSchema: any;
326
+ } | undefined;
327
+ /**
328
+ * Executes a specific tool provided by this MCP server.
329
+ * @param toolId The ID/name of the tool to execute.
330
+ * @param args The arguments to pass to the tool's execute function.
331
+ * @param executionContext Optional context for the tool execution.
332
+ * @returns A promise that resolves to the result of the tool execution.
333
+ * @throws Error if the tool is not found, validation fails, or execution fails.
334
+ */
335
+ executeTool(toolId: string, args: any, executionContext?: {
336
+ messages?: any[];
337
+ toolCallId?: string;
338
+ }): Promise<any>;
298
339
  }
299
340
  export { MCPServer }
300
341
  export { MCPServer as MCPServer_alias_1 }
package/dist/index.cjs CHANGED
@@ -630,15 +630,16 @@ var MCPServer = class extends mcp.MCPServerBase {
630
630
  }
631
631
  /**
632
632
  * Construct a new MCPServer instance.
633
- * @param opts.name - Server name
634
- * @param opts.version - Server version
635
- * @param opts.tools - Tool definitions to register
633
+ * @param opts - Configuration options for the server, including registry metadata.
636
634
  */
637
- constructor({ name, version, tools }) {
638
- super({ name, version, tools });
639
- this.server = new index_js.Server({ name, version }, { capabilities: { tools: {}, logging: { enabled: true } } });
635
+ constructor(opts) {
636
+ super(opts);
637
+ this.server = new index_js.Server(
638
+ { name: this.name, version: this.version },
639
+ { capabilities: { tools: {}, logging: { enabled: true } } }
640
+ );
640
641
  this.logger.info(
641
- `Initialized MCPServer '${name}' v${version} with tools: ${Object.keys(this.convertedTools).join(", ")}`
642
+ `Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")}`
642
643
  );
643
644
  this.sseHonoTransports = /* @__PURE__ */ new Map();
644
645
  this.registerListToolsHandler();
@@ -965,6 +966,117 @@ var MCPServer = class extends mcp.MCPServerBase {
965
966
  this.logger.error("Error closing MCP server:", { error });
966
967
  }
967
968
  }
969
+ /**
970
+ * Gets the basic information about the server, conforming to the Server schema.
971
+ * @returns ServerInfo object.
972
+ */
973
+ getServerInfo() {
974
+ return {
975
+ id: this.id,
976
+ name: this.name,
977
+ description: this.description,
978
+ repository: this.repository,
979
+ version_detail: {
980
+ version: this.version,
981
+ release_date: this.releaseDate,
982
+ is_latest: this.isLatest
983
+ }
984
+ };
985
+ }
986
+ /**
987
+ * Gets detailed information about the server, conforming to the ServerDetail schema.
988
+ * @returns ServerDetailInfo object.
989
+ */
990
+ getServerDetail() {
991
+ return {
992
+ ...this.getServerInfo(),
993
+ package_canonical: this.packageCanonical,
994
+ packages: this.packages,
995
+ remotes: this.remotes
996
+ };
997
+ }
998
+ /**
999
+ * Gets a list of tools provided by this MCP server, including their schemas.
1000
+ * This leverages the same tool information used by the internal ListTools MCP request.
1001
+ * @returns An object containing an array of tool information.
1002
+ */
1003
+ getToolListInfo() {
1004
+ this.logger.debug(`Getting tool list information for MCPServer '${this.name}'`);
1005
+ return {
1006
+ tools: Object.entries(this.convertedTools).map(([toolId, tool]) => ({
1007
+ id: toolId,
1008
+ name: tool.name,
1009
+ description: tool.description,
1010
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters
1011
+ }))
1012
+ };
1013
+ }
1014
+ /**
1015
+ * Gets information for a specific tool provided by this MCP server.
1016
+ * @param toolId The ID/name of the tool to retrieve.
1017
+ * @returns Tool information (name, description, inputSchema) or undefined if not found.
1018
+ */
1019
+ getToolInfo(toolId) {
1020
+ const tool = this.convertedTools[toolId];
1021
+ if (!tool) {
1022
+ this.logger.debug(`Tool '${toolId}' not found on MCPServer '${this.name}'`);
1023
+ return void 0;
1024
+ }
1025
+ this.logger.debug(`Getting info for tool '${toolId}' on MCPServer '${this.name}'`);
1026
+ return {
1027
+ name: tool.name,
1028
+ description: tool.description,
1029
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters
1030
+ };
1031
+ }
1032
+ /**
1033
+ * Executes a specific tool provided by this MCP server.
1034
+ * @param toolId The ID/name of the tool to execute.
1035
+ * @param args The arguments to pass to the tool's execute function.
1036
+ * @param executionContext Optional context for the tool execution.
1037
+ * @returns A promise that resolves to the result of the tool execution.
1038
+ * @throws Error if the tool is not found, validation fails, or execution fails.
1039
+ */
1040
+ async executeTool(toolId, args, executionContext) {
1041
+ const tool = this.convertedTools[toolId];
1042
+ if (!tool) {
1043
+ this.logger.warn(`ExecuteTool: Unknown tool '${toolId}' requested on MCPServer '${this.name}'.`);
1044
+ throw new Error(`Unknown tool: ${toolId}`);
1045
+ }
1046
+ this.logger.debug(`ExecuteTool: Invoking '${toolId}' with arguments:`, args);
1047
+ let validatedArgs = args;
1048
+ if (tool.parameters instanceof zod.z.ZodType && typeof tool.parameters.safeParse === "function") {
1049
+ const validation = tool.parameters.safeParse(args ?? {});
1050
+ if (!validation.success) {
1051
+ const errorMessages = validation.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
1052
+ this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
1053
+ errors: validation.error.format()
1054
+ });
1055
+ throw new zod.z.ZodError(validation.error.issues);
1056
+ }
1057
+ validatedArgs = validation.data;
1058
+ } else {
1059
+ this.logger.debug(
1060
+ `ExecuteTool: Tool '${toolId}' parameters is not a Zod schema with safeParse or is undefined. Skipping validation.`
1061
+ );
1062
+ }
1063
+ if (!tool.execute) {
1064
+ this.logger.error(`ExecuteTool: Tool '${toolId}' does not have an execute function.`);
1065
+ throw new Error(`Tool '${toolId}' cannot be executed.`);
1066
+ }
1067
+ try {
1068
+ const finalExecutionContext = {
1069
+ messages: executionContext?.messages || [],
1070
+ toolCallId: executionContext?.toolCallId || crypto$1.randomUUID()
1071
+ };
1072
+ const result = await tool.execute(validatedArgs, finalExecutionContext);
1073
+ this.logger.info(`ExecuteTool: Tool '${toolId}' executed successfully.`);
1074
+ return result;
1075
+ } catch (error) {
1076
+ this.logger.error(`ExecuteTool: Tool execution failed for '${toolId}':`, { error });
1077
+ throw error instanceof Error ? error : new Error(`Execution of tool '${toolId}' failed: ${String(error)}`);
1078
+ }
1079
+ }
968
1080
  };
969
1081
 
970
1082
  exports.MCPClient = MCPClient;
package/dist/index.js CHANGED
@@ -624,15 +624,16 @@ var MCPServer = class extends MCPServerBase {
624
624
  }
625
625
  /**
626
626
  * Construct a new MCPServer instance.
627
- * @param opts.name - Server name
628
- * @param opts.version - Server version
629
- * @param opts.tools - Tool definitions to register
627
+ * @param opts - Configuration options for the server, including registry metadata.
630
628
  */
631
- constructor({ name, version, tools }) {
632
- super({ name, version, tools });
633
- this.server = new Server({ name, version }, { capabilities: { tools: {}, logging: { enabled: true } } });
629
+ constructor(opts) {
630
+ super(opts);
631
+ this.server = new Server(
632
+ { name: this.name, version: this.version },
633
+ { capabilities: { tools: {}, logging: { enabled: true } } }
634
+ );
634
635
  this.logger.info(
635
- `Initialized MCPServer '${name}' v${version} with tools: ${Object.keys(this.convertedTools).join(", ")}`
636
+ `Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(", ")}`
636
637
  );
637
638
  this.sseHonoTransports = /* @__PURE__ */ new Map();
638
639
  this.registerListToolsHandler();
@@ -959,6 +960,117 @@ var MCPServer = class extends MCPServerBase {
959
960
  this.logger.error("Error closing MCP server:", { error });
960
961
  }
961
962
  }
963
+ /**
964
+ * Gets the basic information about the server, conforming to the Server schema.
965
+ * @returns ServerInfo object.
966
+ */
967
+ getServerInfo() {
968
+ return {
969
+ id: this.id,
970
+ name: this.name,
971
+ description: this.description,
972
+ repository: this.repository,
973
+ version_detail: {
974
+ version: this.version,
975
+ release_date: this.releaseDate,
976
+ is_latest: this.isLatest
977
+ }
978
+ };
979
+ }
980
+ /**
981
+ * Gets detailed information about the server, conforming to the ServerDetail schema.
982
+ * @returns ServerDetailInfo object.
983
+ */
984
+ getServerDetail() {
985
+ return {
986
+ ...this.getServerInfo(),
987
+ package_canonical: this.packageCanonical,
988
+ packages: this.packages,
989
+ remotes: this.remotes
990
+ };
991
+ }
992
+ /**
993
+ * Gets a list of tools provided by this MCP server, including their schemas.
994
+ * This leverages the same tool information used by the internal ListTools MCP request.
995
+ * @returns An object containing an array of tool information.
996
+ */
997
+ getToolListInfo() {
998
+ this.logger.debug(`Getting tool list information for MCPServer '${this.name}'`);
999
+ return {
1000
+ tools: Object.entries(this.convertedTools).map(([toolId, tool]) => ({
1001
+ id: toolId,
1002
+ name: tool.name,
1003
+ description: tool.description,
1004
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters
1005
+ }))
1006
+ };
1007
+ }
1008
+ /**
1009
+ * Gets information for a specific tool provided by this MCP server.
1010
+ * @param toolId The ID/name of the tool to retrieve.
1011
+ * @returns Tool information (name, description, inputSchema) or undefined if not found.
1012
+ */
1013
+ getToolInfo(toolId) {
1014
+ const tool = this.convertedTools[toolId];
1015
+ if (!tool) {
1016
+ this.logger.debug(`Tool '${toolId}' not found on MCPServer '${this.name}'`);
1017
+ return void 0;
1018
+ }
1019
+ this.logger.debug(`Getting info for tool '${toolId}' on MCPServer '${this.name}'`);
1020
+ return {
1021
+ name: tool.name,
1022
+ description: tool.description,
1023
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters
1024
+ };
1025
+ }
1026
+ /**
1027
+ * Executes a specific tool provided by this MCP server.
1028
+ * @param toolId The ID/name of the tool to execute.
1029
+ * @param args The arguments to pass to the tool's execute function.
1030
+ * @param executionContext Optional context for the tool execution.
1031
+ * @returns A promise that resolves to the result of the tool execution.
1032
+ * @throws Error if the tool is not found, validation fails, or execution fails.
1033
+ */
1034
+ async executeTool(toolId, args, executionContext) {
1035
+ const tool = this.convertedTools[toolId];
1036
+ if (!tool) {
1037
+ this.logger.warn(`ExecuteTool: Unknown tool '${toolId}' requested on MCPServer '${this.name}'.`);
1038
+ throw new Error(`Unknown tool: ${toolId}`);
1039
+ }
1040
+ this.logger.debug(`ExecuteTool: Invoking '${toolId}' with arguments:`, args);
1041
+ let validatedArgs = args;
1042
+ if (tool.parameters instanceof z.ZodType && typeof tool.parameters.safeParse === "function") {
1043
+ const validation = tool.parameters.safeParse(args ?? {});
1044
+ if (!validation.success) {
1045
+ const errorMessages = validation.error.errors.map((e) => `${e.path.join(".")}: ${e.message}`).join(", ");
1046
+ this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
1047
+ errors: validation.error.format()
1048
+ });
1049
+ throw new z.ZodError(validation.error.issues);
1050
+ }
1051
+ validatedArgs = validation.data;
1052
+ } else {
1053
+ this.logger.debug(
1054
+ `ExecuteTool: Tool '${toolId}' parameters is not a Zod schema with safeParse or is undefined. Skipping validation.`
1055
+ );
1056
+ }
1057
+ if (!tool.execute) {
1058
+ this.logger.error(`ExecuteTool: Tool '${toolId}' does not have an execute function.`);
1059
+ throw new Error(`Tool '${toolId}' cannot be executed.`);
1060
+ }
1061
+ try {
1062
+ const finalExecutionContext = {
1063
+ messages: executionContext?.messages || [],
1064
+ toolCallId: executionContext?.toolCallId || randomUUID()
1065
+ };
1066
+ const result = await tool.execute(validatedArgs, finalExecutionContext);
1067
+ this.logger.info(`ExecuteTool: Tool '${toolId}' executed successfully.`);
1068
+ return result;
1069
+ } catch (error) {
1070
+ this.logger.error(`ExecuteTool: Tool execution failed for '${toolId}':`, { error });
1071
+ throw error instanceof Error ? error : new Error(`Execution of tool '${toolId}' failed: ${String(error)}`);
1072
+ }
1073
+ }
962
1074
  };
963
1075
 
964
1076
  export { MCPClient, MCPConfiguration, MCPServer, MastraMCPClient };
@@ -3,6 +3,7 @@ import { createServer } from 'node:http';
3
3
  import path from 'path';
4
4
  import { MCPClient } from '@mastra/mcp';
5
5
  import { describe, it, expect, beforeAll, afterAll, vi } from 'vitest';
6
+ import { ServerInfo } from '@mastra/core/mcp';
6
7
 
7
8
  vi.setConfig({ testTimeout: 20000, hookTimeout: 20000 });
8
9
 
@@ -62,7 +63,7 @@ describe('MCPServer through Mastra HTTP Integration (Subprocess)', () => {
62
63
  client = new MCPClient({
63
64
  servers: {
64
65
  [mcpServerId]: {
65
- url: new URL(`http://localhost:${port}/api/servers/${mcpServerId}/mcp`),
66
+ url: new URL(`http://localhost:${port}/api/mcp/${mcpServerId}/mcp`),
66
67
  },
67
68
  },
68
69
  });
@@ -112,7 +113,7 @@ describe('MCPServer through Mastra HTTP Integration (Subprocess)', () => {
112
113
  }, 25000);
113
114
 
114
115
  it('should allow a client to call a tool via Mastra MCP SSE endpoints (Subprocess)', async () => {
115
- const sseUrl = new URL(`http://localhost:${port}/api/servers/${mcpServerId}/sse`);
116
+ const sseUrl = new URL(`http://localhost:${port}/api/mcp/${mcpServerId}/sse`);
116
117
 
117
118
  // Configure MCPClient for SSE transport
118
119
  const sseClient = new MCPClient({
@@ -146,4 +147,98 @@ describe('MCPServer through Mastra HTTP Integration (Subprocess)', () => {
146
147
  const expectedToolResult = 15; // 10 + 5
147
148
  expect(JSON.parse(toolOutput.text)).toEqual(expectedToolResult);
148
149
  }, 25000);
150
+
151
+ // --- New tests for MCP Registry API Style Routes ---
152
+ describe('MCP Registry API Style Endpoints', () => {
153
+ const defaultMcpServerLogicalId = 'myMcpServer'; // Assuming this is the ID of the default server
154
+
155
+ it('GET /api/mcp/v0/servers - should list available MCP servers', async () => {
156
+ const response = await fetch(`http://localhost:${port}/api/mcp/v0/servers`);
157
+ expect(response.status).toBe(200);
158
+ expect(response.headers.get('content-type')).toContain('application/json');
159
+ const body = await response.json();
160
+
161
+ expect(body).toHaveProperty('servers');
162
+ expect(body).toHaveProperty('total_count');
163
+ expect(Array.isArray(body.servers)).toBe(true);
164
+ expect(body.total_count).toBeGreaterThanOrEqual(1); // Expect at least the default server
165
+
166
+ const defaultServerInfo: ServerInfo = body.servers.find((s: ServerInfo) => s.id === defaultMcpServerLogicalId);
167
+ expect(defaultServerInfo).toBeDefined();
168
+ expect(defaultServerInfo).toHaveProperty('name');
169
+ expect(defaultServerInfo).toHaveProperty('version_detail');
170
+ // Based on default mastra dev setup, if myMcpServer is the key, its id becomes 'myMcpServer'
171
+ // And its name might be something like 'my-mcp-server' if not explicitly set in MCPServerConfig for it.
172
+ // For this test, we assume the `id` is the key used in Mastra config.
173
+ expect(defaultServerInfo.id).toBe(defaultMcpServerLogicalId);
174
+ });
175
+
176
+ it('GET /api/mcp/v0/servers/:id - should get specific server details', async () => {
177
+ // First, get all servers to find the actual version of the default server
178
+ const listResponse = await fetch(`http://localhost:${port}/api/mcp/v0/servers`);
179
+ const listBody = await listResponse.json();
180
+ const defaultServer = listBody.servers.find((s: any) => s.id === defaultMcpServerLogicalId);
181
+ expect(defaultServer).toBeDefined();
182
+ const actualVersion = defaultServer.version_detail.version;
183
+
184
+ const response = await fetch(`http://localhost:${port}/api/mcp/v0/servers/${defaultMcpServerLogicalId}`);
185
+ expect(response.status).toBe(200);
186
+ const body = await response.json();
187
+
188
+ expect(body.id).toBe(defaultMcpServerLogicalId);
189
+ expect(body).toHaveProperty('name');
190
+ expect(body).toHaveProperty('version_detail');
191
+ expect(body.version_detail.version).toBe(actualVersion);
192
+ // Add more assertions for package_canonical, packages, remotes if they are expected for the default server
193
+ });
194
+
195
+ it('GET /api/mcp/v0/servers/:id - should get specific server version if it matches', async () => {
196
+ const listResponse = await fetch(`http://localhost:${port}/api/mcp/v0/servers`);
197
+ const listBody = await listResponse.json();
198
+ const defaultServer = listBody.servers.find((s: any) => s.id === defaultMcpServerLogicalId);
199
+ expect(defaultServer).toBeDefined();
200
+ const actualVersion = defaultServer.version_detail.version;
201
+
202
+ const response = await fetch(
203
+ `http://localhost:${port}/api/mcp/v0/servers/${defaultMcpServerLogicalId}?version=${actualVersion}`,
204
+ );
205
+ expect(response.status).toBe(200);
206
+ const body = await response.json();
207
+ expect(body.id).toBe(defaultMcpServerLogicalId);
208
+ expect(body.version_detail.version).toBe(actualVersion);
209
+ });
210
+
211
+ it('GET /api/mcp/v0/servers/:id - should return 404 if specific server version does not match', async () => {
212
+ const nonExistentVersion = '0.0.0-nonexistent';
213
+ const response = await fetch(
214
+ `http://localhost:${port}/api/mcp/v0/servers/${defaultMcpServerLogicalId}?version=${nonExistentVersion}`,
215
+ );
216
+ expect(response.status).toBe(404);
217
+ const body = await response.json();
218
+ expect(body).toHaveProperty('error');
219
+ expect(body.error).toContain(`but not version '${nonExistentVersion}'`);
220
+ });
221
+
222
+ it('GET /api/mcp/v0/servers/:id - should return 404 for a non-existent server ID', async () => {
223
+ const nonExistentId = 'non-existent-server-id-12345';
224
+ const response = await fetch(`http://localhost:${port}/api/mcp/v0/servers/${nonExistentId}`);
225
+ expect(response.status).toBe(404);
226
+ const body = await response.json();
227
+ expect(body).toHaveProperty('error');
228
+ expect(body.error).toContain(`MCP server with ID '${nonExistentId}' not found`);
229
+ });
230
+
231
+ it('GET /api/mcp/v0/servers - should handle pagination (limit=1, offset=0)', async () => {
232
+ const response = await fetch(`http://localhost:${port}/api/mcp/v0/servers?limit=1&offset=0`);
233
+ expect(response.status).toBe(200);
234
+ const body = await response.json();
235
+ expect(body.servers.length).toBe(1);
236
+ expect(body.total_count).toBeGreaterThanOrEqual(1);
237
+ if (body.total_count > 1) {
238
+ expect(body.next).not.toBeNull();
239
+ } else {
240
+ expect(body.next).toBeNull();
241
+ }
242
+ });
243
+ });
149
244
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mastra/mcp",
3
- "version": "0.5.0",
3
+ "version": "0.5.1-alpha.0",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -29,7 +29,7 @@
29
29
  "hono": "^4.7.4",
30
30
  "uuid": "^11.1.0",
31
31
  "zod-from-json-schema": "^0.0.5",
32
- "@mastra/core": "^0.9.4"
32
+ "@mastra/core": "^0.9.5-alpha.0"
33
33
  },
34
34
  "peerDependencies": {
35
35
  "zod": "^3.0.0"
@@ -2,11 +2,11 @@ import type { IncomingMessage, ServerResponse } from 'http';
2
2
  import { createServer } from 'http';
3
3
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
4
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
5
- import {
6
- CallToolRequestSchema,
7
- ListToolsRequestSchema,
5
+ import {
6
+ CallToolRequestSchema,
7
+ ListToolsRequestSchema,
8
8
  ListResourcesRequestSchema,
9
- ReadResourceRequestSchema
9
+ ReadResourceRequestSchema,
10
10
  } from '@modelcontextprotocol/sdk/types.js';
11
11
  import { z } from 'zod';
12
12
  import { zodToJsonSchema } from 'zod-to-json-schema';
@@ -32,7 +32,7 @@ const server = new Server(
32
32
  {
33
33
  capabilities: {
34
34
  tools: {},
35
- resources: {}
35
+ resources: {},
36
36
  },
37
37
  },
38
38
  );
@@ -98,31 +98,31 @@ const weatherResources = [
98
98
  uri: 'weather://current',
99
99
  name: 'Current Weather Data',
100
100
  description: 'Real-time weather data for the current location',
101
- mimeType: 'application/json'
101
+ mimeType: 'application/json',
102
102
  },
103
103
  {
104
104
  uri: 'weather://forecast',
105
105
  name: 'Weather Forecast',
106
106
  description: '5-day weather forecast',
107
- mimeType: 'application/json'
107
+ mimeType: 'application/json',
108
108
  },
109
109
  {
110
110
  uri: 'weather://historical',
111
111
  name: 'Historical Weather Data',
112
112
  description: 'Weather data from the past 30 days',
113
- mimeType: 'application/json'
114
- }
113
+ mimeType: 'application/json',
114
+ },
115
115
  ];
116
116
 
117
117
  // List available resources
118
118
  server.setRequestHandler(ListResourcesRequestSchema, async () => ({
119
- resources: weatherResources
119
+ resources: weatherResources,
120
120
  }));
121
121
 
122
122
  // Read resource contents
123
- server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
123
+ server.setRequestHandler(ReadResourceRequestSchema, async request => {
124
124
  const uri = request.params.uri;
125
-
125
+
126
126
  if (uri === 'weather://current') {
127
127
  return {
128
128
  contents: [
@@ -135,10 +135,10 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
135
135
  conditions: 'Partly Cloudy',
136
136
  humidity: 65,
137
137
  windSpeed: 12,
138
- updated: new Date().toISOString()
139
- })
140
- }
141
- ]
138
+ updated: new Date().toISOString(),
139
+ }),
140
+ },
141
+ ],
142
142
  };
143
143
  } else if (uri === 'weather://forecast') {
144
144
  return {
@@ -151,10 +151,10 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
151
151
  { day: 2, high: 22, low: 14, conditions: 'Clear' },
152
152
  { day: 3, high: 20, low: 13, conditions: 'Partly Cloudy' },
153
153
  { day: 4, high: 18, low: 11, conditions: 'Rain' },
154
- { day: 5, high: 17, low: 10, conditions: 'Showers' }
155
- ])
156
- }
157
- ]
154
+ { day: 5, high: 17, low: 10, conditions: 'Showers' },
155
+ ]),
156
+ },
157
+ ],
158
158
  };
159
159
  } else if (uri === 'weather://historical') {
160
160
  return {
@@ -168,13 +168,13 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
168
168
  rainDays: 8,
169
169
  sunnyDays: 18,
170
170
  recordHigh: 28,
171
- recordLow: 7
172
- })
173
- }
174
- ]
171
+ recordLow: 7,
172
+ }),
173
+ },
174
+ ],
175
175
  };
176
176
  }
177
-
177
+
178
178
  throw new Error(`Resource not found: ${uri}`);
179
179
  });
180
180
 
@@ -2,9 +2,12 @@ import http from 'node:http';
2
2
  import path from 'path';
3
3
  import type { ServerType } from '@hono/node-server';
4
4
  import { serve } from '@hono/node-server';
5
+ import type { ToolsInput } from '@mastra/core/agent';
6
+ import type { MCPServerConfig, Repository, PackageInfo, RemoteInfo } from '@mastra/core/mcp';
5
7
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6
8
  import { Hono } from 'hono';
7
- import { describe, it, expect, beforeAll, afterAll, afterEach, vi } from 'vitest';
9
+ import { describe, it, expect, beforeAll, afterAll, afterEach, vi, beforeEach } from 'vitest';
10
+ import { z } from 'zod';
8
11
  import { weatherTool } from './__fixtures__/tools';
9
12
  import { MCPClient } from './configuration';
10
13
  import { MCPServer } from './server';
@@ -15,7 +18,197 @@ let httpServer: http.Server;
15
18
 
16
19
  vi.setConfig({ testTimeout: 20000, hookTimeout: 20000 });
17
20
 
21
+ // Mock Date constructor for predictable release dates
22
+ const mockDateISO = '2024-01-01T00:00:00.000Z';
23
+ const mockDate = new Date(mockDateISO);
24
+ const OriginalDate = global.Date; // Store original Date
25
+
26
+ // Mock a simple tool
27
+ const mockToolExecute = vi.fn(async (args: any) => ({ result: 'tool executed', args }));
28
+ const mockTools: ToolsInput = {
29
+ testTool: {
30
+ description: 'A test tool',
31
+ parameters: z.object({ input: z.string().optional() }),
32
+ execute: mockToolExecute,
33
+ },
34
+ };
35
+
36
+ const minimalConfig: MCPServerConfig = {
37
+ name: 'TestServer',
38
+ version: '1.0.0',
39
+ tools: mockTools,
40
+ };
41
+
18
42
  describe('MCPServer', () => {
43
+ beforeEach(() => {
44
+ vi.clearAllMocks();
45
+
46
+ // @ts-ignore - Mocking Date completely
47
+ global.Date = vi.fn((...args: any[]) => {
48
+ if (args.length === 0) {
49
+ // new Date()
50
+ return mockDate;
51
+ }
52
+ // @ts-ignore
53
+ return new OriginalDate(...args); // new Date('some-string') or new Date(timestamp)
54
+ }) as any;
55
+
56
+ // @ts-ignore
57
+ global.Date.now = vi.fn(() => mockDate.getTime());
58
+ // @ts-ignore
59
+ global.Date.prototype.toISOString = vi.fn(() => mockDateISO);
60
+ // @ts-ignore // Static Date.toISOString() might be used by some libraries
61
+ global.Date.toISOString = vi.fn(() => mockDateISO);
62
+ });
63
+
64
+ describe('Constructor and Metadata Initialization', () => {
65
+ it('should initialize with default metadata if not provided', () => {
66
+ const server = new MCPServer(minimalConfig);
67
+ expect(server.id).toBeDefined();
68
+ expect(server.name).toBe('TestServer');
69
+ expect(server.version).toBe('1.0.0');
70
+ expect(server.description).toBeUndefined();
71
+ expect(server.repository).toBeUndefined();
72
+ // MCPServerBase stores releaseDate as string, compare directly or re-parse
73
+ expect(server.releaseDate).toBe(mockDateISO);
74
+ expect(server.isLatest).toBe(true);
75
+ expect(server.packageCanonical).toBeUndefined();
76
+ expect(server.packages).toBeUndefined();
77
+ expect(server.remotes).toBeUndefined();
78
+ });
79
+
80
+ it('should initialize with custom metadata when provided', () => {
81
+ const repository: Repository = { url: 'https://github.com/test/repo', source: 'github', id: 'repo-id' };
82
+ const packages: PackageInfo[] = [{ registry_name: 'npm', name: 'test-package', version: '1.0.0' }];
83
+ const remotes: RemoteInfo[] = [{ transport_type: 'sse', url: 'https://test.com/sse' }];
84
+ const customReleaseDate = '2023-12-31T00:00:00.000Z';
85
+ const customConfig: MCPServerConfig = {
86
+ ...minimalConfig,
87
+ id: 'custom-id-doesnt-need-uuid-format-if-set-explicitly',
88
+ description: 'A custom server description',
89
+ repository,
90
+ releaseDate: customReleaseDate,
91
+ isLatest: false,
92
+ packageCanonical: 'npm',
93
+ packages,
94
+ remotes,
95
+ };
96
+ const server = new MCPServer(customConfig);
97
+
98
+ expect(server.id).toBe('custom-id-doesnt-need-uuid-format-if-set-explicitly');
99
+ expect(server.description).toBe('A custom server description');
100
+ expect(server.repository).toEqual(repository);
101
+ expect(server.releaseDate).toBe(customReleaseDate);
102
+ expect(server.isLatest).toBe(false);
103
+ expect(server.packageCanonical).toBe('npm');
104
+ expect(server.packages).toEqual(packages);
105
+ expect(server.remotes).toEqual(remotes);
106
+ });
107
+ });
108
+
109
+ describe('getServerInfo()', () => {
110
+ it('should return correct ServerInfo with default metadata', () => {
111
+ const server = new MCPServer(minimalConfig);
112
+ const serverInfo = server.getServerInfo();
113
+
114
+ expect(serverInfo).toEqual({
115
+ id: expect.any(String),
116
+ name: 'TestServer',
117
+ description: undefined,
118
+ repository: undefined,
119
+ version_detail: {
120
+ version: '1.0.0',
121
+ release_date: mockDateISO,
122
+ is_latest: true,
123
+ },
124
+ });
125
+ });
126
+
127
+ it('should return correct ServerInfo with custom metadata', () => {
128
+ const repository: Repository = { url: 'https://github.com/test/repo', source: 'github', id: 'repo-id' };
129
+ const customReleaseDate = '2023-11-01T00:00:00.000Z';
130
+ const customConfig: MCPServerConfig = {
131
+ ...minimalConfig,
132
+ id: 'custom-id-for-info',
133
+ description: 'Custom description',
134
+ repository,
135
+ releaseDate: customReleaseDate,
136
+ isLatest: false,
137
+ };
138
+ const server = new MCPServer(customConfig);
139
+ const serverInfo = server.getServerInfo();
140
+
141
+ expect(serverInfo).toEqual({
142
+ id: 'custom-id-for-info',
143
+ name: 'TestServer',
144
+ description: 'Custom description',
145
+ repository,
146
+ version_detail: {
147
+ version: '1.0.0',
148
+ release_date: customReleaseDate,
149
+ is_latest: false,
150
+ },
151
+ });
152
+ });
153
+ });
154
+
155
+ describe('getServerDetail()', () => {
156
+ it('should return correct ServerDetailInfo with default metadata', () => {
157
+ const server = new MCPServer(minimalConfig);
158
+ const serverDetail = server.getServerDetail();
159
+
160
+ expect(serverDetail).toEqual({
161
+ id: expect.any(String),
162
+ name: 'TestServer',
163
+ description: undefined,
164
+ repository: undefined,
165
+ version_detail: {
166
+ version: '1.0.0',
167
+ release_date: mockDateISO,
168
+ is_latest: true,
169
+ },
170
+ package_canonical: undefined,
171
+ packages: undefined,
172
+ remotes: undefined,
173
+ });
174
+ });
175
+
176
+ it('should return correct ServerDetailInfo with custom metadata', () => {
177
+ const repository: Repository = { url: 'https://github.com/test/repo', source: 'github', id: 'repo-id' };
178
+ const packages: PackageInfo[] = [{ registry_name: 'npm', name: 'test-package', version: '1.0.0' }];
179
+ const remotes: RemoteInfo[] = [{ transport_type: 'sse', url: 'https://test.com/sse' }];
180
+ const customReleaseDate = '2023-10-01T00:00:00.000Z';
181
+ const customConfig: MCPServerConfig = {
182
+ ...minimalConfig,
183
+ id: 'custom-id-for-detail',
184
+ description: 'Custom detail description',
185
+ repository,
186
+ releaseDate: customReleaseDate,
187
+ isLatest: true,
188
+ packageCanonical: 'docker',
189
+ packages,
190
+ remotes,
191
+ };
192
+ const server = new MCPServer(customConfig);
193
+ const serverDetail = server.getServerDetail();
194
+
195
+ expect(serverDetail).toEqual({
196
+ id: 'custom-id-for-detail',
197
+ name: 'TestServer',
198
+ description: 'Custom detail description',
199
+ repository,
200
+ version_detail: {
201
+ version: '1.0.0',
202
+ release_date: customReleaseDate,
203
+ is_latest: true,
204
+ },
205
+ package_canonical: 'docker',
206
+ packages,
207
+ remotes,
208
+ });
209
+ });
210
+ });
211
+
19
212
  describe('MCPServer SSE transport', () => {
20
213
  let sseRes: Response | undefined;
21
214
  let reader: ReadableStreamDefaultReader<Uint8Array> | undefined;
package/src/server.ts CHANGED
@@ -4,7 +4,14 @@ import type { InternalCoreTool } from '@mastra/core';
4
4
  import { makeCoreTool } from '@mastra/core';
5
5
  import type { ToolsInput } from '@mastra/core/agent';
6
6
  import { MCPServerBase } from '@mastra/core/mcp';
7
- import type { MCPServerSSEOptions, ConvertedTool, MCPServerHonoSSEOptions } from '@mastra/core/mcp';
7
+ import type {
8
+ MCPServerConfig,
9
+ ServerInfo,
10
+ ServerDetailInfo,
11
+ ConvertedTool,
12
+ MCPServerHonoSSEOptions,
13
+ MCPServerSSEOptions,
14
+ } from '@mastra/core/mcp';
8
15
  import { RuntimeContext } from '@mastra/core/runtime-context';
9
16
  import { Server } from '@modelcontextprotocol/sdk/server/index.js';
10
17
  import { SSEServerTransport } from '@modelcontextprotocol/sdk/server/sse.js';
@@ -56,17 +63,18 @@ export class MCPServer extends MCPServerBase {
56
63
 
57
64
  /**
58
65
  * Construct a new MCPServer instance.
59
- * @param opts.name - Server name
60
- * @param opts.version - Server version
61
- * @param opts.tools - Tool definitions to register
66
+ * @param opts - Configuration options for the server, including registry metadata.
62
67
  */
63
- constructor({ name, version, tools }: { name: string; version: string; tools: ToolsInput }) {
64
- super({ name, version, tools });
68
+ constructor(opts: MCPServerConfig) {
69
+ super(opts);
65
70
 
66
- this.server = new Server({ name, version }, { capabilities: { tools: {}, logging: { enabled: true } } });
71
+ this.server = new Server(
72
+ { name: this.name, version: this.version },
73
+ { capabilities: { tools: {}, logging: { enabled: true } } },
74
+ );
67
75
 
68
76
  this.logger.info(
69
- `Initialized MCPServer '${name}' v${version} with tools: ${Object.keys(this.convertedTools).join(', ')}`,
77
+ `Initialized MCPServer '${this.name}' v${this.version} (ID: ${this.id}) with tools: ${Object.keys(this.convertedTools).join(', ')}`,
70
78
  );
71
79
 
72
80
  this.sseHonoTransports = new Map();
@@ -107,7 +115,7 @@ export class MCPServer extends MCPServerBase {
107
115
  name: toolName,
108
116
  description: coreTool.description,
109
117
  parameters: coreTool.parameters,
110
- execute: coreTool.execute,
118
+ execute: coreTool.execute!,
111
119
  };
112
120
  this.logger.info(`Registered tool: '${toolName}' [${toolInstance?.description || 'No description'}]`);
113
121
  }
@@ -430,4 +438,130 @@ export class MCPServer extends MCPServerBase {
430
438
  this.logger.error('Error closing MCP server:', { error });
431
439
  }
432
440
  }
441
+
442
+ /**
443
+ * Gets the basic information about the server, conforming to the Server schema.
444
+ * @returns ServerInfo object.
445
+ */
446
+ public getServerInfo(): ServerInfo {
447
+ return {
448
+ id: this.id,
449
+ name: this.name,
450
+ description: this.description,
451
+ repository: this.repository,
452
+ version_detail: {
453
+ version: this.version,
454
+ release_date: this.releaseDate,
455
+ is_latest: this.isLatest,
456
+ },
457
+ };
458
+ }
459
+
460
+ /**
461
+ * Gets detailed information about the server, conforming to the ServerDetail schema.
462
+ * @returns ServerDetailInfo object.
463
+ */
464
+ public getServerDetail(): ServerDetailInfo {
465
+ return {
466
+ ...this.getServerInfo(),
467
+ package_canonical: this.packageCanonical,
468
+ packages: this.packages,
469
+ remotes: this.remotes,
470
+ };
471
+ }
472
+
473
+ /**
474
+ * Gets a list of tools provided by this MCP server, including their schemas.
475
+ * This leverages the same tool information used by the internal ListTools MCP request.
476
+ * @returns An object containing an array of tool information.
477
+ */
478
+ public getToolListInfo(): { tools: Array<{ name: string; description?: string; inputSchema: any }> } {
479
+ this.logger.debug(`Getting tool list information for MCPServer '${this.name}'`);
480
+ return {
481
+ tools: Object.entries(this.convertedTools).map(([toolId, tool]) => ({
482
+ id: toolId,
483
+ name: tool.name,
484
+ description: tool.description,
485
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters,
486
+ })),
487
+ };
488
+ }
489
+
490
+ /**
491
+ * Gets information for a specific tool provided by this MCP server.
492
+ * @param toolId The ID/name of the tool to retrieve.
493
+ * @returns Tool information (name, description, inputSchema) or undefined if not found.
494
+ */
495
+ public getToolInfo(toolId: string): { name: string; description?: string; inputSchema: any } | undefined {
496
+ const tool = this.convertedTools[toolId];
497
+ if (!tool) {
498
+ this.logger.debug(`Tool '${toolId}' not found on MCPServer '${this.name}'`);
499
+ return undefined;
500
+ }
501
+ this.logger.debug(`Getting info for tool '${toolId}' on MCPServer '${this.name}'`);
502
+ return {
503
+ name: tool.name,
504
+ description: tool.description,
505
+ inputSchema: tool.parameters?.jsonSchema || tool.parameters,
506
+ };
507
+ }
508
+
509
+ /**
510
+ * Executes a specific tool provided by this MCP server.
511
+ * @param toolId The ID/name of the tool to execute.
512
+ * @param args The arguments to pass to the tool's execute function.
513
+ * @param executionContext Optional context for the tool execution.
514
+ * @returns A promise that resolves to the result of the tool execution.
515
+ * @throws Error if the tool is not found, validation fails, or execution fails.
516
+ */
517
+ public async executeTool(
518
+ toolId: string,
519
+ args: any,
520
+ executionContext?: { messages?: any[]; toolCallId?: string },
521
+ ): Promise<any> {
522
+ const tool = this.convertedTools[toolId];
523
+ if (!tool) {
524
+ this.logger.warn(`ExecuteTool: Unknown tool '${toolId}' requested on MCPServer '${this.name}'.`);
525
+ throw new Error(`Unknown tool: ${toolId}`);
526
+ }
527
+
528
+ this.logger.debug(`ExecuteTool: Invoking '${toolId}' with arguments:`, args);
529
+
530
+ let validatedArgs = args;
531
+ if (tool.parameters instanceof z.ZodType && typeof tool.parameters.safeParse === 'function') {
532
+ const validation = tool.parameters.safeParse(args ?? {});
533
+ if (!validation.success) {
534
+ const errorMessages = validation.error.errors
535
+ .map((e: z.ZodIssue) => `${e.path.join('.')}: ${e.message}`)
536
+ .join(', ');
537
+ this.logger.warn(`ExecuteTool: Invalid tool arguments for '${toolId}': ${errorMessages}`, {
538
+ errors: validation.error.format(),
539
+ });
540
+ throw new z.ZodError(validation.error.issues);
541
+ }
542
+ validatedArgs = validation.data;
543
+ } else {
544
+ this.logger.debug(
545
+ `ExecuteTool: Tool '${toolId}' parameters is not a Zod schema with safeParse or is undefined. Skipping validation.`,
546
+ );
547
+ }
548
+
549
+ if (!tool.execute) {
550
+ this.logger.error(`ExecuteTool: Tool '${toolId}' does not have an execute function.`);
551
+ throw new Error(`Tool '${toolId}' cannot be executed.`);
552
+ }
553
+
554
+ try {
555
+ const finalExecutionContext = {
556
+ messages: executionContext?.messages || [],
557
+ toolCallId: executionContext?.toolCallId || randomUUID(),
558
+ };
559
+ const result = await tool.execute(validatedArgs, finalExecutionContext);
560
+ this.logger.info(`ExecuteTool: Tool '${toolId}' executed successfully.`);
561
+ return result;
562
+ } catch (error) {
563
+ this.logger.error(`ExecuteTool: Tool execution failed for '${toolId}':`, { error });
564
+ throw error instanceof Error ? error : new Error(`Execution of tool '${toolId}' failed: ${String(error)}`);
565
+ }
566
+ }
433
567
  }
@@ -1,21 +0,0 @@
1
- #!/bin/sh
2
- basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
3
-
4
- case `uname` in
5
- *CYGWIN*|*MINGW*|*MSYS*)
6
- if command -v cygpath > /dev/null 2>&1; then
7
- basedir=`cygpath -w "$basedir"`
8
- fi
9
- ;;
10
- esac
11
-
12
- if [ -z "$NODE_PATH" ]; then
13
- export NODE_PATH="/home/runner/work/mastra/mastra/packages/cli/dist/node_modules:/home/runner/work/mastra/mastra/packages/cli/node_modules:/home/runner/work/mastra/mastra/packages/node_modules:/home/runner/work/mastra/mastra/node_modules:/home/runner/work/mastra/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules"
14
- else
15
- export NODE_PATH="/home/runner/work/mastra/mastra/packages/cli/dist/node_modules:/home/runner/work/mastra/mastra/packages/cli/node_modules:/home/runner/work/mastra/mastra/packages/node_modules:/home/runner/work/mastra/mastra/node_modules:/home/runner/work/mastra/node_modules:/home/runner/work/node_modules:/home/runner/node_modules:/home/node_modules:/node_modules:/home/runner/work/mastra/mastra/node_modules/.pnpm/node_modules:$NODE_PATH"
16
- fi
17
- if [ -x "$basedir/node" ]; then
18
- exec "$basedir/node" "$basedir/../mastra/dist/index.js" "$@"
19
- else
20
- exec node "$basedir/../mastra/dist/index.js" "$@"
21
- fi