@mastra/mcp 2.8.0 → 2.9.0-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/dist/index.cjs CHANGED
@@ -56,101 +56,6 @@ let _mastra_core_signals = require("@mastra/core/signals");
56
56
  // src/client/types.ts
57
57
  /** The current protocol revision; the one `/mcp` servers speak. */
58
58
  const MCP_CLIENT_PROTOCOL_VERSION = "2026-07-28";
59
- // ../schema-compat/dist/json-schema-traverse-D0CId4oM.js
60
- var __create$1 = Object.create;
61
- var __defProp$2 = Object.defineProperty;
62
- var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
63
- var __getOwnPropNames$1 = Object.getOwnPropertyNames;
64
- var __getProtoOf$1 = Object.getPrototypeOf;
65
- var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
66
- var __commonJSMin$1 = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
67
- var __copyProps$1 = (to, from, except, desc) => {
68
- if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
69
- key = keys[i];
70
- if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$2(to, key, {
71
- get: ((k) => from[k]).bind(null, key),
72
- enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
73
- });
74
- }
75
- return to;
76
- };
77
- var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(__getProtoOf$1(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp$1.call(mod, "default") ? __defProp$2(target, "default", {
78
- value: mod,
79
- enumerable: true
80
- }) : target, mod));
81
- var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin$1(((exports, module) => {
82
- var traverse = module.exports = function(schema, opts, cb) {
83
- if (typeof opts == "function") {
84
- cb = opts;
85
- opts = {};
86
- }
87
- cb = opts.cb || cb;
88
- var pre = typeof cb == "function" ? cb : cb.pre || function() {};
89
- var post = cb.post || function() {};
90
- _traverse(opts, pre, post, schema, "", schema);
91
- };
92
- traverse.keywords = {
93
- additionalItems: true,
94
- items: true,
95
- contains: true,
96
- additionalProperties: true,
97
- propertyNames: true,
98
- not: true,
99
- if: true,
100
- then: true,
101
- else: true
102
- };
103
- traverse.arrayKeywords = {
104
- items: true,
105
- allOf: true,
106
- anyOf: true,
107
- oneOf: true
108
- };
109
- traverse.propsKeywords = {
110
- $defs: true,
111
- definitions: true,
112
- properties: true,
113
- patternProperties: true,
114
- dependencies: true
115
- };
116
- traverse.skipKeywords = {
117
- default: true,
118
- enum: true,
119
- const: true,
120
- required: true,
121
- maximum: true,
122
- minimum: true,
123
- exclusiveMaximum: true,
124
- exclusiveMinimum: true,
125
- multipleOf: true,
126
- maxLength: true,
127
- minLength: true,
128
- pattern: true,
129
- format: true,
130
- maxItems: true,
131
- minItems: true,
132
- uniqueItems: true,
133
- maxProperties: true,
134
- minProperties: true
135
- };
136
- function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
137
- if (schema && typeof schema == "object" && !Array.isArray(schema)) {
138
- pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
139
- for (var key in schema) {
140
- var sch = schema[key];
141
- if (Array.isArray(sch)) {
142
- if (key in traverse.arrayKeywords) for (var i = 0; i < sch.length; i++) _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key + "/" + i, rootSchema, jsonPtr, key, schema, i);
143
- } else if (key in traverse.propsKeywords) {
144
- if (sch && typeof sch == "object") for (var prop in sch) _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop);
145
- } else if (key in traverse.keywords || opts.allKeys && !(key in traverse.skipKeywords)) _traverse(opts, pre, post, sch, jsonPtr + "/" + key, rootSchema, jsonPtr, key, schema);
146
- }
147
- post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
148
- }
149
- }
150
- function escapeJsonPtr(str) {
151
- return str.replace(/~/g, "~0").replace(/\//g, "~1");
152
- }
153
- }));
154
59
  // ../schema-compat/dist/esm-BG70JlG8.js
155
60
  const ignoreOverride$1 = Symbol("Let zodToJsonSchema decide on which parser to use");
156
61
  const defaultOptions$1 = {
@@ -1356,6 +1261,101 @@ var AiSdkSchemaWrapper = class {
1356
1261
  function toStandardSchema$2(schema) {
1357
1262
  return new AiSdkSchemaWrapper(schema);
1358
1263
  }
1264
+ // ../schema-compat/dist/json-schema-traverse-D0CId4oM.js
1265
+ var __create$1 = Object.create;
1266
+ var __defProp$2 = Object.defineProperty;
1267
+ var __getOwnPropDesc$1 = Object.getOwnPropertyDescriptor;
1268
+ var __getOwnPropNames$1 = Object.getOwnPropertyNames;
1269
+ var __getProtoOf$1 = Object.getPrototypeOf;
1270
+ var __hasOwnProp$1 = Object.prototype.hasOwnProperty;
1271
+ var __commonJSMin$1 = (cb, mod) => () => (mod || (cb((mod = { exports: {} }).exports, mod, mod.exports), cb = null), mod.exports);
1272
+ var __copyProps$1 = (to, from, except, desc) => {
1273
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames$1(from), i = 0, n = keys.length, key; i < n; i++) {
1274
+ key = keys[i];
1275
+ if (!__hasOwnProp$1.call(to, key) && key !== except) __defProp$2(to, key, {
1276
+ get: ((k) => from[k]).bind(null, key),
1277
+ enumerable: !(desc = __getOwnPropDesc$1(from, key)) || desc.enumerable
1278
+ });
1279
+ }
1280
+ return to;
1281
+ };
1282
+ var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(__getProtoOf$1(mod)) : {}, __copyProps$1(isNodeMode || !mod || !mod.__esModule || !__hasOwnProp$1.call(mod, "default") ? __defProp$2(target, "default", {
1283
+ value: mod,
1284
+ enumerable: true
1285
+ }) : target, mod));
1286
+ var require_json_schema_traverse = /* @__PURE__ */ __commonJSMin$1(((exports, module) => {
1287
+ var traverse = module.exports = function(schema, opts, cb) {
1288
+ if (typeof opts == "function") {
1289
+ cb = opts;
1290
+ opts = {};
1291
+ }
1292
+ cb = opts.cb || cb;
1293
+ var pre = typeof cb == "function" ? cb : cb.pre || function() {};
1294
+ var post = cb.post || function() {};
1295
+ _traverse(opts, pre, post, schema, "", schema);
1296
+ };
1297
+ traverse.keywords = {
1298
+ additionalItems: true,
1299
+ items: true,
1300
+ contains: true,
1301
+ additionalProperties: true,
1302
+ propertyNames: true,
1303
+ not: true,
1304
+ if: true,
1305
+ then: true,
1306
+ else: true
1307
+ };
1308
+ traverse.arrayKeywords = {
1309
+ items: true,
1310
+ allOf: true,
1311
+ anyOf: true,
1312
+ oneOf: true
1313
+ };
1314
+ traverse.propsKeywords = {
1315
+ $defs: true,
1316
+ definitions: true,
1317
+ properties: true,
1318
+ patternProperties: true,
1319
+ dependencies: true
1320
+ };
1321
+ traverse.skipKeywords = {
1322
+ default: true,
1323
+ enum: true,
1324
+ const: true,
1325
+ required: true,
1326
+ maximum: true,
1327
+ minimum: true,
1328
+ exclusiveMaximum: true,
1329
+ exclusiveMinimum: true,
1330
+ multipleOf: true,
1331
+ maxLength: true,
1332
+ minLength: true,
1333
+ pattern: true,
1334
+ format: true,
1335
+ maxItems: true,
1336
+ minItems: true,
1337
+ uniqueItems: true,
1338
+ maxProperties: true,
1339
+ minProperties: true
1340
+ };
1341
+ function _traverse(opts, pre, post, schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex) {
1342
+ if (schema && typeof schema == "object" && !Array.isArray(schema)) {
1343
+ pre(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
1344
+ for (var key in schema) {
1345
+ var sch = schema[key];
1346
+ if (Array.isArray(sch)) {
1347
+ if (key in traverse.arrayKeywords) for (var i = 0; i < sch.length; i++) _traverse(opts, pre, post, sch[i], jsonPtr + "/" + key + "/" + i, rootSchema, jsonPtr, key, schema, i);
1348
+ } else if (key in traverse.propsKeywords) {
1349
+ if (sch && typeof sch == "object") for (var prop in sch) _traverse(opts, pre, post, sch[prop], jsonPtr + "/" + key + "/" + escapeJsonPtr(prop), rootSchema, jsonPtr, key, schema, prop);
1350
+ } else if (key in traverse.keywords || opts.allKeys && !(key in traverse.skipKeywords)) _traverse(opts, pre, post, sch, jsonPtr + "/" + key, rootSchema, jsonPtr, key, schema);
1351
+ }
1352
+ post(schema, jsonPtr, rootSchema, parentJsonPtr, parentKeyword, parentSchema, keyIndex);
1353
+ }
1354
+ }
1355
+ function escapeJsonPtr(str) {
1356
+ return str.replace(/~/g, "~0").replace(/\//g, "~1");
1357
+ }
1358
+ }));
1359
1359
  // ../schema-compat/dist/json-schema-BdCwqjfN.js
1360
1360
  var require_code$1 = /* @__PURE__ */ __commonJSMin$1(((exports) => {
1361
1361
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -14411,6 +14411,35 @@ function isReconnectableMCPError(error) {
14411
14411
  const errorMessage = error.message.toLowerCase();
14412
14412
  return errorMessage.includes("not connected") || errorMessage.includes("http 400") || errorMessage.includes("http 401") || errorMessage.includes("http 403") || errorMessage.includes("http 404") || errorMessage.includes("econnrefused") || errorMessage.includes("fetch failed") || errorMessage.includes("connection refused") || errorMessage.includes("connection closed") || errorMessage.includes("typeerror: terminated");
14413
14413
  }
14414
+ const CONNECTION_NOT_ESTABLISHED_CODES = /* @__PURE__ */ new Set([
14415
+ "ECONNREFUSED",
14416
+ "ENOTFOUND",
14417
+ "EAI_AGAIN",
14418
+ "UND_ERR_CONNECT_TIMEOUT"
14419
+ ]);
14420
+ const REJECTED_BEFORE_DISPATCH_STATUSES = /* @__PURE__ */ new Set([
14421
+ 400,
14422
+ 401,
14423
+ 403,
14424
+ 404
14425
+ ]);
14426
+ /**
14427
+ * Whether a reconnectable failure proves the request never reached the server's
14428
+ * handler, so sending it again cannot repeat its effects.
14429
+ *
14430
+ * A reset socket (`fetch failed` with `UND_ERR_SOCKET`), a cut-off response
14431
+ * (`terminated`) or a closed transport (`Connection closed`) can all follow a
14432
+ * request the server already received and ran, so they return false.
14433
+ */
14434
+ function isUnsentRequestError(error) {
14435
+ if (!isReconnectableMCPError(error)) return false;
14436
+ const { httpStatus, code } = getMCPDiscoveryErrorDetails(error);
14437
+ if (typeof code === "number") return false;
14438
+ if (httpStatus !== void 0) return REJECTED_BEFORE_DISPATCH_STATUSES.has(httpStatus);
14439
+ const message = error.message.toLowerCase();
14440
+ if (message.includes("not connected") || message.includes("econnrefused") || message.includes("connection refused") || /\bhttp 40[0134]\b/.test(message)) return true;
14441
+ return typeof code === "string" && CONNECTION_NOT_ESTABLISHED_CODES.has(code);
14442
+ }
14414
14443
  const GATEWAY_UNAVAILABLE_STATUSES = /* @__PURE__ */ new Set([
14415
14444
  502,
14416
14445
  503,
@@ -15977,6 +16006,50 @@ function runOutsideDatadogTraceScope(callback) {
15977
16006
  if (!scope) return callback();
15978
16007
  return scope.activate(null, callback);
15979
16008
  }
16009
+ const LOGGABLE_ERROR_NAMES = /* @__PURE__ */ new Set([
16010
+ "Error",
16011
+ "TypeError",
16012
+ "RangeError",
16013
+ "SyntaxError",
16014
+ "AbortError",
16015
+ "TimeoutError",
16016
+ "McpError",
16017
+ "ProtocolError",
16018
+ "SdkError",
16019
+ "SdkHttpError",
16020
+ "SseError",
16021
+ "StreamableHTTPError",
16022
+ "UnauthorizedError",
16023
+ "OAuthError",
16024
+ "UnsupportedProtocolVersionError"
16025
+ ]);
16026
+ const LOGGABLE_ERROR_CODES = /* @__PURE__ */ new Set([
16027
+ "ECONNRESET",
16028
+ "ECONNREFUSED",
16029
+ "ECONNABORTED",
16030
+ "ETIMEDOUT",
16031
+ "ENOTFOUND",
16032
+ "EPIPE",
16033
+ "EAI_AGAIN",
16034
+ "ABORT_ERR",
16035
+ "UND_ERR_SOCKET",
16036
+ "UND_ERR_CONNECT_TIMEOUT",
16037
+ "UND_ERR_HEADERS_TIMEOUT",
16038
+ "UND_ERR_BODY_TIMEOUT"
16039
+ ]);
16040
+ /**
16041
+ * Names a tool-call failure for logs without its message: server error text can echo the submitted
16042
+ * arguments back (Issue #29318). The full error still reaches the caller.
16043
+ */
16044
+ function errorKind(error) {
16045
+ if (error instanceof _mastra_core_error.MastraError) return error.id;
16046
+ if (error instanceof Error) {
16047
+ const name = LOGGABLE_ERROR_NAMES.has(error.name) ? error.name : "Error";
16048
+ const code = error.code;
16049
+ return typeof code === "number" || typeof code === "string" && LOGGABLE_ERROR_CODES.has(code) ? `${name} (${code})` : name;
16050
+ }
16051
+ return typeof error;
16052
+ }
15980
16053
  /**
15981
16054
  * Convert an MCP LoggingLevel to a logger method name that exists in our logger
15982
16055
  */
@@ -17123,10 +17196,7 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
17123
17196
  });
17124
17197
  return res && finishToolCall(res);
17125
17198
  }
17126
- this.log("debug", `Executing tool: ${tool.name}`, {
17127
- toolArgs: input,
17128
- runId: context?.runId
17129
- });
17199
+ this.log("debug", `Executing tool: ${tool.name}`, { runId: context?.runId });
17130
17200
  const progressMeta = this.enableProgressTracking ? { progressToken: context?.runId || crypto.randomUUID() } : void 0;
17131
17201
  const _meta = this.requestMeta({
17132
17202
  ...context?._meta,
@@ -17155,7 +17225,7 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
17155
17225
  const finishToolCall = async (res) => {
17156
17226
  if (res.isError && this.onToolError === "throw") {
17157
17227
  const errorText = extractToolErrorText(res.content);
17158
- this.log("debug", `Tool reported an error: ${tool.name}`, { error: errorText });
17228
+ this.log("debug", `Tool reported an error: ${tool.name}`);
17159
17229
  throw new _mastra_core_error.MastraError({
17160
17230
  id: "MCP_CLIENT_TOOL_EXECUTION_FAILED",
17161
17231
  domain: _mastra_core_error.ErrorDomain.MCP,
@@ -17174,7 +17244,7 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
17174
17244
  const validationSchema = await getOutputValidationSchema();
17175
17245
  const validation = (0, _mastra_core_tools.validateToolOutput)(validationSchema, res.structuredContent, tool.name);
17176
17246
  if (validation.error) {
17177
- this.log("debug", `Tool output failed schema validation: ${tool.name}`, { message: validation.error.message });
17247
+ this.log("debug", `Tool output failed schema validation: ${tool.name}`, { issuePaths: (0, _mastra_core_tools.getValidationIssuePaths)(validation.error, validationSchema, "output") });
17178
17248
  return validation.error;
17179
17249
  }
17180
17250
  }
@@ -17187,25 +17257,40 @@ var InternalMastraMCPClient = class extends _mastra_core_base.MastraBase {
17187
17257
  try {
17188
17258
  return await executeToolCall();
17189
17259
  } catch (e) {
17190
- if (!(e instanceof _mastra_core_error.MastraError && (e.id === "MCP_CLIENT_TOOL_EXECUTION_FAILED" || e.id.startsWith("MCP_CLIENT_TOOL_TASK_"))) && isReconnectableMCPError(e)) {
17191
- this.log("debug", `Transport error detected for tool ${tool.name}, attempting reconnection...`, { error: e instanceof Error ? e.message : String(e) });
17260
+ const isToolExecutionError = e instanceof _mastra_core_error.MastraError && (e.id === "MCP_CLIENT_TOOL_EXECUTION_FAILED" || e.id.startsWith("MCP_CLIENT_TOOL_TASK_"));
17261
+ if (!isToolExecutionError && isReconnectableMCPError(e) && !isUnsentRequestError(e)) {
17262
+ if (annotations?.readOnlyHint === true || annotations?.idempotentHint === true) {
17263
+ this.log("debug", `Retrying replay-safe tool ${tool.name} after a transport error`, { error: errorKind(e) });
17264
+ if (this.transport !== failedTransport || this.reconnectPromise) await this.reconnectAfterTransportFailure(failedTransport, lifecycleGeneration);
17265
+ return await executeToolCall();
17266
+ }
17267
+ this.log("error", `Lost the connection during tool ${tool.name}; it may have run`, { error: errorKind(e) });
17268
+ throw new _mastra_core_error.MastraError({
17269
+ id: "MCP_CLIENT_TOOL_CALL_OUTCOME_UNKNOWN",
17270
+ domain: _mastra_core_error.ErrorDomain.MCP,
17271
+ category: _mastra_core_error.ErrorCategory.THIRD_PARTY,
17272
+ text: `The connection to MCP server "${this.name}" failed before tool "${tool.name}" returned a result. The tool may have run on the server, so it was not called again.`,
17273
+ details: {
17274
+ toolName: tool.name,
17275
+ serverName: this.name
17276
+ }
17277
+ }, e);
17278
+ }
17279
+ if (!isToolExecutionError && isReconnectableMCPError(e)) {
17280
+ this.log("debug", `Transport error detected for tool ${tool.name}, attempting reconnection...`, { error: errorKind(e) });
17192
17281
  try {
17193
17282
  await this.reconnectAfterTransportFailure(failedTransport, lifecycleGeneration);
17194
17283
  this.log("debug", `Retrying tool ${tool.name} after reconnection...`);
17195
17284
  return await executeToolCall();
17196
17285
  } catch (reconnectError) {
17197
17286
  this.log("error", `Reconnection or retry failed for tool ${tool.name}`, {
17198
- originalError: e instanceof Error ? e.message : String(e),
17199
- reconnectError: reconnectError instanceof Error ? reconnectError.stack : String(reconnectError),
17200
- toolArgs: input
17287
+ originalError: errorKind(e),
17288
+ reconnectError: errorKind(reconnectError)
17201
17289
  });
17202
17290
  throw reconnectError;
17203
17291
  }
17204
17292
  }
17205
- this.log("error", `Error calling tool: ${tool.name}`, {
17206
- error: e instanceof Error ? e.stack : JSON.stringify(e, null, 2),
17207
- toolArgs: input
17208
- });
17293
+ this.log("error", `Error calling tool: ${tool.name}`, { error: errorKind(e) });
17209
17294
  throw e;
17210
17295
  }
17211
17296
  });
@@ -17633,12 +17718,12 @@ async function createOAuthCallbackServer(options) {
17633
17718
  },
17634
17719
  close() {
17635
17720
  settle({ error: /* @__PURE__ */ new Error("OAuth callback server closed before receiving an authorization code") });
17636
- server.closeIdleConnections();
17637
17721
  return new Promise((resolve, reject) => {
17638
17722
  server.close((error) => {
17639
17723
  if (error && error.code !== "ERR_SERVER_NOT_RUNNING") reject(error);
17640
17724
  else resolve();
17641
17725
  });
17726
+ server.closeAllConnections();
17642
17727
  });
17643
17728
  }
17644
17729
  };
@@ -19007,7 +19092,7 @@ onListChanged: async (serverName, handler) => {
19007
19092
  const errors = {};
19008
19093
  const errorDetails = {};
19009
19094
  const durations = {};
19010
- const settled = await this.discoverAcrossServers((serverName) => this.getToolsForServer(serverName), {
19095
+ const settled = await this.discoverAcrossServers(async (serverName) => modelVisibleTools(await this.getToolsForServer(serverName)), {
19011
19096
  errorId: "MCP_CLIENT_GET_TOOLS_FAILED",
19012
19097
  logMessage: "Failed to list tools from server:"
19013
19098
  }, options);
@@ -19062,7 +19147,7 @@ onListChanged: async (serverName, handler) => {
19062
19147
  const errors = {};
19063
19148
  const errorDetails = {};
19064
19149
  const durations = {};
19065
- const settled = await this.discoverAcrossServers((serverName) => this.getToolsForServer(serverName), {
19150
+ const settled = await this.discoverAcrossServers(async (serverName) => modelVisibleTools(await this.getToolsForServer(serverName)), {
19066
19151
  errorId: "MCP_CLIENT_GET_TOOLSETS_FAILED",
19067
19152
  logMessage: "Failed to list toolsets from server:"
19068
19153
  }, options);
@@ -19126,7 +19211,7 @@ onListChanged: async (serverName, handler) => {
19126
19211
  const errorDetails = {};
19127
19212
  const durations = {};
19128
19213
  const settled = await this.discoverAcrossServers(async (serverName) => {
19129
- return (await this.getConnectedClientForServer(serverName)).toolDefinitions();
19214
+ return modelVisibleDefinitions(await (await this.getConnectedClientForServer(serverName)).toolDefinitions());
19130
19215
  }, {
19131
19216
  errorId: "MCP_CLIENT_GET_TOOL_DEFINITIONS_FAILED",
19132
19217
  logMessage: "Failed to list tool definitions from server:"
@@ -19195,7 +19280,7 @@ onListChanged: async (serverName, handler) => {
19195
19280
  this.logger.warn("Skipping cached MCP tool definitions for a server that is no longer configured", { serverName });
19196
19281
  continue;
19197
19282
  }
19198
- for (const [toolName, definition] of Object.entries(definitions)) try {
19283
+ for (const [toolName, definition] of Object.entries(modelVisibleDefinitions(definitions))) try {
19199
19284
  tools[`${serverName}_${toolName}`] = await this.toolFromDefinition({
19200
19285
  serverName,
19201
19286
  definition
@@ -19463,6 +19548,22 @@ onListChanged: async (serverName, handler) => {
19463
19548
  return {};
19464
19549
  }
19465
19550
  };
19551
+ /**
19552
+ * Whether a tool may be handed to the model. MCP App servers mark helpers their app calls itself
19553
+ * with `_meta.ui.visibility` lacking `'model'`; per the MCP Apps spec, hosts must not give those
19554
+ * to the model. They stay callable by the app through the server.
19555
+ */
19556
+ function isModelVisible(meta) {
19557
+ const ui = meta?.ui;
19558
+ const visibility = ui && typeof ui === "object" && "visibility" in ui ? ui.visibility : void 0;
19559
+ return !Array.isArray(visibility) || visibility.includes("model");
19560
+ }
19561
+ function modelVisibleTools(tools) {
19562
+ return Object.fromEntries(Object.entries(tools).filter(([, tool]) => isModelVisible(tool.mcp?._meta)));
19563
+ }
19564
+ function modelVisibleDefinitions(definitions) {
19565
+ return Object.fromEntries(Object.entries(definitions).filter(([, definition]) => isModelVisible(definition._meta)));
19566
+ }
19466
19567
  // src/server/actions.ts
19467
19568
  /** Runtime tool catalogue mutations. Every change is published to listening clients. */
19468
19569
  var ServerToolActions = class {
@@ -19594,8 +19695,17 @@ function callerTrace(ctx) {
19594
19695
  return callerTraceContext(meta?.traceparent, meta?.tracestate) ?? callerTraceContext(headers?.get("traceparent"), headers?.get("tracestate"));
19595
19696
  }
19596
19697
  /**
19698
+ * Where `@mastra/server` attaches, on `req.auth`, the request context its auth
19699
+ * resolved (user, permissions, roles, resource id, tenant, auth mode). A symbol
19700
+ * cannot come out of a token or JSON payload, so only server code can supply it.
19701
+ */
19702
+ const MCP_TRUSTED_REQUEST_CONTEXT = Symbol.for("mastra.mcp.trustedRequestContext");
19703
+ const MASTRA_USER_KEY = "mastra__user";
19704
+ /**
19597
19705
  * Builds the trusted application context for one request. Auth is re-derived from
19598
- * the transport every time; nothing is carried between continuation rounds. The W3C
19706
+ * the transport every time; nothing is carried between continuation rounds. When
19707
+ * the request came through a Mastra server route, the values its auth resolved are
19708
+ * copied in, so tools are authorized the same way as that server's routes. The W3C
19599
19709
  * trace fields sent by the client are exposed under `traceContext` as opaque
19600
19710
  * strings for observability only; they are never consulted for authorization.
19601
19711
  */
@@ -19605,6 +19715,12 @@ async function toRequestContext(ctx, mapAuthInfoToUser) {
19605
19715
  if (traceContext) requestContext.set("traceContext", traceContext);
19606
19716
  const authInfo = ctx.http?.authInfo;
19607
19717
  if (!authInfo) return requestContext;
19718
+ const trusted = authInfo[MCP_TRUSTED_REQUEST_CONTEXT];
19719
+ if (trusted && typeof trusted === "object") {
19720
+ for (const [key, value] of Object.entries(trusted)) requestContext.set(key, value);
19721
+ const trustedUser = trusted[MASTRA_USER_KEY];
19722
+ if (trustedUser) requestContext.set("user", trustedUser);
19723
+ }
19608
19724
  requestContext.set("authInfo", authInfo);
19609
19725
  const user = await mapAuthInfoToUser?.({
19610
19726
  authInfo,
@@ -22807,6 +22923,17 @@ function toSummary(listing) {
22807
22923
  function isWorkflowTaskContext(context) {
22808
22924
  return context?.kind === "workflow" && typeof context.runId === "string" && typeof context.workflowKey === "string";
22809
22925
  }
22926
+ /**
22927
+ * A run's result with its step results read: a storage v2 result's `steps` is a loader that reads
22928
+ * them from storage, so a view, and the text a tool answers with, gets them as for v1.
22929
+ */
22930
+ async function withLoadedSteps(result) {
22931
+ const steps = result.steps;
22932
+ return typeof steps === "function" ? {
22933
+ ...result,
22934
+ steps: await steps()
22935
+ } : result;
22936
+ }
22810
22937
  /** Statuses a run never leaves. */
22811
22938
  const FINAL_STATUSES = /* @__PURE__ */ new Set([
22812
22939
  "success",
@@ -22872,6 +22999,136 @@ function stepAt(workflow, path) {
22872
22999
  }
22873
23000
  return step;
22874
23001
  }
23002
+ // src/server/uri-template.ts
23003
+ /**
23004
+ * Whether `uri` is an RFC 6570 expansion of `template`. Decides membership only,
23005
+ * without extracting variables.
23006
+ *
23007
+ * The SDK's `UriTemplate.match` is a backtracking regex that takes minutes on a
23008
+ * few KB of client URI when variables are adjacent (`x://{a}-{b}-{c}-{d}`); this
23009
+ * walks the URI once per template segment instead.
23010
+ *
23011
+ * Plain `{x}` and `{+x}` must be non-empty, since an empty path segment never
23012
+ * names a resource. Outside `{+x}` and `{#x}`, values must be percent-encoded:
23013
+ * an ASCII reserved character other than `,` ends the value. Raw non-ASCII
23014
+ * characters are accepted because clients often send them unencoded.
23015
+ */
23016
+ function matchesUriTemplate(template, uri) {
23017
+ return advance(uri, /* @__PURE__ */ new Set([0]), parse(template)).has(uri.length);
23018
+ }
23019
+ const encoded = (char) => /[\w\-.~,]/.test(char) || char.charCodeAt(0) > 127;
23020
+ const anything = () => true;
23021
+ const literal = (text) => ({
23022
+ kind: "literal",
23023
+ text
23024
+ });
23025
+ const run = (allowed, min = 0) => ({
23026
+ kind: "run",
23027
+ allowed,
23028
+ min
23029
+ });
23030
+ const optional = (...segments) => ({
23031
+ kind: "optional",
23032
+ segments
23033
+ });
23034
+ const repeat = (...segments) => ({
23035
+ kind: "repeat",
23036
+ segments
23037
+ });
23038
+ const choice = (options) => ({
23039
+ kind: "choice",
23040
+ options
23041
+ });
23042
+ function parse(template) {
23043
+ const segments = [];
23044
+ let i = 0;
23045
+ while (i < template.length) {
23046
+ const open = template.indexOf("{", i);
23047
+ const close = open === -1 ? -1 : template.indexOf("}", open);
23048
+ if (close === -1) {
23049
+ segments.push(literal(template.slice(i)));
23050
+ break;
23051
+ }
23052
+ if (open > i) segments.push(literal(template.slice(i, open)));
23053
+ segments.push(...expression(template.slice(open + 1, close)));
23054
+ i = close + 1;
23055
+ }
23056
+ return segments;
23057
+ }
23058
+ function expression(body) {
23059
+ const operator = "+#./;?&".includes(body[0] ?? "") ? body[0] : "";
23060
+ const specs = body.slice(operator.length).split(",").map((spec) => ({
23061
+ name: spec.replace(/[*]|:\d*$/g, "").trim(),
23062
+ exploded: spec.includes("*")
23063
+ })).filter((spec) => spec.name);
23064
+ switch (operator) {
23065
+ case "": return [run(encoded, 1)];
23066
+ case "+": return [run(anything, 1)];
23067
+ case "#": return [optional(literal("#"), run(anything))];
23068
+ case "/": return specs.map(({ exploded }) => exploded ? repeat(literal("/"), run(encoded)) : optional(literal("/"), run(encoded)));
23069
+ case ".": return specs.map(() => optional(literal("."), run(encoded)));
23070
+ case ";": return specs.map(({ name, exploded }) => (exploded ? repeat : optional)(literal(`;${name}`), optional(literal("="), run(encoded))));
23071
+ default: {
23072
+ const pair = ({ name, exploded }, separator) => {
23073
+ const first = [literal(`${separator}${name}=`), run(encoded)];
23074
+ return exploded ? [...first, repeat(literal(`&${name}=`), run(encoded))] : first;
23075
+ };
23076
+ const firstPresent = specs.map((spec, i) => [...pair(spec, operator), ...specs.slice(i + 1).map((later) => optional(...pair(later, "&")))]);
23077
+ return [optional(choice(firstPresent))];
23078
+ }
23079
+ }
23080
+ }
23081
+ /** Every URI offset reachable after matching `segments` from any offset in `from`. */
23082
+ function advance(uri, from, segments) {
23083
+ let positions = from;
23084
+ for (const segment of segments) {
23085
+ if (positions.size === 0) break;
23086
+ positions = step(uri, positions, segment);
23087
+ }
23088
+ return positions;
23089
+ }
23090
+ function step(uri, positions, segment) {
23091
+ const next = /* @__PURE__ */ new Set();
23092
+ switch (segment.kind) {
23093
+ case "literal":
23094
+ for (const p of positions) if (uri.startsWith(segment.text, p)) next.add(p + segment.text.length);
23095
+ return next;
23096
+ case "choice":
23097
+ for (const option of segment.options) for (const p of advance(uri, positions, option)) next.add(p);
23098
+ return next;
23099
+ case "run": {
23100
+ let end = 0;
23101
+ for (const p of [...positions].sort((a, b) => a - b)) {
23102
+ const covered = p < end && next.has(p);
23103
+ if (segment.min === 0) next.add(p);
23104
+ if (covered) continue;
23105
+ end = p;
23106
+ for (let width = charWidth(uri, end, segment.allowed); width > 0; width = charWidth(uri, end, segment.allowed)) next.add(end += width);
23107
+ }
23108
+ return next;
23109
+ }
23110
+ case "optional":
23111
+ for (const p of positions) next.add(p);
23112
+ for (const p of advance(uri, positions, segment.segments)) next.add(p);
23113
+ return next;
23114
+ case "repeat": {
23115
+ for (const p of positions) next.add(p);
23116
+ let frontier = positions;
23117
+ while (frontier.size > 0) {
23118
+ const reached = [...advance(uri, frontier, segment.segments)].filter((p) => !next.has(p));
23119
+ for (const p of reached) next.add(p);
23120
+ frontier = new Set(reached);
23121
+ }
23122
+ return next;
23123
+ }
23124
+ }
23125
+ }
23126
+ /** Characters a run consumes at `i`: a whole `%HH` triplet, one allowed character, or none. */
23127
+ function charWidth(uri, i, allowed) {
23128
+ if (i >= uri.length) return 0;
23129
+ if (uri[i] === "%") return /^%[0-9A-Fa-f]{2}/.test(uri.slice(i, i + 3)) ? 3 : 0;
23130
+ return allowed(uri[i]) ? 1 : 0;
23131
+ }
22875
23132
  // src/server/server.ts
22876
23133
  const EMPTY_OBJECT_SCHEMA = {
22877
23134
  type: "object",
@@ -23076,11 +23333,11 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
23076
23333
  inputSchema: workflow.inputSchema,
23077
23334
  mcp: { toolType: "workflow" },
23078
23335
  execute: async (inputData, context) => {
23079
- return (await workflow.createRun({ runId: context?.requestContext?.get("runId") })).start({
23336
+ return withLoadedSteps(await (await workflow.createRun({ runId: context?.requestContext?.get("runId") })).start({
23080
23337
  inputData,
23081
23338
  requestContext: context?.requestContext,
23082
23339
  tracingContext: context?.tracingContext
23083
- });
23340
+ }));
23084
23341
  }
23085
23342
  }));
23086
23343
  }
@@ -23870,7 +24127,7 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
23870
24127
  if (!workflow) throw new Error(`Workflow '${workflowKey}' is not served`);
23871
24128
  const input = (0, _mastra_core_tools.validateToolInput)(workflow.inputSchema, args, name);
23872
24129
  if (input.error) {
23873
- this.logger.warn(`Tool '${name}' rejected its input.`, { error: input.error.message });
24130
+ this.logger.warn(`Tool '${name}' rejected its input.`, { issuePaths: (0, _mastra_core_tools.getValidationIssuePaths)(input.error, workflow.inputSchema) });
23874
24131
  return errorResult$1(input.error.message);
23875
24132
  }
23876
24133
  const context = {
@@ -23893,11 +24150,11 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
23893
24150
  };
23894
24151
  const start = (runTracing) => {
23895
24152
  const run = workflow.createRun({ runId: context.runId });
23896
- const result = run.then((started) => started.start({
24153
+ const result = run.then(async (started) => withLoadedSteps(await started.start({
23897
24154
  inputData: input.data,
23898
24155
  requestContext,
23899
24156
  tracingContext: runTracing
23900
- }));
24157
+ })));
23901
24158
  const cancel = () => void run.then((started) => started.cancel()).catch((error) => this.logger.warn("Could not cancel workflow run", {
23902
24159
  tool: name,
23903
24160
  error
@@ -24026,12 +24283,12 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
24026
24283
  error
24027
24284
  })));
24028
24285
  try {
24029
- view = await run.resume({
24286
+ view = await withLoadedSteps(await run.resume({
24030
24287
  step: answered.step,
24031
24288
  resumeData: answer.content ?? {},
24032
24289
  requestContext,
24033
24290
  ...span && { tracingContext: { currentSpan: span } }
24034
- });
24291
+ }));
24035
24292
  } catch (error) {
24036
24293
  if (isResumeClaimLost(error)) return this.watchWorkflowRun(workflow, context, attempt.signal);
24037
24294
  const now = await workflow.getWorkflowRunById(context.runId, { withNestedWorkflows: false });
@@ -24199,7 +24456,7 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
24199
24456
  }
24200
24457
  toCallToolResult(name, tool, value) {
24201
24458
  if ((0, _mastra_core_tools.isValidationError)(value)) {
24202
- this.logger.warn(`Tool '${name}' rejected its input.`, { error: value.message });
24459
+ this.logger.warn(`Tool '${name}' rejected its input.`, { issuePaths: (0, _mastra_core_tools.getValidationIssuePaths)(value, tool.parameters) });
24203
24460
  return errorResult$1(value.message);
24204
24461
  }
24205
24462
  const resourceUri = uiResourceUri(tool.mcp?._meta);
@@ -24244,10 +24501,24 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
24244
24501
  extra: toToolExecutionContext(ctx, this.name).extra,
24245
24502
  requestContext
24246
24503
  }) ?? []];
24504
+ const findResource = async (uri, ctx, requestContext) => {
24505
+ const listed = (await listResources(ctx, requestContext)).find((r) => r.uri === uri);
24506
+ if (listed || !options?.resourceTemplates) return listed;
24507
+ const template = (await options.resourceTemplates({
24508
+ extra: toToolExecutionContext(ctx, this.name).extra,
24509
+ requestContext
24510
+ })).find((t) => matchesUriTemplate(t.uriTemplate, uri));
24511
+ return template && {
24512
+ uri,
24513
+ name: template.name,
24514
+ mimeType: template.mimeType,
24515
+ _meta: template._meta
24516
+ };
24517
+ };
24247
24518
  this.setTracedHandler(server, "resources/list", async (_request, ctx, trace) => ({ resources: await listResources(ctx, trace.requestContext) }));
24248
24519
  this.setTracedHandler(server, "resources/read", async (request, ctx, trace) => {
24249
24520
  const uri = request.params.uri;
24250
- const resource = (await listResources(ctx, trace.requestContext)).find((r) => r.uri === uri);
24521
+ const resource = await findResource(uri, ctx, trace.requestContext);
24251
24522
  if (!resource) throw new _modelcontextprotocol_server.ProtocolError(_modelcontextprotocol_server.ProtocolErrorCode.InvalidParams, `Resource not found: ${uri}`);
24252
24523
  const html = this.appResourceHtml.get(uri);
24253
24524
  if (html !== void 0) return { contents: [this.resourceContents(resource, { text: html })] };
@@ -24644,10 +24915,7 @@ var MCPServer = class extends _mastra_core_mcp.MCPServerBase {
24644
24915
  id: "MCP_SERVER_TOOL_EXECUTE_FAILED",
24645
24916
  domain: _mastra_core_error.ErrorDomain.MCP,
24646
24917
  category: _mastra_core_error.ErrorCategory.USER,
24647
- details: {
24648
- toolId,
24649
- args: JSON.stringify(args)
24650
- }
24918
+ details: { toolId }
24651
24919
  }, error);
24652
24920
  this.logger.trackException(mastraError);
24653
24921
  throw mastraError;