@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/CHANGELOG.md +167 -0
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/types.d.ts +29 -0
- package/dist/client/types.d.ts.map +1 -1
- package/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-auth-fga.md +26 -0
- package/dist/docs/references/docs-connections-overview.md +1 -1
- package/dist/docs/references/reference-tools-mcp-client.md +2 -0
- package/dist/docs/references/reference-tools-mcp-server.md +50 -2
- package/dist/index.cjs +315 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +318 -61
- package/dist/index.js.map +1 -1
- package/dist/server/mrtrElicitation.d.ts +39 -0
- package/dist/server/mrtrElicitation.d.ts.map +1 -0
- package/dist/server/promptActions.d.ts +8 -1
- package/dist/server/promptActions.d.ts.map +1 -1
- package/dist/server/resourceActions.d.ts +8 -1
- package/dist/server/resourceActions.d.ts.map +1 -1
- package/dist/server/server.d.ts +83 -17
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/toolActions.d.ts +8 -1
- package/dist/server/toolActions.d.ts.map +1 -1
- package/dist/server/types.d.ts +29 -1
- package/dist/server/types.d.ts.map +1 -1
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -16,9 +16,9 @@ import { RequestContext } from "@mastra/core/request-context";
|
|
|
16
16
|
import { makeCoreTool } from "@mastra/core/utils";
|
|
17
17
|
import { PromptSchema } from "@modelcontextprotocol/core";
|
|
18
18
|
import { RESOURCE_MIME_TYPE, RESOURCE_URI_META_KEY } from "@modelcontextprotocol/ext-apps";
|
|
19
|
-
import { NodeStreamableHTTPServerTransport } from "@modelcontextprotocol/node";
|
|
20
|
-
import { ProtocolError, ProtocolErrorCode as ProtocolErrorCode$1, Server } from "@modelcontextprotocol/server";
|
|
21
|
-
import { StdioServerTransport } from "@modelcontextprotocol/server/stdio";
|
|
19
|
+
import { NodeStreamableHTTPServerTransport, hostHeaderValidation, originValidation, toNodeHandler } from "@modelcontextprotocol/node";
|
|
20
|
+
import { ProtocolError, ProtocolErrorCode as ProtocolErrorCode$1, Server, createMcpHandler } from "@modelcontextprotocol/server";
|
|
21
|
+
import { StdioServerTransport, serveStdio } from "@modelcontextprotocol/server/stdio";
|
|
22
22
|
import { SSEServerTransport } from "@modelcontextprotocol/server-legacy/sse";
|
|
23
23
|
//#region \0rolldown/runtime.js
|
|
24
24
|
var __defProp$3 = Object.defineProperty;
|
|
@@ -5010,7 +5010,7 @@ const allowsEval = /* @__PURE__*/ cached(() => {
|
|
|
5010
5010
|
return false;
|
|
5011
5011
|
}
|
|
5012
5012
|
});
|
|
5013
|
-
function isPlainObject(o) {
|
|
5013
|
+
function isPlainObject$1(o) {
|
|
5014
5014
|
if (isObject(o) === false) return false;
|
|
5015
5015
|
const ctor = o.constructor;
|
|
5016
5016
|
if (ctor === void 0) return true;
|
|
@@ -5021,7 +5021,7 @@ function isPlainObject(o) {
|
|
|
5021
5021
|
return true;
|
|
5022
5022
|
}
|
|
5023
5023
|
function shallowClone(o) {
|
|
5024
|
-
if (isPlainObject(o)) return { ...o };
|
|
5024
|
+
if (isPlainObject$1(o)) return { ...o };
|
|
5025
5025
|
if (Array.isArray(o)) return [...o];
|
|
5026
5026
|
if (o instanceof Map) return new Map(o);
|
|
5027
5027
|
if (o instanceof Set) return new Set(o);
|
|
@@ -5104,7 +5104,7 @@ function omit(schema, mask) {
|
|
|
5104
5104
|
}));
|
|
5105
5105
|
}
|
|
5106
5106
|
function extend(schema, shape) {
|
|
5107
|
-
if (!isPlainObject(shape)) throw new Error("Invalid input to extend: expected a plain object");
|
|
5107
|
+
if (!isPlainObject$1(shape)) throw new Error("Invalid input to extend: expected a plain object");
|
|
5108
5108
|
const checks = schema._zod.def.checks;
|
|
5109
5109
|
if (checks && checks.length > 0) {
|
|
5110
5110
|
const existingShape = schema._zod.def.shape;
|
|
@@ -5120,7 +5120,7 @@ function extend(schema, shape) {
|
|
|
5120
5120
|
} }));
|
|
5121
5121
|
}
|
|
5122
5122
|
function safeExtend(schema, shape) {
|
|
5123
|
-
if (!isPlainObject(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
5123
|
+
if (!isPlainObject$1(shape)) throw new Error("Invalid input to safeExtend: expected a plain object");
|
|
5124
5124
|
return clone(schema, mergeDefs(schema._zod.def, { get shape() {
|
|
5125
5125
|
const _shape = {
|
|
5126
5126
|
...schema._zod.def.shape,
|
|
@@ -6805,7 +6805,7 @@ function mergeValues(a, b) {
|
|
|
6805
6805
|
valid: true,
|
|
6806
6806
|
data: a
|
|
6807
6807
|
};
|
|
6808
|
-
if (isPlainObject(a) && isPlainObject(b)) {
|
|
6808
|
+
if (isPlainObject$1(a) && isPlainObject$1(b)) {
|
|
6809
6809
|
const bKeys = Object.keys(b);
|
|
6810
6810
|
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);
|
|
6811
6811
|
const newObj = {
|
|
@@ -6973,7 +6973,7 @@ const $ZodRecord = /*@__PURE__*/ $constructor("$ZodRecord", (inst, def) => {
|
|
|
6973
6973
|
$ZodType.init(inst, def);
|
|
6974
6974
|
inst._zod.parse = (payload, ctx) => {
|
|
6975
6975
|
const input = payload.value;
|
|
6976
|
-
if (!isPlainObject(input)) {
|
|
6976
|
+
if (!isPlainObject$1(input)) {
|
|
6977
6977
|
payload.issues.push({
|
|
6978
6978
|
expected: "record",
|
|
6979
6979
|
code: "invalid_type",
|
|
@@ -21301,44 +21301,21 @@ function extractModelTextFromToolContent(content) {
|
|
|
21301
21301
|
* `toModelOutput` can read MCP `content` without changing the execute return shape.
|
|
21302
21302
|
*/
|
|
21303
21303
|
const MCP_CALL_TOOL_CONTENT = Symbol.for("mastra.mcp.callToolContent");
|
|
21304
|
-
function
|
|
21305
|
-
|
|
21306
|
-
|
|
21307
|
-
|
|
21308
|
-
|
|
21309
|
-
|
|
21310
|
-
if (!reservation) return;
|
|
21311
|
-
const index = queue.indexOf(reservation);
|
|
21312
|
-
if (index !== -1) queue.splice(index, 1);
|
|
21313
|
-
}
|
|
21314
|
-
function attachMcpCallToolContent(structuredContent, content, scalarMcpContentQueue, reservation) {
|
|
21315
|
-
if (structuredContent !== null && typeof structuredContent === "object") {
|
|
21316
|
-
removeScalarMcpContentReservation(scalarMcpContentQueue, reservation);
|
|
21317
|
-
Object.defineProperty(structuredContent, MCP_CALL_TOOL_CONTENT, {
|
|
21318
|
-
value: content,
|
|
21319
|
-
enumerable: false,
|
|
21320
|
-
configurable: true
|
|
21321
|
-
});
|
|
21322
|
-
return structuredContent;
|
|
21323
|
-
}
|
|
21324
|
-
if (reservation) {
|
|
21325
|
-
reservation.output = structuredContent;
|
|
21326
|
-
reservation.content = content;
|
|
21327
|
-
reservation.ready = true;
|
|
21328
|
-
}
|
|
21304
|
+
function attachMcpCallToolContent(structuredContent, content) {
|
|
21305
|
+
if (structuredContent !== null && typeof structuredContent === "object") Object.defineProperty(structuredContent, MCP_CALL_TOOL_CONTENT, {
|
|
21306
|
+
value: content,
|
|
21307
|
+
enumerable: false,
|
|
21308
|
+
configurable: true
|
|
21309
|
+
});
|
|
21329
21310
|
return structuredContent;
|
|
21330
21311
|
}
|
|
21331
|
-
function getMcpCallToolContent(output
|
|
21332
|
-
if (output
|
|
21333
|
-
|
|
21334
|
-
if (attached !== void 0) return attached;
|
|
21335
|
-
}
|
|
21336
|
-
const index = scalarMcpContentQueue.findIndex((entry) => entry.ready && Object.is(entry.output, output));
|
|
21337
|
-
if (index !== -1) return scalarMcpContentQueue.splice(index, 1)[0]?.content;
|
|
21312
|
+
function getMcpCallToolContent(output) {
|
|
21313
|
+
if (output === null || typeof output !== "object") return void 0;
|
|
21314
|
+
return output[MCP_CALL_TOOL_CONTENT];
|
|
21338
21315
|
}
|
|
21339
|
-
function createStructuredToolToModelOutput(
|
|
21316
|
+
function createStructuredToolToModelOutput() {
|
|
21340
21317
|
return (output) => {
|
|
21341
|
-
const modelText = extractModelTextFromToolContent(getMcpCallToolContent(output
|
|
21318
|
+
const modelText = extractModelTextFromToolContent(getMcpCallToolContent(output));
|
|
21342
21319
|
if (modelText !== void 0) return {
|
|
21343
21320
|
type: "text",
|
|
21344
21321
|
value: modelText
|
|
@@ -21462,12 +21439,14 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
21462
21439
|
"io.modelcontextprotocol/ui": {}
|
|
21463
21440
|
}
|
|
21464
21441
|
};
|
|
21442
|
+
const versionNegotiation = server.protocolVersion === void 0 ? void 0 : { mode: server.protocolVersion === "auto" ? "auto" : { pin: server.protocolVersion } };
|
|
21465
21443
|
this.client = new Client({
|
|
21466
21444
|
name,
|
|
21467
21445
|
version
|
|
21468
21446
|
}, {
|
|
21469
21447
|
capabilities: clientCapabilities,
|
|
21470
|
-
...server.jsonSchemaValidator ? { jsonSchemaValidator: server.jsonSchemaValidator } : {}
|
|
21448
|
+
...server.jsonSchemaValidator ? { jsonSchemaValidator: server.jsonSchemaValidator } : {},
|
|
21449
|
+
...versionNegotiation ? { versionNegotiation } : {}
|
|
21471
21450
|
});
|
|
21472
21451
|
this.setupLogging();
|
|
21473
21452
|
if (hasRoots) this.setupRootsHandler();
|
|
@@ -21635,7 +21614,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
21635
21614
|
this.markNeedsAuth(streamableTransport);
|
|
21636
21615
|
throw error;
|
|
21637
21616
|
}
|
|
21638
|
-
if (isUrlPolicyError(error)) throw error;
|
|
21617
|
+
if (isUrlPolicyError(error) || this.serverConfig.protocolVersion === "2026-07-28") throw error;
|
|
21639
21618
|
const status = error instanceof SdkHttpError ? error.status : void 0;
|
|
21640
21619
|
if (status !== void 0 && !SSE_FALLBACK_STATUS_CODES.includes(status)) throw error;
|
|
21641
21620
|
shouldTrySSE = true;
|
|
@@ -22186,7 +22165,6 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
22186
22165
|
...toolMeta ? { _meta: toolMeta } : {},
|
|
22187
22166
|
...annotations ? { annotations } : {}
|
|
22188
22167
|
} } : {};
|
|
22189
|
-
const scalarMcpContentQueue = [];
|
|
22190
22168
|
const mastraTool = createTool({
|
|
22191
22169
|
id: `${this.name}_${tool.name}`,
|
|
22192
22170
|
description: tool.description || "",
|
|
@@ -22202,11 +22180,10 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
22202
22180
|
forwardInstructions: this.forwardInstructions,
|
|
22203
22181
|
instructionsMaxLength: this.instructionsMaxLength
|
|
22204
22182
|
},
|
|
22205
|
-
...tool.outputSchema ? { toModelOutput: createStructuredToolToModelOutput(
|
|
22183
|
+
...tool.outputSchema ? { toModelOutput: createStructuredToolToModelOutput() } : {},
|
|
22206
22184
|
execute: async (input, context) => {
|
|
22207
22185
|
if (serverMeta.connectFirst) await this.connect();
|
|
22208
22186
|
const operationContext = context?.requestContext ?? null;
|
|
22209
|
-
const scalarMcpContentReservation = tool.outputSchema ? reserveScalarMcpContent(scalarMcpContentQueue) : void 0;
|
|
22210
22187
|
return this.operationContextStore.run(operationContext, async () => {
|
|
22211
22188
|
const executeToolCall = async () => {
|
|
22212
22189
|
this.log("debug", `Executing tool: ${tool.name}`, {
|
|
@@ -22242,8 +22219,7 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
22242
22219
|
});
|
|
22243
22220
|
}
|
|
22244
22221
|
this.log("debug", `Tool executed successfully: ${tool.name}`);
|
|
22245
|
-
if (res.structuredContent !== void 0) return attachMcpCallToolContent(res.structuredContent, res.content
|
|
22246
|
-
removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
|
|
22222
|
+
if (res.structuredContent !== void 0) return attachMcpCallToolContent(res.structuredContent, res.content);
|
|
22247
22223
|
return res;
|
|
22248
22224
|
};
|
|
22249
22225
|
const failedTransport = this.transport;
|
|
@@ -22263,7 +22239,6 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
22263
22239
|
reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
|
|
22264
22240
|
toolArgs: input
|
|
22265
22241
|
});
|
|
22266
|
-
removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
|
|
22267
22242
|
throw reconnectError;
|
|
22268
22243
|
}
|
|
22269
22244
|
}
|
|
@@ -22271,7 +22246,6 @@ var InternalMastraMCPClient = class extends MastraBase {
|
|
|
22271
22246
|
error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
|
|
22272
22247
|
toolArgs: input
|
|
22273
22248
|
});
|
|
22274
|
-
removeScalarMcpContentReservation(scalarMcpContentQueue, scalarMcpContentReservation);
|
|
22275
22249
|
throw e;
|
|
22276
22250
|
}
|
|
22277
22251
|
});
|
|
@@ -24299,7 +24273,7 @@ var streamSSE = (c, cb, onError) => {
|
|
|
24299
24273
|
return c.newResponse(stream.responseReadable);
|
|
24300
24274
|
};
|
|
24301
24275
|
//#endregion
|
|
24302
|
-
//#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@modelcontextprotocol/sdk/1.29.0/
|
|
24276
|
+
//#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@modelcontextprotocol/sdk/1.29.0/7d2f0982429dbe53f84f7249d02074ef571f5d1c95137997d9ed2dfe102a137b/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js
|
|
24303
24277
|
const RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
|
|
24304
24278
|
/**
|
|
24305
24279
|
* Assert 'object' type schema.
|
|
@@ -26130,7 +26104,7 @@ union([
|
|
|
26130
26104
|
CreateTaskResultSchema
|
|
26131
26105
|
]);
|
|
26132
26106
|
//#endregion
|
|
26133
|
-
//#region ../../../../../setup-pnpm/node_modules/.bin/store/v11/links/@/hono-mcp-server-sse-transport/0.0.7/
|
|
26107
|
+
//#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
|
|
26134
26108
|
const MAXIMUM_MESSAGE_SIZE = 4 * 1024 * 1024;
|
|
26135
26109
|
var SSETransport = class {
|
|
26136
26110
|
messageUrl;
|
|
@@ -26205,6 +26179,132 @@ var SSETransport = class {
|
|
|
26205
26179
|
}
|
|
26206
26180
|
};
|
|
26207
26181
|
//#endregion
|
|
26182
|
+
//#region src/server/mrtrElicitation.ts
|
|
26183
|
+
/**
|
|
26184
|
+
* Elicitation on the 2026-07-28 protocol revision (multi-round-trip requests).
|
|
26185
|
+
*
|
|
26186
|
+
* The modern era has no server→client request channel, so `server.elicitInput()`
|
|
26187
|
+
* throws there by design. Instead, the `tools/call` handler must RETURN an
|
|
26188
|
+
* `input_required` result carrying the embedded elicitation request; the client
|
|
26189
|
+
* answers it locally and RETRIES the call with the response attached
|
|
26190
|
+
* (`inputResponses`). This module keeps the promise-shaped
|
|
26191
|
+
* `elicitation.sendRequest()` API tools already use by replaying the tool:
|
|
26192
|
+
*
|
|
26193
|
+
* 1. Each `sendRequest` call is assigned a deterministic sequence key.
|
|
26194
|
+
* 2. If the current (retried) request carries an answer for that key — either in
|
|
26195
|
+
* this round's `inputResponses` or in the accumulated `requestState` from
|
|
26196
|
+
* prior rounds — the promise resolves with it immediately.
|
|
26197
|
+
* 3. Otherwise a {@link ElicitationReplayInterrupt} is thrown; the `tools/call`
|
|
26198
|
+
* handler converts it into an {@link InputRequiredResult} return, and the
|
|
26199
|
+
* client's retry re-executes the tool from the top.
|
|
26200
|
+
*
|
|
26201
|
+
* Consequences tools must live with on the modern leg:
|
|
26202
|
+
* - Code before an unanswered elicitation re-executes on every round; keep side
|
|
26203
|
+
* effects idempotent or place them after the last elicitation.
|
|
26204
|
+
* - Elicitation order must be deterministic for a given input, since answers are
|
|
26205
|
+
* matched by call sequence.
|
|
26206
|
+
*/
|
|
26207
|
+
const ELICIT_KEY_PREFIX = "mastra_elicit_";
|
|
26208
|
+
/**
|
|
26209
|
+
* Thrown by the replay-based `sendRequest` when the current round carries no
|
|
26210
|
+
* answer for the elicitation. Never surfaces to tools or clients: the
|
|
26211
|
+
* `tools/call` handler converts it into an `input_required` result.
|
|
26212
|
+
*/
|
|
26213
|
+
var ElicitationReplayInterrupt = class extends Error {
|
|
26214
|
+
key;
|
|
26215
|
+
params;
|
|
26216
|
+
answered;
|
|
26217
|
+
constructor(key, params, answered) {
|
|
26218
|
+
super(`Elicitation '${key}' requires client input (multi-round-trip)`);
|
|
26219
|
+
this.key = key;
|
|
26220
|
+
this.params = params;
|
|
26221
|
+
this.answered = answered;
|
|
26222
|
+
this.name = "ElicitationReplayInterrupt";
|
|
26223
|
+
}
|
|
26224
|
+
};
|
|
26225
|
+
const isPlainObject = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
26226
|
+
const looksLikeElicitResult = (value) => isPlainObject(value) && typeof value.action === "string";
|
|
26227
|
+
/**
|
|
26228
|
+
* Decodes the accumulated prior-round answers from the request's opaque
|
|
26229
|
+
* `requestState` echo. The state round-trips through the client, so it is
|
|
26230
|
+
* attacker-controlled input on re-entry — but its only content is the client's
|
|
26231
|
+
* own past elicitation answers, which are untrusted user input either way, so
|
|
26232
|
+
* tampering grants the client nothing it does not already control. Anything
|
|
26233
|
+
* that does not parse as a map of elicitation results is discarded.
|
|
26234
|
+
*/
|
|
26235
|
+
const decodePriorAnswers = (extra) => {
|
|
26236
|
+
const raw = extra.mcpReq.requestState();
|
|
26237
|
+
if (typeof raw !== "string" || raw.length === 0) return {};
|
|
26238
|
+
try {
|
|
26239
|
+
const parsed = JSON.parse(raw);
|
|
26240
|
+
if (!isPlainObject(parsed)) return {};
|
|
26241
|
+
const answers = {};
|
|
26242
|
+
for (const [key, value] of Object.entries(parsed)) if (key.startsWith(ELICIT_KEY_PREFIX) && looksLikeElicitResult(value)) answers[key] = value;
|
|
26243
|
+
return answers;
|
|
26244
|
+
} catch {
|
|
26245
|
+
return {};
|
|
26246
|
+
}
|
|
26247
|
+
};
|
|
26248
|
+
/** This round's bare responses that answer our elicitation keys. */
|
|
26249
|
+
const currentRoundAnswers = (extra) => {
|
|
26250
|
+
const answers = {};
|
|
26251
|
+
for (const [key, value] of Object.entries(extra.mcpReq.inputResponses ?? {})) if (key.startsWith(ELICIT_KEY_PREFIX) && looksLikeElicitResult(value)) answers[key] = value;
|
|
26252
|
+
return answers;
|
|
26253
|
+
};
|
|
26254
|
+
/**
|
|
26255
|
+
* Whether this request is served on the 2026-07-28 era. Modern clients always
|
|
26256
|
+
* attach the reserved per-request `_meta` envelope (the 2026 codec enforces the
|
|
26257
|
+
* required keys at dispatch); legacy requests never carry it.
|
|
26258
|
+
*/
|
|
26259
|
+
const isModernEraRequest = (extra) => extra.mcpReq.envelope !== void 0;
|
|
26260
|
+
/**
|
|
26261
|
+
* Builds the replay-based {@link ElicitationActions} for one modern-era tool
|
|
26262
|
+
* execution. Answers already provided by the client resolve immediately; the
|
|
26263
|
+
* first unanswered elicitation throws {@link ElicitationReplayInterrupt}.
|
|
26264
|
+
*
|
|
26265
|
+
* The interrupt is also reported through `onInterrupt` before it is thrown:
|
|
26266
|
+
* tool wrappers (e.g. Mastra's tool builder) re-wrap thrown errors, so the
|
|
26267
|
+
* `tools/call` handler cannot rely on `instanceof` on whatever escapes the
|
|
26268
|
+
* tool — the out-of-band capture is the authoritative signal.
|
|
26269
|
+
*/
|
|
26270
|
+
const createReplayElicitation = (extra, onInterrupt) => {
|
|
26271
|
+
const prior = decodePriorAnswers(extra);
|
|
26272
|
+
const current = currentRoundAnswers(extra);
|
|
26273
|
+
const answered = {
|
|
26274
|
+
...prior,
|
|
26275
|
+
...current
|
|
26276
|
+
};
|
|
26277
|
+
let sequence = 0;
|
|
26278
|
+
return { sendRequest: async (request) => {
|
|
26279
|
+
const key = `${ELICIT_KEY_PREFIX}${sequence++}`;
|
|
26280
|
+
const answer = answered[key];
|
|
26281
|
+
if (answer !== void 0) return answer;
|
|
26282
|
+
const interrupt = new ElicitationReplayInterrupt(key, request, answered);
|
|
26283
|
+
onInterrupt(interrupt);
|
|
26284
|
+
throw interrupt;
|
|
26285
|
+
} };
|
|
26286
|
+
};
|
|
26287
|
+
/**
|
|
26288
|
+
* Converts a caught {@link ElicitationReplayInterrupt} into the
|
|
26289
|
+
* `input_required` result the `tools/call` handler returns to the client.
|
|
26290
|
+
* Prior answers travel in `requestState` because each retry only carries the
|
|
26291
|
+
* current round's `inputResponses`.
|
|
26292
|
+
*/
|
|
26293
|
+
const replayInterruptToInputRequired = (interrupt) => {
|
|
26294
|
+
const params = {
|
|
26295
|
+
mode: "form",
|
|
26296
|
+
...interrupt.params
|
|
26297
|
+
};
|
|
26298
|
+
return {
|
|
26299
|
+
resultType: "input_required",
|
|
26300
|
+
inputRequests: { [interrupt.key]: {
|
|
26301
|
+
method: "elicitation/create",
|
|
26302
|
+
params
|
|
26303
|
+
} },
|
|
26304
|
+
...Object.keys(interrupt.answered).length > 0 ? { requestState: JSON.stringify(interrupt.answered) } : {}
|
|
26305
|
+
};
|
|
26306
|
+
};
|
|
26307
|
+
//#endregion
|
|
26208
26308
|
//#region src/server/notificationBroadcast.ts
|
|
26209
26309
|
/**
|
|
26210
26310
|
* Sends a notification to every active server instance (the main stdio/SSE
|
|
@@ -26261,6 +26361,7 @@ var ServerPromptActions = class {
|
|
|
26261
26361
|
getLogger;
|
|
26262
26362
|
getSdkServers;
|
|
26263
26363
|
clearDefinedPrompts;
|
|
26364
|
+
getModernEraNotifier;
|
|
26264
26365
|
/**
|
|
26265
26366
|
* @internal
|
|
26266
26367
|
*/
|
|
@@ -26268,6 +26369,7 @@ var ServerPromptActions = class {
|
|
|
26268
26369
|
this.getLogger = dependencies.getLogger;
|
|
26269
26370
|
this.getSdkServers = dependencies.getSdkServers;
|
|
26270
26371
|
this.clearDefinedPrompts = dependencies.clearDefinedPrompts;
|
|
26372
|
+
this.getModernEraNotifier = dependencies.getModernEraNotifier;
|
|
26271
26373
|
}
|
|
26272
26374
|
/**
|
|
26273
26375
|
* Notifies clients that the overall list of available prompts has changed.
|
|
@@ -26286,6 +26388,7 @@ var ServerPromptActions = class {
|
|
|
26286
26388
|
async notifyListChanged() {
|
|
26287
26389
|
this.getLogger().info("Prompt list change externally notified. Clearing definedPrompts and sending notification.");
|
|
26288
26390
|
this.clearDefinedPrompts();
|
|
26391
|
+
this.getModernEraNotifier?.()?.promptsChanged();
|
|
26289
26392
|
await broadcastNotification({
|
|
26290
26393
|
servers: this.getSdkServers(),
|
|
26291
26394
|
send: (server) => server.sendPromptListChanged(),
|
|
@@ -26312,6 +26415,7 @@ var ServerResourceActions = class {
|
|
|
26312
26415
|
getSubscribedServers;
|
|
26313
26416
|
getLogger;
|
|
26314
26417
|
getSdkServers;
|
|
26418
|
+
getModernEraNotifier;
|
|
26315
26419
|
/**
|
|
26316
26420
|
* @internal
|
|
26317
26421
|
*/
|
|
@@ -26319,6 +26423,7 @@ var ServerResourceActions = class {
|
|
|
26319
26423
|
this.getSubscribedServers = dependencies.getSubscribedServers;
|
|
26320
26424
|
this.getLogger = dependencies.getLogger;
|
|
26321
26425
|
this.getSdkServers = dependencies.getSdkServers;
|
|
26426
|
+
this.getModernEraNotifier = dependencies.getModernEraNotifier;
|
|
26322
26427
|
}
|
|
26323
26428
|
/**
|
|
26324
26429
|
* Notifies subscribed clients that a specific resource has been updated.
|
|
@@ -26338,6 +26443,7 @@ var ServerResourceActions = class {
|
|
|
26338
26443
|
* ```
|
|
26339
26444
|
*/
|
|
26340
26445
|
async notifyUpdated({ uri }) {
|
|
26446
|
+
this.getModernEraNotifier?.()?.resourceUpdated(uri);
|
|
26341
26447
|
const subscribedServers = this.getSubscribedServers(uri);
|
|
26342
26448
|
if (subscribedServers.length === 0) {
|
|
26343
26449
|
this.getLogger().debug(`Resource ${uri} was updated, but no active subscriptions for it.`);
|
|
@@ -26370,6 +26476,7 @@ var ServerResourceActions = class {
|
|
|
26370
26476
|
*/
|
|
26371
26477
|
async notifyListChanged() {
|
|
26372
26478
|
this.getLogger().info("Resource list change externally notified. Sending notification.");
|
|
26479
|
+
this.getModernEraNotifier?.()?.resourcesChanged();
|
|
26373
26480
|
await broadcastNotification({
|
|
26374
26481
|
servers: this.getSdkServers(),
|
|
26375
26482
|
send: (server) => server.sendResourceListChanged(),
|
|
@@ -26395,6 +26502,7 @@ var ServerToolActions = class {
|
|
|
26395
26502
|
getSdkServers;
|
|
26396
26503
|
addTools;
|
|
26397
26504
|
removeTools;
|
|
26505
|
+
getModernEraNotifier;
|
|
26398
26506
|
/**
|
|
26399
26507
|
* @internal
|
|
26400
26508
|
*/
|
|
@@ -26403,6 +26511,7 @@ var ServerToolActions = class {
|
|
|
26403
26511
|
this.getSdkServers = dependencies.getSdkServers;
|
|
26404
26512
|
this.addTools = dependencies.addTools;
|
|
26405
26513
|
this.removeTools = dependencies.removeTools;
|
|
26514
|
+
this.getModernEraNotifier = dependencies.getModernEraNotifier;
|
|
26406
26515
|
}
|
|
26407
26516
|
/**
|
|
26408
26517
|
* Registers new tools on the running server and notifies connected clients
|
|
@@ -26461,6 +26570,7 @@ var ServerToolActions = class {
|
|
|
26461
26570
|
*/
|
|
26462
26571
|
async notifyListChanged() {
|
|
26463
26572
|
this.getLogger().info("Tool list changed. Sending notification.");
|
|
26573
|
+
this.getModernEraNotifier?.()?.toolsChanged();
|
|
26464
26574
|
await broadcastNotification({
|
|
26465
26575
|
servers: this.getSdkServers(),
|
|
26466
26576
|
send: (server) => server.sendToolListChanged(),
|
|
@@ -26501,6 +26611,14 @@ const LOG_LEVEL_SEVERITY = {
|
|
|
26501
26611
|
alert: 6,
|
|
26502
26612
|
emergency: 7
|
|
26503
26613
|
};
|
|
26614
|
+
const ACCEPTED_MODERN_ERA_HTTP_OPTION_KEYS = /* @__PURE__ */ new Set([
|
|
26615
|
+
"allowedHosts",
|
|
26616
|
+
"allowedOrigins",
|
|
26617
|
+
"enableDnsRebindingProtection",
|
|
26618
|
+
"serverless",
|
|
26619
|
+
"serverlessStreaming",
|
|
26620
|
+
"sessionIdGenerator"
|
|
26621
|
+
]);
|
|
26504
26622
|
/**
|
|
26505
26623
|
* MCPServer exposes Mastra tools, agents, and workflows as a Model Context Protocol (MCP) server.
|
|
26506
26624
|
*
|
|
@@ -26547,6 +26665,12 @@ var MCPServer = class extends MCPServerBase {
|
|
|
26547
26665
|
fga;
|
|
26548
26666
|
subscriptionsByInstance = /* @__PURE__ */ new WeakMap();
|
|
26549
26667
|
loggingLevels = /* @__PURE__ */ new WeakMap();
|
|
26668
|
+
protocolVersion;
|
|
26669
|
+
cacheHints;
|
|
26670
|
+
modernEraHandler;
|
|
26671
|
+
modernEraNodeHandler;
|
|
26672
|
+
stdioHandle;
|
|
26673
|
+
stdioServerInstance;
|
|
26550
26674
|
/**
|
|
26551
26675
|
* Provides methods to notify clients about resource changes.
|
|
26552
26676
|
*
|
|
@@ -26698,6 +26822,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
26698
26822
|
*/
|
|
26699
26823
|
constructor(opts) {
|
|
26700
26824
|
super(opts);
|
|
26825
|
+
this.protocolVersion = opts.protocolVersion;
|
|
26826
|
+
this.cacheHints = opts.cacheHints;
|
|
26701
26827
|
this.resourceOptions = this.mergeAppResources(opts.resources, opts.appResources);
|
|
26702
26828
|
this.hasUiResources = !!opts.appResources && Object.keys(opts.appResources).length > 0;
|
|
26703
26829
|
this.promptOptions = opts.prompts;
|
|
@@ -26723,7 +26849,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
26723
26849
|
}, {
|
|
26724
26850
|
capabilities,
|
|
26725
26851
|
...this.instructions ? { instructions: this.instructions } : {},
|
|
26726
|
-
...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {}
|
|
26852
|
+
...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {},
|
|
26853
|
+
...this.servesModernEra() && this.cacheHints ? { cacheHints: this.cacheHints } : {}
|
|
26727
26854
|
});
|
|
26728
26855
|
this.logger.info("Initialized MCPServer", {
|
|
26729
26856
|
name: this.name,
|
|
@@ -26734,23 +26861,27 @@ var MCPServer = class extends MCPServerBase {
|
|
|
26734
26861
|
});
|
|
26735
26862
|
this.sseHonoTransports = /* @__PURE__ */ new Map();
|
|
26736
26863
|
this.registerHandlersOnServer(this.server);
|
|
26864
|
+
const getModernEraNotifier = () => this.servesModernEra() ? this.modernEraHandler?.notify : void 0;
|
|
26737
26865
|
this.resources = new ServerResourceActions({
|
|
26738
26866
|
getSubscribedServers: (uri) => this.getAllSdkServers().filter((server) => this.subscriptionsByInstance.get(server)?.has(uri)),
|
|
26739
26867
|
getLogger: () => this.logger,
|
|
26740
|
-
getSdkServers: () => this.getAllSdkServers()
|
|
26868
|
+
getSdkServers: () => this.getAllSdkServers(),
|
|
26869
|
+
getModernEraNotifier
|
|
26741
26870
|
});
|
|
26742
26871
|
this.prompts = new ServerPromptActions({
|
|
26743
26872
|
getLogger: () => this.logger,
|
|
26744
26873
|
getSdkServers: () => this.getAllSdkServers(),
|
|
26745
26874
|
clearDefinedPrompts: () => {
|
|
26746
26875
|
this.definedPrompts = void 0;
|
|
26747
|
-
}
|
|
26876
|
+
},
|
|
26877
|
+
getModernEraNotifier
|
|
26748
26878
|
});
|
|
26749
26879
|
this.toolActions = new ServerToolActions({
|
|
26750
26880
|
getLogger: () => this.logger,
|
|
26751
26881
|
getSdkServers: () => this.getAllSdkServers(),
|
|
26752
26882
|
addTools: (tools) => this.addTools(tools),
|
|
26753
|
-
removeTools: (toolIds) => this.removeTools(toolIds)
|
|
26883
|
+
removeTools: (toolIds) => this.removeTools(toolIds),
|
|
26884
|
+
getModernEraNotifier
|
|
26754
26885
|
});
|
|
26755
26886
|
this.elicitation = { sendRequest: async (request, options) => {
|
|
26756
26887
|
return this.handleElicitationRequest(request, void 0, options);
|
|
@@ -26766,7 +26897,69 @@ var MCPServer = class extends MCPServerBase {
|
|
|
26766
26897
|
* cannot receive notifications.
|
|
26767
26898
|
*/
|
|
26768
26899
|
getAllSdkServers() {
|
|
26769
|
-
return [
|
|
26900
|
+
return [
|
|
26901
|
+
this.server,
|
|
26902
|
+
this.stdioServerInstance,
|
|
26903
|
+
...this.httpServerInstances.values()
|
|
26904
|
+
].filter((server) => server?.transport !== void 0);
|
|
26905
|
+
}
|
|
26906
|
+
/**
|
|
26907
|
+
* Whether the server is pinned to the `2026-07-28` protocol revision.
|
|
26908
|
+
* When false (the default), all behavior is byte-identical to the legacy era.
|
|
26909
|
+
*/
|
|
26910
|
+
servesModernEra() {
|
|
26911
|
+
return this.protocolVersion === "2026-07-28";
|
|
26912
|
+
}
|
|
26913
|
+
assertModernEraHTTPOptions(options) {
|
|
26914
|
+
if (!options) return;
|
|
26915
|
+
const incompatibleOptions = new Set(Object.keys(options).filter((option) => !ACCEPTED_MODERN_ERA_HTTP_OPTION_KEYS.has(option)));
|
|
26916
|
+
if (options.sessionIdGenerator !== void 0) incompatibleOptions.add("sessionIdGenerator");
|
|
26917
|
+
if (options.serverless === false) incompatibleOptions.add("serverless");
|
|
26918
|
+
if (options.serverlessStreaming === false) incompatibleOptions.add("serverlessStreaming");
|
|
26919
|
+
if (incompatibleOptions.size === 0) return;
|
|
26920
|
+
const names = [...incompatibleOptions].sort();
|
|
26921
|
+
throw new MastraError({
|
|
26922
|
+
id: "MCP_SERVER_MODERN_HTTP_OPTIONS_INCOMPATIBLE",
|
|
26923
|
+
domain: ErrorDomain.MCP,
|
|
26924
|
+
category: ErrorCategory.USER,
|
|
26925
|
+
text: `startHTTP options ${names.map((name) => `"${name}"`).join(", ")} are incompatible with protocolVersion "2026-07-28"`,
|
|
26926
|
+
details: { incompatibleOptions: names.join(", ") }
|
|
26927
|
+
});
|
|
26928
|
+
}
|
|
26929
|
+
validateHTTPRequestHeaders(req, res, options) {
|
|
26930
|
+
if (!options?.enableDnsRebindingProtection) return true;
|
|
26931
|
+
if (options.allowedHosts?.length) {
|
|
26932
|
+
if (!hostHeaderValidation(options.allowedHosts.map((host) => new URL(`http://${host}`).hostname))(req, res)) return false;
|
|
26933
|
+
}
|
|
26934
|
+
if (options.allowedOrigins?.length) {
|
|
26935
|
+
if (!originValidation(options.allowedOrigins.map((origin) => new URL(origin).hostname))(req, res)) return false;
|
|
26936
|
+
}
|
|
26937
|
+
return true;
|
|
26938
|
+
}
|
|
26939
|
+
/**
|
|
26940
|
+
* Lazily creates the dual-era HTTP handler used when `protocolVersion: '2026-07-28'`
|
|
26941
|
+
* is set: modern (per-request envelope) clients are served natively and legacy
|
|
26942
|
+
* clients are served through the SDK's built-in stateless fallback, both from the
|
|
26943
|
+
* same endpoint. Each request gets a fresh server instance from
|
|
26944
|
+
* `createServerInstance()`, so all registered handlers apply to both eras.
|
|
26945
|
+
*/
|
|
26946
|
+
getModernEraHandler() {
|
|
26947
|
+
if (!this.modernEraHandler) this.modernEraHandler = createMcpHandler(() => this.createServerInstance(), {
|
|
26948
|
+
legacy: "stateless",
|
|
26949
|
+
onerror: (error) => {
|
|
26950
|
+
this.logger.error("MCP handler error", { error: error.toString() });
|
|
26951
|
+
}
|
|
26952
|
+
});
|
|
26953
|
+
return this.modernEraHandler;
|
|
26954
|
+
}
|
|
26955
|
+
/**
|
|
26956
|
+
* Node `(req, res)` adapter over the dual-era handler's web-standard `fetch`.
|
|
26957
|
+
*/
|
|
26958
|
+
getModernEraNodeHandler() {
|
|
26959
|
+
if (!this.modernEraNodeHandler) this.modernEraNodeHandler = toNodeHandler(this.getModernEraHandler(), { onerror: (error) => {
|
|
26960
|
+
this.logger.error("MCP Node handler adapter error", { error: error.toString() });
|
|
26961
|
+
} });
|
|
26962
|
+
return this.modernEraNodeHandler;
|
|
26770
26963
|
}
|
|
26771
26964
|
/**
|
|
26772
26965
|
* Determines whether a log message at the given level should be sent to the
|
|
@@ -27003,7 +27196,8 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27003
27196
|
}, {
|
|
27004
27197
|
capabilities,
|
|
27005
27198
|
...this.instructions ? { instructions: this.instructions } : {},
|
|
27006
|
-
...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {}
|
|
27199
|
+
...this.jsonSchemaValidator ? { jsonSchemaValidator: this.jsonSchemaValidator } : {},
|
|
27200
|
+
...this.servesModernEra() && this.cacheHints ? { cacheHints: this.cacheHints } : {}
|
|
27007
27201
|
});
|
|
27008
27202
|
this.registerHandlersOnServer(serverInstance);
|
|
27009
27203
|
return serverInstance;
|
|
@@ -27046,6 +27240,7 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27046
27240
|
serverInstance.setRequestHandler("tools/call", async (request, ctx) => {
|
|
27047
27241
|
const startTime = Date.now();
|
|
27048
27242
|
const extra = toMCPRequestHandlerExtra(ctx);
|
|
27243
|
+
let replayInterrupt;
|
|
27049
27244
|
try {
|
|
27050
27245
|
const tool = this.convertedTools[request.params.name];
|
|
27051
27246
|
if (!tool) {
|
|
@@ -27085,11 +27280,20 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27085
27280
|
isError: true
|
|
27086
27281
|
};
|
|
27087
27282
|
}
|
|
27088
|
-
const sessionElicitation =
|
|
27283
|
+
const sessionElicitation = isModernEraRequest(extra) ? createReplayElicitation(extra, (interrupt) => {
|
|
27284
|
+
replayInterrupt = interrupt;
|
|
27285
|
+
}) : { sendRequest: async (request, options) => {
|
|
27089
27286
|
return this.handleElicitationRequest(request, serverInstance, options);
|
|
27090
27287
|
} };
|
|
27091
27288
|
const proxiedContext = await this.createProxiedRequestContext(extra);
|
|
27092
27289
|
const sessionLog = async (level, message, data) => {
|
|
27290
|
+
if (this.servesModernEra()) {
|
|
27291
|
+
await extra.mcpReq.log(level, {
|
|
27292
|
+
message,
|
|
27293
|
+
...data
|
|
27294
|
+
}, this.name);
|
|
27295
|
+
return;
|
|
27296
|
+
}
|
|
27093
27297
|
if (!this.shouldSendLog(serverInstance, level)) return;
|
|
27094
27298
|
await extra.sendNotification({
|
|
27095
27299
|
method: "notifications/message",
|
|
@@ -27179,6 +27383,10 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27179
27383
|
}];
|
|
27180
27384
|
return response;
|
|
27181
27385
|
} catch (error) {
|
|
27386
|
+
if (replayInterrupt !== void 0) {
|
|
27387
|
+
this.logger.debug(`CallTool: Tool '${request.params.name}' requires client input (multi-round-trip).`, { key: replayInterrupt.key });
|
|
27388
|
+
return replayInterruptToInputRequired(replayInterrupt);
|
|
27389
|
+
}
|
|
27182
27390
|
const duration = Date.now() - startTime;
|
|
27183
27391
|
if (error instanceof Error && "issues" in error && Array.isArray(error.issues)) {
|
|
27184
27392
|
const issues = error.issues;
|
|
@@ -27594,6 +27802,17 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27594
27802
|
* ```
|
|
27595
27803
|
*/
|
|
27596
27804
|
async startStdio() {
|
|
27805
|
+
if (this.servesModernEra()) {
|
|
27806
|
+
this.stdioHandle = serveStdio(() => {
|
|
27807
|
+
const serverInstance = this.createServerInstance();
|
|
27808
|
+
this.stdioServerInstance = serverInstance;
|
|
27809
|
+
return serverInstance;
|
|
27810
|
+
}, { onerror: (error) => {
|
|
27811
|
+
this.logger.error("MCP stdio handler error", { error: error.toString() });
|
|
27812
|
+
} });
|
|
27813
|
+
this.logger.info("Started MCP Server (stdio, 2026-07-28 dual-era)");
|
|
27814
|
+
return;
|
|
27815
|
+
}
|
|
27597
27816
|
this.stdioTransport = new StdioServerTransport();
|
|
27598
27817
|
try {
|
|
27599
27818
|
await this.server.connect(this.stdioTransport);
|
|
@@ -27828,6 +28047,34 @@ var MCPServer = class extends MCPServerBase {
|
|
|
27828
28047
|
res.end();
|
|
27829
28048
|
return;
|
|
27830
28049
|
}
|
|
28050
|
+
if (this.servesModernEra()) this.assertModernEraHTTPOptions(options);
|
|
28051
|
+
if (!this.validateHTTPRequestHeaders(req, res, options)) return;
|
|
28052
|
+
if (this.servesModernEra()) {
|
|
28053
|
+
try {
|
|
28054
|
+
await this.getModernEraNodeHandler()(req, res);
|
|
28055
|
+
} catch (error) {
|
|
28056
|
+
const mastraError = new MastraError({
|
|
28057
|
+
id: "MCP_SERVER_HTTP_CONNECTION_FAILED",
|
|
28058
|
+
domain: ErrorDomain.MCP,
|
|
28059
|
+
category: ErrorCategory.USER,
|
|
28060
|
+
text: "Failed to handle MCP request on the 2026-07-28 protocol path"
|
|
28061
|
+
}, error);
|
|
28062
|
+
this.logger.trackException(mastraError);
|
|
28063
|
+
this.logger.error("Error handling HTTP request (2026-07-28 path)", { error: mastraError });
|
|
28064
|
+
if (!res.headersSent) {
|
|
28065
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
28066
|
+
res.end(JSON.stringify({
|
|
28067
|
+
jsonrpc: "2.0",
|
|
28068
|
+
error: {
|
|
28069
|
+
code: -32603,
|
|
28070
|
+
message: "Internal server error"
|
|
28071
|
+
},
|
|
28072
|
+
id: null
|
|
28073
|
+
}));
|
|
28074
|
+
}
|
|
28075
|
+
}
|
|
28076
|
+
return;
|
|
28077
|
+
}
|
|
27831
28078
|
if (options?.serverless || options && "sessionIdGenerator" in options && options.sessionIdGenerator === void 0) {
|
|
27832
28079
|
this.logger.debug("Running in stateless mode");
|
|
27833
28080
|
const enableJsonResponse = options?.enableJsonResponse ?? !options?.serverlessStreaming;
|
|
@@ -28136,6 +28383,16 @@ var MCPServer = class extends MCPServerBase {
|
|
|
28136
28383
|
*/
|
|
28137
28384
|
async close() {
|
|
28138
28385
|
try {
|
|
28386
|
+
if (this.stdioHandle) {
|
|
28387
|
+
await this.stdioHandle.close();
|
|
28388
|
+
this.stdioHandle = void 0;
|
|
28389
|
+
this.stdioServerInstance = void 0;
|
|
28390
|
+
}
|
|
28391
|
+
if (this.modernEraHandler) {
|
|
28392
|
+
await this.modernEraHandler.close();
|
|
28393
|
+
this.modernEraHandler = void 0;
|
|
28394
|
+
this.modernEraNodeHandler = void 0;
|
|
28395
|
+
}
|
|
28139
28396
|
if (this.stdioTransport) {
|
|
28140
28397
|
await this.stdioTransport.close?.();
|
|
28141
28398
|
this.stdioTransport = void 0;
|