@mastra/mcp 0.14.4 → 0.14.5-alpha.1
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/CHANGELOG.md +22 -0
- package/dist/client/client.d.ts +27 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/index.cjs +164 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +165 -3
- package/dist/index.js.map +1 -1
- package/dist/server/server.d.ts +37 -1
- package/dist/server/server.d.ts.map +1 -1
- package/package.json +6 -4
package/dist/index.js
CHANGED
|
@@ -8,7 +8,7 @@ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
|
|
|
8
8
|
import { StdioClientTransport, getDefaultEnvironment } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
9
9
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
10
10
|
import { DEFAULT_REQUEST_TIMEOUT_MSEC } from '@modelcontextprotocol/sdk/shared/protocol.js';
|
|
11
|
-
import { ListToolsRequestSchema, CallToolRequestSchema, SetLevelRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListPromptsRequestSchema, PromptSchema, GetPromptRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ListPromptsResultSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, ElicitRequestSchema,
|
|
11
|
+
import { ListToolsRequestSchema, CallToolRequestSchema, SetLevelRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, ListResourceTemplatesRequestSchema, SubscribeRequestSchema, UnsubscribeRequestSchema, ListPromptsRequestSchema, PromptSchema, GetPromptRequestSchema, ListResourcesResultSchema, ReadResourceResultSchema, ListResourceTemplatesResultSchema, ListPromptsResultSchema, GetPromptResultSchema, PromptListChangedNotificationSchema, ResourceUpdatedNotificationSchema, ResourceListChangedNotificationSchema, ElicitRequestSchema, JSONRPCMessageSchema, CallToolResultSchema, ErrorCode } from '@modelcontextprotocol/sdk/types.js';
|
|
12
12
|
import { asyncExitHook, gracefulExit } from 'exit-hook';
|
|
13
13
|
import { z } from 'zod';
|
|
14
14
|
import { convertJsonSchemaToZod } from 'zod-from-json-schema';
|
|
@@ -603,9 +603,57 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
603
603
|
throw e;
|
|
604
604
|
} finally {
|
|
605
605
|
this.transport = void 0;
|
|
606
|
-
this.isConnected =
|
|
606
|
+
this.isConnected = null;
|
|
607
607
|
}
|
|
608
608
|
}
|
|
609
|
+
/**
|
|
610
|
+
* Checks if an error indicates a session invalidation that requires reconnection.
|
|
611
|
+
*
|
|
612
|
+
* Common session-related errors include:
|
|
613
|
+
* - "No valid session ID provided" (HTTP 400)
|
|
614
|
+
* - "Server not initialized" (HTTP 400)
|
|
615
|
+
* - "Not connected" (protocol state error)
|
|
616
|
+
* - Connection refused errors
|
|
617
|
+
*
|
|
618
|
+
* @param error - The error to check
|
|
619
|
+
* @returns true if the error indicates a session problem requiring reconnection
|
|
620
|
+
*
|
|
621
|
+
* @internal
|
|
622
|
+
*/
|
|
623
|
+
isSessionError(error) {
|
|
624
|
+
if (!(error instanceof Error)) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
const errorMessage = error.message.toLowerCase();
|
|
628
|
+
return errorMessage.includes("no valid session") || errorMessage.includes("session") || errorMessage.includes("server not initialized") || errorMessage.includes("not connected") || errorMessage.includes("http 400") || errorMessage.includes("http 401") || errorMessage.includes("http 403") || errorMessage.includes("econnrefused") || errorMessage.includes("fetch failed") || errorMessage.includes("connection refused");
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Forces a reconnection to the MCP server by disconnecting and reconnecting.
|
|
632
|
+
*
|
|
633
|
+
* This is useful when the session becomes invalid (e.g., after server restart)
|
|
634
|
+
* and the client needs to establish a fresh connection.
|
|
635
|
+
*
|
|
636
|
+
* @returns Promise resolving when reconnection is complete
|
|
637
|
+
* @throws {Error} If reconnection fails
|
|
638
|
+
*
|
|
639
|
+
* @internal
|
|
640
|
+
*/
|
|
641
|
+
async forceReconnect() {
|
|
642
|
+
this.log("debug", "Forcing reconnection to MCP server...");
|
|
643
|
+
try {
|
|
644
|
+
if (this.transport) {
|
|
645
|
+
await this.transport.close();
|
|
646
|
+
}
|
|
647
|
+
} catch (e) {
|
|
648
|
+
this.log("debug", "Error during force disconnect (ignored)", {
|
|
649
|
+
error: e instanceof Error ? e.message : String(e)
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
this.transport = void 0;
|
|
653
|
+
this.isConnected = null;
|
|
654
|
+
await this.connect();
|
|
655
|
+
this.log("debug", "Successfully reconnected to MCP server");
|
|
656
|
+
}
|
|
609
657
|
async listResources() {
|
|
610
658
|
this.log("debug", `Requesting resources from MCP server`);
|
|
611
659
|
return await this.client.request({ method: "resources/list" }, ListResourcesResultSchema, {
|
|
@@ -778,7 +826,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
778
826
|
execute: async ({ context, runtimeContext }) => {
|
|
779
827
|
const previousContext = this.currentOperationContext;
|
|
780
828
|
this.currentOperationContext = runtimeContext || null;
|
|
781
|
-
|
|
829
|
+
const executeToolCall = async () => {
|
|
782
830
|
this.log("debug", `Executing tool: ${tool.name}`, { toolArgs: context });
|
|
783
831
|
const res = await this.client.callTool(
|
|
784
832
|
{
|
|
@@ -795,7 +843,27 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
795
843
|
return res.structuredContent;
|
|
796
844
|
}
|
|
797
845
|
return res;
|
|
846
|
+
};
|
|
847
|
+
try {
|
|
848
|
+
return await executeToolCall();
|
|
798
849
|
} catch (e) {
|
|
850
|
+
if (this.isSessionError(e)) {
|
|
851
|
+
this.log("debug", `Session error detected for tool ${tool.name}, attempting reconnection...`, {
|
|
852
|
+
error: e instanceof Error ? e.message : String(e)
|
|
853
|
+
});
|
|
854
|
+
try {
|
|
855
|
+
await this.forceReconnect();
|
|
856
|
+
this.log("debug", `Retrying tool ${tool.name} after reconnection...`);
|
|
857
|
+
return await executeToolCall();
|
|
858
|
+
} catch (reconnectError) {
|
|
859
|
+
this.log("error", `Reconnection or retry failed for tool ${tool.name}`, {
|
|
860
|
+
originalError: e instanceof Error ? e.message : String(e),
|
|
861
|
+
reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
|
|
862
|
+
toolArgs: context
|
|
863
|
+
});
|
|
864
|
+
throw e;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
799
867
|
this.log("error", `Error calling tool: ${tool.name}`, {
|
|
800
868
|
error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
|
|
801
869
|
toolArgs: context
|
|
@@ -2963,6 +3031,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2963
3031
|
* @param options.options.onsessioninitialized - Callback when a new session is initialized
|
|
2964
3032
|
* @param options.options.enableJsonResponse - If true, return JSON instead of SSE streaming
|
|
2965
3033
|
* @param options.options.eventStore - Event store for message resumability
|
|
3034
|
+
* @param options.options.serverless - If true, run in stateless mode without session management (ideal for serverless environments)
|
|
2966
3035
|
*
|
|
2967
3036
|
* @throws {MastraError} If HTTP connection setup fails
|
|
2968
3037
|
*
|
|
@@ -2988,6 +3057,25 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
2988
3057
|
*
|
|
2989
3058
|
* httpServer.listen(1234);
|
|
2990
3059
|
* ```
|
|
3060
|
+
*
|
|
3061
|
+
* @example Serverless mode (Cloudflare Workers, Vercel Edge, etc.)
|
|
3062
|
+
* ```typescript
|
|
3063
|
+
* export default {
|
|
3064
|
+
* async fetch(request: Request) {
|
|
3065
|
+
* const url = new URL(request.url);
|
|
3066
|
+
* if (url.pathname === '/mcp') {
|
|
3067
|
+
* await server.startHTTP({
|
|
3068
|
+
* url,
|
|
3069
|
+
* httpPath: '/mcp',
|
|
3070
|
+
* req: request,
|
|
3071
|
+
* res: response,
|
|
3072
|
+
* options: { serverless: true },
|
|
3073
|
+
* });
|
|
3074
|
+
* }
|
|
3075
|
+
* return new Response('Not found', { status: 404 });
|
|
3076
|
+
* },
|
|
3077
|
+
* };
|
|
3078
|
+
* ```
|
|
2991
3079
|
*/
|
|
2992
3080
|
async startHTTP({
|
|
2993
3081
|
url,
|
|
@@ -3003,6 +3091,12 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3003
3091
|
res.end();
|
|
3004
3092
|
return;
|
|
3005
3093
|
}
|
|
3094
|
+
const isStatelessMode = options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0;
|
|
3095
|
+
if (isStatelessMode) {
|
|
3096
|
+
this.logger.debug("startHTTP: Running in stateless mode (serverless or sessionIdGenerator: undefined)");
|
|
3097
|
+
await this.handleServerlessRequest(req, res);
|
|
3098
|
+
return;
|
|
3099
|
+
}
|
|
3006
3100
|
const mergedOptions = {
|
|
3007
3101
|
sessionIdGenerator: () => randomUUID(),
|
|
3008
3102
|
// default: enabled
|
|
@@ -3144,6 +3238,74 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
|
|
|
3144
3238
|
}
|
|
3145
3239
|
}
|
|
3146
3240
|
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Handles a stateless, serverless HTTP request without session management.
|
|
3243
|
+
*
|
|
3244
|
+
* This method bypasses all session/transport state and handles each request independently.
|
|
3245
|
+
* For serverless environments (Cloudflare Workers, Vercel Edge, etc.) where
|
|
3246
|
+
* persistent connections and session state cannot be maintained across requests.
|
|
3247
|
+
*
|
|
3248
|
+
* Each request gets a fresh transport and server instance that are discarded after the response.
|
|
3249
|
+
*
|
|
3250
|
+
* @param req - Incoming HTTP request
|
|
3251
|
+
* @param res - HTTP response object
|
|
3252
|
+
* @private
|
|
3253
|
+
*/
|
|
3254
|
+
async handleServerlessRequest(req, res) {
|
|
3255
|
+
try {
|
|
3256
|
+
this.logger.debug(`handleServerlessRequest: Received ${req.method} request`);
|
|
3257
|
+
const body = req.method === "POST" ? await new Promise((resolve, reject) => {
|
|
3258
|
+
let data = "";
|
|
3259
|
+
req.on("data", (chunk) => data += chunk);
|
|
3260
|
+
req.on("end", () => {
|
|
3261
|
+
try {
|
|
3262
|
+
resolve(JSON.parse(data));
|
|
3263
|
+
} catch (e) {
|
|
3264
|
+
reject(new Error(`Invalid JSON in request body: ${e instanceof Error ? e.message : String(e)}`));
|
|
3265
|
+
}
|
|
3266
|
+
});
|
|
3267
|
+
req.on("error", reject);
|
|
3268
|
+
}) : void 0;
|
|
3269
|
+
this.logger.debug(`handleServerlessRequest: Processing ${req.method} request`, {
|
|
3270
|
+
method: body?.method,
|
|
3271
|
+
id: body?.id
|
|
3272
|
+
});
|
|
3273
|
+
const transientServer = this.createServerInstance();
|
|
3274
|
+
const tempTransport = new StreamableHTTPServerTransport({
|
|
3275
|
+
sessionIdGenerator: void 0,
|
|
3276
|
+
enableJsonResponse: true
|
|
3277
|
+
});
|
|
3278
|
+
await transientServer.connect(tempTransport);
|
|
3279
|
+
await tempTransport.handleRequest(req, res, body);
|
|
3280
|
+
this.logger.debug(`handleServerlessRequest: Completed ${body?.method} request`, { id: body?.id });
|
|
3281
|
+
} catch (error) {
|
|
3282
|
+
const mastraError = new MastraError(
|
|
3283
|
+
{
|
|
3284
|
+
id: "MCP_SERVER_SERVERLESS_REQUEST_FAILED",
|
|
3285
|
+
domain: ErrorDomain.MCP,
|
|
3286
|
+
category: ErrorCategory.USER,
|
|
3287
|
+
text: "Failed to handle serverless MCP request"
|
|
3288
|
+
},
|
|
3289
|
+
error
|
|
3290
|
+
);
|
|
3291
|
+
this.logger.trackException(mastraError);
|
|
3292
|
+
this.logger.error("handleServerlessRequest: Error handling request:", { error: mastraError });
|
|
3293
|
+
if (!res.headersSent) {
|
|
3294
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
3295
|
+
res.end(
|
|
3296
|
+
JSON.stringify({
|
|
3297
|
+
jsonrpc: "2.0",
|
|
3298
|
+
error: {
|
|
3299
|
+
code: -32603,
|
|
3300
|
+
message: "Internal server error",
|
|
3301
|
+
data: error instanceof Error ? error.message : String(error)
|
|
3302
|
+
},
|
|
3303
|
+
id: null
|
|
3304
|
+
})
|
|
3305
|
+
);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3147
3309
|
/**
|
|
3148
3310
|
* Establishes the SSE connection for the MCP server.
|
|
3149
3311
|
*
|