@mastra/connect 0.7.0-alpha.1 → 1.0.0-alpha.4

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 (83) hide show
  1. package/README.md +47 -42
  2. package/dist/channels.d.ts +40 -22
  3. package/dist/channels.d.ts.map +1 -1
  4. package/dist/client.d.ts +21 -0
  5. package/dist/client.d.ts.map +1 -1
  6. package/dist/errors.d.ts +23 -0
  7. package/dist/errors.d.ts.map +1 -1
  8. package/dist/index.cjs +821 -465
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.ts +4 -10
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +822 -458
  13. package/dist/index.js.map +1 -1
  14. package/dist/instrumentation.d.ts +48 -0
  15. package/dist/instrumentation.d.ts.map +1 -0
  16. package/dist/multi-connection.d.ts +1 -1
  17. package/dist/multi-connection.d.ts.map +1 -1
  18. package/dist/providers/channels.d.ts +5 -1
  19. package/dist/providers/channels.d.ts.map +1 -1
  20. package/dist/providers/clerk/tools/create-organization-domain.d.ts +9 -9
  21. package/dist/providers/clerk/tools/create-organization-domain.d.ts.map +1 -1
  22. package/dist/providers/clerk/tools/create-organization-invitation.d.ts +3 -3
  23. package/dist/providers/clerk/tools/create-user.d.ts.map +1 -1
  24. package/dist/providers/clerk/tools/delete-email-address.d.ts.map +1 -1
  25. package/dist/providers/clerk/tools/delete-organization-domain.d.ts.map +1 -1
  26. package/dist/providers/clerk/tools/delete-organization-membership.d.ts.map +1 -1
  27. package/dist/providers/clerk/tools/delete-organization-role.d.ts.map +1 -1
  28. package/dist/providers/clerk/tools/delete-phone-number.d.ts.map +1 -1
  29. package/dist/providers/clerk/tools/get-email-address.d.ts.map +1 -1
  30. package/dist/providers/clerk/tools/get-organization-invitation.d.ts +3 -3
  31. package/dist/providers/clerk/tools/get-organization-role.d.ts.map +1 -1
  32. package/dist/providers/clerk/tools/get-phone-number.d.ts.map +1 -1
  33. package/dist/providers/clerk/tools/get-session.d.ts +3 -3
  34. package/dist/providers/clerk/tools/get-session.d.ts.map +1 -1
  35. package/dist/providers/clerk/tools/list-organization-domains.d.ts +9 -9
  36. package/dist/providers/clerk/tools/list-organization-domains.d.ts.map +1 -1
  37. package/dist/providers/clerk/tools/list-organization-invitations.d.ts +3 -3
  38. package/dist/providers/clerk/tools/list-organization-invitations.d.ts.map +1 -1
  39. package/dist/providers/clerk/tools/list-organization-memberships.d.ts.map +1 -1
  40. package/dist/providers/clerk/tools/list-organization-roles.d.ts.map +1 -1
  41. package/dist/providers/clerk/tools/list-organizations.d.ts.map +1 -1
  42. package/dist/providers/clerk/tools/list-sessions.d.ts +3 -5
  43. package/dist/providers/clerk/tools/list-sessions.d.ts.map +1 -1
  44. package/dist/providers/clerk/tools/list-users.d.ts +12 -0
  45. package/dist/providers/clerk/tools/list-users.d.ts.map +1 -1
  46. package/dist/providers/clerk/tools/revoke-organization-invitation.d.ts +3 -3
  47. package/dist/providers/clerk/tools/revoke-session.d.ts +3 -3
  48. package/dist/providers/clerk/tools/revoke-session.d.ts.map +1 -1
  49. package/dist/providers/clerk/tools/update-organization-domain.d.ts +9 -9
  50. package/dist/providers/clerk/tools/update-organization-domain.d.ts.map +1 -1
  51. package/dist/providers/clerk/tools/update-organization-role.d.ts.map +1 -1
  52. package/dist/providers/clerk/tools/update-organization.d.ts +2 -0
  53. package/dist/providers/clerk/tools/update-organization.d.ts.map +1 -1
  54. package/dist/providers/clerk/tools/update-user.d.ts +10 -10
  55. package/dist/providers/clerk/tools/verify-organization-domain.d.ts +12 -7
  56. package/dist/providers/clerk/tools/verify-organization-domain.d.ts.map +1 -1
  57. package/dist/providers/discord/tools/list-channels.d.ts +2 -2
  58. package/dist/providers/discord/tools/list-channels.d.ts.map +1 -1
  59. package/dist/providers/jira/tools/get-create-issue-metadata.d.ts +42 -0
  60. package/dist/providers/jira/tools/get-create-issue-metadata.d.ts.map +1 -1
  61. package/dist/providers/jira/tools/list-issue-types.d.ts +6 -2
  62. package/dist/providers/jira/tools/list-issue-types.d.ts.map +1 -1
  63. package/dist/providers/resend/tools/cancel-email.d.ts +2 -2
  64. package/dist/providers/resend/tools/get-email.d.ts +2 -2
  65. package/dist/providers/resend/tools/get-received-email.d.ts +2 -2
  66. package/dist/providers/resend/tools/get-template.d.ts +2 -2
  67. package/dist/providers/resend/tools/list-emails.d.ts +2 -2
  68. package/dist/providers/resend/tools/list-received-emails.d.ts +2 -2
  69. package/dist/providers/resend/tools/update-contact-topics.d.ts.map +1 -1
  70. package/dist/registry.d.ts +1 -8
  71. package/dist/registry.d.ts.map +1 -1
  72. package/dist/resolution.d.ts +3 -11
  73. package/dist/resolution.d.ts.map +1 -1
  74. package/dist/runtime/platform-proxy.d.ts.map +1 -1
  75. package/dist/tools.d.ts +94 -33
  76. package/dist/tools.d.ts.map +1 -1
  77. package/dist/toolset.d.ts +39 -13
  78. package/dist/toolset.d.ts.map +1 -1
  79. package/package.json +12 -8
  80. package/dist/connect.d.ts +0 -22
  81. package/dist/connect.d.ts.map +0 -1
  82. package/dist/environment.d.ts +0 -48
  83. package/dist/environment.d.ts.map +0 -1
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { MCPClient } from "@mastra/mcp";
2
2
  import { z } from "zod";
3
+ import { SpanType, resolveCurrentSpan } from "@mastra/core/observability";
3
4
  import { createTool } from "@mastra/core/tools";
4
5
  //#region src/errors.ts
5
6
  const MAX_DETAIL_LENGTH = 2e3;
@@ -7,18 +8,56 @@ var MastraConnectError = class extends Error {
7
8
  code;
8
9
  status;
9
10
  detail;
11
+ /**
12
+ * Axios-compatible alias for {@link status}. Generated provider tools come
13
+ * from upstream Nango templates whose error handlers check
14
+ * `error.response.status` (the axios/Nango SDK shape). Exposing the HTTP
15
+ * status under `response.status` as well lets that generated code work
16
+ * unchanged against the errors this package throws. Only set when the error
17
+ * carries an HTTP status.
18
+ */
19
+ response;
10
20
  constructor(code, message, options) {
11
21
  super(message);
12
22
  this.name = "MastraConnectError";
13
23
  this.code = code;
14
24
  this.status = options?.status;
15
25
  this.detail = options?.detail ? truncate(options.detail) : void 0;
26
+ if (typeof options?.status === "number") this.response = { status: options.status };
27
+ }
28
+ };
29
+ /**
30
+ * A caller configuration mistake (for example an unknown tool name in
31
+ * `requireApproval`) that must fail resolution instead of being downgraded
32
+ * to the resolver's warn-and-skip path, which exists for provider-side
33
+ * failures. Internal to the package: callers observe it as a regular
34
+ * `MastraConnectError` with code `invalid_options`.
35
+ *
36
+ * @internal
37
+ */
38
+ var MastraConnectConfigError = class extends MastraConnectError {
39
+ constructor(message) {
40
+ super("invalid_options", message);
16
41
  }
17
42
  };
18
43
  function truncate(text) {
19
44
  return text.length > MAX_DETAIL_LENGTH ? `${text.slice(0, MAX_DETAIL_LENGTH)}…` : text;
20
45
  }
21
46
  /**
47
+ * Pulls the first human-readable message from an array of error entries. Many
48
+ * providers (Clerk, Linear's GraphQL envelope, Nango v2) return
49
+ * `{ errors: [{ message, long_message, code }] }`; surfacing the first message
50
+ * is the most useful detail without echoing the whole payload.
51
+ */
52
+ function extractFirstArrayMessage(errors) {
53
+ for (const entry of errors) {
54
+ if (!entry || typeof entry !== "object") continue;
55
+ const record = entry;
56
+ const message = typeof record.long_message === "string" ? record.long_message : record.message;
57
+ if (typeof message === "string" && message) return message;
58
+ }
59
+ }
60
+ /**
22
61
  * Extracts a human-readable detail string from an RFC-7807 problem JSON body
23
62
  * (or a plain `{ error }` body) without echoing anything else from the response.
24
63
  * Returns undefined when the body is not parseable JSON.
@@ -49,6 +88,14 @@ async function extractProblemDetail(response) {
49
88
  };
50
89
  }
51
90
  }
91
+ if (Array.isArray(data.errors)) {
92
+ const message = extractFirstArrayMessage(data.errors);
93
+ if (message) return {
94
+ detail: truncate(message),
95
+ code,
96
+ isProblemJson
97
+ };
98
+ }
52
99
  return {
53
100
  code,
54
101
  isProblemJson
@@ -58,6 +105,98 @@ async function extractProblemDetail(response) {
58
105
  return { isProblemJson };
59
106
  }
60
107
  //#endregion
108
+ //#region src/instrumentation.ts
109
+ const ID_SEGMENT_ROUTES = [{
110
+ prefix: "/v2/connections/",
111
+ placeholder: "{connectionId}",
112
+ key: "connectionId"
113
+ }, {
114
+ prefix: "/v2/projects/",
115
+ placeholder: "{projectId}",
116
+ key: "projectId"
117
+ }];
118
+ const PROXY_SEGMENT = "/proxy/";
119
+ /** Describes a platform request for span naming without leaking query params, ids, or vendor path segments. */
120
+ function describePlatformCall(method, rawPath) {
121
+ const path = rawPath.split("?")[0];
122
+ let route = path;
123
+ const descriptor = {
124
+ method,
125
+ route,
126
+ isProxy: false
127
+ };
128
+ for (const idRoute of ID_SEGMENT_ROUTES) {
129
+ if (!path.startsWith(idRoute.prefix)) continue;
130
+ const rest = path.slice(idRoute.prefix.length);
131
+ const slash = rest.indexOf("/");
132
+ const id = slash === -1 ? rest : rest.slice(0, slash);
133
+ const tail = slash === -1 ? "" : rest.slice(slash);
134
+ descriptor[idRoute.key] = safeDecode(id);
135
+ route = `${idRoute.prefix}${idRoute.placeholder}${tail}`;
136
+ break;
137
+ }
138
+ const proxyIndex = route.indexOf(PROXY_SEGMENT);
139
+ if (proxyIndex !== -1) {
140
+ descriptor.isProxy = true;
141
+ route = `${route.slice(0, proxyIndex + 7)}*`;
142
+ }
143
+ descriptor.route = route;
144
+ return descriptor;
145
+ }
146
+ function safeDecode(segment) {
147
+ try {
148
+ return decodeURIComponent(segment);
149
+ } catch {
150
+ return segment;
151
+ }
152
+ }
153
+ /**
154
+ * Starts a GENERIC child span for an outbound connect HTTP call, or returns
155
+ * `undefined` when no ambient span is active. Span creation failures are
156
+ * swallowed: instrumentation must never break a request.
157
+ */
158
+ function startPlatformCallSpan(descriptor) {
159
+ let parent;
160
+ try {
161
+ parent = resolveCurrentSpan();
162
+ if (!parent) return void 0;
163
+ return parent.createChildSpan({
164
+ type: SpanType.GENERIC,
165
+ name: `connect.${descriptor.isProxy ? "proxy" : "platform"} ${descriptor.method} ${descriptor.route}`,
166
+ metadata: {
167
+ method: descriptor.method,
168
+ route: descriptor.route,
169
+ ...descriptor.connectionId ? { connectionId: descriptor.connectionId } : {},
170
+ ...descriptor.projectId ? { projectId: descriptor.projectId } : {}
171
+ }
172
+ });
173
+ } catch {
174
+ return;
175
+ }
176
+ }
177
+ /** Ends a platform-call span with the response status. Never throws. */
178
+ function endPlatformCallSpan(span, status) {
179
+ if (!span) return;
180
+ try {
181
+ span.end({ metadata: { status } });
182
+ } catch {}
183
+ }
184
+ /**
185
+ * Records a transport-level failure on a platform-call span. Nothing from the
186
+ * original error is recorded — fetch failures can embed the request URL in
187
+ * both `message` and `name`, which may carry secret query params — so the
188
+ * span gets a fixed, constant error. Never throws.
189
+ */
190
+ function errorPlatformCallSpan(span, _error) {
191
+ if (!span) return;
192
+ try {
193
+ span.error({
194
+ error: /* @__PURE__ */ new Error("connect request failed"),
195
+ endSpan: true
196
+ });
197
+ } catch {}
198
+ }
199
+ //#endregion
61
200
  //#region src/client.ts
62
201
  const DEFAULT_INTEGRATIONS_URL = "https://integrations.mastra.ai";
63
202
  const REGIONAL_INTEGRATIONS_URLS = {
@@ -100,20 +239,25 @@ function redact(message, accessToken) {
100
239
  return message.split(accessToken).join("[REDACTED]");
101
240
  }
102
241
  async function platformFetch(client, path, init) {
242
+ const span = startPlatformCallSpan(describePlatformCall(init?.method ?? "GET", path));
103
243
  try {
104
- return await client.fetch(`${client.baseUrl}${path}`, {
244
+ const response = await client.fetch(`${client.baseUrl}${path}`, {
105
245
  ...init,
106
246
  headers: {
107
247
  ...client.headers,
108
248
  ...init?.headers
109
249
  }
110
250
  });
251
+ endPlatformCallSpan(span, response.status);
252
+ return response;
111
253
  } catch (error) {
112
254
  if (error instanceof Error && error.message.includes(client.accessToken)) {
113
255
  const redacted = new Error(redact(error.message, client.accessToken));
114
256
  redacted.name = error.name;
257
+ errorPlatformCallSpan(span, redacted);
115
258
  throw redacted;
116
259
  }
260
+ errorPlatformCallSpan(span, error);
117
261
  throw error;
118
262
  }
119
263
  }
@@ -188,14 +332,35 @@ const integrationCatalogEntrySchema = z.object({
188
332
  capabilities: z.object({ mcp: z.boolean().optional() })
189
333
  });
190
334
  const integrationCatalogResponseSchema = z.object({ integrations: z.array(integrationCatalogEntrySchema) });
191
- const credentialSchema = z.discriminatedUnion("type", [z.object({
192
- type: z.literal("oauth2"),
335
+ /**
336
+ * Secondary access token minted by the vendor alongside the primary oauth2
337
+ * credential (e.g. `devPortalAccessToken` on Microsoft Teams connections).
338
+ * Keyed by the vendor's connection-config field name. Treat this like any
339
+ * other bearer credential — it authenticates second-audience API calls
340
+ * (Teams Dev Portal, etc.) and must not be logged or forwarded to code that
341
+ * shouldn't hold connection secrets.
342
+ */
343
+ const secondaryAccessTokenSchema = z.object({
193
344
  accessToken: z.string(),
194
345
  expiresAt: z.string().nullable()
195
- }), z.object({
196
- type: z.literal("api_key"),
197
- apiKey: z.string()
198
- })]);
346
+ });
347
+ const credentialSchema = z.discriminatedUnion("type", [
348
+ z.object({
349
+ type: z.literal("oauth2"),
350
+ accessToken: z.string(),
351
+ expiresAt: z.string().nullable(),
352
+ secondaryAccessTokens: z.record(z.string(), secondaryAccessTokenSchema).optional()
353
+ }),
354
+ z.object({
355
+ type: z.literal("api_key"),
356
+ apiKey: z.string()
357
+ }),
358
+ z.object({
359
+ type: z.literal("two_step"),
360
+ token: z.string(),
361
+ expiresAt: z.string().nullable()
362
+ })
363
+ ]);
199
364
  const connectionContextSchema = z.object({
200
365
  connection_config: z.record(z.string(), z.unknown()).nullable(),
201
366
  metadata: z.record(z.string(), z.unknown()).nullable()
@@ -271,9 +436,6 @@ function assertValidBaseUrlOverride(baseUrlOverride) {
271
436
  if (parsed.protocol !== "https:") throw new MastraConnectError("invalid_options", `Invalid baseUrlOverride '${baseUrlOverride}': only https:// URLs are allowed.`);
272
437
  if (parsed.username || parsed.password) throw new MastraConnectError("invalid_options", `Invalid baseUrlOverride '${baseUrlOverride}': embedded credentials are not allowed.`);
273
438
  }
274
- async function proxyRequest(client, connectionId, options) {
275
- return (await proxyRequestWithResponse(client, connectionId, options)).data;
276
- }
277
439
  /** Preserves HTTP metadata for tools with status-dependent provider contracts. */
278
440
  async function proxyRequestWithResponse(client, connectionId, options) {
279
441
  const cleanPath = options.path.replace(/^\/+/, "");
@@ -286,7 +448,12 @@ async function proxyRequestWithResponse(client, connectionId, options) {
286
448
  }
287
449
  const queryString = search.size > 0 ? `?${search.toString()}` : "";
288
450
  const url = `/v2/connections/${encodeURIComponent(connectionId)}/proxy/${cleanPath}${queryString}`;
289
- const headers = { ...options.headers };
451
+ const headers = {};
452
+ for (const [name, value] of Object.entries(options.headers ?? {})) {
453
+ const lower = name.toLowerCase();
454
+ if (lower === "authorization" || lower === "proxy-authorization") continue;
455
+ headers[name] = value;
456
+ }
290
457
  if (options.baseUrlOverride !== void 0) {
291
458
  assertValidBaseUrlOverride(options.baseUrlOverride);
292
459
  headers["base-url-override"] = options.baseUrlOverride;
@@ -355,37 +522,64 @@ async function proxyRequestWithResponse(client, connectionId, options) {
355
522
  }
356
523
  //#endregion
357
524
  //#region src/toolset.ts
358
- /** Resolves a connection id lazily at execute time: option → env var → typed error naming the env var. */
359
- function resolveConnectionId(envVar, connectionId) {
360
- const resolved = connectionId?.trim() || process.env[envVar]?.trim();
361
- if (!resolved) throw new MastraConnectError("missing_connection_id", `Missing connection id: set ${envVar} or pass connectionId.`);
362
- return resolved;
525
+ /**
526
+ * Compiles a tool-filter entry containing `*` wildcards into an anchored
527
+ * regex; every other character matches literally.
528
+ */
529
+ function compileToolPattern(pattern) {
530
+ const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
531
+ return new RegExp(`^${escaped}$`);
363
532
  }
364
533
  /**
365
- * Wraps `createTool` so the tool executes through the platform connection
366
- * proxy. Connection id and client config resolve lazily inside execute, so
367
- * building tools without env vars set never throws.
534
+ * Returns a predicate testing tool keys against filter `entries`, where an
535
+ * entry containing `*` is a glob and anything else matches literally. Used
536
+ * where keys are only known later (MCP approval predicates); list-shaped
537
+ * call sites expand eagerly with {@link expandToolPatterns} instead.
368
538
  */
369
- function defineProxyTool(context, config) {
370
- return createTool({
371
- id: config.id,
372
- description: config.description,
373
- inputSchema: config.inputSchema,
374
- outputSchema: config.outputSchema,
375
- execute: async (input) => {
376
- const connectionId = resolveConnectionId(context.envVar, context.options?.connectionId);
377
- const raw = await proxyRequest(resolveClient(context.options?.client), connectionId, config.request(input));
378
- return config.transform(raw, input);
539
+ function compileToolMatcher(entries) {
540
+ const literals = /* @__PURE__ */ new Set();
541
+ const patterns = [];
542
+ for (const entry of entries) if (entry.includes("*")) patterns.push(compileToolPattern(entry));
543
+ else literals.add(entry);
544
+ return (toolKey) => literals.has(toolKey) || patterns.some((pattern) => pattern.test(toolKey));
545
+ }
546
+ /**
547
+ * Expands `*` glob entries against the known tool keys, preserving order and
548
+ * de-duplicating. Literal entries pass through untouched (callers validate
549
+ * those against their own key sets). A glob that matches nothing throws:
550
+ * like an unknown literal name, a dead pattern in access-limiting config is
551
+ * a typo that must surface instead of silently filtering nothing.
552
+ */
553
+ function expandToolPatterns(entries, knownKeys, label) {
554
+ const expanded = [];
555
+ const seen = /* @__PURE__ */ new Set();
556
+ const push = (key) => {
557
+ if (seen.has(key)) return;
558
+ seen.add(key);
559
+ expanded.push(key);
560
+ };
561
+ for (const entry of entries) {
562
+ if (!entry.includes("*")) {
563
+ push(entry);
564
+ continue;
379
565
  }
380
- });
566
+ const pattern = compileToolPattern(entry);
567
+ const matches = knownKeys.filter((key) => pattern.test(key));
568
+ if (matches.length === 0) throw new MastraConnectConfigError(`Pattern '${entry}' in ${label} matched no tools. Known tools: ${knownKeys.join(", ")}.`);
569
+ for (const match of matches) push(match);
570
+ }
571
+ return expanded;
381
572
  }
382
573
  /**
383
- * Filters a toolset by tool key. Throws at build time on unknown names so
384
- * typos in access-limiting config surface immediately.
574
+ * Filters a toolset by tool key. Entries containing `*` are globs expanded
575
+ * against the toolset's keys. Throws at build time on unknown names and on
576
+ * globs that match nothing, so typos in access-limiting config surface
577
+ * immediately.
385
578
  */
386
- function applyAllowTools(tools, allowTools) {
387
- if (!allowTools) return tools;
579
+ function applyAllowTools(tools, allowToolsInput) {
580
+ if (!allowToolsInput) return tools;
388
581
  const known = Object.keys(tools);
582
+ const allowTools = expandToolPatterns(allowToolsInput, known, "allowTools");
389
583
  const unknown = allowTools.filter((name) => !known.includes(name));
390
584
  if (unknown.length > 0) throw new Error(`Unknown tool name(s) in allowTools: ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
391
585
  const filtered = {};
@@ -393,13 +587,15 @@ function applyAllowTools(tools, allowTools) {
393
587
  return filtered;
394
588
  }
395
589
  /**
396
- * Removes tool keys from a toolset. Throws at build time on unknown names so
397
- * typos in access-limiting config surface immediately instead of silently
398
- * removing nothing.
590
+ * Removes tool keys from a toolset. Entries containing `*` are globs
591
+ * expanded against the toolset's keys. Throws at build time on unknown names
592
+ * and on globs that match nothing, so typos in access-limiting config
593
+ * surface immediately instead of silently removing nothing.
399
594
  */
400
- function applyDisallowTools(tools, disallowTools) {
401
- if (!disallowTools || disallowTools.length === 0) return tools;
595
+ function applyDisallowTools(tools, disallowToolsInput) {
596
+ if (!disallowToolsInput || disallowToolsInput.length === 0) return tools;
402
597
  const known = Object.keys(tools);
598
+ const disallowTools = expandToolPatterns(disallowToolsInput, known, "disallowTools");
403
599
  const unknown = disallowTools.filter((name) => !known.includes(name));
404
600
  if (unknown.length > 0) throw new Error(`Unknown tool name(s) in disallowTools: ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
405
601
  const remove = new Set(disallowTools);
@@ -636,8 +832,8 @@ function buildProxyMultiConnectionTools(input) {
636
832
  * client cache so stale clients can be reaped elsewhere.
637
833
  */
638
834
  async function buildMcpMultiConnectionTools(input) {
639
- const { registration, connections, allowTools, disallowTools, autoApproveTools, client, mcpClients, resolverId } = input;
640
- const autoApproved = new Set(autoApproveTools ?? []);
835
+ const { registration, connections, allowTools, disallowTools, requireApproval, client, mcpClients, resolverId } = input;
836
+ const requireApprovalFor = Array.isArray(requireApproval) ? compileToolMatcher(requireApproval) : void 0;
641
837
  const listToolKey = listConnectionsToolKey(registration.integrationId);
642
838
  const innerAllowTools = allowTools?.filter((name) => name !== listToolKey);
643
839
  const innerDisallowTools = disallowTools?.filter((name) => name !== listToolKey);
@@ -661,7 +857,7 @@ async function buildMcpMultiConnectionTools(input) {
661
857
  id: `mastra-connect-${resolverId}-${registration.integrationId}-${connection.id}`,
662
858
  servers: { [registration.integrationId]: {
663
859
  ...transport,
664
- requireToolApproval: ({ toolName }) => !autoApproved.has(`${registration.integrationId}_${String(toolName)}`)
860
+ ...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor(`${registration.integrationId}_${String(toolName)}`) } : {}
665
861
  } }
666
862
  })
667
863
  };
@@ -678,8 +874,10 @@ async function buildMcpMultiConnectionTools(input) {
678
874
  }
679
875
  const catalogUnion = /* @__PURE__ */ new Set();
680
876
  for (const catalog of rawInnerCatalogs.values()) for (const key of Object.keys(catalog)) catalogUnion.add(key);
681
- const unknownAutoApprove = [...autoApproved].filter((name) => !catalogUnion.has(name));
682
- if (unknownAutoApprove.length > 0) throw new MastraConnectError("invalid_options", `Unknown tool name(s) in autoApproveTools for '${registration.integrationId}': ${unknownAutoApprove.join(", ")}. Known tools: ${[...catalogUnion].join(", ")}.`);
877
+ if (Array.isArray(requireApproval)) {
878
+ const unknown = expandToolPatterns(requireApproval, [...catalogUnion], `requireApproval for '${registration.integrationId}'`).filter((name) => !catalogUnion.has(name));
879
+ if (unknown.length > 0) throw new MastraConnectConfigError(`Unknown tool name(s) in requireApproval for '${registration.integrationId}': ${unknown.join(", ")}. Known tools: ${[...catalogUnion].join(", ")}.`);
880
+ }
683
881
  innerToolsByConnectionId = /* @__PURE__ */ new Map();
684
882
  for (const [connectionId, catalog] of rawInnerCatalogs) {
685
883
  const filtered = applyToolFilter(catalog, {
@@ -707,11 +905,15 @@ async function buildMcpMultiConnectionTools(input) {
707
905
  }
708
906
  //#endregion
709
907
  //#region src/providers/channels.ts
710
- function credentialToken$1(credential) {
711
- return credential.type === "oauth2" ? credential.accessToken : credential.apiKey;
908
+ function credentialToken(credential) {
909
+ switch (credential.type) {
910
+ case "oauth2": return credential.accessToken;
911
+ case "two_step": return credential.token;
912
+ default: return credential.apiKey;
913
+ }
712
914
  }
713
915
  const RESERVED_OPTION_KEYS = {
714
- slack: [
916
+ "slack-channels": [
715
917
  "baseUrl",
716
918
  "refreshToken",
717
919
  "token",
@@ -725,7 +927,14 @@ const RESERVED_OPTION_KEYS = {
725
927
  "tokenResolver",
726
928
  "encryptionKey"
727
929
  ],
728
- discord: ["baseUrl", "encryptionKey"]
930
+ discord: ["baseUrl", "encryptionKey"],
931
+ "microsoft-teams": [
932
+ "baseUrl",
933
+ "appId",
934
+ "appPassword",
935
+ "tokenResolver",
936
+ "encryptionKey"
937
+ ]
729
938
  };
730
939
  function stripReservedOptions(integrationId, options) {
731
940
  if (!options) return options;
@@ -745,12 +954,12 @@ function stripReservedOptions(integrationId, options) {
745
954
  }
746
955
  const CHANNELS$1 = [
747
956
  {
748
- integrationId: "slack",
957
+ integrationId: "slack-channels",
749
958
  async create(options, runtime) {
750
959
  const mod = await import("@mastra/slack");
751
- const safeOptions = stripReservedOptions("slack", options);
960
+ const safeOptions = stripReservedOptions("slack-channels", options);
752
961
  const tokenResolver = async () => {
753
- return credentialToken$1(await runtime.getCredential());
962
+ return credentialToken(await runtime.getCredential());
754
963
  };
755
964
  return { provider: new mod.SlackProvider({
756
965
  tokenResolver,
@@ -764,7 +973,7 @@ const CHANNELS$1 = [
764
973
  const mod = await import("@mastra/telegram");
765
974
  const safeOptions = stripReservedOptions("telegram", options);
766
975
  const tokenResolver = async () => {
767
- return credentialToken$1(await runtime.getCredential());
976
+ return credentialToken(await runtime.getCredential());
768
977
  };
769
978
  return { provider: new mod.TelegramProvider({
770
979
  tokenResolver,
@@ -796,17 +1005,42 @@ const CHANNELS$1 = [
796
1005
  }
797
1006
  };
798
1007
  }
1008
+ },
1009
+ {
1010
+ integrationId: "microsoft-teams",
1011
+ async create(options, runtime) {
1012
+ const mod = await import("@mastra/teams");
1013
+ if (!process.env.MASTRA_ENCRYPTION_KEY) throw new MastraConnectError("invalid_options", "Microsoft Teams channel: MASTRA_ENCRYPTION_KEY is not set. The Teams install store persists per-agent bot secrets and requires an at-rest encryption key (a 32-byte value, base64-encoded).");
1014
+ const safeOptions = stripReservedOptions("microsoft-teams", options);
1015
+ const tokenResolver = async (scope) => {
1016
+ const fresh = await runtime.getCredential();
1017
+ if (!(Array.isArray(scope) ? scope : [scope]).includes(mod.TEAMS_DEV_PORTAL_SCOPE)) return credentialToken(fresh);
1018
+ const token = fresh.type === "oauth2" ? fresh.secondaryAccessTokens?.devPortalAccessToken?.accessToken : void 0;
1019
+ if (!token) throw new MastraConnectError("no_active_connection", `Teams connection ${runtime.getConnectionId()} has no Dev Portal token on its credential. Reconnect with an integration that requests the ${mod.TEAMS_DEV_PORTAL_SCOPE} scope.`);
1020
+ return token;
1021
+ };
1022
+ return { provider: new mod.TeamsProvider({
1023
+ tokenResolver,
1024
+ ...safeOptions ?? {}
1025
+ }) };
1026
+ }
799
1027
  }
800
1028
  ];
801
1029
  //#endregion
802
1030
  //#region src/runtime/platform-proxy.ts
803
1031
  /** Maps the platform credential to the field names templates are written against. */
804
1032
  function toTemplateCredentials(credential) {
805
- return credential.type === "oauth2" ? {
1033
+ if (credential.type === "oauth2") return {
806
1034
  type: "OAUTH2",
807
1035
  access_token: credential.accessToken,
808
1036
  expires_at: credential.expiresAt
809
- } : {
1037
+ };
1038
+ if (credential.type === "two_step") return {
1039
+ type: "TWO_STEP",
1040
+ token: credential.token,
1041
+ expires_at: credential.expiresAt
1042
+ };
1043
+ return {
810
1044
  type: "API_KEY",
811
1045
  apiKey: credential.apiKey
812
1046
  };
@@ -1734,8 +1968,8 @@ const anthropicProvider = {
1734
1968
  //#endregion
1735
1969
  //#region src/providers/clerk/tools/assign-organization-role-permission.ts
1736
1970
  const assignOrganizationRolePermissionInputSchema = z.object({
1737
- organization_role_id: z.string(),
1738
- permission_id: z.string()
1971
+ organization_role_id: z.string().min(1),
1972
+ permission_id: z.string().min(1)
1739
1973
  });
1740
1974
  const ResourceSchema$49 = z.object({
1741
1975
  id: z.string(),
@@ -1771,8 +2005,8 @@ function assignOrganizationRolePermissionTool(proxy) {
1771
2005
  //#endregion
1772
2006
  //#region src/providers/clerk/tools/create-email-address.ts
1773
2007
  const createEmailAddressInputSchema = z.object({
1774
- user_id: z.string(),
1775
- email_address: z.string(),
2008
+ user_id: z.string().min(1),
2009
+ email_address: z.string().email(),
1776
2010
  verified: z.boolean().optional(),
1777
2011
  primary: z.boolean().optional(),
1778
2012
  notify_primary_email_address_changed: z.boolean().optional()
@@ -1817,16 +2051,22 @@ function createEmailAddressTool(proxy) {
1817
2051
  }
1818
2052
  //#endregion
1819
2053
  //#region src/providers/clerk/tools/create-organization-domain.ts
1820
- const EnrollmentModeSchema$2 = z.enum([
2054
+ const EnrollmentModeSchema$3 = z.enum([
1821
2055
  "manual_invitation",
1822
2056
  "automatic_invitation",
1823
2057
  "automatic_suggestion",
1824
2058
  "enterprise_sso"
1825
2059
  ]);
2060
+ const EnrollmentModeSchemaWidened$2 = z.enum([
2061
+ "manual_invitation",
2062
+ "automatic_invitation",
2063
+ "automatic_suggestion",
2064
+ "enterprise_sso"
2065
+ ]).or(z.string());
1826
2066
  const createOrganizationDomainInputSchema$1 = z.object({
1827
- organization_id: z.string(),
2067
+ organization_id: z.string().min(1),
1828
2068
  name: z.string(),
1829
- enrollment_mode: EnrollmentModeSchema$2,
2069
+ enrollment_mode: EnrollmentModeSchema$3,
1830
2070
  verified: z.boolean().optional()
1831
2071
  });
1832
2072
  const ResourceSchema$47 = z.object({
@@ -1834,15 +2074,15 @@ const ResourceSchema$47 = z.object({
1834
2074
  object: z.string().optional(),
1835
2075
  organization_id: z.string().optional(),
1836
2076
  name: z.string(),
1837
- enrollment_mode: EnrollmentModeSchema$2.optional(),
2077
+ enrollment_mode: EnrollmentModeSchemaWidened$2.optional(),
1838
2078
  affiliation_verification: z.object({
1839
2079
  attempts: z.number().optional(),
1840
2080
  status: z.string().optional()
1841
- }).passthrough().optional(),
2081
+ }).passthrough().nullable().optional(),
1842
2082
  verification: z.object({
1843
2083
  attempts: z.number().optional(),
1844
2084
  status: z.string().optional()
1845
- }).passthrough().optional(),
2085
+ }).passthrough().nullable().optional(),
1846
2086
  verified: z.boolean().optional(),
1847
2087
  created_at: z.number().optional(),
1848
2088
  updated_at: z.number().optional()
@@ -1871,7 +2111,7 @@ function createOrganizationDomainTool$1(proxy) {
1871
2111
  //#endregion
1872
2112
  //#region src/providers/clerk/tools/create-organization-invitation.ts
1873
2113
  const createOrganizationInvitationInputSchema = z.object({
1874
- organization_id: z.string(),
2114
+ organization_id: z.string().min(1),
1875
2115
  email_address: z.string().email(),
1876
2116
  role: z.string(),
1877
2117
  inviter_user_id: z.string().optional(),
@@ -1892,7 +2132,7 @@ const ResourceSchema$46 = z.object({
1892
2132
  "accepted",
1893
2133
  "revoked",
1894
2134
  "expired"
1895
- ]).optional(),
2135
+ ]).or(z.string()).optional(),
1896
2136
  created_at: z.number().optional(),
1897
2137
  updated_at: z.number().optional()
1898
2138
  }).passthrough();
@@ -1925,8 +2165,8 @@ function createOrganizationInvitationTool(proxy) {
1925
2165
  //#endregion
1926
2166
  //#region src/providers/clerk/tools/create-organization-membership.ts
1927
2167
  const createOrganizationMembershipInputSchema$1 = z.object({
1928
- organization_id: z.string(),
1929
- user_id: z.string(),
2168
+ organization_id: z.string().min(1),
2169
+ user_id: z.string().min(1),
1930
2170
  role: z.string(),
1931
2171
  public_metadata: z.record(z.string(), z.unknown()).optional(),
1932
2172
  private_metadata: z.record(z.string(), z.unknown()).optional()
@@ -2062,7 +2302,7 @@ function createOrganizationTool$1(proxy) {
2062
2302
  //#endregion
2063
2303
  //#region src/providers/clerk/tools/create-phone-number.ts
2064
2304
  const createPhoneNumberInputSchema = z.object({
2065
- user_id: z.string(),
2305
+ user_id: z.string().min(1),
2066
2306
  phone_number: z.string(),
2067
2307
  verified: z.boolean().optional(),
2068
2308
  primary: z.boolean().optional(),
@@ -2125,7 +2365,7 @@ const createUserInputSchema$2 = z.object({
2125
2365
  public_metadata: z.record(z.string(), z.unknown()).optional(),
2126
2366
  private_metadata: z.record(z.string(), z.unknown()).optional(),
2127
2367
  unsafe_metadata: z.record(z.string(), z.unknown()).optional()
2128
- });
2368
+ }).refine((value) => (value.email_address?.length ?? 0) > 0 || (value.phone_number?.length ?? 0) > 0 || value.username !== void 0 || value.password !== void 0, { message: "Provide at least one of email_address, phone_number, username, or password." });
2129
2369
  const UserSchema$27 = z.object({
2130
2370
  id: z.string(),
2131
2371
  object: z.string().optional(),
@@ -2177,7 +2417,7 @@ function createUserTool$2(proxy) {
2177
2417
  }
2178
2418
  //#endregion
2179
2419
  //#region src/providers/clerk/tools/delete-email-address.ts
2180
- const deleteEmailAddressInputSchema = z.object({ email_address_id: z.string() });
2420
+ const deleteEmailAddressInputSchema = z.object({ email_address_id: z.string().min(1) });
2181
2421
  const deleteEmailAddressOutputSchema = z.object({
2182
2422
  id: z.string(),
2183
2423
  success: z.boolean()
@@ -2203,8 +2443,8 @@ function deleteEmailAddressTool(proxy) {
2203
2443
  //#endregion
2204
2444
  //#region src/providers/clerk/tools/delete-organization-domain.ts
2205
2445
  const deleteOrganizationDomainInputSchema$1 = z.object({
2206
- organization_id: z.string(),
2207
- domain_id: z.string()
2446
+ organization_id: z.string().min(1),
2447
+ domain_id: z.string().min(1)
2208
2448
  });
2209
2449
  const deleteOrganizationDomainOutputSchema$1 = z.object({
2210
2450
  id: z.string(),
@@ -2231,8 +2471,8 @@ function deleteOrganizationDomainTool$1(proxy) {
2231
2471
  //#endregion
2232
2472
  //#region src/providers/clerk/tools/delete-organization-membership.ts
2233
2473
  const deleteOrganizationMembershipInputSchema$1 = z.object({
2234
- organization_id: z.string(),
2235
- user_id: z.string()
2474
+ organization_id: z.string().min(1),
2475
+ user_id: z.string().min(1)
2236
2476
  });
2237
2477
  const deleteOrganizationMembershipOutputSchema$1 = z.object({
2238
2478
  id: z.string(),
@@ -2258,7 +2498,7 @@ function deleteOrganizationMembershipTool$1(proxy) {
2258
2498
  }
2259
2499
  //#endregion
2260
2500
  //#region src/providers/clerk/tools/delete-organization-role.ts
2261
- const deleteOrganizationRoleInputSchema = z.object({ organization_role_id: z.string() });
2501
+ const deleteOrganizationRoleInputSchema = z.object({ organization_role_id: z.string().min(1) });
2262
2502
  const deleteOrganizationRoleOutputSchema = z.object({
2263
2503
  id: z.string(),
2264
2504
  success: z.boolean()
@@ -2308,7 +2548,7 @@ function deleteOrganizationTool$1(proxy) {
2308
2548
  }
2309
2549
  //#endregion
2310
2550
  //#region src/providers/clerk/tools/delete-phone-number.ts
2311
- const deletePhoneNumberInputSchema = z.object({ phone_number_id: z.string() });
2551
+ const deletePhoneNumberInputSchema = z.object({ phone_number_id: z.string().min(1) });
2312
2552
  const deletePhoneNumberOutputSchema = z.object({
2313
2553
  id: z.string(),
2314
2554
  success: z.boolean()
@@ -2358,7 +2598,7 @@ function deleteUserTool$2(proxy) {
2358
2598
  }
2359
2599
  //#endregion
2360
2600
  //#region src/providers/clerk/tools/get-email-address.ts
2361
- const getEmailAddressInputSchema = z.object({ email_address_id: z.string() });
2601
+ const getEmailAddressInputSchema = z.object({ email_address_id: z.string().min(1) });
2362
2602
  const ResourceSchema$42 = z.object({
2363
2603
  id: z.string(),
2364
2604
  object: z.string().optional(),
@@ -2393,8 +2633,8 @@ function getEmailAddressTool(proxy) {
2393
2633
  //#endregion
2394
2634
  //#region src/providers/clerk/tools/get-organization-invitation.ts
2395
2635
  const getOrganizationInvitationInputSchema = z.object({
2396
- organization_id: z.string(),
2397
- invitation_id: z.string()
2636
+ organization_id: z.string().min(1),
2637
+ invitation_id: z.string().min(1)
2398
2638
  });
2399
2639
  const ResourceSchema$41 = z.object({
2400
2640
  id: z.string(),
@@ -2407,7 +2647,7 @@ const ResourceSchema$41 = z.object({
2407
2647
  "accepted",
2408
2648
  "revoked",
2409
2649
  "expired"
2410
- ]).optional(),
2650
+ ]).or(z.string()).optional(),
2411
2651
  created_at: z.number().optional(),
2412
2652
  updated_at: z.number().optional()
2413
2653
  }).passthrough();
@@ -2429,7 +2669,7 @@ function getOrganizationInvitationTool(proxy) {
2429
2669
  }
2430
2670
  //#endregion
2431
2671
  //#region src/providers/clerk/tools/get-organization-role.ts
2432
- const getOrganizationRoleInputSchema = z.object({ organization_role_id: z.string() });
2672
+ const getOrganizationRoleInputSchema = z.object({ organization_role_id: z.string().min(1) });
2433
2673
  const ResourceSchema$40 = z.object({
2434
2674
  id: z.string(),
2435
2675
  object: z.string().optional(),
@@ -2501,7 +2741,7 @@ function getOrganizationTool$1(proxy) {
2501
2741
  }
2502
2742
  //#endregion
2503
2743
  //#region src/providers/clerk/tools/get-phone-number.ts
2504
- const getPhoneNumberInputSchema = z.object({ phone_number_id: z.string() });
2744
+ const getPhoneNumberInputSchema = z.object({ phone_number_id: z.string().min(1) });
2505
2745
  const ResourceSchema$39 = z.object({
2506
2746
  id: z.string(),
2507
2747
  object: z.string().optional(),
@@ -2536,7 +2776,7 @@ function getPhoneNumberTool(proxy) {
2536
2776
  }
2537
2777
  //#endregion
2538
2778
  //#region src/providers/clerk/tools/get-session.ts
2539
- const getSessionInputSchema = z.object({ session_id: z.string() });
2779
+ const getSessionInputSchema = z.object({ session_id: z.string().min(1) });
2540
2780
  const ResourceSchema$38 = z.object({
2541
2781
  id: z.string(),
2542
2782
  object: z.string().optional(),
@@ -2550,7 +2790,7 @@ const ResourceSchema$38 = z.object({
2550
2790
  "removed",
2551
2791
  "replaced",
2552
2792
  "revoked"
2553
- ]).optional(),
2793
+ ]).or(z.string()).optional(),
2554
2794
  last_active_at: z.number().optional(),
2555
2795
  expire_at: z.number().optional(),
2556
2796
  abandon_at: z.number().optional(),
@@ -2610,33 +2850,39 @@ function getUserTool$7(proxy) {
2610
2850
  }
2611
2851
  //#endregion
2612
2852
  //#region src/providers/clerk/tools/list-organization-domains.ts
2613
- const EnrollmentModeSchema$1 = z.enum([
2853
+ const EnrollmentModeSchema$2 = z.enum([
2614
2854
  "manual_invitation",
2615
2855
  "automatic_invitation",
2616
2856
  "automatic_suggestion",
2617
2857
  "enterprise_sso"
2618
2858
  ]);
2859
+ const EnrollmentModeSchemaWidened$1 = z.enum([
2860
+ "manual_invitation",
2861
+ "automatic_invitation",
2862
+ "automatic_suggestion",
2863
+ "enterprise_sso"
2864
+ ]).or(z.string());
2619
2865
  const listOrganizationDomainsInputSchema = z.object({
2620
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2866
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2621
2867
  limit: z.number().int().min(1).max(500).optional(),
2622
- organization_id: z.string(),
2868
+ organization_id: z.string().min(1),
2623
2869
  verified: z.boolean().optional(),
2624
- enrollment_mode: EnrollmentModeSchema$1.optional()
2870
+ enrollment_mode: EnrollmentModeSchema$2.optional()
2625
2871
  });
2626
2872
  const ResourceSchema$37 = z.object({
2627
2873
  id: z.string(),
2628
2874
  object: z.string().optional(),
2629
2875
  organization_id: z.string().optional(),
2630
2876
  name: z.string(),
2631
- enrollment_mode: EnrollmentModeSchema$1.optional(),
2877
+ enrollment_mode: EnrollmentModeSchemaWidened$1.optional(),
2632
2878
  affiliation_verification: z.object({
2633
2879
  attempts: z.number().optional(),
2634
2880
  status: z.string().optional()
2635
- }).passthrough().optional(),
2881
+ }).passthrough().nullable().optional(),
2636
2882
  verification: z.object({
2637
2883
  attempts: z.number().optional(),
2638
2884
  status: z.string().optional()
2639
- }).passthrough().optional(),
2885
+ }).passthrough().nullable().optional(),
2640
2886
  verified: z.boolean().optional(),
2641
2887
  created_at: z.number().optional(),
2642
2888
  updated_at: z.number().optional()
@@ -2647,7 +2893,7 @@ const ProviderResponseSchema$244 = z.object({
2647
2893
  });
2648
2894
  const listOrganizationDomainsOutputSchema = z.object({
2649
2895
  items: z.array(ResourceSchema$37),
2650
- next_cursor: z.string().optional(),
2896
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2651
2897
  total: z.number()
2652
2898
  });
2653
2899
  function listOrganizationDomainsTool(proxy) {
@@ -2658,11 +2904,7 @@ function listOrganizationDomainsTool(proxy) {
2658
2904
  outputSchema: listOrganizationDomainsOutputSchema,
2659
2905
  execute: async (input, { requestContext }) => {
2660
2906
  const platformProxy = proxy.withRequestContext(requestContext);
2661
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
2662
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
2663
- type: "invalid_cursor",
2664
- message: "Cursor must be a non-negative integer."
2665
- });
2907
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
2666
2908
  const response = await platformProxy.get({
2667
2909
  endpoint: `/v1/organizations/${encodeURIComponent(input.organization_id)}/domains`,
2668
2910
  params: {
@@ -2686,9 +2928,9 @@ function listOrganizationDomainsTool(proxy) {
2686
2928
  //#endregion
2687
2929
  //#region src/providers/clerk/tools/list-organization-invitations.ts
2688
2930
  const listOrganizationInvitationsInputSchema = z.object({
2689
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2931
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2690
2932
  limit: z.number().int().min(1).max(500).optional(),
2691
- organization_id: z.string(),
2933
+ organization_id: z.string().min(1),
2692
2934
  status: z.enum([
2693
2935
  "pending",
2694
2936
  "accepted",
@@ -2709,7 +2951,7 @@ const ResourceSchema$36 = z.object({
2709
2951
  "accepted",
2710
2952
  "revoked",
2711
2953
  "expired"
2712
- ]).optional(),
2954
+ ]).or(z.string()).optional(),
2713
2955
  created_at: z.number().optional(),
2714
2956
  updated_at: z.number().optional()
2715
2957
  }).passthrough();
@@ -2719,7 +2961,7 @@ const ProviderResponseSchema$243 = z.object({
2719
2961
  });
2720
2962
  const listOrganizationInvitationsOutputSchema = z.object({
2721
2963
  items: z.array(ResourceSchema$36),
2722
- next_cursor: z.string().optional(),
2964
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2723
2965
  total: z.number()
2724
2966
  });
2725
2967
  function listOrganizationInvitationsTool(proxy) {
@@ -2730,11 +2972,7 @@ function listOrganizationInvitationsTool(proxy) {
2730
2972
  outputSchema: listOrganizationInvitationsOutputSchema,
2731
2973
  execute: async (input, { requestContext }) => {
2732
2974
  const platformProxy = proxy.withRequestContext(requestContext);
2733
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
2734
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
2735
- type: "invalid_cursor",
2736
- message: "Cursor must be a non-negative integer."
2737
- });
2975
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
2738
2976
  const response = await platformProxy.get({
2739
2977
  endpoint: `/v1/organizations/${encodeURIComponent(input.organization_id)}/invitations`,
2740
2978
  params: {
@@ -2759,9 +2997,9 @@ function listOrganizationInvitationsTool(proxy) {
2759
2997
  //#endregion
2760
2998
  //#region src/providers/clerk/tools/list-organization-memberships.ts
2761
2999
  const listOrganizationMembershipsInputSchema$1 = z.object({
2762
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3000
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2763
3001
  limit: z.number().int().min(1).max(500).optional(),
2764
- organization_id: z.string(),
3002
+ organization_id: z.string().min(1),
2765
3003
  user_id: z.array(z.string()).optional(),
2766
3004
  email_address: z.array(z.string()).optional(),
2767
3005
  phone_number: z.array(z.string()).optional(),
@@ -2795,7 +3033,7 @@ const ProviderResponseSchema$242 = z.object({
2795
3033
  });
2796
3034
  const listOrganizationMembershipsOutputSchema$1 = z.object({
2797
3035
  items: z.array(ResourceSchema$35),
2798
- next_cursor: z.string().optional(),
3036
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2799
3037
  total: z.number()
2800
3038
  });
2801
3039
  function listOrganizationMembershipsTool$1(proxy) {
@@ -2806,11 +3044,7 @@ function listOrganizationMembershipsTool$1(proxy) {
2806
3044
  outputSchema: listOrganizationMembershipsOutputSchema$1,
2807
3045
  execute: async (input, { requestContext }) => {
2808
3046
  const platformProxy = proxy.withRequestContext(requestContext);
2809
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
2810
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
2811
- type: "invalid_cursor",
2812
- message: "Cursor must be a non-negative integer."
2813
- });
3047
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
2814
3048
  const response = await platformProxy.get({
2815
3049
  endpoint: `/v1/organizations/${encodeURIComponent(input.organization_id)}/memberships`,
2816
3050
  params: {
@@ -2848,7 +3082,7 @@ function listOrganizationMembershipsTool$1(proxy) {
2848
3082
  //#endregion
2849
3083
  //#region src/providers/clerk/tools/list-organization-roles.ts
2850
3084
  const listOrganizationRolesInputSchema = z.object({
2851
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3085
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2852
3086
  limit: z.number().int().min(1).max(500).optional(),
2853
3087
  query: z.string().optional(),
2854
3088
  order_by: z.string().optional()
@@ -2873,7 +3107,7 @@ const ProviderResponseSchema$241 = z.object({
2873
3107
  });
2874
3108
  const listOrganizationRolesOutputSchema = z.object({
2875
3109
  items: z.array(ResourceSchema$34),
2876
- next_cursor: z.string().optional(),
3110
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2877
3111
  total: z.number()
2878
3112
  });
2879
3113
  function listOrganizationRolesTool(proxy) {
@@ -2884,11 +3118,7 @@ function listOrganizationRolesTool(proxy) {
2884
3118
  outputSchema: listOrganizationRolesOutputSchema,
2885
3119
  execute: async (input, { requestContext }) => {
2886
3120
  const platformProxy = proxy.withRequestContext(requestContext);
2887
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
2888
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
2889
- type: "invalid_cursor",
2890
- message: "Cursor must be a non-negative integer."
2891
- });
3121
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
2892
3122
  const response = await platformProxy.get({
2893
3123
  endpoint: "/v1/organization_roles",
2894
3124
  params: {
@@ -2912,7 +3142,7 @@ function listOrganizationRolesTool(proxy) {
2912
3142
  //#endregion
2913
3143
  //#region src/providers/clerk/tools/list-organizations.ts
2914
3144
  const listOrganizationsInputSchema$1 = z.object({
2915
- cursor: z.string().optional(),
3145
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2916
3146
  limit: z.number().int().min(1).max(500).optional(),
2917
3147
  query: z.string().optional(),
2918
3148
  order_by: z.string().optional().describe("Sort by name, created_at, or members_count, prefixed with + or -."),
@@ -2952,11 +3182,7 @@ function listOrganizationsTool$1(proxy) {
2952
3182
  outputSchema: listOrganizationsOutputSchema$1,
2953
3183
  execute: async (input, { requestContext }) => {
2954
3184
  const platformProxy = proxy.withRequestContext(requestContext);
2955
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
2956
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
2957
- type: "invalid_cursor",
2958
- message: "Cursor must be a non-negative integer."
2959
- });
3185
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
2960
3186
  const response = await platformProxy.get({
2961
3187
  endpoint: "/v1/organizations",
2962
3188
  params: {
@@ -2982,10 +3208,10 @@ function listOrganizationsTool$1(proxy) {
2982
3208
  //#endregion
2983
3209
  //#region src/providers/clerk/tools/list-sessions.ts
2984
3210
  const listSessionsInputSchema = z.object({
2985
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3211
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
2986
3212
  limit: z.number().int().min(1).max(500).optional(),
2987
- client_id: z.string().optional(),
2988
- user_id: z.string().optional(),
3213
+ client_id: z.string().min(1).optional(),
3214
+ user_id: z.string().min(1).optional(),
2989
3215
  status: z.enum([
2990
3216
  "abandoned",
2991
3217
  "active",
@@ -2995,7 +3221,7 @@ const listSessionsInputSchema = z.object({
2995
3221
  "replaced",
2996
3222
  "revoked"
2997
3223
  ]).optional()
2998
- });
3224
+ }).refine((value) => value.client_id !== void 0 || value.user_id !== void 0, { message: "Provide at least one of client_id or user_id." });
2999
3225
  const ResourceSchema$33 = z.object({
3000
3226
  id: z.string(),
3001
3227
  object: z.string().optional(),
@@ -3009,21 +3235,17 @@ const ResourceSchema$33 = z.object({
3009
3235
  "removed",
3010
3236
  "replaced",
3011
3237
  "revoked"
3012
- ]).optional(),
3238
+ ]).or(z.string()).optional(),
3013
3239
  last_active_at: z.number().optional(),
3014
3240
  expire_at: z.number().optional(),
3015
3241
  abandon_at: z.number().optional(),
3016
3242
  created_at: z.number().optional(),
3017
3243
  updated_at: z.number().optional()
3018
3244
  }).passthrough();
3019
- const ProviderResponseSchema$239 = z.object({
3020
- data: z.array(ResourceSchema$33),
3021
- total_count: z.number()
3022
- });
3245
+ const ProviderResponseSchema$239 = z.array(ResourceSchema$33);
3023
3246
  const listSessionsOutputSchema = z.object({
3024
3247
  items: z.array(ResourceSchema$33),
3025
- next_cursor: z.string().optional(),
3026
- total: z.number()
3248
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page.")
3027
3249
  });
3028
3250
  function listSessionsTool(proxy) {
3029
3251
  return createTool({
@@ -3033,28 +3255,23 @@ function listSessionsTool(proxy) {
3033
3255
  outputSchema: listSessionsOutputSchema,
3034
3256
  execute: async (input, { requestContext }) => {
3035
3257
  const platformProxy = proxy.withRequestContext(requestContext);
3036
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
3037
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
3038
- type: "invalid_cursor",
3039
- message: "Cursor must be a non-negative integer."
3040
- });
3258
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
3259
+ const limit = input.limit ?? 10;
3041
3260
  const response = await platformProxy.get({
3042
3261
  endpoint: "/v1/sessions",
3043
3262
  params: {
3044
3263
  offset: String(offset),
3045
- ...input.limit !== void 0 && { limit: String(input.limit) },
3264
+ limit: String(limit),
3046
3265
  ...input.client_id !== void 0 && { client_id: input.client_id },
3047
3266
  ...input.user_id !== void 0 && { user_id: input.user_id },
3048
3267
  ...input.status !== void 0 && { status: input.status }
3049
3268
  },
3050
3269
  retries: 3
3051
3270
  });
3052
- const provider = ProviderResponseSchema$239.parse(response.data);
3053
- const nextOffset = offset + provider.data.length;
3271
+ const sessions = ProviderResponseSchema$239.parse(response.data);
3054
3272
  return {
3055
- items: provider.data,
3056
- ...nextOffset < provider.total_count && { next_cursor: String(nextOffset) },
3057
- total: provider.total_count
3273
+ items: sessions,
3274
+ ...sessions.length === limit && { next_cursor: String(offset + sessions.length) }
3058
3275
  };
3059
3276
  }
3060
3277
  });
@@ -3062,13 +3279,19 @@ function listSessionsTool(proxy) {
3062
3279
  //#endregion
3063
3280
  //#region src/providers/clerk/tools/list-users.ts
3064
3281
  const listUsersInputSchema$9 = z.object({
3065
- cursor: z.string().optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3282
+ cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3066
3283
  limit: z.number().int().min(1).max(500).optional().describe("Maximum number of users to return. Maximum 500."),
3067
3284
  query: z.string().optional().describe("Search across names, identifiers, email addresses, phone numbers, and usernames."),
3068
3285
  email_address: z.array(z.string()).max(100).optional().describe("Filter by email addresses."),
3286
+ phone_number: z.array(z.string()).max(100).optional().describe("Filter by phone numbers."),
3287
+ username: z.array(z.string()).max(100).optional().describe("Filter by usernames."),
3069
3288
  user_id: z.array(z.string()).max(100).optional().describe("Filter by Clerk user IDs."),
3070
3289
  external_id: z.array(z.string()).max(100).optional().describe("Filter by external user IDs."),
3071
3290
  organization_id: z.array(z.string()).max(100).optional().describe("Filter by organization IDs."),
3291
+ last_active_at_before: z.number().int().optional().describe("Filter users last active before this Unix timestamp in milliseconds."),
3292
+ last_active_at_after: z.number().int().optional().describe("Filter users last active after this Unix timestamp in milliseconds."),
3293
+ created_at_before: z.number().int().optional().describe("Filter users created before this Unix timestamp in milliseconds."),
3294
+ created_at_after: z.number().int().optional().describe("Filter users created after this Unix timestamp in milliseconds."),
3072
3295
  order_by: z.string().optional().describe("Sort field prefixed with + for ascending or - for descending. Example: \"-created_at\"")
3073
3296
  });
3074
3297
  const UserSchema$25 = z.object({
@@ -3087,13 +3310,11 @@ const UserSchema$25 = z.object({
3087
3310
  updated_at: z.number().optional(),
3088
3311
  locale: z.string().nullable().optional()
3089
3312
  }).passthrough();
3090
- const ProviderResponseSchema$238 = z.object({
3091
- data: z.array(UserSchema$25),
3092
- total_count: z.number()
3093
- });
3313
+ const ProviderResponseSchema$238 = z.array(UserSchema$25);
3314
+ const ProviderCountSchema = z.object({ total_count: z.number() }).passthrough();
3094
3315
  const listUsersOutputSchema$9 = z.object({
3095
3316
  items: z.array(UserSchema$25),
3096
- next_cursor: z.string().optional(),
3317
+ next_cursor: z.string().regex(/^\d+$/, "Cursor must be a non-negative integer.").refine((value) => Number.isSafeInteger(Number(value)), "Cursor must be a non-negative integer.").optional().describe("Pagination cursor returned by a previous request. Omit for the first page."),
3097
3318
  total: z.number()
3098
3319
  });
3099
3320
  function listUsersTool$9(proxy) {
@@ -3104,31 +3325,42 @@ function listUsersTool$9(proxy) {
3104
3325
  outputSchema: listUsersOutputSchema$9,
3105
3326
  execute: async (input, { requestContext }) => {
3106
3327
  const platformProxy = proxy.withRequestContext(requestContext);
3107
- const offset = input.cursor === void 0 ? 0 : Number.parseInt(input.cursor, 10);
3108
- if (!Number.isInteger(offset) || offset < 0) throw new platformProxy.ActionError({
3109
- type: "invalid_cursor",
3110
- message: "Cursor must be a non-negative integer."
3111
- });
3328
+ const offset = input.cursor === void 0 ? 0 : Number(input.cursor);
3329
+ const filterParams = {
3330
+ ...input.query !== void 0 && { query: input.query },
3331
+ ...input.email_address !== void 0 && { email_address: input.email_address },
3332
+ ...input.phone_number !== void 0 && { phone_number: input.phone_number },
3333
+ ...input.username !== void 0 && { username: input.username },
3334
+ ...input.user_id !== void 0 && { user_id: input.user_id },
3335
+ ...input.external_id !== void 0 && { external_id: input.external_id },
3336
+ ...input.organization_id !== void 0 && { organization_id: input.organization_id },
3337
+ ...input.last_active_at_before !== void 0 && { last_active_at_before: String(input.last_active_at_before) },
3338
+ ...input.last_active_at_after !== void 0 && { last_active_at_after: String(input.last_active_at_after) },
3339
+ ...input.created_at_before !== void 0 && { created_at_before: String(input.created_at_before) },
3340
+ ...input.created_at_after !== void 0 && { created_at_after: String(input.created_at_after) }
3341
+ };
3112
3342
  const response = await platformProxy.get({
3113
3343
  endpoint: "/v1/users",
3114
3344
  params: {
3115
3345
  offset: String(offset),
3116
3346
  ...input.limit !== void 0 && { limit: String(input.limit) },
3117
- ...input.query !== void 0 && { query: input.query },
3118
- ...input.email_address !== void 0 && { email_address: input.email_address },
3119
- ...input.user_id !== void 0 && { user_id: input.user_id },
3120
- ...input.external_id !== void 0 && { external_id: input.external_id },
3121
- ...input.organization_id !== void 0 && { organization_id: input.organization_id },
3122
- ...input.order_by !== void 0 && { order_by: input.order_by }
3347
+ ...input.order_by !== void 0 && { order_by: input.order_by },
3348
+ ...filterParams
3123
3349
  },
3124
3350
  retries: 3
3125
3351
  });
3126
- const provider = ProviderResponseSchema$238.parse(response.data);
3127
- const nextOffset = offset + provider.data.length;
3352
+ const users = ProviderResponseSchema$238.parse(response.data);
3353
+ const countResponse = await platformProxy.get({
3354
+ endpoint: "/v1/users/count",
3355
+ params: filterParams,
3356
+ retries: 3
3357
+ });
3358
+ const total = ProviderCountSchema.parse(countResponse.data).total_count;
3359
+ const nextOffset = offset + users.length;
3128
3360
  return {
3129
- items: provider.data,
3130
- ...nextOffset < provider.total_count && { next_cursor: String(nextOffset) },
3131
- total: provider.total_count
3361
+ items: users,
3362
+ ...users.length > 0 && nextOffset < total && { next_cursor: String(nextOffset) },
3363
+ total
3132
3364
  };
3133
3365
  }
3134
3366
  });
@@ -3136,8 +3368,8 @@ function listUsersTool$9(proxy) {
3136
3368
  //#endregion
3137
3369
  //#region src/providers/clerk/tools/remove-organization-role-permission.ts
3138
3370
  const removeOrganizationRolePermissionInputSchema = z.object({
3139
- organization_role_id: z.string(),
3140
- permission_id: z.string()
3371
+ organization_role_id: z.string().min(1),
3372
+ permission_id: z.string().min(1)
3141
3373
  });
3142
3374
  const removeOrganizationRolePermissionOutputSchema = z.object({
3143
3375
  id: z.string(),
@@ -3164,8 +3396,8 @@ function removeOrganizationRolePermissionTool(proxy) {
3164
3396
  //#endregion
3165
3397
  //#region src/providers/clerk/tools/revoke-organization-invitation.ts
3166
3398
  const revokeOrganizationInvitationInputSchema = z.object({
3167
- organization_id: z.string(),
3168
- invitation_id: z.string(),
3399
+ organization_id: z.string().min(1),
3400
+ invitation_id: z.string().min(1),
3169
3401
  requesting_user_id: z.string().optional()
3170
3402
  });
3171
3403
  const ResourceSchema$32 = z.object({
@@ -3179,7 +3411,7 @@ const ResourceSchema$32 = z.object({
3179
3411
  "accepted",
3180
3412
  "revoked",
3181
3413
  "expired"
3182
- ]).optional(),
3414
+ ]).or(z.string()).optional(),
3183
3415
  created_at: z.number().optional(),
3184
3416
  updated_at: z.number().optional()
3185
3417
  }).passthrough();
@@ -3202,7 +3434,7 @@ function revokeOrganizationInvitationTool(proxy) {
3202
3434
  }
3203
3435
  //#endregion
3204
3436
  //#region src/providers/clerk/tools/revoke-session.ts
3205
- const revokeSessionInputSchema = z.object({ session_id: z.string() });
3437
+ const revokeSessionInputSchema = z.object({ session_id: z.string().min(1) });
3206
3438
  const ResourceSchema$31 = z.object({
3207
3439
  id: z.string(),
3208
3440
  object: z.string().optional(),
@@ -3216,7 +3448,7 @@ const ResourceSchema$31 = z.object({
3216
3448
  "removed",
3217
3449
  "replaced",
3218
3450
  "revoked"
3219
- ]).optional(),
3451
+ ]).or(z.string()).optional(),
3220
3452
  last_active_at: z.number().optional(),
3221
3453
  expire_at: z.number().optional(),
3222
3454
  abandon_at: z.number().optional(),
@@ -3243,7 +3475,7 @@ function revokeSessionTool(proxy) {
3243
3475
  //#endregion
3244
3476
  //#region src/providers/clerk/tools/update-email-address.ts
3245
3477
  const updateEmailAddressInputSchema = z.object({
3246
- email_address_id: z.string(),
3478
+ email_address_id: z.string().min(1),
3247
3479
  verified: z.boolean().optional(),
3248
3480
  primary: z.boolean().optional(),
3249
3481
  notify_primary_email_address_changed: z.boolean().optional()
@@ -3286,16 +3518,22 @@ function updateEmailAddressTool(proxy) {
3286
3518
  }
3287
3519
  //#endregion
3288
3520
  //#region src/providers/clerk/tools/update-organization-domain.ts
3289
- const EnrollmentModeSchema = z.enum([
3521
+ const EnrollmentModeSchema$1 = z.enum([
3290
3522
  "manual_invitation",
3291
3523
  "automatic_invitation",
3292
3524
  "automatic_suggestion",
3293
3525
  "enterprise_sso"
3294
3526
  ]);
3527
+ const EnrollmentModeSchemaWidened = z.enum([
3528
+ "manual_invitation",
3529
+ "automatic_invitation",
3530
+ "automatic_suggestion",
3531
+ "enterprise_sso"
3532
+ ]).or(z.string());
3295
3533
  const updateOrganizationDomainInputSchema = z.object({
3296
- organization_id: z.string(),
3297
- domain_id: z.string(),
3298
- enrollment_mode: EnrollmentModeSchema.optional(),
3534
+ organization_id: z.string().min(1),
3535
+ domain_id: z.string().min(1),
3536
+ enrollment_mode: EnrollmentModeSchema$1.optional(),
3299
3537
  verified: z.boolean().optional()
3300
3538
  });
3301
3539
  const ResourceSchema$29 = z.object({
@@ -3303,15 +3541,15 @@ const ResourceSchema$29 = z.object({
3303
3541
  object: z.string().optional(),
3304
3542
  organization_id: z.string().optional(),
3305
3543
  name: z.string(),
3306
- enrollment_mode: EnrollmentModeSchema.optional(),
3544
+ enrollment_mode: EnrollmentModeSchemaWidened.optional(),
3307
3545
  affiliation_verification: z.object({
3308
3546
  attempts: z.number().optional(),
3309
3547
  status: z.string().optional()
3310
- }).passthrough().optional(),
3548
+ }).passthrough().nullable().optional(),
3311
3549
  verification: z.object({
3312
3550
  attempts: z.number().optional(),
3313
3551
  status: z.string().optional()
3314
- }).passthrough().optional(),
3552
+ }).passthrough().nullable().optional(),
3315
3553
  verified: z.boolean().optional(),
3316
3554
  created_at: z.number().optional(),
3317
3555
  updated_at: z.number().optional()
@@ -3339,8 +3577,8 @@ function updateOrganizationDomainTool(proxy) {
3339
3577
  //#endregion
3340
3578
  //#region src/providers/clerk/tools/update-organization-membership-metadata.ts
3341
3579
  const updateOrganizationMembershipMetadataInputSchema = z.object({
3342
- organization_id: z.string(),
3343
- user_id: z.string(),
3580
+ organization_id: z.string().min(1),
3581
+ user_id: z.string().min(1),
3344
3582
  public_metadata: z.record(z.string(), z.unknown()).optional(),
3345
3583
  private_metadata: z.record(z.string(), z.unknown()).optional()
3346
3584
  });
@@ -3377,8 +3615,8 @@ function updateOrganizationMembershipMetadataTool(proxy) {
3377
3615
  //#endregion
3378
3616
  //#region src/providers/clerk/tools/update-organization-membership.ts
3379
3617
  const updateOrganizationMembershipInputSchema$1 = z.object({
3380
- organization_id: z.string(),
3381
- user_id: z.string(),
3618
+ organization_id: z.string().min(1),
3619
+ user_id: z.string().min(1),
3382
3620
  role: z.string()
3383
3621
  });
3384
3622
  const ResourceSchema$27 = z.object({
@@ -3411,12 +3649,12 @@ function updateOrganizationMembershipTool$1(proxy) {
3411
3649
  //#endregion
3412
3650
  //#region src/providers/clerk/tools/update-organization-role.ts
3413
3651
  const updateOrganizationRoleInputSchema = z.object({
3414
- organization_role_id: z.string(),
3652
+ organization_role_id: z.string().min(1),
3415
3653
  name: z.string().optional(),
3416
3654
  key: z.string().optional(),
3417
3655
  description: z.string().optional(),
3418
3656
  permissions: z.array(z.string()).optional()
3419
- });
3657
+ }).refine((value) => value.name !== void 0 || value.key !== void 0 || value.description !== void 0 || value.permissions !== void 0, { message: "Provide at least one of name, key, description, or permissions." });
3420
3658
  const ResourceSchema$26 = z.object({
3421
3659
  id: z.string(),
3422
3660
  object: z.string().optional(),
@@ -3460,7 +3698,8 @@ const updateOrganizationInputSchema$1 = z.object({
3460
3698
  name: z.string().min(1).optional(),
3461
3699
  slug: z.string().optional(),
3462
3700
  admin_delete_enabled: z.boolean().optional(),
3463
- max_allowed_memberships: z.number().int().min(0).optional()
3701
+ max_allowed_memberships: z.number().int().min(0).optional(),
3702
+ public_metadata: z.record(z.string(), z.unknown()).optional()
3464
3703
  });
3465
3704
  const OrganizationSchema$1 = z.object({
3466
3705
  id: z.string(),
@@ -3492,7 +3731,8 @@ function updateOrganizationTool$1(proxy) {
3492
3731
  ...input.name !== void 0 && { name: input.name },
3493
3732
  ...input.slug !== void 0 && { slug: input.slug },
3494
3733
  ...input.admin_delete_enabled !== void 0 && { admin_delete_enabled: input.admin_delete_enabled },
3495
- ...input.max_allowed_memberships !== void 0 && { max_allowed_memberships: input.max_allowed_memberships }
3734
+ ...input.max_allowed_memberships !== void 0 && { max_allowed_memberships: input.max_allowed_memberships },
3735
+ ...input.public_metadata !== void 0 && { public_metadata: input.public_metadata }
3496
3736
  },
3497
3737
  retries: 3
3498
3738
  });
@@ -3503,7 +3743,7 @@ function updateOrganizationTool$1(proxy) {
3503
3743
  //#endregion
3504
3744
  //#region src/providers/clerk/tools/update-phone-number.ts
3505
3745
  const updatePhoneNumberInputSchema = z.object({
3506
- phone_number_id: z.string(),
3746
+ phone_number_id: z.string().min(1),
3507
3747
  verified: z.boolean().optional(),
3508
3748
  primary: z.boolean().optional(),
3509
3749
  reserved_for_second_factor: z.boolean().optional()
@@ -3549,14 +3789,14 @@ function updatePhoneNumberTool(proxy) {
3549
3789
  //#region src/providers/clerk/tools/update-user.ts
3550
3790
  const updateUserInputSchema$1 = z.object({
3551
3791
  user_id: z.string().min(1),
3552
- first_name: z.string().optional(),
3553
- last_name: z.string().optional(),
3554
- username: z.string().optional(),
3792
+ first_name: z.string().nullable().optional().describe("Pass null to clear the first name."),
3793
+ last_name: z.string().nullable().optional().describe("Pass null to clear the last name."),
3794
+ username: z.string().nullable().optional().describe("Pass null to clear the username."),
3555
3795
  password: z.string().min(8).optional(),
3556
3796
  skip_password_checks: z.boolean().optional(),
3557
3797
  sign_out_of_other_sessions: z.boolean().optional(),
3558
- external_id: z.string().optional(),
3559
- locale: z.string().optional()
3798
+ external_id: z.string().nullable().optional().describe("Pass null to clear the external ID."),
3799
+ locale: z.string().nullable().optional().describe("Pass null to clear the locale.")
3560
3800
  });
3561
3801
  const UserSchema$24 = z.object({
3562
3802
  id: z.string(),
@@ -3602,24 +3842,30 @@ function updateUserTool$1(proxy) {
3602
3842
  }
3603
3843
  //#endregion
3604
3844
  //#region src/providers/clerk/tools/verify-organization-domain.ts
3845
+ const EnrollmentModeSchema = z.enum([
3846
+ "manual_invitation",
3847
+ "automatic_invitation",
3848
+ "automatic_suggestion",
3849
+ "enterprise_sso"
3850
+ ]).or(z.string());
3605
3851
  const verifyOrganizationDomainInputSchema$1 = z.object({
3606
- organization_id: z.string(),
3607
- domain_id: z.string()
3852
+ organization_id: z.string().min(1),
3853
+ domain_id: z.string().min(1)
3608
3854
  });
3609
3855
  const ResourceSchema$24 = z.object({
3610
3856
  id: z.string(),
3611
3857
  object: z.string().optional(),
3612
3858
  organization_id: z.string().optional(),
3613
3859
  name: z.string(),
3614
- enrollment_mode: z.string().optional(),
3860
+ enrollment_mode: EnrollmentModeSchema.optional(),
3615
3861
  affiliation_verification: z.object({
3616
3862
  attempts: z.number().optional(),
3617
3863
  status: z.string().optional()
3618
- }).passthrough().optional(),
3864
+ }).passthrough().nullable().optional(),
3619
3865
  verification: z.object({
3620
3866
  attempts: z.number().optional(),
3621
3867
  status: z.string().optional()
3622
- }).passthrough().optional(),
3868
+ }).passthrough().nullable().optional(),
3623
3869
  verified: z.boolean().optional(),
3624
3870
  created_at: z.number().optional(),
3625
3871
  updated_at: z.number().optional()
@@ -5177,7 +5423,7 @@ const ChannelSchema$2 = z.object({
5177
5423
  recipients: z.array(z.unknown()).optional(),
5178
5424
  icon: z.string().nullable().optional(),
5179
5425
  owner_id: z.string().optional(),
5180
- application_id: z.string().optional(),
5426
+ application_id: z.string().nullable().optional(),
5181
5427
  managed: z.boolean().optional(),
5182
5428
  parent_id: z.string().nullable().optional(),
5183
5429
  last_pin_timestamp: z.string().nullable().optional(),
@@ -34495,12 +34741,35 @@ const getCreateIssueMetadataInputSchema = z.object({
34495
34741
  issuetypeIds: z.array(z.string()).optional().describe("List of issue type IDs to filter by. Example: [\"10000\", \"10001\"]"),
34496
34742
  expand: z.string().optional().describe("Use expand to include additional information about issue metadata in the response. Supported values: projects, projects.issuetypes, projects.issuetypes.fields")
34497
34743
  });
34744
+ const CreateIssueFieldSchema = z.object({
34745
+ required: z.boolean().optional(),
34746
+ schema: z.record(z.string(), z.unknown()).optional(),
34747
+ name: z.string().optional(),
34748
+ key: z.string().optional(),
34749
+ hasDefaultValue: z.boolean().optional(),
34750
+ operations: z.array(z.string()).optional(),
34751
+ allowedValues: z.array(z.unknown()).optional(),
34752
+ defaultValue: z.unknown().optional(),
34753
+ autoCompleteUrl: z.string().optional()
34754
+ });
34755
+ const IssueTypeSchema$1 = z.object({
34756
+ id: z.string(),
34757
+ name: z.string(),
34758
+ self: z.string().optional(),
34759
+ description: z.string().optional(),
34760
+ iconUrl: z.string().optional(),
34761
+ avatarId: z.number().optional(),
34762
+ subtask: z.boolean().optional(),
34763
+ hierarchyLevel: z.number().optional(),
34764
+ fields: z.record(z.string(), CreateIssueFieldSchema).optional()
34765
+ });
34498
34766
  const ProjectSchema$3 = z.object({
34499
34767
  id: z.string(),
34500
34768
  key: z.string(),
34501
34769
  name: z.string(),
34502
34770
  self: z.string().optional(),
34503
- avatarUrls: z.record(z.string(), z.string()).optional()
34771
+ avatarUrls: z.record(z.string(), z.string()).optional(),
34772
+ issuetypes: z.array(IssueTypeSchema$1).optional()
34504
34773
  });
34505
34774
  const ProviderResponseSchema$149 = z.object({ projects: z.array(ProjectSchema$3).optional() });
34506
34775
  const getCreateIssueMetadataOutputSchema = z.object({ projects: z.array(ProjectSchema$3).optional() });
@@ -35620,7 +35889,7 @@ z.object({
35620
35889
  cloudId: z.string().optional(),
35621
35890
  baseUrl: z.string().optional()
35622
35891
  });
35623
- const listIssueTypesInputSchema = z.object({});
35892
+ const listIssueTypesInputSchema = z.object({ projectId: z.string().optional().describe("Project ID used to list only issue types available in that project.") });
35624
35893
  const IssueTypeSchema = z.object({
35625
35894
  id: z.string(),
35626
35895
  name: z.string(),
@@ -35649,7 +35918,7 @@ const AccessibleResourceSchema$1 = z.object({
35649
35918
  function listIssueTypesTool(proxy) {
35650
35919
  return createTool({
35651
35920
  id: "jira_list_issue_types",
35652
- description: "List Jira issue types available to the user",
35921
+ description: "List Jira issue types available to the user. Pass projectId to list only the issue types available in that project (recommended before creating an issue).",
35653
35922
  inputSchema: listIssueTypesInputSchema,
35654
35923
  outputSchema: listIssueTypesOutputSchema,
35655
35924
  execute: async (input, { requestContext }) => {
@@ -35684,7 +35953,12 @@ function listIssueTypesTool(proxy) {
35684
35953
  baseUrl
35685
35954
  });
35686
35955
  }
35687
- const config = {
35956
+ const config = input.projectId ? {
35957
+ endpoint: `/ex/jira/${cloudId}/rest/api/3/issuetype/project`,
35958
+ params: { projectId: input.projectId },
35959
+ headers: { "X-Atlassian-Token": "no-check" },
35960
+ retries: 3
35961
+ } : {
35688
35962
  endpoint: `/ex/jira/${cloudId}/rest/api/3/issuetype`,
35689
35963
  headers: { "X-Atlassian-Token": "no-check" },
35690
35964
  retries: 3
@@ -55242,7 +55516,7 @@ const cancelEmailInputSchema = z.object({ email_id: z.string() });
55242
55516
  const ProviderResponseSchema$107 = z.object({
55243
55517
  object: z.string().optional(),
55244
55518
  id: z.string().optional(),
55245
- message_id: z.string().optional(),
55519
+ message_id: z.string().nullable().optional(),
55246
55520
  to: z.array(z.string()).optional(),
55247
55521
  from: z.string().optional(),
55248
55522
  created_at: z.string().optional(),
@@ -56492,7 +56766,7 @@ const getEmailInputSchema = z.object({ email_id: z.string() });
56492
56766
  const ProviderResponseSchema$76 = z.object({
56493
56767
  object: z.string().optional(),
56494
56768
  id: z.string().optional(),
56495
- message_id: z.string().optional(),
56769
+ message_id: z.string().nullable().optional(),
56496
56770
  to: z.array(z.string()).optional(),
56497
56771
  from: z.string().optional(),
56498
56772
  created_at: z.string().optional(),
@@ -56579,7 +56853,7 @@ const ProviderResponseSchema$74 = z.object({
56579
56853
  to: z.array(z.string()).optional(),
56580
56854
  from: z.string().optional(),
56581
56855
  subject: z.string().optional(),
56582
- message_id: z.string().optional(),
56856
+ message_id: z.string().nullable().optional(),
56583
56857
  bcc: z.array(z.string()).nullable().optional(),
56584
56858
  cc: z.array(z.string()).nullable().optional(),
56585
56859
  reply_to: z.array(z.string()).nullable().optional(),
@@ -56653,7 +56927,7 @@ const ProviderResponseSchema$72 = z.object({
56653
56927
  current_version_id: z.string().optional(),
56654
56928
  name: z.string().optional(),
56655
56929
  alias: z.string().optional(),
56656
- from: z.string().optional(),
56930
+ from: z.string().nullable().optional(),
56657
56931
  subject: z.string().optional(),
56658
56932
  reply_to: z.array(z.string()).nullable().optional(),
56659
56933
  html: z.string().optional(),
@@ -57376,7 +57650,7 @@ const ProviderResponseSchema$57 = z.object({
57376
57650
  data: z.array(z.object({
57377
57651
  object: z.string().optional(),
57378
57652
  id: z.string().optional(),
57379
- message_id: z.string().optional(),
57653
+ message_id: z.string().nullable().optional(),
57380
57654
  to: z.array(z.string()).optional(),
57381
57655
  from: z.string().optional(),
57382
57656
  created_at: z.string().optional(),
@@ -57495,7 +57769,7 @@ const ProviderResponseSchema$55 = z.object({
57495
57769
  to: z.array(z.string()).optional(),
57496
57770
  from: z.string().optional(),
57497
57771
  subject: z.string().nullable().optional(),
57498
- message_id: z.string().optional(),
57772
+ message_id: z.string().nullable().optional(),
57499
57773
  bcc: z.array(z.string()).nullable().optional(),
57500
57774
  cc: z.array(z.string()).nullable().optional(),
57501
57775
  reply_to: z.array(z.string()).nullable().optional(),
@@ -58183,7 +58457,7 @@ function updateContactTopicsTool(proxy) {
58183
58457
  const config = {
58184
58458
  endpoint: `/contacts/${encodeURIComponent(input["contact_id"])}/topics`,
58185
58459
  retries: 3,
58186
- data: input.body
58460
+ data: input.body.topics
58187
58461
  };
58188
58462
  const response = await platformProxy.patch(config);
58189
58463
  return ProviderResponseSchema$39.parse(response.data);
@@ -73973,7 +74247,7 @@ function createWorkosTools(options) {
73973
74247
  * Providers with checked-in HTTP toolsets. MCP-backed providers are discovered
73974
74248
  * from the Platform integration catalog at runtime.
73975
74249
  */
73976
- const TOOLS = [
74250
+ const PROVIDERS = [
73977
74251
  anthropicProvider,
73978
74252
  clerkProvider,
73979
74253
  discordProvider,
@@ -74006,27 +74280,16 @@ const TOOLS = [
74006
74280
  }
74007
74281
  ];
74008
74282
  /**
74009
- * @deprecated Use `TOOLS`. This alias is retained for one release cycle so
74010
- * downstream consumers keep working; it will be removed in the next minor.
74011
- */
74012
- const PROVIDERS = TOOLS;
74013
- function findRegistration(integrationId) {
74014
- return TOOLS.find((p) => p.integrationId === integrationId);
74015
- }
74016
- /**
74017
74283
  * Channel-capable providers with a hand-maintained `channels()` registration.
74018
74284
  * The launch ships three (Slack, Telegram, Discord); the provider generator
74019
74285
  * grows to cover new entries when a fourth lands.
74020
74286
  */
74021
74287
  const CHANNELS = CHANNELS$1;
74022
- function findChannelRegistration(integrationId) {
74023
- return CHANNELS.find((c) => c.integrationId === integrationId);
74024
- }
74025
74288
  //#endregion
74026
74289
  //#region src/resolution.ts
74027
74290
  const INTEGRATION_ID_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
74028
- function validateIntegrationOverrides(integrations) {
74029
- for (const integrationId of Object.keys(integrations ?? {})) if (!INTEGRATION_ID_PATTERN.test(integrationId)) throw new MastraConnectError("invalid_options", `Invalid provider '${integrationId}' in integrations option: expected 1-128 letters, numbers, underscores, or hyphens.`);
74291
+ function validateProviderIds(providers) {
74292
+ for (const integrationId of Object.keys(providers ?? {})) if (!INTEGRATION_ID_PATTERN.test(integrationId)) throw new MastraConnectError("invalid_options", `Invalid provider '${integrationId}' in providers option: expected 1-128 letters, numbers, underscores, or hyphens.`);
74030
74293
  }
74031
74294
  function connectionIdEnvVar(integrationId) {
74032
74295
  return `MASTRA_${integrationId.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_CONNECTION_ID`;
@@ -74041,42 +74304,8 @@ function groupByIntegrationId(connections) {
74041
74304
  return byIntegrationId;
74042
74305
  }
74043
74306
  /**
74044
- * Resolves the connection to use for one provider, per the contract:
74045
- * option/env var wins; else a single active connection; anything else
74046
- * (ambiguity, needs_reauth, no usable candidate) warns and skips so one bad
74047
- * integration never takes down the whole resolution. A needs_reauth
74048
- * connection is never silently mapped.
74049
- */
74050
- function resolveConnection(request, candidates) {
74051
- const integrationId = request.integrationId;
74052
- const directed = request.connectionId?.trim() || process.env[request.envVar]?.trim() || void 0;
74053
- if (directed) {
74054
- const match = candidates.find((connection) => connection.id === directed);
74055
- if (!match) {
74056
- console.warn(`[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`);
74057
- return;
74058
- }
74059
- if (match.status === "needs_reauth") {
74060
- console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);
74061
- return;
74062
- }
74063
- if (match.status !== "active") {
74064
- console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`);
74065
- return;
74066
- }
74067
- return directed;
74068
- }
74069
- const active = candidates.filter((connection) => connection.status === "active");
74070
- if (active.length === 1) return active[0].id;
74071
- if (active.length === 0) {
74072
- console.warn(`[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`);
74073
- return;
74074
- }
74075
- console.warn(`[@mastra/connect] Skipping ${integrationId}: ${active.length} active connections; pin one with connectionId or ${request.envVar}.`);
74076
- }
74077
- /**
74078
74307
  * Resolves how to configure one provider given its candidate connections:
74079
- * - An explicit pin (option or env var) forces `single` at that id.
74308
+ * - An explicit pin (`connectionId`) forces `single` at that id.
74080
74309
  * - Exactly one active connection → `single`.
74081
74310
  * - Two or more active connections → `multi`; the caller wraps tools with
74082
74311
  * `connection_name` so the agent chooses per call at execute time.
@@ -74127,16 +74356,18 @@ const FAILURE_COOLDOWN_MS$1 = 3e4;
74127
74356
  let nextResolverId = 0;
74128
74357
  /**
74129
74358
  * Returns a live toolset resolver over the project's Platform connections.
74130
- * HTTP providers are loaded from the shipped `TOOLS` registry. MCP providers
74359
+ * HTTP providers are loaded from the shipped `PROVIDERS` registry. MCP providers
74131
74360
  * are discovered from the Platform integration catalog. Tools from every supported
74132
74361
  * provider with a matching project connection are merged into one flat record
74133
74362
  * (matched by `integrationId`). The resolver serves a cached snapshot,
74134
- * revalidating from the platform every `ttlMs`, so integrations attached
74135
- * to (or detached from) the project are picked up (or dropped) without a
74363
+ * revalidating from the platform every `ttlMs`, so providers connected
74364
+ * to (or disconnected from) the project are picked up (or dropped) without a
74136
74365
  * restart.
74137
74366
  *
74138
- * Configuration errors (missing project id, bad ttlMs, malformed integration id)
74139
- * throw here — at call time — so they surface at startup.
74367
+ * Configuration errors (missing project id, bad ttlMs, malformed integration id,
74368
+ * the removed `autoApproveTools` key) throw here — at call time — so they
74369
+ * surface at startup. Configuration errors that need discovery to detect
74370
+ * (an unknown tool name in `requireApproval`) fail the resolution instead.
74140
74371
  * Expected provider absence is silently skipped. Actionable per-integration
74141
74372
  * problems during resolution (needs re-auth or ambiguity) are downgraded to
74142
74373
  * warn-and-skip so one bad integration never takes down the whole toolset.
@@ -74149,9 +74380,14 @@ function tools(options = {}) {
74149
74380
  const client = resolveClient(options.client);
74150
74381
  const resolverId = ++nextResolverId;
74151
74382
  const mcpClients = /* @__PURE__ */ new Map();
74152
- const integrationOverrides = normalizeIntegrationOverrides(options.integrations);
74153
- validateIntegrationOverrides(integrationOverrides);
74154
- validateIntegrationXor(integrationOverrides);
74383
+ const normalizedProviders = normalizeProviderOverrides(options.providers);
74384
+ const providerOverrides = normalizedProviders.overrides;
74385
+ validateProviderIds(providerOverrides);
74386
+ validateProviderXor(providerOverrides);
74387
+ validateRequireApproval(providerOverrides);
74388
+ rejectRemovedAutoApproveTools(providerOverrides);
74389
+ if (options.allowTools !== void 0 && options.disallowTools !== void 0) throw new MastraConnectError("invalid_options", "Top-level allowTools and disallowTools are mutually exclusive; set at most one.");
74390
+ if (options.requireApproval !== void 0 && typeof options.requireApproval !== "boolean" && !(Array.isArray(options.requireApproval) && options.requireApproval.every((name) => typeof name === "string"))) throw new MastraConnectError("invalid_options", "Top-level requireApproval must be a boolean or an array of tool keys.");
74155
74391
  let cache;
74156
74392
  let inflight;
74157
74393
  let closing;
@@ -74167,15 +74403,17 @@ function tools(options = {}) {
74167
74403
  const connections = connectionsResult.value;
74168
74404
  if (catalogResult.status === "fulfilled") return {
74169
74405
  connections,
74170
- catalog: catalogResult.value
74406
+ catalog: catalogResult.value,
74407
+ catalogAvailable: true
74171
74408
  };
74172
- const checkedIn = new Set(TOOLS.map((registration) => registration.integrationId));
74173
- if (connections.some((connection) => connection.status === "active" && !checkedIn.has(connection.integrationId) && !integrationOverrides[connection.integrationId]?.disabled)) throw catalogResult.reason;
74409
+ const checkedIn = new Set(PROVIDERS.map((registration) => registration.integrationId));
74410
+ if (connections.some((connection) => connection.status === "active" && !checkedIn.has(connection.integrationId) && !providerOverrides[connection.integrationId]?.disabled && (normalizedProviders.only === void 0 || normalizedProviders.only.has(connection.integrationId)))) throw catalogResult.reason;
74174
74411
  const reason = catalogResult.reason;
74175
74412
  console.warn(`[@mastra/connect] Platform catalog unavailable (${reason instanceof Error ? reason.message : String(reason)}); resolving checked-in providers only.`);
74176
74413
  return {
74177
74414
  connections,
74178
- catalog: []
74415
+ catalog: [],
74416
+ catalogAvailable: false
74179
74417
  };
74180
74418
  };
74181
74419
  /**
@@ -74186,8 +74424,8 @@ function tools(options = {}) {
74186
74424
  if (closing) return closing.then(refresh);
74187
74425
  if (!inflight) inflight = (async () => {
74188
74426
  try {
74189
- const { connections, catalog } = await loadSnapshotInputs();
74190
- const snapshot = await mapTools(connections, buildRequests(integrationOverrides, catalog), options, client, mcpClients, resolverId);
74427
+ const { connections, catalog, catalogAvailable } = await loadSnapshotInputs();
74428
+ const snapshot = await mapTools(connections, buildRequests(normalizedProviders, catalog, catalogAvailable), options, client, mcpClients, resolverId);
74191
74429
  cache = {
74192
74430
  snapshot,
74193
74431
  fetchedAt: Date.now()
@@ -74217,10 +74455,7 @@ function tools(options = {}) {
74217
74455
  }
74218
74456
  return refresh();
74219
74457
  };
74220
- return Object.assign(resolve, {
74221
- invalidate: () => {
74222
- cache = void 0;
74223
- },
74458
+ const resolver = Object.assign(resolve, {
74224
74459
  refresh,
74225
74460
  disconnect: () => {
74226
74461
  closing ??= (async () => {
@@ -74235,28 +74470,75 @@ function tools(options = {}) {
74235
74470
  }
74236
74471
  })();
74237
74472
  return closing;
74238
- }
74473
+ },
74474
+ with: (extra) => withExtraTools(resolver, extra)
74239
74475
  });
74476
+ return resolver;
74240
74477
  }
74241
74478
  /**
74242
- * Turns the two accepted `integrations` shapes into the internal Record form.
74243
- * The string-array shorthand (`["linear", "github"]`) becomes
74244
- * `{ linear: {}, github: {} }`; the object form passes through unchanged.
74245
- * Also rejects malformed inputs early (non-string array entries, duplicates)
74246
- * so a bad option throws at tools() time rather than at first refresh.
74479
+ * Builds the resolver `.with()` returns: resolutions merge the base
74480
+ * resolver's tools with `extra` (extra wins on key collision), cache handles
74481
+ * delegate to the base, and further `.with()` calls chain.
74247
74482
  */
74248
- function normalizeIntegrationOverrides(integrations) {
74249
- if (integrations === void 0) return {};
74250
- if (Array.isArray(integrations)) {
74251
- const record = {};
74252
- for (const entry of integrations) {
74253
- if (typeof entry !== "string") throw new MastraConnectError("invalid_options", `Invalid integrations entry: expected a string integration id, got ${typeof entry}.`);
74254
- if (record[entry] !== void 0) throw new MastraConnectError("invalid_options", `Duplicate integration '${entry}' in integrations array.`);
74255
- record[entry] = {};
74256
- }
74257
- return record;
74483
+ function withExtraTools(base, extra) {
74484
+ const resolveExtra = typeof extra === "function" ? extra : () => extra;
74485
+ const resolve = async (ctx) => {
74486
+ const [baseTools, extraTools] = await Promise.all([base(ctx), resolveExtra(ctx)]);
74487
+ return {
74488
+ ...baseTools,
74489
+ ...extraTools
74490
+ };
74491
+ };
74492
+ const resolver = Object.assign(resolve, {
74493
+ refresh: async () => {
74494
+ const [baseTools, extraTools] = await Promise.all([base.refresh(), resolveExtra()]);
74495
+ return {
74496
+ ...baseTools,
74497
+ ...extraTools
74498
+ };
74499
+ },
74500
+ disconnect: () => base.disconnect(),
74501
+ with: (more) => withExtraTools(resolver, more)
74502
+ });
74503
+ return resolver;
74504
+ }
74505
+ /**
74506
+ * Turns the two accepted `providers` shapes into the internal form. The
74507
+ * array form (`["linear", "github"]`) becomes an allowlist with default
74508
+ * options; the record form expands boolean shorthands (`true` → `{}`,
74509
+ * `false` → an internal exclusion marker). Malformed inputs (non-string
74510
+ * array entries, duplicates, non-boolean/non-object record values) throw at
74511
+ * tools() time rather than at first refresh.
74512
+ */
74513
+ function normalizeProviderOverrides(providers) {
74514
+ if (providers === void 0) return {
74515
+ overrides: {},
74516
+ only: void 0
74517
+ };
74518
+ if (Array.isArray(providers)) {
74519
+ const overrides = {};
74520
+ for (const entry of providers) {
74521
+ if (typeof entry !== "string") throw new MastraConnectError("invalid_options", `Invalid providers entry: expected a string provider id, got ${typeof entry}.`);
74522
+ if (overrides[entry] !== void 0) throw new MastraConnectError("invalid_options", `Duplicate provider '${entry}' in providers array.`);
74523
+ overrides[entry] = {};
74524
+ }
74525
+ return {
74526
+ overrides,
74527
+ only: new Set(Object.keys(overrides))
74528
+ };
74258
74529
  }
74259
- return integrations;
74530
+ const overrides = {};
74531
+ for (const [providerId, value] of Object.entries(providers)) {
74532
+ if (value === void 0) continue;
74533
+ if (value === true) overrides[providerId] = {};
74534
+ else if (value === false) overrides[providerId] = { disabled: true };
74535
+ else if (typeof value === "object" && value !== null && !Array.isArray(value)) overrides[providerId] = value;
74536
+ else throw new MastraConnectError("invalid_options", `Invalid providers entry for '${providerId}': expected true, false, or an options object, got ${value === null ? "null" : Array.isArray(value) ? "an array" : typeof value}.`);
74537
+ }
74538
+ return {
74539
+ overrides,
74540
+ only: void 0
74541
+ };
74260
74542
  }
74261
74543
  /**
74262
74544
  * Rejects any provider that sets both `allowTools` and `disallowTools`.
@@ -74264,15 +74546,39 @@ function normalizeIntegrationOverrides(integrations) {
74264
74546
  * runtime guard catches loosely typed inputs (e.g. built from JSON or a
74265
74547
  * `Record<string, unknown>` upstream).
74266
74548
  */
74267
- function validateIntegrationXor(integrations) {
74268
- for (const [integrationId, providerOptions] of Object.entries(integrations)) {
74549
+ function validateProviderXor(providers) {
74550
+ for (const [integrationId, providerOptions] of Object.entries(providers)) {
74269
74551
  const filters = providerOptions;
74270
74552
  if (filters.allowTools !== void 0 && filters.disallowTools !== void 0) throw new MastraConnectError("invalid_options", `Invalid options for '${integrationId}': allowTools and disallowTools are mutually exclusive; set at most one.`);
74271
74553
  }
74272
74554
  }
74273
- function buildRequests(integrations, catalog) {
74274
- const overrides = integrations;
74275
- const registrations = new Map(TOOLS.map((registration) => [registration.integrationId, registration]));
74555
+ /**
74556
+ * Rejects a `requireApproval` value that is not a boolean or an array of
74557
+ * string tool keys. The approval branches check for literal `true` or an
74558
+ * array, so a loosely typed value like `'true'` would otherwise disable
74559
+ * approval on MCP providers silently (and crash the HTTP path into
74560
+ * warn-and-skip) instead of gating tools as the author intended.
74561
+ */
74562
+ function validateRequireApproval(providers) {
74563
+ for (const [integrationId, providerOptions] of Object.entries(providers)) {
74564
+ const requireApproval = providerOptions.requireApproval;
74565
+ if (requireApproval === void 0 || typeof requireApproval === "boolean") continue;
74566
+ if (Array.isArray(requireApproval) && requireApproval.every((name) => typeof name === "string")) continue;
74567
+ throw new MastraConnectError("invalid_options", `Invalid options for '${integrationId}': requireApproval must be a boolean or an array of tool keys.`);
74568
+ }
74569
+ }
74570
+ /**
74571
+ * Rejects the removed `autoApproveTools` option by name. Discovered MCP tools
74572
+ * no longer require approval by default, so a loosely typed config still
74573
+ * carrying this key would otherwise be ignored silently and its tools would
74574
+ * run without the prompts the author expected.
74575
+ */
74576
+ function rejectRemovedAutoApproveTools(providers) {
74577
+ for (const [integrationId, providerOptions] of Object.entries(providers)) if ("autoApproveTools" in providerOptions) throw new MastraConnectError("invalid_options", `Invalid options for '${integrationId}': autoApproveTools was removed. Tools no longer require approval by default; opt in with requireApproval: true or a list of tool keys to gate.`);
74578
+ }
74579
+ function buildRequests(providers, catalog, catalogAvailable) {
74580
+ const { overrides, only } = providers;
74581
+ const registrations = new Map(PROVIDERS.map((registration) => [registration.integrationId, registration]));
74276
74582
  const catalogIds = new Set(catalog.map((integration) => integration.id));
74277
74583
  for (const integration of catalog) {
74278
74584
  if (!integration.capabilities.mcp) continue;
@@ -74282,9 +74588,14 @@ function buildRequests(integrations, catalog) {
74282
74588
  transport: "mcp"
74283
74589
  });
74284
74590
  }
74285
- for (const integrationId of Object.keys(overrides)) if (!registrations.has(integrationId) && !catalogIds.has(integrationId)) console.warn(`[@mastra/connect] Ignoring unknown integration override '${integrationId}'.`);
74591
+ const unknown = Object.keys(overrides).filter((integrationId) => !registrations.has(integrationId) && !catalogIds.has(integrationId) && !overrides[integrationId]?.disabled);
74592
+ if (unknown.length > 0) {
74593
+ if (catalogAvailable) throw new MastraConnectConfigError(`Unknown provider${unknown.length > 1 ? "s" : ""} in the providers option: ${unknown.map((id) => `'${id}'`).join(", ")}. Expected a checked-in provider id or a platform catalog integration id.`);
74594
+ for (const integrationId of unknown) console.warn(`[@mastra/connect] Platform catalog unavailable; cannot verify provider '${integrationId}'. Skipping it this resolution.`);
74595
+ }
74286
74596
  const requests = [];
74287
74597
  for (const registration of registrations.values()) {
74598
+ if (only !== void 0 && !only.has(registration.integrationId)) continue;
74288
74599
  const providerOptions = overrides[registration.integrationId] ?? {};
74289
74600
  if (providerOptions.disabled) continue;
74290
74601
  requests.push({
@@ -74300,6 +74611,7 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
74300
74611
  const activeMcpKeys = /* @__PURE__ */ new Set();
74301
74612
  const result = {};
74302
74613
  const toolOwners = /* @__PURE__ */ new Map();
74614
+ const matchedDefaultEntries = /* @__PURE__ */ new Set();
74303
74615
  for (const request of requests) {
74304
74616
  const integrationId = request.registration.integrationId;
74305
74617
  let providerTools;
@@ -74319,16 +74631,19 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
74319
74631
  connectionId: resolution.connectionId,
74320
74632
  allowTools: request.options.allowTools,
74321
74633
  disallowTools: request.options.disallowTools,
74322
- autoApproveTools: request.options.autoApproveTools,
74634
+ requireApproval: request.options.requireApproval,
74323
74635
  client,
74324
74636
  mcpClients,
74325
74637
  resolverId
74326
74638
  });
74327
- } else providerTools = request.registration.createTools({
74328
- connectionId: resolution.connectionId,
74329
- client: options.client,
74330
- ...providerFilterOptions(request.options)
74331
- });
74639
+ } else {
74640
+ providerTools = request.registration.createTools({
74641
+ connectionId: resolution.connectionId,
74642
+ client: options.client,
74643
+ ...providerFilterOptions(request.options)
74644
+ });
74645
+ applyProxyRequireApproval(providerTools, integrationId, request.options.requireApproval);
74646
+ }
74332
74647
  else if (request.registration.transport === "mcp") {
74333
74648
  for (const connection of resolution.connections) activeMcpKeys.add(`${integrationId}::${connection.id}`);
74334
74649
  providerTools = await buildMcpMultiConnectionTools({
@@ -74336,22 +74651,31 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
74336
74651
  connections: resolution.connections,
74337
74652
  allowTools: request.options.allowTools,
74338
74653
  disallowTools: request.options.disallowTools,
74339
- autoApproveTools: request.options.autoApproveTools,
74654
+ requireApproval: request.options.requireApproval,
74340
74655
  client,
74341
74656
  mcpClients,
74342
74657
  resolverId
74343
74658
  });
74344
- } else providerTools = buildProxyMultiConnectionTools({
74345
- registration: request.registration,
74346
- connections: resolution.connections,
74347
- allowTools: request.options.allowTools,
74348
- disallowTools: request.options.disallowTools,
74349
- client: options.client
74350
- });
74659
+ } else {
74660
+ providerTools = buildProxyMultiConnectionTools({
74661
+ registration: request.registration,
74662
+ connections: resolution.connections,
74663
+ allowTools: request.options.allowTools,
74664
+ disallowTools: request.options.disallowTools,
74665
+ client: options.client
74666
+ });
74667
+ applyProxyRequireApproval(providerTools, integrationId, request.options.requireApproval, [listConnectionsToolKey(integrationId)]);
74668
+ }
74351
74669
  } catch (error) {
74670
+ if (error instanceof MastraConnectConfigError) throw error;
74352
74671
  console.warn(`[@mastra/connect] Skipping ${integrationId}: ${error instanceof Error ? error.message : String(error)}`);
74353
74672
  continue;
74354
74673
  }
74674
+ if (!(request.options.allowTools !== void 0 || request.options.disallowTools !== void 0) && (options.allowTools !== void 0 || options.disallowTools !== void 0)) providerTools = applyDefaultFilter(providerTools, options, integrationId, matchedDefaultEntries);
74675
+ if (request.options.requireApproval === void 0 && options.requireApproval !== void 0) {
74676
+ const policy = options.requireApproval;
74677
+ if (policy !== false) applyDefaultRequireApproval(providerTools, policy, integrationId, matchedDefaultEntries);
74678
+ }
74355
74679
  for (const toolKey of Object.keys(providerTools)) {
74356
74680
  const existingOwner = toolOwners.get(toolKey);
74357
74681
  if (existingOwner) throw new MastraConnectError("invalid_options", `Duplicate tool key '${toolKey}' from providers '${existingOwner}' and '${integrationId}'.`);
@@ -74359,22 +74683,114 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
74359
74683
  }
74360
74684
  Object.assign(result, providerTools);
74361
74685
  }
74686
+ if (Object.keys(result).length > 0) {
74687
+ const defaultEntries = [
74688
+ ...options.allowTools ?? [],
74689
+ ...options.disallowTools ?? [],
74690
+ ...Array.isArray(options.requireApproval) ? options.requireApproval : []
74691
+ ];
74692
+ for (const entry of defaultEntries) if (!matchedDefaultEntries.has(entry)) console.warn(`[@mastra/connect] Top-level entry '${entry}' matched no tools on any resolved provider.`);
74693
+ }
74362
74694
  const staleClients = Array.from(mcpClients.entries()).filter(([key]) => !activeMcpKeys.has(key));
74363
74695
  for (const [key] of staleClients) mcpClients.delete(key);
74364
74696
  await Promise.allSettled(staleClients.map(([, entry]) => entry.client.disconnect()));
74365
74697
  return result;
74366
74698
  }
74367
74699
  /**
74368
- * Extracts whichever provider tool filter is set. Since `ToolsIntegrationOptions`
74700
+ * Applies the top-level default filter to a provider that set no filter of
74701
+ * its own. Lenient by design: the provider set is live, so a default entry
74702
+ * that matches nothing on this provider simply does not apply here. Entries
74703
+ * that match are recorded in `matchedEntries` so mapTools can warn about
74704
+ * defaults that matched nothing anywhere. The synthetic
74705
+ * `<provider>__list_connections` wrapper is never filtered by defaults,
74706
+ * matching the per-provider multi-connection behavior.
74707
+ */
74708
+ function applyDefaultFilter(providerTools, defaults, integrationId, matchedEntries) {
74709
+ const listToolKey = listConnectionsToolKey(integrationId);
74710
+ const matchers = (defaults.allowTools ?? defaults.disallowTools ?? []).map((entry) => ({
74711
+ entry,
74712
+ matches: compileToolMatcher([entry])
74713
+ }));
74714
+ const filtered = {};
74715
+ for (const [key, tool] of Object.entries(providerTools)) {
74716
+ if (key === listToolKey) {
74717
+ filtered[key] = tool;
74718
+ continue;
74719
+ }
74720
+ let matchedAny = false;
74721
+ for (const { entry, matches } of matchers) if (matches(key)) {
74722
+ matchedEntries.add(entry);
74723
+ matchedAny = true;
74724
+ }
74725
+ if (defaults.allowTools !== void 0 ? matchedAny : !matchedAny) filtered[key] = tool;
74726
+ }
74727
+ return filtered;
74728
+ }
74729
+ /**
74730
+ * Applies the top-level default `requireApproval` policy to a provider that
74731
+ * set no policy of its own. `true` gates every tool except the synthetic
74732
+ * `<provider>__list_connections` wrapper; an array gates matching keys with
74733
+ * the same lenient semantics as {@link applyDefaultFilter}.
74734
+ */
74735
+ function applyDefaultRequireApproval(providerTools, policy, integrationId, matchedEntries) {
74736
+ const listToolKey = listConnectionsToolKey(integrationId);
74737
+ if (policy === true) {
74738
+ for (const [key, tool] of Object.entries(providerTools)) {
74739
+ if (key === listToolKey) continue;
74740
+ tool.requireApproval = true;
74741
+ }
74742
+ return;
74743
+ }
74744
+ const matchers = policy.map((entry) => ({
74745
+ entry,
74746
+ matches: compileToolMatcher([entry])
74747
+ }));
74748
+ for (const [key, tool] of Object.entries(providerTools)) {
74749
+ if (key === listToolKey) continue;
74750
+ for (const { entry, matches } of matchers) if (matches(key)) {
74751
+ matchedEntries.add(entry);
74752
+ tool.requireApproval = true;
74753
+ }
74754
+ }
74755
+ }
74756
+ /**
74757
+ * Extracts whichever provider tool filter is set. Since `ToolsProviderOptions`
74369
74758
  * is an XOR union and tools() validates co-occurrence up front, at most one
74370
74759
  * of the two will ever be defined here.
74371
74760
  */
74372
74761
  function providerFilterOptions(options) {
74373
74762
  return options.allowTools !== void 0 ? { allowTools: options.allowTools } : options.disallowTools !== void 0 ? { disallowTools: options.disallowTools } : {};
74374
74763
  }
74764
+ /**
74765
+ * Applies a per-provider `requireApproval` policy to a checked-in HTTP
74766
+ * toolset by marking the selected tools. `true` gates every provider tool
74767
+ * (but never the synthetic `<provider>__list_connections` wrapper tool,
74768
+ * matching the MCP paths, where only server-discovered tools are gated);
74769
+ * an array gates the listed keys — entries containing `*` are globs,
74770
+ * expanded against the provider tools minus the synthetic wrapper so a
74771
+ * broad glob behaves like `true` — and rejects unknown names and dead
74772
+ * globs with the same `invalid_options` error MCP discovery raises, so a
74773
+ * typo never silently widens access.
74774
+ */
74775
+ function applyProxyRequireApproval(providerTools, integrationId, requireApproval, excludeKeys = []) {
74776
+ if (!requireApproval) return;
74777
+ const excluded = new Set(excludeKeys);
74778
+ if (requireApproval === true) {
74779
+ for (const [toolKey, tool] of Object.entries(providerTools)) {
74780
+ if (excluded.has(toolKey)) continue;
74781
+ tool.requireApproval = true;
74782
+ }
74783
+ return;
74784
+ }
74785
+ const known = Object.keys(providerTools);
74786
+ const expanded = expandToolPatterns(requireApproval, known.filter((key) => !excluded.has(key)), `requireApproval for '${integrationId}'`);
74787
+ const unknown = expanded.filter((name) => !known.includes(name));
74788
+ if (unknown.length > 0) throw new MastraConnectConfigError(`Unknown tool name(s) in requireApproval for '${integrationId}': ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
74789
+ for (const name of expanded) providerTools[name].requireApproval = true;
74790
+ }
74375
74791
  async function discoverMcpTools(input) {
74376
- const { registration, connectionId, allowTools, disallowTools, autoApproveTools, client, mcpClients, resolverId } = input;
74377
- const autoApproved = new Set(autoApproveTools ?? []);
74792
+ const { registration, connectionId, allowTools, disallowTools, requireApproval, client, mcpClients, resolverId } = input;
74793
+ const requireApprovalFor = Array.isArray(requireApproval) ? compileToolMatcher(requireApproval) : void 0;
74378
74794
  const cacheKey = `${registration.integrationId}::${connectionId}`;
74379
74795
  let entry = mcpClients.get(cacheKey);
74380
74796
  if (!entry) {
@@ -74386,7 +74802,7 @@ async function discoverMcpTools(input) {
74386
74802
  id: `mastra-connect-${resolverId}-${registration.integrationId}-${connectionId}`,
74387
74803
  servers: { [registration.integrationId]: {
74388
74804
  ...transport,
74389
- requireToolApproval: ({ toolName }) => !autoApproved.has(`${registration.integrationId}_${String(toolName)}`)
74805
+ ...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor(`${registration.integrationId}_${String(toolName)}`) } : {}
74390
74806
  } }
74391
74807
  })
74392
74808
  };
@@ -74395,31 +74811,16 @@ async function discoverMcpTools(input) {
74395
74811
  const discovery = await entry.client.listToolsWithErrors();
74396
74812
  const error = discovery.errors[registration.integrationId];
74397
74813
  if (error) throw new Error(`MCP tool discovery failed: ${error}`);
74398
- const unknown = [...autoApproved].filter((name) => !(name in discovery.tools));
74399
- if (unknown.length > 0) throw new MastraConnectError("invalid_options", `Unknown tool name(s) in autoApproveTools for '${registration.integrationId}': ${unknown.join(", ")}. Known tools: ${Object.keys(discovery.tools).join(", ")}.`);
74814
+ if (Array.isArray(requireApproval)) {
74815
+ const unknown = expandToolPatterns(requireApproval, Object.keys(discovery.tools), `requireApproval for '${registration.integrationId}'`).filter((name) => !(name in discovery.tools));
74816
+ if (unknown.length > 0) throw new MastraConnectConfigError(`Unknown tool name(s) in requireApproval for '${registration.integrationId}': ${unknown.join(", ")}. Known tools: ${Object.keys(discovery.tools).join(", ")}.`);
74817
+ }
74400
74818
  return applyToolFilter(discovery.tools, {
74401
74819
  allowTools,
74402
74820
  disallowTools
74403
74821
  });
74404
74822
  }
74405
74823
  //#endregion
74406
- //#region src/connect.ts
74407
- /**
74408
- * @deprecated Use `tools` from `@mastra/connect` instead. This module is a
74409
- * thin re-export retained for one release cycle so downstream consumers keep
74410
- * working; it will be removed in the next minor.
74411
- */
74412
- /**
74413
- * @deprecated Renamed to {@link tools}.
74414
- *
74415
- * `connect(options)` is a drop-in alias for `tools(options)`. Prefer `tools`
74416
- * from `@mastra/connect` in new code; this export will be removed in the next
74417
- * minor version.
74418
- */
74419
- function connect(options = {}) {
74420
- return tools(options);
74421
- }
74422
- //#endregion
74423
74824
  //#region src/channels.ts
74424
74825
  const DEFAULT_TTL_MS = 3e4;
74425
74826
  /** Minimum wait after a failed platform fetch before another background revalidation. */
@@ -74432,7 +74833,7 @@ const FAILURE_COOLDOWN_MS = 3e4;
74432
74833
  * Discord) are constructed once, up front and credential-less, so their
74433
74834
  * webhook/OAuth routes can mount at Mastra construction. Each resolution
74434
74835
  * fetches the project's connections and late-binds credentials into the live
74435
- * instances: integrations with an active connection appear in the resolved
74836
+ * instances: providers with an active connection appear in the resolved
74436
74837
  * map, others don't — so connecting a new channel on the platform takes
74437
74838
  * effect without redeploying the app.
74438
74839
  *
@@ -74457,12 +74858,17 @@ async function channels(options = {}) {
74457
74858
  const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
74458
74859
  if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
74459
74860
  if (options.ttlMs !== void 0 && (!Number.isFinite(options.ttlMs) || options.ttlMs < 0)) throw new MastraConnectError("invalid_options", `Invalid ttlMs (${options.ttlMs}): expected a finite number of milliseconds >= 0.`);
74460
- validateIntegrationOverrides(options.integrations);
74861
+ const { overrides: providerOverrides, only } = normalizeChannelProviders(options.providers);
74862
+ validateProviderIds(providerOverrides);
74863
+ const knownChannelIds = new Set(CHANNELS.map((registration) => registration.integrationId));
74864
+ const unknownChannelIds = Object.keys(providerOverrides).filter((integrationId) => !knownChannelIds.has(integrationId) && !providerOverrides[integrationId]?.disabled);
74865
+ if (unknownChannelIds.length > 0) throw new MastraConnectError("invalid_options", `Unknown channel${unknownChannelIds.length > 1 ? "s" : ""} in the providers option: ${unknownChannelIds.map((id) => `'${id}'`).join(", ")}. Known channels: ${[...knownChannelIds].join(", ")}.`);
74461
74866
  const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
74462
74867
  const client = resolveClient(options.client);
74463
74868
  const states = [];
74464
74869
  for (const registration of CHANNELS) {
74465
- const overrides = options.integrations?.[registration.integrationId] ?? {};
74870
+ if (only !== void 0 && !only.has(registration.integrationId)) continue;
74871
+ const overrides = providerOverrides[registration.integrationId] ?? {};
74466
74872
  if (overrides.disabled) continue;
74467
74873
  const state = {
74468
74874
  registration,
@@ -74498,6 +74904,7 @@ async function channels(options = {}) {
74498
74904
  let cache;
74499
74905
  let inflight;
74500
74906
  let lastFailureAt;
74907
+ let warnedStaleSlackConnection = false;
74501
74908
  const buildSnapshot = async () => {
74502
74909
  let connections;
74503
74910
  try {
@@ -74508,9 +74915,13 @@ async function channels(options = {}) {
74508
74915
  }
74509
74916
  const byIntegrationId = groupByIntegrationId(connections);
74510
74917
  const providers = {};
74918
+ if (!warnedStaleSlackConnection && states.some((state) => state.registration.integrationId === "slack-channels") && (byIntegrationId.get("slack")?.length ?? 0) > 0 && (byIntegrationId.get("slack-channels")?.length ?? 0) === 0) {
74919
+ warnedStaleSlackConnection = true;
74920
+ console.warn(`[@mastra/connect] Project ${projectId} has an active 'slack' connection but no 'slack-channels' connection: the Slack channel is now keyed off 'slack-channels'. Connect Slack again as 'slack-channels' to restore it; the existing 'slack' connection still powers the generated Slack tools.`);
74921
+ }
74511
74922
  for (const state of states) {
74512
74923
  const integrationId = state.registration.integrationId;
74513
- const overrides = options.integrations?.[integrationId] ?? {};
74924
+ const overrides = providerOverrides[integrationId] ?? {};
74514
74925
  const candidates = byIntegrationId.get(integrationId) ?? [];
74515
74926
  const connectionId = candidates.length === 0 ? void 0 : selectChannelConnection(integrationId, overrides.connectionId, candidates);
74516
74927
  state.connectionId = connectionId;
@@ -74557,9 +74968,6 @@ async function channels(options = {}) {
74557
74968
  };
74558
74969
  const invocable = ((_context) => resolve());
74559
74970
  invocable.getRoutes = () => states.flatMap((state) => state.instance.provider.getRoutes());
74560
- invocable.invalidate = () => {
74561
- cache = void 0;
74562
- };
74563
74971
  invocable.refresh = refresh;
74564
74972
  invocable.disconnect = async () => {
74565
74973
  cache = void 0;
@@ -74567,15 +74975,73 @@ async function channels(options = {}) {
74567
74975
  return invocable;
74568
74976
  }
74569
74977
  /**
74978
+ * Friendly channel-key aliases. The Slack channel is keyed `slack-channels`
74979
+ * on the platform (plain `slack` names the Slack tools provider), but inside
74980
+ * a channels() config `slack` is unambiguous — accept it and canonicalize to
74981
+ * the platform key for registration and connection lookup.
74982
+ */
74983
+ const CHANNEL_KEY_ALIASES = { slack: "slack-channels" };
74984
+ const canonicalChannelId = (id) => CHANNEL_KEY_ALIASES[id] ?? id;
74985
+ /** Throws when two option keys (e.g. `slack` and `slack-channels`) name the same channel. */
74986
+ function rejectAliasCollision(sourceKeys, canonical, key) {
74987
+ const existing = sourceKeys[canonical];
74988
+ if (existing === void 0) return;
74989
+ throw new MastraConnectError("invalid_options", existing === key ? `Duplicate provider '${key}' in providers array.` : `Both '${existing}' and '${key}' name the '${canonical}' channel in the providers option; use one key.`);
74990
+ }
74991
+ /**
74992
+ * Turns the two accepted `providers` shapes into the internal form, mirroring
74993
+ * `tools()`. The array form becomes an allowlist with default options; the
74994
+ * record form expands boolean shorthands (`true` → `{}`, `false` → an
74995
+ * internal exclusion marker). Alias keys canonicalize first, so `slack` and
74996
+ * `slack-channels` configure the same channel (and collide loudly). Malformed
74997
+ * inputs throw at channels() time.
74998
+ */
74999
+ function normalizeChannelProviders(providers) {
75000
+ if (providers === void 0) return {
75001
+ overrides: {},
75002
+ only: void 0
75003
+ };
75004
+ const sourceKeys = {};
75005
+ if (Array.isArray(providers)) {
75006
+ const overrides = {};
75007
+ for (const entry of providers) {
75008
+ if (typeof entry !== "string") throw new MastraConnectError("invalid_options", `Invalid providers entry: expected a string channel id, got ${typeof entry}.`);
75009
+ const canonical = canonicalChannelId(entry);
75010
+ rejectAliasCollision(sourceKeys, canonical, entry);
75011
+ sourceKeys[canonical] = entry;
75012
+ overrides[canonical] = {};
75013
+ }
75014
+ return {
75015
+ overrides,
75016
+ only: new Set(Object.keys(overrides))
75017
+ };
75018
+ }
75019
+ const overrides = {};
75020
+ for (const [providerId, value] of Object.entries(providers)) {
75021
+ if (value === void 0) continue;
75022
+ const canonical = canonicalChannelId(providerId);
75023
+ rejectAliasCollision(sourceKeys, canonical, providerId);
75024
+ sourceKeys[canonical] = providerId;
75025
+ if (value === true) overrides[canonical] = {};
75026
+ else if (value === false) overrides[canonical] = { disabled: true };
75027
+ else if (typeof value === "object" && value !== null && !Array.isArray(value)) overrides[canonical] = value;
75028
+ else throw new MastraConnectError("invalid_options", `Invalid providers entry for '${providerId}': expected true, false, or an options object, got ${value === null ? "null" : Array.isArray(value) ? "an array" : typeof value}.`);
75029
+ }
75030
+ return {
75031
+ overrides,
75032
+ only: void 0
75033
+ };
75034
+ }
75035
+ /**
74570
75036
  * Selects the connection `channels()` should use for one provider.
74571
75037
  *
74572
- * - Pinned `connectionId` (from `integrations.<id>.connectionId`) wins, but is
75038
+ * - Pinned `connectionId` (from `providers.<id>.connectionId`) wins, but is
74573
75039
  * skipped if the pinned id isn't attached to the project or the connection
74574
75040
  * isn't `active`.
74575
75041
  * - Otherwise, the single active connection is used.
74576
75042
  * - When more than one active connection exists, this warns naming the chosen
74577
75043
  * id and picks the first active connection. There is no env-var fallback;
74578
- * pin explicitly with `integrations.<id>.connectionId` when the deterministic
75044
+ * pin explicitly with `providers.<id>.connectionId` when the deterministic
74579
75045
  * choice matters.
74580
75046
  */
74581
75047
  function selectChannelConnection(integrationId, pinnedId, candidates) {
@@ -74604,7 +75070,7 @@ function selectChannelConnection(integrationId, pinnedId, candidates) {
74604
75070
  if (active.length === 1) return active[0].id;
74605
75071
  const chosen = active[0].id;
74606
75072
  const others = active.slice(1).map((connection) => connection.id).join(", ");
74607
- console.warn(`[@mastra/connect] ${integrationId} channel: found ${active.length} active connections; using ${chosen}. Ignoring ${others}. Pin one with integrations.${integrationId}.connectionId to silence this warning.`);
75073
+ console.warn(`[@mastra/connect] ${integrationId} channel: found ${active.length} active connections; using ${chosen}. Ignoring ${others}. Pin one with providers.${integrationId}.connectionId to silence this warning.`);
74608
75074
  return chosen;
74609
75075
  }
74610
75076
  //#endregion
@@ -74617,108 +75083,6 @@ async function credential(connectionId, options) {
74617
75083
  return getCredential(resolveClient(options?.client), connectionId);
74618
75084
  }
74619
75085
  //#endregion
74620
- //#region src/environment.ts
74621
- function credentialToken(credential) {
74622
- return credential.type === "oauth2" ? credential.accessToken : credential.apiKey;
74623
- }
74624
- /**
74625
- * Wires git's credential helper to read the token from the environment at use
74626
- * time, so the secret lives in the process env rather than in git config or
74627
- * remote URLs. `gh` needs no setup: it honors GH_TOKEN directly.
74628
- */
74629
- const GITHUB_GIT_CREDENTIAL_HELPER = "!f() { echo username=x-access-token; echo \"password=$GH_TOKEN\"; }; f";
74630
- /** Providers that know how to materialize sandbox credentials. */
74631
- const ENVIRONMENT_CONTRIBUTORS = [{
74632
- integrationId: "github",
74633
- envVar: "MASTRA_GITHUB_CONNECTION_ID",
74634
- build(credential) {
74635
- const token = credentialToken(credential);
74636
- return {
74637
- env: {
74638
- GH_TOKEN: token,
74639
- GITHUB_TOKEN: token
74640
- },
74641
- onStart: async ({ sandbox }) => {
74642
- if (!sandbox.executeCommand) {
74643
- console.warn("[@mastra/connect] github environment: sandbox does not support executeCommand; skipping git credential setup (gh still works via GH_TOKEN).");
74644
- return;
74645
- }
74646
- await sandbox.executeCommand("git", [
74647
- "config",
74648
- "--global",
74649
- "credential.https://github.com.helper",
74650
- GITHUB_GIT_CREDENTIAL_HELPER
74651
- ]);
74652
- }
74653
- };
74654
- }
74655
- }];
74656
- /**
74657
- * Resolves the project's Platform connections into sandbox credentials:
74658
- * environment variables plus a start hook that finishes in-sandbox setup.
74659
- * Currently GitHub-aware (`GH_TOKEN`/`GITHUB_TOKEN` + git credential helper);
74660
- * more providers plug in over time.
74661
- *
74662
- * Connection resolution matches `connect()`: a per-integration `connectionId`
74663
- * or the provider's `MASTRA_*_CONNECTION_ID` env var wins, else a single
74664
- * active connection; ambiguity, needs_reauth, and credential-fetch failures
74665
- * warn and skip that provider. Configuration errors (missing project id,
74666
- * malformed integration id) throw here so they surface at startup.
74667
- *
74668
- * The returned `env` is a snapshot: call `environment()` again for a fresh
74669
- * token when starting new sandboxes.
74670
- *
74671
- * @example
74672
- * const { env, onStart } = await environment({ projectId });
74673
- * const sandbox = new E2BSandbox({ env, onStart });
74674
- * // in the sandbox: `gh pr list`, `git clone https://github.com/org/repo` just work
74675
- */
74676
- async function environment(options = {}) {
74677
- const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
74678
- if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
74679
- validateIntegrationOverrides(options.integrations);
74680
- const client = resolveClient(options.client);
74681
- const byIntegrationId = groupByIntegrationId(await listProjectConnections(client, projectId));
74682
- const env = {};
74683
- const hooks = [];
74684
- for (const contributor of ENVIRONMENT_CONTRIBUTORS) {
74685
- const integrationId = contributor.integrationId;
74686
- const overrides = options.integrations?.[integrationId] ?? {};
74687
- if (overrides.disabled) continue;
74688
- const candidates = byIntegrationId.get(integrationId) ?? [];
74689
- if (candidates.length === 0) continue;
74690
- const connectionId = resolveConnection({
74691
- integrationId,
74692
- envVar: contributor.envVar,
74693
- connectionId: overrides.connectionId
74694
- }, candidates);
74695
- if (!connectionId) continue;
74696
- let credential;
74697
- try {
74698
- credential = await getCredential(client, connectionId);
74699
- } catch (error) {
74700
- console.warn(`[@mastra/connect] Skipping ${integrationId} environment: ${error instanceof Error ? error.message : String(error)}`);
74701
- continue;
74702
- }
74703
- const contribution = contributor.build(credential);
74704
- for (const [key, value] of Object.entries(contribution.env)) {
74705
- if (key in env) console.warn(`[@mastra/connect] ${integrationId} environment overwrites env var '${key}' set by an earlier provider.`);
74706
- env[key] = value;
74707
- }
74708
- if (contribution.onStart) hooks.push({
74709
- integrationId,
74710
- hook: contribution.onStart
74711
- });
74712
- }
74713
- const onStart = async (args) => {
74714
- for (const { hook } of hooks) await hook(args);
74715
- };
74716
- return {
74717
- env,
74718
- onStart
74719
- };
74720
- }
74721
- //#endregion
74722
- export { CHANNELS, MastraConnectError, PROVIDERS, TOOLS, applyAllowTools, channels, connect, credential, defineProxyTool, environment, findChannelRegistration, findRegistration, resolveConnectionId, tools };
75086
+ export { CHANNELS, MastraConnectError, PROVIDERS, channels, credential, tools };
74723
75087
 
74724
75088
  //# sourceMappingURL=index.js.map