@mastra/mcp 0.14.4 → 0.14.5-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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @mastra/mcp
2
2
 
3
+ ## 0.14.5-alpha.0
4
+
5
+ ### Patch Changes
6
+
7
+ - add flag to skip sessions and streaming in serverless mcp ([#10927](https://github.com/mastra-ai/mastra/pull/10927))
8
+
9
+ - Updated dependencies [[`b685c9c`](https://github.com/mastra-ai/mastra/commit/b685c9c0b89f49e0d4542c4ac72569682db69794)]:
10
+ - @mastra/core@0.24.7-alpha.2
11
+
3
12
  ## 0.14.4
4
13
 
5
14
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -2970,6 +2970,7 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2970
2970
  * @param options.options.onsessioninitialized - Callback when a new session is initialized
2971
2971
  * @param options.options.enableJsonResponse - If true, return JSON instead of SSE streaming
2972
2972
  * @param options.options.eventStore - Event store for message resumability
2973
+ * @param options.options.serverless - If true, run in stateless mode without session management (ideal for serverless environments)
2973
2974
  *
2974
2975
  * @throws {MastraError} If HTTP connection setup fails
2975
2976
  *
@@ -2995,6 +2996,25 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
2995
2996
  *
2996
2997
  * httpServer.listen(1234);
2997
2998
  * ```
2999
+ *
3000
+ * @example Serverless mode (Cloudflare Workers, Vercel Edge, etc.)
3001
+ * ```typescript
3002
+ * export default {
3003
+ * async fetch(request: Request) {
3004
+ * const url = new URL(request.url);
3005
+ * if (url.pathname === '/mcp') {
3006
+ * await server.startHTTP({
3007
+ * url,
3008
+ * httpPath: '/mcp',
3009
+ * req: request,
3010
+ * res: response,
3011
+ * options: { serverless: true },
3012
+ * });
3013
+ * }
3014
+ * return new Response('Not found', { status: 404 });
3015
+ * },
3016
+ * };
3017
+ * ```
2998
3018
  */
2999
3019
  async startHTTP({
3000
3020
  url,
@@ -3010,6 +3030,12 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3010
3030
  res.end();
3011
3031
  return;
3012
3032
  }
3033
+ const isStatelessMode = options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0;
3034
+ if (isStatelessMode) {
3035
+ this.logger.debug("startHTTP: Running in stateless mode (serverless or sessionIdGenerator: undefined)");
3036
+ await this.handleServerlessRequest(req, res);
3037
+ return;
3038
+ }
3013
3039
  const mergedOptions = {
3014
3040
  sessionIdGenerator: () => crypto$1.randomUUID(),
3015
3041
  // default: enabled
@@ -3151,6 +3177,74 @@ Provided arguments: ${JSON.stringify(request.params.arguments, null, 2)}`
3151
3177
  }
3152
3178
  }
3153
3179
  }
3180
+ /**
3181
+ * Handles a stateless, serverless HTTP request without session management.
3182
+ *
3183
+ * This method bypasses all session/transport state and handles each request independently.
3184
+ * For serverless environments (Cloudflare Workers, Vercel Edge, etc.) where
3185
+ * persistent connections and session state cannot be maintained across requests.
3186
+ *
3187
+ * Each request gets a fresh transport and server instance that are discarded after the response.
3188
+ *
3189
+ * @param req - Incoming HTTP request
3190
+ * @param res - HTTP response object
3191
+ * @private
3192
+ */
3193
+ async handleServerlessRequest(req, res) {
3194
+ try {
3195
+ this.logger.debug(`handleServerlessRequest: Received ${req.method} request`);
3196
+ const body = req.method === "POST" ? await new Promise((resolve, reject) => {
3197
+ let data = "";
3198
+ req.on("data", (chunk) => data += chunk);
3199
+ req.on("end", () => {
3200
+ try {
3201
+ resolve(JSON.parse(data));
3202
+ } catch (e) {
3203
+ reject(new Error(`Invalid JSON in request body: ${e instanceof Error ? e.message : String(e)}`));
3204
+ }
3205
+ });
3206
+ req.on("error", reject);
3207
+ }) : void 0;
3208
+ this.logger.debug(`handleServerlessRequest: Processing ${req.method} request`, {
3209
+ method: body?.method,
3210
+ id: body?.id
3211
+ });
3212
+ const transientServer = this.createServerInstance();
3213
+ const tempTransport = new streamableHttp_js.StreamableHTTPServerTransport({
3214
+ sessionIdGenerator: void 0,
3215
+ enableJsonResponse: true
3216
+ });
3217
+ await transientServer.connect(tempTransport);
3218
+ await tempTransport.handleRequest(req, res, body);
3219
+ this.logger.debug(`handleServerlessRequest: Completed ${body?.method} request`, { id: body?.id });
3220
+ } catch (error$1) {
3221
+ const mastraError = new error.MastraError(
3222
+ {
3223
+ id: "MCP_SERVER_SERVERLESS_REQUEST_FAILED",
3224
+ domain: error.ErrorDomain.MCP,
3225
+ category: error.ErrorCategory.USER,
3226
+ text: "Failed to handle serverless MCP request"
3227
+ },
3228
+ error$1
3229
+ );
3230
+ this.logger.trackException(mastraError);
3231
+ this.logger.error("handleServerlessRequest: Error handling request:", { error: mastraError });
3232
+ if (!res.headersSent) {
3233
+ res.writeHead(500, { "Content-Type": "application/json" });
3234
+ res.end(
3235
+ JSON.stringify({
3236
+ jsonrpc: "2.0",
3237
+ error: {
3238
+ code: -32603,
3239
+ message: "Internal server error",
3240
+ data: error$1 instanceof Error ? error$1.message : String(error$1)
3241
+ },
3242
+ id: null
3243
+ })
3244
+ );
3245
+ }
3246
+ }
3247
+ }
3154
3248
  /**
3155
3249
  * Establishes the SSE connection for the MCP server.
3156
3250
  *