@mastra/mcp 1.17.0-alpha.1 → 1.17.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/dist/index.cjs CHANGED
@@ -5031,7 +5031,7 @@ const allowsEval = /* @__PURE__*/ cached(() => {
5031
5031
  return false;
5032
5032
  }
5033
5033
  });
5034
- function isPlainObject(o) {
5034
+ function isPlainObject$1(o) {
5035
5035
  if (isObject(o) === false) return false;
5036
5036
  const ctor = o.constructor;
5037
5037
  if (ctor === void 0) return true;
@@ -5042,7 +5042,7 @@ function isPlainObject(o) {
5042
5042
  return true;
5043
5043
  }
5044
5044
  function shallowClone(o) {
5045
- if (isPlainObject(o)) return { ...o };
5045
+ if (isPlainObject$1(o)) return { ...o };
5046
5046
  if (Array.isArray(o)) return [...o];
5047
5047
  if (o instanceof Map) return new Map(o);
5048
5048
  if (o instanceof Set) return new Set(o);
@@ -5125,7 +5125,7 @@ function omit(schema, mask) {
5125
5125
  }));
5126
5126
  }
5127
5127
  function extend(schema, shape) {
5128
- if (!isPlainObject(shape)) throw new Error("Invalid input to extend: expected a plain object");
5128
+ if (!isPlainObject$1(shape)) throw new Error("Invalid input to extend: expected a plain object");
5129
5129
  const checks = schema._zod.def.checks;
5130
5130
  if (checks && checks.length > 0) {
5131
5131
  const existingShape = schema._zod.def.shape;
@@ -5141,7 +5141,7 @@ function extend(schema, shape) {
5141
5141
  } }));
5142
5142
  }
5143
5143
  function safeExtend(schema, shape) {
5144
- if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
5144
+ if (!isPlainObject$1(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
5145
5145
  return clone(schema, mergeDefs(schema._zod.def, { get shape() {
5146
5146
  const _shape = {
5147
5147
  ...schema._zod.def.shape,
@@ -6826,7 +6826,7 @@ function mergeValues(a, b) {
6826
6826
  valid: true,
6827
6827
  data: a
6828
6828
  };
6829
- if (isPlainObject(a) && isPlainObject(b)) {
6829
+ if (isPlainObject$1(a) && isPlainObject$1(b)) {
6830
6830
  const bKeys = Object.keys(b);
6831
6831
  const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
6832
6832
  const newObj = {
@@ -6994,7 +6994,7 @@ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
6994
6994
  $ZodType.init(inst, def);
6995
6995
  inst._zod.parse = (payload, ctx) => {
6996
6996
  const input = payload.value;
6997
- if (!isPlainObject(input)) {
6997
+ if (!isPlainObject$1(input)) {
6998
6998
  payload.issues.push({
6999
6999
  expected: "record",
7000
7000
  code: "invalid_type",
@@ -21322,44 +21322,21 @@ function extractModelTextFromToolContent(content) {
21322
21322
  * `toModelOutput` can read MCP `content` without changing the execute return shape.
21323
21323
  */
21324
21324
  const MCP_CALL_TOOL_CONTENT = Symbol.for("mastra.mcp.callToolContent");
21325
- function reserveScalarMcpContent(queue) {
21326
- const reservation = { ready: false };
21327
- queue.push(reservation);
21328
- return reservation;
21329
- }
21330
- function removeScalarMcpContentReservation(queue, reservation) {
21331
- if (!reservation) return;
21332
- const index = queue.indexOf(reservation);
21333
- if (index !== -1) queue.splice(index, 1);
21334
- }
21335
- function attachMcpCallToolContent(structuredContent, content, scalarMcpContentQueue, reservation) {
21336
- if (structuredContent !== null && typeof structuredContent === "object") {
21337
- removeScalarMcpContentReservation(scalarMcpContentQueue, reservation);
21338
- Object.defineProperty(structuredContent, MCP_CALL_TOOL_CONTENT, {
21339
- value: content,
21340
- enumerable: false,
21341
- configurable: true
21342
- });
21343
- return structuredContent;
21344
- }
21345
- if (reservation) {
21346
- reservation.output = structuredContent;
21347
- reservation.content = content;
21348
- reservation.ready = true;
21349
- }
21325
+ function attachMcpCallToolContent(structuredContent, content) {
21326
+ if (structuredContent !== null && typeof structuredContent === "object") Object.defineProperty(structuredContent, MCP_CALL_TOOL_CONTENT, {
21327
+ value: content,
21328
+ enumerable: false,
21329
+ configurable: true
21330
+ });
21350
21331
  return structuredContent;
21351
21332
  }
21352
- function getMcpCallToolContent(output, scalarMcpContentQueue) {
21353
- if (output !== null && typeof output === "object") {
21354
- const attached = output[MCP_CALL_TOOL_CONTENT];
21355
- if (attached !== void 0) return attached;
21356
- }
21357
- const index = scalarMcpContentQueue.findIndex((entry) => entry.ready && Object.is(entry.output, output));
21358
- if (index !== -1) return scalarMcpContentQueue.splice(index, 1)[0]?.content;
21333
+ function getMcpCallToolContent(output) {
21334
+ if (output === null || typeof output !== "object") return void 0;
21335
+ return output[MCP_CALL_TOOL_CONTENT];
21359
21336
  }
21360
- function createStructuredToolToModelOutput(scalarMcpContentQueue) {
21337
+ function createStructuredToolToModelOutput() {
21361
21338
  return (output) => {
21362
- const modelText = extractModelTextFromToolContent(getMcpCallToolContent(output, scalarMcpContentQueue));
21339
+ const modelText = extractModelTextFromToolContent(getMcpCallToolContent(output));
21363
21340
  if (modelText !== void 0) return {
21364
21341
  type: "text",
21365
21342
  value: modelText
@@ -21483,12 +21460,14 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
21483
21460
  "io.modelcontextprotocol/ui": {}
21484
21461
  }
21485
21462
  };
21463
+ const versionNegotiation = server.protocolVersion === void 0 ? void 0 : { mode: server.protocolVersion === "auto" ? "auto" : { pin: server.protocolVersion } };
21486
21464
  this.client = new _modelcontextprotocol_client.Client({
21487
21465
  name,
21488
21466
  version
21489
21467
  }, {
21490
21468
  capabilities: clientCapabilities,
21491
- ...server.jsonSchemaValidator ? { jsonSchemaValidator: server.jsonSchemaValidator } : {}
21469
+ ...server.jsonSchemaValidator ? { jsonSchemaValidator: server.jsonSchemaValidator } : {},
21470
+ ...versionNegotiation ? { versionNegotiation } : {}
21492
21471
  });
21493
21472
  this.setupLogging();
21494
21473
  if (hasRoots) this.setupRootsHandler();
@@ -21656,7 +21635,7 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
21656
21635
  this.markNeedsAuth(streamableTransport);
21657
21636
  throw error;
21658
21637
  }
21659
- if (isUrlPolicyError(error)) throw error;
21638
+ if (isUrlPolicyError(error) || this.serverConfig.protocolVersion === "2026-07-28") throw error;
21660
21639
  const status = error instanceof _modelcontextprotocol_client.SdkHttpError ? error.status : void 0;
21661
21640
  if (status !== void 0 && !SSE_FALLBACK_STATUS_CODES.includes(status)) throw error;
21662
21641
  shouldTrySSE = true;
@@ -22207,7 +22186,6 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
22207
22186
  ...toolMeta ? { _meta: toolMeta } : {},
22208
22187
  ...annotations ? { annotations } : {}
22209
22188
  } } : {};
22210
- const scalarMcpContentQueue = [];
22211
22189
  const mastraTool = (0, _mastra_core_tools.createTool)({
22212
22190
  id: `${this.name}_${tool.name}`,
22213
22191
  description: tool.description || "",
@@ -22223,11 +22201,10 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
22223
22201
  forwardInstructions: this.forwardInstructions,
22224
22202
  instructionsMaxLength: this.instructionsMaxLength
22225
22203
  },
22226
- ...tool.outputSchema ? { toModelOutput: createStructuredToolToModelOutput(scalarMcpContentQueue) } : {},
22204
+ ...tool.outputSchema ? { toModelOutput: createStructuredToolToModelOutput() } : {},
22227
22205
  execute: async (input, context) => {
22228
22206
  if (serverMeta.connectFirst) await this.connect();
22229
22207
  const operationContext = context?.requestContext ?? null;
22230
- const scalarMcpContentReservation = tool.outputSchema ? reserveScalarMcpContent(scalarMcpContentQueue) : void 0;
22231
22208
  return this.operationContextStore.run(operationContext, async () => {
22232
22209
  const executeToolCall = async () => {
22233
22210
  this.log("debug", `Executing tool: ${tool.name}`, {
@@ -22263,8 +22240,7 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
22263
22240
  });
22264
22241
  }
22265
22242
  this.log("debug", `Tool executed successfully: ${tool.name}`);
22266
- if (res.structuredContent !== void 0) return attachMcpCallToolContent(res.structuredContent, res.content, scalarMcpContentQueue, scalarMcpContentReservation);
22267
- removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
22243
+ if (res.structuredContent !== void 0) return attachMcpCallToolContent(res.structuredContent, res.content);
22268
22244
  return res;
22269
22245
  };
22270
22246
  const failedTransport = this.transport;
@@ -22284,7 +22260,6 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
22284
22260
  reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
22285
22261
  toolArgs: input
22286
22262
  });
22287
- removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
22288
22263
  throw reconnectError;
22289
22264
  }
22290
22265
  }
@@ -22292,7 +22267,6 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
22292
22267
  error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
22293
22268
  toolArgs: input
22294
22269
  });
22295
- removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
22296
22270
  throw e;
22297
22271
  }
22298
22272
  });
@@ -24320,7 +24294,7 @@ var streamSSE = (c, cb, onError) => {
24320
24294
  return c.newResponse(stream.responseReadable);
24321
24295
  };
24322
24296
  //#endregion
24323
- //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@modelcontextprotocol/sdk/1.29.0/989ce0cb833930f9ef8da3e25bac11110cfeac46669bc69ae9534a6aa55b0aa7/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
24297
+ //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@modelcontextprotocol/sdk/1.29.0/7d2f0982429dbe53f84f7249d02074ef571f5d1c95137997d9ed2dfe102a137b/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
24324
24298
  const RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
24325
24299
  /**
24326
24300
  * Assert 'object' type schema.
@@ -26151,7 +26125,7 @@ union([
26151
26125
  CreateTaskResultSchema
26152
26126
  ]);
26153
26127
  //#endregion
26154
- //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono-mcp-server-sse-transport/0.0.7/518df2f53e0bbb46e48def5118fc7f6b5bba3a268854c8a5c7e83260299d376e/node_modules/hono-mcp-server-sse-transport/build/sse.js
26128
+ //#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono-mcp-server-sse-transport/0.0.7/d5ae5a23aa2b79db7055621f11a7cbc8d27f7caba1eeb308ab2cf5bcb1a8e5b8/node_modules/hono-mcp-server-sse-transport/build/sse.js
26155
26129
  const MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
26156
26130
  var SSETransport = class {
26157
26131
  messageUrl;
@@ -26226,6 +26200,132 @@ var SSETransport = class {
26226
26200
  }
26227
26201
  };
26228
26202
  //#endregion
26203
+ //#region src/server/mrtrElicitation.ts
26204
+ /**
26205
+ * Elicitation on the 2026-07-28 protocol revision (multi-round-trip requests).
26206
+ *
26207
+ * The modern era has no server→client request channel, so `server.elicitInput()`
26208
+ * throws there by design. Instead, the `tools/call` handler must RETURN an
26209
+ * `input_required` result carrying the embedded elicitation request; the client
26210
+ * answers it locally and RETRIES the call with the response attached
26211
+ * (`inputResponses`). This module keeps the promise-shaped
26212
+ * `elicitation.sendRequest()` API tools already use by replaying the tool:
26213
+ *
26214
+ * 1. Each `sendRequest` call is assigned a deterministic sequence key.
26215
+ * 2. If the current (retried) request carries an answer for that key — either in
26216
+ * this round's `inputResponses` or in the accumulated `requestState` from
26217
+ * prior rounds — the promise resolves with it immediately.
26218
+ * 3. Otherwise a {@link ElicitationReplayInterrupt} is thrown; the `tools/call`
26219
+ * handler converts it into an {@link InputRequiredResult} return, and the
26220
+ * client's retry re-executes the tool from the top.
26221
+ *
26222
+ * Consequences tools must live with on the modern leg:
26223
+ * - Code before an unanswered elicitation re-executes on every round; keep side
26224
+ * effects idempotent or place them after the last elicitation.
26225
+ * - Elicitation order must be deterministic for a given input, since answers are
26226
+ * matched by call sequence.
26227
+ */
26228
+ const ELICIT_KEY_PREFIX = "mastra_elicit_";
26229
+ /**
26230
+ * Thrown by the replay-based `sendRequest` when the current round carries no
26231
+ * answer for the elicitation. Never surfaces to tools or clients: the
26232
+ * `tools/call` handler converts it into an `input_required` result.
26233
+ */
26234
+ var ElicitationReplayInterrupt = class extends Error {
26235
+ key;
26236
+ params;
26237
+ answered;
26238
+ constructor(key, params, answered) {
26239
+ super(`Elicitation '${key}' requires client input (multi-round-trip)`);
26240
+ this.key = key;
26241
+ this.params = params;
26242
+ this.answered = answered;
26243
+ this.name = "ElicitationReplayInterrupt";
26244
+ }
26245
+ };
26246
+ const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
26247
+ const looksLikeElicitResult = (value) => isPlainObject(value) && typeof value.action === "string";
26248
+ /**
26249
+ * Decodes the accumulated prior-round answers from the request's opaque
26250
+ * `requestState` echo. The state round-trips through the client, so it is
26251
+ * attacker-controlled input on re-entry — but its only content is the client's
26252
+ * own past elicitation answers, which are untrusted user input either way, so
26253
+ * tampering grants the client nothing it does not already control. Anything
26254
+ * that does not parse as a map of elicitation results is discarded.
26255
+ */
26256
+ const decodePriorAnswers = (extra) => {
26257
+ const raw = extra.mcpReq.requestState();
26258
+ if (typeof raw !== "string" || raw.length === 0) return {};
26259
+ try {
26260
+ const parsed = JSON.parse(raw);
26261
+ if (!isPlainObject(parsed)) return {};
26262
+ const answers = {};
26263
+ for (const [key, value] of Object.entries(parsed)) if (key.startsWith(ELICIT_KEY_PREFIX) && looksLikeElicitResult(value)) answers[key] = value;
26264
+ return answers;
26265
+ } catch {
26266
+ return {};
26267
+ }
26268
+ };
26269
+ /** This round's bare responses that answer our elicitation keys. */
26270
+ const currentRoundAnswers = (extra) => {
26271
+ const answers = {};
26272
+ for (const [key, value] of Object.entries(extra.mcpReq.inputResponses ?? {})) if (key.startsWith(ELICIT_KEY_PREFIX) && looksLikeElicitResult(value)) answers[key] = value;
26273
+ return answers;
26274
+ };
26275
+ /**
26276
+ * Whether this request is served on the 2026-07-28 era. Modern clients always
26277
+ * attach the reserved per-request `_meta` envelope (the 2026 codec enforces the
26278
+ * required keys at dispatch); legacy requests never carry it.
26279
+ */
26280
+ const isModernEraRequest = (extra) => extra.mcpReq.envelope !== void 0;
26281
+ /**
26282
+ * Builds the replay-based {@link ElicitationActions} for one modern-era tool
26283
+ * execution. Answers already provided by the client resolve immediately; the
26284
+ * first unanswered elicitation throws {@link ElicitationReplayInterrupt}.
26285
+ *
26286
+ * The interrupt is also reported through `onInterrupt` before it is thrown:
26287
+ * tool wrappers (e.g. Mastra's tool builder) re-wrap thrown errors, so the
26288
+ * `tools/call` handler cannot rely on `instanceof` on whatever escapes the
26289
+ * tool — the out-of-band capture is the authoritative signal.
26290
+ */
26291
+ const createReplayElicitation = (extra, onInterrupt) => {
26292
+ const prior = decodePriorAnswers(extra);
26293
+ const current = currentRoundAnswers(extra);
26294
+ const answered = {
26295
+ ...prior,
26296
+ ...current
26297
+ };
26298
+ let sequence = 0;
26299
+ return { sendRequest: async (request) => {
26300
+ const key = `${ELICIT_KEY_PREFIX}${sequence++}`;
26301
+ const answer = answered[key];
26302
+ if (answer !== void 0) return answer;
26303
+ const interrupt = new ElicitationReplayInterrupt(key, request, answered);
26304
+ onInterrupt(interrupt);
26305
+ throw interrupt;
26306
+ } };
26307
+ };
26308
+ /**
26309
+ * Converts a caught {@link ElicitationReplayInterrupt} into the
26310
+ * `input_required` result the `tools/call` handler returns to the client.
26311
+ * Prior answers travel in `requestState` because each retry only carries the
26312
+ * current round's `inputResponses`.
26313
+ */
26314
+ const replayInterruptToInputRequired = (interrupt) => {
26315
+ const params = {
26316
+ mode: "form",
26317
+ ...interrupt.params
26318
+ };
26319
+ return {
26320
+ resultType: "input_required",
26321
+ inputRequests: { [interrupt.key]: {
26322
+ method: "elicitation/create",
26323
+ params
26324
+ } },
26325
+ ...Object.keys(interrupt.answered).length > 0 ? { requestState: JSON.stringify(interrupt.answered) } : {}
26326
+ };
26327
+ };
26328
+ //#endregion
26229
26329
  //#region src/server/notificationBroadcast.ts
26230
26330
  /**
26231
26331
  * Sends a notification to every active server instance (the main stdio/SSE
@@ -26282,6 +26382,7 @@ var ServerPromptActions = class {
26282
26382
  getLogger;
26283
26383
  getSdkServers;
26284
26384
  clearDefinedPrompts;
26385
+ getModernEraNotifier;
26285
26386
  /**
26286
26387
  * @internal
26287
26388
  */
@@ -26289,6 +26390,7 @@ var ServerPromptActions = class {
26289
26390
  this.getLogger = dependencies.getLogger;
26290
26391
  this.getSdkServers = dependencies.getSdkServers;
26291
26392
  this.clearDefinedPrompts = dependencies.clearDefinedPrompts;
26393
+ this.getModernEraNotifier = dependencies.getModernEraNotifier;
26292
26394
  }
26293
26395
  /**
26294
26396
  * Notifies clients that the overall list of available prompts has changed.
@@ -26307,6 +26409,7 @@ var ServerPromptActions = class {
26307
26409
  async notifyListChanged() {
26308
26410
  this.getLogger().info("Prompt list change externally notified. Clearing definedPrompts and sending notification.");
26309
26411
  this.clearDefinedPrompts();
26412
+ this.getModernEraNotifier?.()?.promptsChanged();
26310
26413
  await broadcastNotification({
26311
26414
  servers: this.getSdkServers(),
26312
26415
  send: (server) => server.sendPromptListChanged(),
@@ -26333,6 +26436,7 @@ var ServerResourceActions = class {
26333
26436
  getSubscribedServers;
26334
26437
  getLogger;
26335
26438
  getSdkServers;
26439
+ getModernEraNotifier;
26336
26440
  /**
26337
26441
  * @internal
26338
26442
  */
@@ -26340,6 +26444,7 @@ var ServerResourceActions = class {
26340
26444
  this.getSubscribedServers = dependencies.getSubscribedServers;
26341
26445
  this.getLogger = dependencies.getLogger;
26342
26446
  this.getSdkServers = dependencies.getSdkServers;
26447
+ this.getModernEraNotifier = dependencies.getModernEraNotifier;
26343
26448
  }
26344
26449
  /**
26345
26450
  * Notifies subscribed clients that a specific resource has been updated.
@@ -26359,6 +26464,7 @@ var ServerResourceActions = class {
26359
26464
  * ```
26360
26465
  */
26361
26466
  async notifyUpdated({ uri }) {
26467
+ this.getModernEraNotifier?.()?.resourceUpdated(uri);
26362
26468
  const subscribedServers = this.getSubscribedServers(uri);
26363
26469
  if (subscribedServers.length === 0) {
26364
26470
  this.getLogger().debug(`Resource ${uri} was updated, but no active subscriptions for it.`);
@@ -26391,6 +26497,7 @@ var ServerResourceActions = class {
26391
26497
  */
26392
26498
  async notifyListChanged() {
26393
26499
  this.getLogger().info("Resource list change externally notified. Sending notification.");
26500
+ this.getModernEraNotifier?.()?.resourcesChanged();
26394
26501
  await broadcastNotification({
26395
26502
  servers: this.getSdkServers(),
26396
26503
  send: (server) => server.sendResourceListChanged(),
@@ -26416,6 +26523,7 @@ var ServerToolActions = class {
26416
26523
  getSdkServers;
26417
26524
  addTools;
26418
26525
  removeTools;
26526
+ getModernEraNotifier;
26419
26527
  /**
26420
26528
  * @internal
26421
26529
  */
@@ -26424,6 +26532,7 @@ var ServerToolActions = class {
26424
26532
  this.getSdkServers = dependencies.getSdkServers;
26425
26533
  this.addTools = dependencies.addTools;
26426
26534
  this.removeTools = dependencies.removeTools;
26535
+ this.getModernEraNotifier = dependencies.getModernEraNotifier;
26427
26536
  }
26428
26537
  /**
26429
26538
  * Registers new tools on the running server and notifies connected clients
@@ -26482,6 +26591,7 @@ var ServerToolActions = class {
26482
26591
  */
26483
26592
  async notifyListChanged() {
26484
26593
  this.getLogger().info("Tool list changed. Sending notification.");
26594
+ this.getModernEraNotifier?.()?.toolsChanged();
26485
26595
  await broadcastNotification({
26486
26596
  servers: this.getSdkServers(),
26487
26597
  send: (server) => server.sendToolListChanged(),
@@ -26522,6 +26632,14 @@ const LOG_LEVEL_SEVERITY = {
26522
26632
  alert: 6,
26523
26633
  emergency: 7
26524
26634
  };
26635
+ const ACCEPTED_MODERN_ERA_HTTP_OPTION_KEYS = /* @__PURE__ */ new Set([
26636
+ "allowedHosts",
26637
+ "allowedOrigins",
26638
+ "enableDnsRebindingProtection",
26639
+ "serverless",
26640
+ "serverlessStreaming",
26641
+ "sessionIdGenerator"
26642
+ ]);
26525
26643
  /**
26526
26644
  * MCPServer exposes Mastra tools, agents, and workflows as a Model Context Protocol (MCP) server.
26527
26645
  *
@@ -26568,6 +26686,12 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
26568
26686
  fga;
26569
26687
  subscriptionsByInstance = /* @__PURE__ */ new WeakMap();
26570
26688
  loggingLevels = /* @__PURE__ */ new WeakMap();
26689
+ protocolVersion;
26690
+ cacheHints;
26691
+ modernEraHandler;
26692
+ modernEraNodeHandler;
26693
+ stdioHandle;
26694
+ stdioServerInstance;
26571
26695
  /**
26572
26696
  * Provides methods to notify clients about resource changes.
26573
26697
  *
@@ -26719,6 +26843,8 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
26719
26843
  */
26720
26844
  constructor(opts) {
26721
26845
  super(opts);
26846
+ this.protocolVersion = opts.protocolVersion;
26847
+ this.cacheHints = opts.cacheHints;
26722
26848
  this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
26723
26849
  this.hasUiResources = !!opts.appResources && Object.keys(opts.appResources).length > 0;
26724
26850
  this.promptOptions = opts.prompts;
@@ -26744,7 +26870,8 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
26744
26870
  }, {
26745
26871
  capabilities,
26746
26872
  ...this.instructions ? { instructions: this.instructions } : {},
26747
- ...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {}
26873
+ ...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {},
26874
+ ...this.servesModernEra() && this.cacheHints ? { cacheHints: this.cacheHints } : {}
26748
26875
  });
26749
26876
  this.logger.info("Initialized MCPServer", {
26750
26877
  name: this.name,
@@ -26755,23 +26882,27 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
26755
26882
  });
26756
26883
  this.sseHonoTransports = /* @__PURE__ */ new Map();
26757
26884
  this.registerHandlersOnServer(this.server);
26885
+ const getModernEraNotifier = () => this.servesModernEra() ? this.modernEraHandler?.notify : void 0;
26758
26886
  this.resources = new ServerResourceActions({
26759
26887
  getSubscribedServers: (uri) => this.getAllSdkServers().filter((server) => this.subscriptionsByInstance.get(server)?.has(uri)),
26760
26888
  getLogger: () => this.logger,
26761
- getSdkServers: () => this.getAllSdkServers()
26889
+ getSdkServers: () => this.getAllSdkServers(),
26890
+ getModernEraNotifier
26762
26891
  });
26763
26892
  this.prompts = new ServerPromptActions({
26764
26893
  getLogger: () => this.logger,
26765
26894
  getSdkServers: () => this.getAllSdkServers(),
26766
26895
  clearDefinedPrompts: () => {
26767
26896
  this.definedPrompts = void 0;
26768
- }
26897
+ },
26898
+ getModernEraNotifier
26769
26899
  });
26770
26900
  this.toolActions = new ServerToolActions({
26771
26901
  getLogger: () => this.logger,
26772
26902
  getSdkServers: () => this.getAllSdkServers(),
26773
26903
  addTools: (tools) => this.addTools(tools),
26774
- removeTools: (toolIds) => this.removeTools(toolIds)
26904
+ removeTools: (toolIds) => this.removeTools(toolIds),
26905
+ getModernEraNotifier
26775
26906
  });
26776
26907
  this.elicitation = { sendRequest: async (request, options) => {
26777
26908
  return this.handleElicitationRequest(request, void 0, options);
@@ -26787,7 +26918,69 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
26787
26918
  * cannot receive notifications.
26788
26919
  */
26789
26920
  getAllSdkServers() {
26790
- return [this.server, ...this.httpServerInstances.values()].filter((server) => server.transport !== void 0);
26921
+ return [
26922
+ this.server,
26923
+ this.stdioServerInstance,
26924
+ ...this.httpServerInstances.values()
26925
+ ].filter((server) => server?.transport !== void 0);
26926
+ }
26927
+ /**
26928
+ * Whether the server is pinned to the `2026-07-28` protocol revision.
26929
+ * When false (the default), all behavior is byte-identical to the legacy era.
26930
+ */
26931
+ servesModernEra() {
26932
+ return this.protocolVersion === "2026-07-28";
26933
+ }
26934
+ assertModernEraHTTPOptions(options) {
26935
+ if (!options) return;
26936
+ const incompatibleOptions = new Set(Object.keys(options).filter((option) => !ACCEPTED_MODERN_ERA_HTTP_OPTION_KEYS.has(option)));
26937
+ if (options.sessionIdGenerator !== void 0) incompatibleOptions.add("sessionIdGenerator");
26938
+ if (options.serverless === false) incompatibleOptions.add("serverless");
26939
+ if (options.serverlessStreaming === false) incompatibleOptions.add("serverlessStreaming");
26940
+ if (incompatibleOptions.size === 0) return;
26941
+ const names = [...incompatibleOptions].sort();
26942
+ throw new _mastra_core_error.MastraError({
26943
+ id: "MCP_SERVER_MODERN_HTTP_OPTIONS_INCOMPATIBLE",
26944
+ domain: _mastra_core_error.ErrorDomain.MCP,
26945
+ category: _mastra_core_error.ErrorCategory.USER,
26946
+ text: `startHTTP options ${names.map((name) => `"${name}"`).join(", ")} are incompatible with protocolVersion "2026-07-28"`,
26947
+ details: { incompatibleOptions: names.join(", ") }
26948
+ });
26949
+ }
26950
+ validateHTTPRequestHeaders(req, res, options) {
26951
+ if (!options?.enableDnsRebindingProtection) return true;
26952
+ if (options.allowedHosts?.length) {
26953
+ if (!(0, _modelcontextprotocol_node.hostHeaderValidation)(options.allowedHosts.map((host) => new URL(`http://${host}`).hostname))(req, res)) return false;
26954
+ }
26955
+ if (options.allowedOrigins?.length) {
26956
+ if (!(0, _modelcontextprotocol_node.originValidation)(options.allowedOrigins.map((origin) => new URL(origin).hostname))(req, res)) return false;
26957
+ }
26958
+ return true;
26959
+ }
26960
+ /**
26961
+ * Lazily creates the dual-era HTTP handler used when `protocolVersion: '2026-07-28'`
26962
+ * is set: modern (per-request envelope) clients are served natively and legacy
26963
+ * clients are served through the SDK's built-in stateless fallback, both from the
26964
+ * same endpoint. Each request gets a fresh server instance from
26965
+ * `createServerInstance()`, so all registered handlers apply to both eras.
26966
+ */
26967
+ getModernEraHandler() {
26968
+ if (!this.modernEraHandler) this.modernEraHandler = (0, _modelcontextprotocol_server.createMcpHandler)(() => this.createServerInstance(), {
26969
+ legacy: "stateless",
26970
+ onerror: (error) => {
26971
+ this.logger.error("MCP handler error", { error: error.toString() });
26972
+ }
26973
+ });
26974
+ return this.modernEraHandler;
26975
+ }
26976
+ /**
26977
+ * Node `(req, res)` adapter over the dual-era handler's web-standard `fetch`.
26978
+ */
26979
+ getModernEraNodeHandler() {
26980
+ if (!this.modernEraNodeHandler) this.modernEraNodeHandler = (0, _modelcontextprotocol_node.toNodeHandler)(this.getModernEraHandler(), { onerror: (error) => {
26981
+ this.logger.error("MCP Node handler adapter error", { error: error.toString() });
26982
+ } });
26983
+ return this.modernEraNodeHandler;
26791
26984
  }
26792
26985
  /**
26793
26986
  * Determines whether a log message at the given level should be sent to the
@@ -27024,7 +27217,8 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27024
27217
  }, {
27025
27218
  capabilities,
27026
27219
  ...this.instructions ? { instructions: this.instructions } : {},
27027
- ...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {}
27220
+ ...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {},
27221
+ ...this.servesModernEra() && this.cacheHints ? { cacheHints: this.cacheHints } : {}
27028
27222
  });
27029
27223
  this.registerHandlersOnServer(serverInstance);
27030
27224
  return serverInstance;
@@ -27067,6 +27261,7 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27067
27261
  serverInstance.setRequestHandler("tools/call", async (request, ctx) => {
27068
27262
  const startTime = Date.now();
27069
27263
  const extra = toMCPRequestHandlerExtra(ctx);
27264
+ let replayInterrupt;
27070
27265
  try {
27071
27266
  const tool = this.convertedTools[request.params.name];
27072
27267
  if (!tool) {
@@ -27106,11 +27301,20 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27106
27301
  isError: true
27107
27302
  };
27108
27303
  }
27109
- const sessionElicitation = { sendRequest: async (request, options) => {
27304
+ const sessionElicitation = isModernEraRequest(extra) ? createReplayElicitation(extra, (interrupt) => {
27305
+ replayInterrupt = interrupt;
27306
+ }) : { sendRequest: async (request, options) => {
27110
27307
  return this.handleElicitationRequest(request, serverInstance, options);
27111
27308
  } };
27112
27309
  const proxiedContext = await this.createProxiedRequestContext(extra);
27113
27310
  const sessionLog = async (level, message, data) => {
27311
+ if (this.servesModernEra()) {
27312
+ await extra.mcpReq.log(level, {
27313
+ message,
27314
+ ...data
27315
+ }, this.name);
27316
+ return;
27317
+ }
27114
27318
  if (!this.shouldSendLog(serverInstance, level)) return;
27115
27319
  await extra.sendNotification({
27116
27320
  method: "notifications/message",
@@ -27200,6 +27404,10 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27200
27404
  }];
27201
27405
  return response;
27202
27406
  } catch (error) {
27407
+ if (replayInterrupt !== void 0) {
27408
+ this.logger.debug(`CallTool: Tool '${request.params.name}' requires client input (multi-round-trip).`, { key: replayInterrupt.key });
27409
+ return replayInterruptToInputRequired(replayInterrupt);
27410
+ }
27203
27411
  const duration = Date.now() - startTime;
27204
27412
  if (error instanceof Error && "issues" in error && Array.isArray(error.issues)) {
27205
27413
  const issues = error.issues;
@@ -27615,6 +27823,17 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27615
27823
  * ```
27616
27824
  */
27617
27825
  async startStdio() {
27826
+ if (this.servesModernEra()) {
27827
+ this.stdioHandle = (0, _modelcontextprotocol_server_stdio.serveStdio)(() => {
27828
+ const serverInstance = this.createServerInstance();
27829
+ this.stdioServerInstance = serverInstance;
27830
+ return serverInstance;
27831
+ }, { onerror: (error) => {
27832
+ this.logger.error("MCP stdio handler error", { error: error.toString() });
27833
+ } });
27834
+ this.logger.info("Started MCP Server (stdio, 2026-07-28 dual-era)");
27835
+ return;
27836
+ }
27618
27837
  this.stdioTransport = new _modelcontextprotocol_server_stdio.StdioServerTransport();
27619
27838
  try {
27620
27839
  await this.server.connect(this.stdioTransport);
@@ -27849,6 +28068,34 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
27849
28068
  res.end();
27850
28069
  return;
27851
28070
  }
28071
+ if (this.servesModernEra()) this.assertModernEraHTTPOptions(options);
28072
+ if (!this.validateHTTPRequestHeaders(req, res, options)) return;
28073
+ if (this.servesModernEra()) {
28074
+ try {
28075
+ await this.getModernEraNodeHandler()(req, res);
28076
+ } catch (error) {
28077
+ const mastraError = new _mastra_core_error.MastraError({
28078
+ id: "MCP_SERVER_HTTP_CONNECTION_FAILED",
28079
+ domain: _mastra_core_error.ErrorDomain.MCP,
28080
+ category: _mastra_core_error.ErrorCategory.USER,
28081
+ text: "Failed to handle MCP request on the 2026-07-28 protocol path"
28082
+ }, error);
28083
+ this.logger.trackException(mastraError);
28084
+ this.logger.error("Error handling HTTP request (2026-07-28 path)", { error: mastraError });
28085
+ if (!res.headersSent) {
28086
+ res.writeHead(500, { "Content-Type": "application/json" });
28087
+ res.end(JSON.stringify({
28088
+ jsonrpc: "2.0",
28089
+ error: {
28090
+ code: -32603,
28091
+ message: "Internal server error"
28092
+ },
28093
+ id: null
28094
+ }));
28095
+ }
28096
+ }
28097
+ return;
28098
+ }
27852
28099
  if (options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0) {
27853
28100
  this.logger.debug("Running in stateless mode");
27854
28101
  const enableJsonResponse = options?.enableJsonResponse ?? !options?.serverlessStreaming;
@@ -28157,6 +28404,16 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
28157
28404
  */
28158
28405
  async close() {
28159
28406
  try {
28407
+ if (this.stdioHandle) {
28408
+ await this.stdioHandle.close();
28409
+ this.stdioHandle = void 0;
28410
+ this.stdioServerInstance = void 0;
28411
+ }
28412
+ if (this.modernEraHandler) {
28413
+ await this.modernEraHandler.close();
28414
+ this.modernEraHandler = void 0;
28415
+ this.modernEraNodeHandler = void 0;
28416
+ }
28160
28417
  if (this.stdioTransport) {
28161
28418
  await this.stdioTransport.close?.();
28162
28419
  this.stdioTransport = void 0;