@mastra/mcp 2.5.0-alpha.3 → 2.6.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (40) hide show
  1. package/dist/cli/generate-K-rW1Wck.js +219 -0
  2. package/dist/cli/generate-K-rW1Wck.js.map +1 -0
  3. package/dist/cli/generate.d.ts +18 -0
  4. package/dist/cli/generate.d.ts.map +1 -0
  5. package/dist/cli/index.d.ts +3 -0
  6. package/dist/cli/index.d.ts.map +1 -0
  7. package/dist/cli/index.js +33 -0
  8. package/dist/cli/index.js.map +1 -0
  9. package/dist/cli/json-schema-dialect-B5B7l9Po.js +133 -0
  10. package/dist/cli/json-schema-dialect-B5B7l9Po.js.map +1 -0
  11. package/dist/cli/typegen-CLDjVF16.js +364 -0
  12. package/dist/cli/typegen-CLDjVF16.js.map +1 -0
  13. package/dist/cli/typegen.d.ts +14 -0
  14. package/dist/cli/typegen.d.ts.map +1 -0
  15. package/dist/client/client.d.ts +2 -1
  16. package/dist/client/client.d.ts.map +1 -1
  17. package/dist/client/configuration.d.ts +30 -10
  18. package/dist/client/configuration.d.ts.map +1 -1
  19. package/dist/client/index.d.ts +1 -1
  20. package/dist/client/index.d.ts.map +1 -1
  21. package/dist/client/types.d.ts +38 -1
  22. package/dist/client/types.d.ts.map +1 -1
  23. package/dist/docs/SKILL.md +2 -3
  24. package/dist/docs/assets/SOURCE_MAP.json +1 -1
  25. package/dist/docs/references/docs-connections-mcp.md +159 -50
  26. package/dist/docs/references/docs-connections-overview.md +1 -1
  27. package/dist/docs/references/docs-guides-mcp-authentication-authorization.md +2 -2
  28. package/dist/docs/references/reference-migrations-mcp-v2.md +1 -1
  29. package/dist/docs/references/reference-tools-mcp-client.md +202 -11
  30. package/dist/docs/references/reference-tools-mcp-server.md +5 -3
  31. package/dist/index.cjs +116 -121
  32. package/dist/index.cjs.map +1 -1
  33. package/dist/index.js +116 -121
  34. package/dist/index.js.map +1 -1
  35. package/dist/server/server.d.ts +13 -1
  36. package/dist/server/server.d.ts.map +1 -1
  37. package/dist/shared/json-schema-dialect.d.ts +34 -0
  38. package/dist/shared/json-schema-dialect.d.ts.map +1 -1
  39. package/package.json +22 -17
  40. package/dist/docs/references/docs-guides-mcp-events.md +0 -306
package/dist/index.js CHANGED
@@ -12952,6 +12952,89 @@ const SCHEMA_MAP_KEYWORDS$1 = /* @__PURE__ */ new Set([
12952
12952
  const MAX_JSON_SCHEMA_NODES = 1e4;
12953
12953
  var Unconvertible = class extends Error {};
12954
12954
  /**
12955
+ * Bounds the work a validator or type generator can be asked to do for an untrusted schema.
12956
+ * Only schema-bearing keywords are walked, so deeply nested annotation data such as `default`
12957
+ * or `examples` does not count. The counted nodes are returned so callers can also budget
12958
+ * across a whole catalogue rather than one schema at a time.
12959
+ *
12960
+ * The keyword lists are intentionally wider than the conversion sets above: this walk has to
12961
+ * reach every position that can carry validation rules, including `dependencies`,
12962
+ * `prefixItems`, and `contentSchema`.
12963
+ */
12964
+ function jsonSchemaComplexity(schema) {
12965
+ const seen = /* @__PURE__ */ new Set();
12966
+ let nodes = 0;
12967
+ const stack = [{
12968
+ value: schema,
12969
+ depth: 0
12970
+ }];
12971
+ const schemaMapKeywords = [
12972
+ "$defs",
12973
+ "definitions",
12974
+ "properties",
12975
+ "patternProperties",
12976
+ "dependentSchemas",
12977
+ "dependencies"
12978
+ ];
12979
+ const schemaArrayKeywords = [
12980
+ "prefixItems",
12981
+ "allOf",
12982
+ "anyOf",
12983
+ "oneOf",
12984
+ "items"
12985
+ ];
12986
+ const schemaKeywords = [
12987
+ "additionalProperties",
12988
+ "unevaluatedProperties",
12989
+ "additionalItems",
12990
+ "unevaluatedItems",
12991
+ "items",
12992
+ "contains",
12993
+ "propertyNames",
12994
+ "not",
12995
+ "if",
12996
+ "then",
12997
+ "else",
12998
+ "contentSchema"
12999
+ ];
13000
+ while (stack.length > 0) {
13001
+ const { value, depth } = stack.pop();
13002
+ if (value === null || typeof value !== "object" || Array.isArray(value) || seen.has(value)) continue;
13003
+ seen.add(value);
13004
+ nodes += 1;
13005
+ if (depth > 128) return {
13006
+ error: `JSON Schema exceeds the maximum depth of 128`,
13007
+ limit: "depth",
13008
+ nodes
13009
+ };
13010
+ if (nodes > 1e4) return {
13011
+ error: `JSON Schema exceeds the maximum node count of ${MAX_JSON_SCHEMA_NODES}`,
13012
+ limit: "nodes",
13013
+ nodes
13014
+ };
13015
+ const record = value;
13016
+ for (const keyword of schemaMapKeywords) {
13017
+ const schemas = record[keyword];
13018
+ if (schemas && typeof schemas === "object" && !Array.isArray(schemas)) for (const child of Object.values(schemas)) stack.push({
13019
+ value: child,
13020
+ depth: depth + 1
13021
+ });
13022
+ }
13023
+ for (const keyword of schemaArrayKeywords) {
13024
+ const schemas = record[keyword];
13025
+ if (Array.isArray(schemas)) for (const child of schemas) stack.push({
13026
+ value: child,
13027
+ depth: depth + 1
13028
+ });
13029
+ }
13030
+ for (const keyword of schemaKeywords) if (record[keyword] !== void 0) stack.push({
13031
+ value: record[keyword],
13032
+ depth: depth + 1
13033
+ });
13034
+ }
13035
+ return { nodes };
13036
+ }
13037
+ /**
12955
13038
  * Converts a 2019-09 schema (e.g. from zod v3) into 2020-12 form. Tuples are the
12956
13039
  * only structural difference handled: array-form `items` becomes `prefixItems`,
12957
13040
  * and `additionalItems` becomes `items`.
@@ -15142,75 +15225,6 @@ async function fetchFollowingAllowedRedirects(fetchImpl, url, init, allowedHosts
15142
15225
  //#endregion
15143
15226
  //#region src/client/client.ts
15144
15227
  const DEFAULT_SERVER_CONNECT_TIMEOUT_MSEC = 3e3;
15145
- /**
15146
- * Bounds the work a validator can be asked to do for an untrusted tool catalogue.
15147
- * Only schema-bearing keywords are walked, so deeply nested annotation data such as
15148
- * `default` or `examples` does not count.
15149
- */
15150
- function getJsonSchemaComplexityError(schema) {
15151
- const seen = /* @__PURE__ */ new Set();
15152
- let nodes = 0;
15153
- const stack = [{
15154
- value: schema,
15155
- depth: 0
15156
- }];
15157
- const schemaMapKeywords = [
15158
- "$defs",
15159
- "definitions",
15160
- "properties",
15161
- "patternProperties",
15162
- "dependentSchemas",
15163
- "dependencies"
15164
- ];
15165
- const schemaArrayKeywords = [
15166
- "prefixItems",
15167
- "allOf",
15168
- "anyOf",
15169
- "oneOf",
15170
- "items"
15171
- ];
15172
- const schemaKeywords = [
15173
- "additionalProperties",
15174
- "unevaluatedProperties",
15175
- "additionalItems",
15176
- "unevaluatedItems",
15177
- "items",
15178
- "contains",
15179
- "propertyNames",
15180
- "not",
15181
- "if",
15182
- "then",
15183
- "else",
15184
- "contentSchema"
15185
- ];
15186
- while (stack.length > 0) {
15187
- const { value, depth } = stack.pop();
15188
- if (value === null || typeof value !== "object" || Array.isArray(value) || seen.has(value)) continue;
15189
- seen.add(value);
15190
- nodes += 1;
15191
- if (depth > 128) return `JSON Schema exceeds the maximum depth of 128`;
15192
- if (nodes > 1e4) return `JSON Schema exceeds the maximum node count of ${MAX_JSON_SCHEMA_NODES}`;
15193
- const record = value;
15194
- for (const keyword of schemaMapKeywords) {
15195
- const schemas = record[keyword];
15196
- if (schemas && typeof schemas === "object" && !Array.isArray(schemas)) for (const child of Object.values(schemas)) stack.push({
15197
- value: child,
15198
- depth: depth + 1
15199
- });
15200
- }
15201
- for (const keyword of schemaArrayKeywords) {
15202
- const schemas = record[keyword];
15203
- if (Array.isArray(schemas)) for (const child of schemas) stack.push({
15204
- value: child,
15205
- depth: depth + 1
15206
- });
15207
- }
15208
- for (const keyword of schemaKeywords) if (record[keyword] !== void 0) stack.push({
15209
- value: record[keyword],
15210
- depth: depth + 1
15211
- });
15212
- }
15213
- }
15214
15228
  const SUPPORTED_DIALECTS = /* @__PURE__ */ new Set([
15215
15229
  JSON_SCHEMA_2020_12,
15216
15230
  `${JSON_SCHEMA_2020_12}#`,
@@ -15416,6 +15430,7 @@ var InternalMastraMCPClient = class extends MastraBase {
15416
15430
  name;
15417
15431
  client;
15418
15432
  timeout;
15433
+ processHooks;
15419
15434
  logHandler;
15420
15435
  serverLogLevel;
15421
15436
  enableProgressTracking;
@@ -15452,12 +15467,13 @@ var InternalMastraMCPClient = class extends MastraBase {
15452
15467
  /**
15453
15468
  * @internal
15454
15469
  */
15455
- constructor({ name, version = "1.0.0", clientInfo, server, timeout = DEFAULT_REQUEST_TIMEOUT_MSEC, webhook }) {
15470
+ constructor({ name, version = "1.0.0", clientInfo, server, timeout = DEFAULT_REQUEST_TIMEOUT_MSEC, registerProcessHooks = true, webhook }) {
15456
15471
  super({ name: "MastraMCPClient" });
15457
15472
  this.webhook = webhook;
15458
15473
  loadInputSchemaMetaValidator();
15459
15474
  this.name = name;
15460
15475
  this.timeout = timeout;
15476
+ this.processHooks = registerProcessHooks;
15461
15477
  this.logHandler = server.logger;
15462
15478
  this.serverLogLevel = server.enableServerLogs ?? true ? server.serverLogLevel ?? DEFAULT_SERVER_LOG_LEVEL : void 0;
15463
15479
  this.serverConfig = server;
@@ -15800,7 +15816,7 @@ var InternalMastraMCPClient = class extends MastraBase {
15800
15816
  reject(e);
15801
15817
  }
15802
15818
  });
15803
- this.registerProcessHooks();
15819
+ if (this.processHooks) this.registerProcessHooks();
15804
15820
  return this.isConnected;
15805
15821
  }
15806
15822
  /**
@@ -16343,7 +16359,7 @@ var InternalMastraMCPClient = class extends MastraBase {
16343
16359
  buildToolFromListEntry(tool, serverMeta) {
16344
16360
  const rawInputSchema = tool.inputSchema;
16345
16361
  const inputSchema = rawInputSchema && typeof rawInputSchema === "object" && "jsonSchema" in rawInputSchema ? rawInputSchema.jsonSchema : rawInputSchema;
16346
- const inputSchemaError = getJsonSchemaComplexityError(inputSchema) ?? getInputSchemaError(inputSchema);
16362
+ const inputSchemaError = jsonSchemaComplexity(inputSchema).error ?? getInputSchemaError(inputSchema);
16347
16363
  if (inputSchemaError) {
16348
16364
  const details = {
16349
16365
  serverName: this.name,
@@ -16385,7 +16401,7 @@ var InternalMastraMCPClient = class extends MastraBase {
16385
16401
  ...annotations ? { annotations } : {}
16386
16402
  } } : {};
16387
16403
  const outputSchema = tool.outputSchema ? withDefaultDialect("jsonSchema" in tool.outputSchema ? tool.outputSchema.jsonSchema : tool.outputSchema) : void 0;
16388
- const outputSchemaComplexityError = outputSchema ? getJsonSchemaComplexityError(outputSchema) : void 0;
16404
+ const outputSchemaComplexityError = outputSchema ? jsonSchemaComplexity(outputSchema).error : void 0;
16389
16405
  let outputValidationSchema;
16390
16406
  const getOutputValidationSchema = async () => {
16391
16407
  if (!outputSchema) return void 0;
@@ -17327,6 +17343,10 @@ var MCPClientServerProxy = class extends MCPServerBase {
17327
17343
  //#endregion
17328
17344
  //#region src/client/configuration.ts
17329
17345
  const mcpClientInstances = /* @__PURE__ */ new Map();
17346
+ /** Reads the shared cache without promising the snapshot shape it was created with. */
17347
+ function cachedClient(id) {
17348
+ return mcpClientInstances.get(id);
17349
+ }
17330
17350
  const TOOL_DISCOVERY_MAX_ATTEMPTS = 2;
17331
17351
  const LOOPBACK_IPV4 = /^127\.(?:\d{1,3})\.(?:\d{1,3})\.(?:\d{1,3})$/;
17332
17352
  function isLoopbackHostname(hostname) {
@@ -17359,10 +17379,12 @@ function isLoopbackHostname(hostname) {
17359
17379
  * if packaged docs are unavailable.
17360
17380
  */
17361
17381
  var MCPClient = class extends MastraBase {
17382
+ typegen;
17362
17383
  serverConfigs = {};
17363
17384
  id;
17364
17385
  defaultTimeout;
17365
17386
  defaultClientInfo;
17387
+ registerProcessHooks;
17366
17388
  webhook;
17367
17389
  mcpClientsById = /* @__PURE__ */ new Map();
17368
17390
  disconnectPromise = null;
@@ -17407,6 +17429,7 @@ var MCPClient = class extends MastraBase {
17407
17429
  super({ name: "MCPClient" });
17408
17430
  this.defaultTimeout = args.timeout ?? DEFAULT_REQUEST_TIMEOUT_MSEC;
17409
17431
  this.defaultClientInfo = args.clientInfo && { ...args.clientInfo };
17432
+ this.registerProcessHooks = args.registerProcessHooks ?? true;
17410
17433
  this.serverConfigs = args.servers;
17411
17434
  this.webhook = args.webhook && {
17412
17435
  options: { ...args.webhook },
@@ -17415,16 +17438,13 @@ var MCPClient = class extends MastraBase {
17415
17438
  this.id = args.id ?? this.makeId();
17416
17439
  if (args.id) {
17417
17440
  this.id = args.id;
17418
- const cached = mcpClientInstances.get(this.id);
17419
- if (cached && (!equal(cached.serverConfigs, args.servers) || !equal(cached.defaultClientInfo, args.clientInfo))) {
17420
- const existingInstance = mcpClientInstances.get(this.id);
17421
- if (existingInstance) {
17422
- existingInstance.disconnect();
17423
- mcpClientInstances.delete(this.id);
17424
- }
17441
+ const cached = cachedClient(this.id);
17442
+ if (cached && (!equal(cached.serverConfigs, args.servers) || !equal(cached.defaultClientInfo, args.clientInfo) || cached.registerProcessHooks !== this.registerProcessHooks || cached.typegen?.outFile !== args.typegen?.outFile)) {
17443
+ cached.disconnect();
17444
+ mcpClientInstances.delete(this.id);
17425
17445
  }
17426
17446
  } else this.id = this.makeId();
17427
- const existingInstance = mcpClientInstances.get(this.id);
17447
+ const existingInstance = cachedClient(this.id);
17428
17448
  if (existingInstance) {
17429
17449
  if (!args.id) throw new Error(`MCPClient was initialized multiple times with the same configuration options.
17430
17450
 
@@ -17437,6 +17457,11 @@ To fix this you have three different options:
17437
17457
  `);
17438
17458
  return existingInstance;
17439
17459
  }
17460
+ const { typegen } = args;
17461
+ if (typegen !== void 0) {
17462
+ if (!typegen || typeof typegen.outFile !== "string" || !typegen.outFile.trim() || typegen.outFile.includes("\0")) throw new Error("typegen.outFile must be a nonempty file path");
17463
+ this.typegen = Object.freeze({ outFile: typegen.outFile });
17464
+ }
17440
17465
  mcpClientInstances.set(this.id, this);
17441
17466
  this.addToInstanceCache();
17442
17467
  return this;
@@ -17900,9 +17925,10 @@ onListChanged: async (serverName, handler) => {
17900
17925
  if (!mcpClientInstances.has(this.id)) mcpClientInstances.set(this.id, this);
17901
17926
  }
17902
17927
  makeId() {
17903
- const text = JSON.stringify(this.defaultClientInfo === void 0 ? this.serverConfigs : {
17928
+ const text = JSON.stringify(this.defaultClientInfo === void 0 && this.registerProcessHooks ? this.serverConfigs : {
17904
17929
  servers: this.serverConfigs,
17905
- clientInfo: this.defaultClientInfo
17930
+ ...this.defaultClientInfo === void 0 ? {} : { clientInfo: this.defaultClientInfo },
17931
+ ...this.registerProcessHooks ? {} : { registerProcessHooks: false }
17906
17932
  }).normalize("NFKC");
17907
17933
  return createHash("sha256").update("MCPClient").update(text).digest("hex");
17908
17934
  }
@@ -18143,24 +18169,6 @@ onListChanged: async (serverName, handler) => {
18143
18169
  async listTools() {
18144
18170
  return (await this.listToolsWithErrors()).tools;
18145
18171
  }
18146
- /**
18147
- * Retrieves all tools from all configured servers with namespaced names,
18148
- * along with any per-server errors.
18149
- *
18150
- * Like listTools(), but also returns errors for servers that failed to connect
18151
- * or list tools. This allows callers to report specific failure reasons per server.
18152
- *
18153
- * @returns Object with successful `tools`, legacy string `errors`, and structured `errorDetails`.
18154
- * Transient connection failures are retried once after reconnecting the affected server.
18155
- *
18156
- * @example
18157
- * ```typescript
18158
- * const { tools, errors, errorDetails } = await mcp.listToolsWithErrors();
18159
- * for (const [name, err] of Object.entries(errors)) {
18160
- * console.error(`Server ${name} failed: ${err}`, errorDetails[name]);
18161
- * }
18162
- * ```
18163
- */
18164
18172
  async listToolsWithErrors(options) {
18165
18173
  this.addToInstanceCache();
18166
18174
  const connectedTools = {};
@@ -18216,23 +18224,6 @@ onListChanged: async (serverName, handler) => {
18216
18224
  async listToolsets() {
18217
18225
  return (await this.listToolsetsWithErrors()).toolsets;
18218
18226
  }
18219
- /**
18220
- * Returns toolsets organized by server name, along with any per-server errors.
18221
- *
18222
- * Like listToolsets(), but also returns errors for servers that failed to connect
18223
- * or list tools. This allows callers to report specific failure reasons per server.
18224
- *
18225
- * @returns Object with successful `toolsets`, legacy string `errors`, and structured `errorDetails`.
18226
- * Transient connection failures are retried once after reconnecting the affected server.
18227
- *
18228
- * @example
18229
- * ```typescript
18230
- * const { toolsets, errors, errorDetails } = await mcp.listToolsetsWithErrors();
18231
- * for (const [name, err] of Object.entries(errors)) {
18232
- * console.error(`Server ${name} failed: ${err}`, errorDetails[name]);
18233
- * }
18234
- * ```
18235
- */
18236
18227
  async listToolsetsWithErrors(options) {
18237
18228
  this.addToInstanceCache();
18238
18229
  const connectedToolsets = {};
@@ -18563,6 +18554,7 @@ onListChanged: async (serverName, handler) => {
18563
18554
  name,
18564
18555
  server: config,
18565
18556
  timeout: config.timeout ?? this.defaultTimeout,
18557
+ registerProcessHooks: this.registerProcessHooks,
18566
18558
  clientInfo: {
18567
18559
  name: config.clientInfo?.name ?? this.defaultClientInfo?.name,
18568
18560
  version: config.clientInfo?.version ?? this.defaultClientInfo?.version
@@ -20243,6 +20235,7 @@ var MCPServer = class extends MCPServerBase {
20243
20235
  eventsOptions;
20244
20236
  eventsInspector;
20245
20237
  webhooks;
20238
+ legacy;
20246
20239
  httpHandler;
20247
20240
  nodeHandler;
20248
20241
  stdioHandle;
@@ -20258,6 +20251,7 @@ var MCPServer = class extends MCPServerBase {
20258
20251
  this.loadAppResources(config.appResources);
20259
20252
  this.requestStateCodec = this.createRequestStateCodec(config.requestState);
20260
20253
  this.eventsOptions = config.eventsOptions ?? {};
20254
+ this.legacy = config.legacy ?? "reject";
20261
20255
  const webhook = this.eventsOptions.webhook;
20262
20256
  this.eventRegistry = new ServerEventRegistry(config.events, { webhook: webhook !== void 0 });
20263
20257
  if (webhook && this.eventRegistry.size > 0) this.webhooks = new WebhookDispatcher({
@@ -20787,7 +20781,7 @@ var MCPServer = class extends MCPServerBase {
20787
20781
  resumeSchema: execution.resumeSchema
20788
20782
  });
20789
20783
  }
20790
- const result = this.toCallToolResult(name, tool, execution.output);
20784
+ const result = server.projectCallToolResult(this.toCallToolResult(name, tool, execution.output), this.toolInfo(name, tool).outputSchema);
20791
20785
  this.logger.info(`Tool '${name}' finished in ${Date.now() - startedAt}ms.`);
20792
20786
  return result;
20793
20787
  } catch (error) {
@@ -21053,7 +21047,7 @@ var MCPServer = class extends MCPServerBase {
21053
21047
  getNodeHandler() {
21054
21048
  if (!this.nodeHandler) {
21055
21049
  this.httpHandler = createMcpHandler(() => this.createServerInstance(), {
21056
- legacy: "reject",
21050
+ legacy: this.legacy,
21057
21051
  onerror: (error) => this.logger.error("MCP handler error", { error: error.toString() })
21058
21052
  });
21059
21053
  this.nodeHandler = toNodeHandler(this.httpHandler, { onerror: (error) => this.logger.error("MCP Node handler adapter error", { error: error.toString() }) });
@@ -21073,7 +21067,8 @@ var MCPServer = class extends MCPServerBase {
21073
21067
  }
21074
21068
  /**
21075
21069
  * Handles one Streamable HTTP request at `httpPath`. Every request is
21076
- * self-contained; requests without a 2026-07-28 envelope are rejected.
21070
+ * self-contained; requests without a 2026-07-28 envelope are rejected unless
21071
+ * `legacy: 'stateless'` is configured.
21077
21072
  */
21078
21073
  async startHTTP({ url, httpPath, req, res, options }) {
21079
21074
  if (url.pathname !== httpPath) {