@mastra/connect 1.0.0-alpha.2 → 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.
- package/README.md +47 -42
- package/dist/channels.d.ts +38 -22
- package/dist/channels.d.ts.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/index.cjs +454 -288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +4 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +455 -281
- package/dist/index.js.map +1 -1
- package/dist/instrumentation.d.ts +48 -0
- package/dist/instrumentation.d.ts.map +1 -0
- package/dist/multi-connection.d.ts.map +1 -1
- package/dist/providers/jira/tools/get-create-issue-metadata.d.ts +42 -0
- package/dist/providers/jira/tools/get-create-issue-metadata.d.ts.map +1 -1
- package/dist/providers/jira/tools/list-issue-types.d.ts +6 -2
- package/dist/providers/jira/tools/list-issue-types.d.ts.map +1 -1
- package/dist/registry.d.ts +1 -8
- package/dist/registry.d.ts.map +1 -1
- package/dist/resolution.d.ts +3 -11
- package/dist/resolution.d.ts.map +1 -1
- package/dist/tools.d.ts +85 -29
- package/dist/tools.d.ts.map +1 -1
- package/dist/toolset.d.ts +39 -13
- package/dist/toolset.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/connect.d.ts +0 -22
- package/dist/connect.d.ts.map +0 -1
- package/dist/environment.d.ts +0 -48
- package/dist/environment.d.ts.map +0 -1
package/dist/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
let _mastra_mcp = require("@mastra/mcp");
|
|
3
3
|
let zod = require("zod");
|
|
4
|
+
let _mastra_core_observability = require("@mastra/core/observability");
|
|
4
5
|
let _mastra_core_tools = require("@mastra/core/tools");
|
|
5
6
|
//#region src/errors.ts
|
|
6
7
|
const MAX_DETAIL_LENGTH = 2e3;
|
|
@@ -105,6 +106,98 @@ async function extractProblemDetail(response) {
|
|
|
105
106
|
return { isProblemJson };
|
|
106
107
|
}
|
|
107
108
|
//#endregion
|
|
109
|
+
//#region src/instrumentation.ts
|
|
110
|
+
const ID_SEGMENT_ROUTES = [{
|
|
111
|
+
prefix: "/v2/connections/",
|
|
112
|
+
placeholder: "{connectionId}",
|
|
113
|
+
key: "connectionId"
|
|
114
|
+
}, {
|
|
115
|
+
prefix: "/v2/projects/",
|
|
116
|
+
placeholder: "{projectId}",
|
|
117
|
+
key: "projectId"
|
|
118
|
+
}];
|
|
119
|
+
const PROXY_SEGMENT = "/proxy/";
|
|
120
|
+
/** Describes a platform request for span naming without leaking query params, ids, or vendor path segments. */
|
|
121
|
+
function describePlatformCall(method, rawPath) {
|
|
122
|
+
const path = rawPath.split("?")[0];
|
|
123
|
+
let route = path;
|
|
124
|
+
const descriptor = {
|
|
125
|
+
method,
|
|
126
|
+
route,
|
|
127
|
+
isProxy: false
|
|
128
|
+
};
|
|
129
|
+
for (const idRoute of ID_SEGMENT_ROUTES) {
|
|
130
|
+
if (!path.startsWith(idRoute.prefix)) continue;
|
|
131
|
+
const rest = path.slice(idRoute.prefix.length);
|
|
132
|
+
const slash = rest.indexOf("/");
|
|
133
|
+
const id = slash === -1 ? rest : rest.slice(0, slash);
|
|
134
|
+
const tail = slash === -1 ? "" : rest.slice(slash);
|
|
135
|
+
descriptor[idRoute.key] = safeDecode(id);
|
|
136
|
+
route = `${idRoute.prefix}${idRoute.placeholder}${tail}`;
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
const proxyIndex = route.indexOf(PROXY_SEGMENT);
|
|
140
|
+
if (proxyIndex !== -1) {
|
|
141
|
+
descriptor.isProxy = true;
|
|
142
|
+
route = `${route.slice(0, proxyIndex + 7)}*`;
|
|
143
|
+
}
|
|
144
|
+
descriptor.route = route;
|
|
145
|
+
return descriptor;
|
|
146
|
+
}
|
|
147
|
+
function safeDecode(segment) {
|
|
148
|
+
try {
|
|
149
|
+
return decodeURIComponent(segment);
|
|
150
|
+
} catch {
|
|
151
|
+
return segment;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Starts a GENERIC child span for an outbound connect HTTP call, or returns
|
|
156
|
+
* `undefined` when no ambient span is active. Span creation failures are
|
|
157
|
+
* swallowed: instrumentation must never break a request.
|
|
158
|
+
*/
|
|
159
|
+
function startPlatformCallSpan(descriptor) {
|
|
160
|
+
let parent;
|
|
161
|
+
try {
|
|
162
|
+
parent = (0, _mastra_core_observability.resolveCurrentSpan)();
|
|
163
|
+
if (!parent) return void 0;
|
|
164
|
+
return parent.createChildSpan({
|
|
165
|
+
type: _mastra_core_observability.SpanType.GENERIC,
|
|
166
|
+
name: `connect.${descriptor.isProxy ? "proxy" : "platform"} ${descriptor.method} ${descriptor.route}`,
|
|
167
|
+
metadata: {
|
|
168
|
+
method: descriptor.method,
|
|
169
|
+
route: descriptor.route,
|
|
170
|
+
...descriptor.connectionId ? { connectionId: descriptor.connectionId } : {},
|
|
171
|
+
...descriptor.projectId ? { projectId: descriptor.projectId } : {}
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
} catch {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Ends a platform-call span with the response status. Never throws. */
|
|
179
|
+
function endPlatformCallSpan(span, status) {
|
|
180
|
+
if (!span) return;
|
|
181
|
+
try {
|
|
182
|
+
span.end({ metadata: { status } });
|
|
183
|
+
} catch {}
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Records a transport-level failure on a platform-call span. Nothing from the
|
|
187
|
+
* original error is recorded — fetch failures can embed the request URL in
|
|
188
|
+
* both `message` and `name`, which may carry secret query params — so the
|
|
189
|
+
* span gets a fixed, constant error. Never throws.
|
|
190
|
+
*/
|
|
191
|
+
function errorPlatformCallSpan(span, _error) {
|
|
192
|
+
if (!span) return;
|
|
193
|
+
try {
|
|
194
|
+
span.error({
|
|
195
|
+
error: /* @__PURE__ */ new Error("connect request failed"),
|
|
196
|
+
endSpan: true
|
|
197
|
+
});
|
|
198
|
+
} catch {}
|
|
199
|
+
}
|
|
200
|
+
//#endregion
|
|
108
201
|
//#region src/client.ts
|
|
109
202
|
const DEFAULT_INTEGRATIONS_URL = "https://integrations.mastra.ai";
|
|
110
203
|
const REGIONAL_INTEGRATIONS_URLS = {
|
|
@@ -147,20 +240,25 @@ function redact(message, accessToken) {
|
|
|
147
240
|
return message.split(accessToken).join("[REDACTED]");
|
|
148
241
|
}
|
|
149
242
|
async function platformFetch(client, path, init) {
|
|
243
|
+
const span = startPlatformCallSpan(describePlatformCall(init?.method ?? "GET", path));
|
|
150
244
|
try {
|
|
151
|
-
|
|
245
|
+
const response = await client.fetch(`${client.baseUrl}${path}`, {
|
|
152
246
|
...init,
|
|
153
247
|
headers: {
|
|
154
248
|
...client.headers,
|
|
155
249
|
...init?.headers
|
|
156
250
|
}
|
|
157
251
|
});
|
|
252
|
+
endPlatformCallSpan(span, response.status);
|
|
253
|
+
return response;
|
|
158
254
|
} catch (error) {
|
|
159
255
|
if (error instanceof Error && error.message.includes(client.accessToken)) {
|
|
160
256
|
const redacted = new Error(redact(error.message, client.accessToken));
|
|
161
257
|
redacted.name = error.name;
|
|
258
|
+
errorPlatformCallSpan(span, redacted);
|
|
162
259
|
throw redacted;
|
|
163
260
|
}
|
|
261
|
+
errorPlatformCallSpan(span, error);
|
|
164
262
|
throw error;
|
|
165
263
|
}
|
|
166
264
|
}
|
|
@@ -339,9 +437,6 @@ function assertValidBaseUrlOverride(baseUrlOverride) {
|
|
|
339
437
|
if (parsed.protocol !== "https:") throw new MastraConnectError("invalid_options", `Invalid baseUrlOverride '${baseUrlOverride}': only https:// URLs are allowed.`);
|
|
340
438
|
if (parsed.username || parsed.password) throw new MastraConnectError("invalid_options", `Invalid baseUrlOverride '${baseUrlOverride}': embedded credentials are not allowed.`);
|
|
341
439
|
}
|
|
342
|
-
async function proxyRequest(client, connectionId, options) {
|
|
343
|
-
return (await proxyRequestWithResponse(client, connectionId, options)).data;
|
|
344
|
-
}
|
|
345
440
|
/** Preserves HTTP metadata for tools with status-dependent provider contracts. */
|
|
346
441
|
async function proxyRequestWithResponse(client, connectionId, options) {
|
|
347
442
|
const cleanPath = options.path.replace(/^\/+/, "");
|
|
@@ -428,37 +523,64 @@ async function proxyRequestWithResponse(client, connectionId, options) {
|
|
|
428
523
|
}
|
|
429
524
|
//#endregion
|
|
430
525
|
//#region src/toolset.ts
|
|
431
|
-
/**
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
526
|
+
/**
|
|
527
|
+
* Compiles a tool-filter entry containing `*` wildcards into an anchored
|
|
528
|
+
* regex; every other character matches literally.
|
|
529
|
+
*/
|
|
530
|
+
function compileToolPattern(pattern) {
|
|
531
|
+
const escaped = pattern.split("*").map((part) => part.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")).join(".*");
|
|
532
|
+
return new RegExp(`^${escaped}$`);
|
|
436
533
|
}
|
|
437
534
|
/**
|
|
438
|
-
*
|
|
439
|
-
*
|
|
440
|
-
*
|
|
535
|
+
* Returns a predicate testing tool keys against filter `entries`, where an
|
|
536
|
+
* entry containing `*` is a glob and anything else matches literally. Used
|
|
537
|
+
* where keys are only known later (MCP approval predicates); list-shaped
|
|
538
|
+
* call sites expand eagerly with {@link expandToolPatterns} instead.
|
|
441
539
|
*/
|
|
442
|
-
function
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
540
|
+
function compileToolMatcher(entries) {
|
|
541
|
+
const literals = /* @__PURE__ */ new Set();
|
|
542
|
+
const patterns = [];
|
|
543
|
+
for (const entry of entries) if (entry.includes("*")) patterns.push(compileToolPattern(entry));
|
|
544
|
+
else literals.add(entry);
|
|
545
|
+
return (toolKey) => literals.has(toolKey) || patterns.some((pattern) => pattern.test(toolKey));
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Expands `*` glob entries against the known tool keys, preserving order and
|
|
549
|
+
* de-duplicating. Literal entries pass through untouched (callers validate
|
|
550
|
+
* those against their own key sets). A glob that matches nothing throws:
|
|
551
|
+
* like an unknown literal name, a dead pattern in access-limiting config is
|
|
552
|
+
* a typo that must surface instead of silently filtering nothing.
|
|
553
|
+
*/
|
|
554
|
+
function expandToolPatterns(entries, knownKeys, label) {
|
|
555
|
+
const expanded = [];
|
|
556
|
+
const seen = /* @__PURE__ */ new Set();
|
|
557
|
+
const push = (key) => {
|
|
558
|
+
if (seen.has(key)) return;
|
|
559
|
+
seen.add(key);
|
|
560
|
+
expanded.push(key);
|
|
561
|
+
};
|
|
562
|
+
for (const entry of entries) {
|
|
563
|
+
if (!entry.includes("*")) {
|
|
564
|
+
push(entry);
|
|
565
|
+
continue;
|
|
452
566
|
}
|
|
453
|
-
|
|
567
|
+
const pattern = compileToolPattern(entry);
|
|
568
|
+
const matches = knownKeys.filter((key) => pattern.test(key));
|
|
569
|
+
if (matches.length === 0) throw new MastraConnectConfigError(`Pattern '${entry}' in ${label} matched no tools. Known tools: ${knownKeys.join(", ")}.`);
|
|
570
|
+
for (const match of matches) push(match);
|
|
571
|
+
}
|
|
572
|
+
return expanded;
|
|
454
573
|
}
|
|
455
574
|
/**
|
|
456
|
-
* Filters a toolset by tool key.
|
|
457
|
-
*
|
|
575
|
+
* Filters a toolset by tool key. Entries containing `*` are globs expanded
|
|
576
|
+
* against the toolset's keys. Throws at build time on unknown names and on
|
|
577
|
+
* globs that match nothing, so typos in access-limiting config surface
|
|
578
|
+
* immediately.
|
|
458
579
|
*/
|
|
459
|
-
function applyAllowTools(tools,
|
|
460
|
-
if (!
|
|
580
|
+
function applyAllowTools(tools, allowToolsInput) {
|
|
581
|
+
if (!allowToolsInput) return tools;
|
|
461
582
|
const known = Object.keys(tools);
|
|
583
|
+
const allowTools = expandToolPatterns(allowToolsInput, known, "allowTools");
|
|
462
584
|
const unknown = allowTools.filter((name) => !known.includes(name));
|
|
463
585
|
if (unknown.length > 0) throw new Error(`Unknown tool name(s) in allowTools: ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
|
|
464
586
|
const filtered = {};
|
|
@@ -466,13 +588,15 @@ function applyAllowTools(tools, allowTools) {
|
|
|
466
588
|
return filtered;
|
|
467
589
|
}
|
|
468
590
|
/**
|
|
469
|
-
* Removes tool keys from a toolset.
|
|
470
|
-
*
|
|
471
|
-
*
|
|
591
|
+
* Removes tool keys from a toolset. Entries containing `*` are globs
|
|
592
|
+
* expanded against the toolset's keys. Throws at build time on unknown names
|
|
593
|
+
* and on globs that match nothing, so typos in access-limiting config
|
|
594
|
+
* surface immediately instead of silently removing nothing.
|
|
472
595
|
*/
|
|
473
|
-
function applyDisallowTools(tools,
|
|
474
|
-
if (!
|
|
596
|
+
function applyDisallowTools(tools, disallowToolsInput) {
|
|
597
|
+
if (!disallowToolsInput || disallowToolsInput.length === 0) return tools;
|
|
475
598
|
const known = Object.keys(tools);
|
|
599
|
+
const disallowTools = expandToolPatterns(disallowToolsInput, known, "disallowTools");
|
|
476
600
|
const unknown = disallowTools.filter((name) => !known.includes(name));
|
|
477
601
|
if (unknown.length > 0) throw new Error(`Unknown tool name(s) in disallowTools: ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
|
|
478
602
|
const remove = new Set(disallowTools);
|
|
@@ -710,7 +834,7 @@ function buildProxyMultiConnectionTools(input) {
|
|
|
710
834
|
*/
|
|
711
835
|
async function buildMcpMultiConnectionTools(input) {
|
|
712
836
|
const { registration, connections, allowTools, disallowTools, requireApproval, client, mcpClients, resolverId } = input;
|
|
713
|
-
const requireApprovalFor = Array.isArray(requireApproval) ?
|
|
837
|
+
const requireApprovalFor = Array.isArray(requireApproval) ? compileToolMatcher(requireApproval) : void 0;
|
|
714
838
|
const listToolKey = listConnectionsToolKey(registration.integrationId);
|
|
715
839
|
const innerAllowTools = allowTools?.filter((name) => name !== listToolKey);
|
|
716
840
|
const innerDisallowTools = disallowTools?.filter((name) => name !== listToolKey);
|
|
@@ -734,7 +858,7 @@ async function buildMcpMultiConnectionTools(input) {
|
|
|
734
858
|
id: `mastra-connect-${resolverId}-${registration.integrationId}-${connection.id}`,
|
|
735
859
|
servers: { [registration.integrationId]: {
|
|
736
860
|
...transport,
|
|
737
|
-
...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor
|
|
861
|
+
...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor(`${registration.integrationId}_${String(toolName)}`) } : {}
|
|
738
862
|
} }
|
|
739
863
|
})
|
|
740
864
|
};
|
|
@@ -751,8 +875,8 @@ async function buildMcpMultiConnectionTools(input) {
|
|
|
751
875
|
}
|
|
752
876
|
const catalogUnion = /* @__PURE__ */ new Set();
|
|
753
877
|
for (const catalog of rawInnerCatalogs.values()) for (const key of Object.keys(catalog)) catalogUnion.add(key);
|
|
754
|
-
if (
|
|
755
|
-
const unknown = [...
|
|
878
|
+
if (Array.isArray(requireApproval)) {
|
|
879
|
+
const unknown = expandToolPatterns(requireApproval, [...catalogUnion], `requireApproval for '${registration.integrationId}'`).filter((name) => !catalogUnion.has(name));
|
|
756
880
|
if (unknown.length > 0) throw new MastraConnectConfigError(`Unknown tool name(s) in requireApproval for '${registration.integrationId}': ${unknown.join(", ")}. Known tools: ${[...catalogUnion].join(", ")}.`);
|
|
757
881
|
}
|
|
758
882
|
innerToolsByConnectionId = /* @__PURE__ */ new Map();
|
|
@@ -782,7 +906,7 @@ async function buildMcpMultiConnectionTools(input) {
|
|
|
782
906
|
}
|
|
783
907
|
//#endregion
|
|
784
908
|
//#region src/providers/channels.ts
|
|
785
|
-
function credentialToken
|
|
909
|
+
function credentialToken(credential) {
|
|
786
910
|
switch (credential.type) {
|
|
787
911
|
case "oauth2": return credential.accessToken;
|
|
788
912
|
case "two_step": return credential.token;
|
|
@@ -836,7 +960,7 @@ const CHANNELS$1 = [
|
|
|
836
960
|
const mod = await import("@mastra/slack");
|
|
837
961
|
const safeOptions = stripReservedOptions("slack-channels", options);
|
|
838
962
|
const tokenResolver = async () => {
|
|
839
|
-
return credentialToken
|
|
963
|
+
return credentialToken(await runtime.getCredential());
|
|
840
964
|
};
|
|
841
965
|
return { provider: new mod.SlackProvider({
|
|
842
966
|
tokenResolver,
|
|
@@ -850,7 +974,7 @@ const CHANNELS$1 = [
|
|
|
850
974
|
const mod = await import("@mastra/telegram");
|
|
851
975
|
const safeOptions = stripReservedOptions("telegram", options);
|
|
852
976
|
const tokenResolver = async () => {
|
|
853
|
-
return credentialToken
|
|
977
|
+
return credentialToken(await runtime.getCredential());
|
|
854
978
|
};
|
|
855
979
|
return { provider: new mod.TelegramProvider({
|
|
856
980
|
tokenResolver,
|
|
@@ -891,7 +1015,7 @@ const CHANNELS$1 = [
|
|
|
891
1015
|
const safeOptions = stripReservedOptions("microsoft-teams", options);
|
|
892
1016
|
const tokenResolver = async (scope) => {
|
|
893
1017
|
const fresh = await runtime.getCredential();
|
|
894
|
-
if (!(Array.isArray(scope) ? scope : [scope]).includes(mod.TEAMS_DEV_PORTAL_SCOPE)) return credentialToken
|
|
1018
|
+
if (!(Array.isArray(scope) ? scope : [scope]).includes(mod.TEAMS_DEV_PORTAL_SCOPE)) return credentialToken(fresh);
|
|
895
1019
|
const token = fresh.type === "oauth2" ? fresh.secondaryAccessTokens?.devPortalAccessToken?.accessToken : void 0;
|
|
896
1020
|
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.`);
|
|
897
1021
|
return token;
|
|
@@ -34618,12 +34742,35 @@ const getCreateIssueMetadataInputSchema = zod.z.object({
|
|
|
34618
34742
|
issuetypeIds: zod.z.array(zod.z.string()).optional().describe("List of issue type IDs to filter by. Example: [\"10000\", \"10001\"]"),
|
|
34619
34743
|
expand: zod.z.string().optional().describe("Use expand to include additional information about issue metadata in the response. Supported values: projects, projects.issuetypes, projects.issuetypes.fields")
|
|
34620
34744
|
});
|
|
34745
|
+
const CreateIssueFieldSchema = zod.z.object({
|
|
34746
|
+
required: zod.z.boolean().optional(),
|
|
34747
|
+
schema: zod.z.record(zod.z.string(), zod.z.unknown()).optional(),
|
|
34748
|
+
name: zod.z.string().optional(),
|
|
34749
|
+
key: zod.z.string().optional(),
|
|
34750
|
+
hasDefaultValue: zod.z.boolean().optional(),
|
|
34751
|
+
operations: zod.z.array(zod.z.string()).optional(),
|
|
34752
|
+
allowedValues: zod.z.array(zod.z.unknown()).optional(),
|
|
34753
|
+
defaultValue: zod.z.unknown().optional(),
|
|
34754
|
+
autoCompleteUrl: zod.z.string().optional()
|
|
34755
|
+
});
|
|
34756
|
+
const IssueTypeSchema$1 = zod.z.object({
|
|
34757
|
+
id: zod.z.string(),
|
|
34758
|
+
name: zod.z.string(),
|
|
34759
|
+
self: zod.z.string().optional(),
|
|
34760
|
+
description: zod.z.string().optional(),
|
|
34761
|
+
iconUrl: zod.z.string().optional(),
|
|
34762
|
+
avatarId: zod.z.number().optional(),
|
|
34763
|
+
subtask: zod.z.boolean().optional(),
|
|
34764
|
+
hierarchyLevel: zod.z.number().optional(),
|
|
34765
|
+
fields: zod.z.record(zod.z.string(), CreateIssueFieldSchema).optional()
|
|
34766
|
+
});
|
|
34621
34767
|
const ProjectSchema$3 = zod.z.object({
|
|
34622
34768
|
id: zod.z.string(),
|
|
34623
34769
|
key: zod.z.string(),
|
|
34624
34770
|
name: zod.z.string(),
|
|
34625
34771
|
self: zod.z.string().optional(),
|
|
34626
|
-
avatarUrls: zod.z.record(zod.z.string(), zod.z.string()).optional()
|
|
34772
|
+
avatarUrls: zod.z.record(zod.z.string(), zod.z.string()).optional(),
|
|
34773
|
+
issuetypes: zod.z.array(IssueTypeSchema$1).optional()
|
|
34627
34774
|
});
|
|
34628
34775
|
const ProviderResponseSchema$149 = zod.z.object({ projects: zod.z.array(ProjectSchema$3).optional() });
|
|
34629
34776
|
const getCreateIssueMetadataOutputSchema = zod.z.object({ projects: zod.z.array(ProjectSchema$3).optional() });
|
|
@@ -35743,7 +35890,7 @@ zod.z.object({
|
|
|
35743
35890
|
cloudId: zod.z.string().optional(),
|
|
35744
35891
|
baseUrl: zod.z.string().optional()
|
|
35745
35892
|
});
|
|
35746
|
-
const listIssueTypesInputSchema = zod.z.object({});
|
|
35893
|
+
const listIssueTypesInputSchema = zod.z.object({ projectId: zod.z.string().optional().describe("Project ID used to list only issue types available in that project.") });
|
|
35747
35894
|
const IssueTypeSchema = zod.z.object({
|
|
35748
35895
|
id: zod.z.string(),
|
|
35749
35896
|
name: zod.z.string(),
|
|
@@ -35772,7 +35919,7 @@ const AccessibleResourceSchema$1 = zod.z.object({
|
|
|
35772
35919
|
function listIssueTypesTool(proxy) {
|
|
35773
35920
|
return (0, _mastra_core_tools.createTool)({
|
|
35774
35921
|
id: "jira_list_issue_types",
|
|
35775
|
-
description: "List Jira issue types available to the user",
|
|
35922
|
+
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).",
|
|
35776
35923
|
inputSchema: listIssueTypesInputSchema,
|
|
35777
35924
|
outputSchema: listIssueTypesOutputSchema,
|
|
35778
35925
|
execute: async (input, { requestContext }) => {
|
|
@@ -35807,7 +35954,12 @@ function listIssueTypesTool(proxy) {
|
|
|
35807
35954
|
baseUrl
|
|
35808
35955
|
});
|
|
35809
35956
|
}
|
|
35810
|
-
const config = {
|
|
35957
|
+
const config = input.projectId ? {
|
|
35958
|
+
endpoint: `/ex/jira/${cloudId}/rest/api/3/issuetype/project`,
|
|
35959
|
+
params: { projectId: input.projectId },
|
|
35960
|
+
headers: { "X-Atlassian-Token": "no-check" },
|
|
35961
|
+
retries: 3
|
|
35962
|
+
} : {
|
|
35811
35963
|
endpoint: `/ex/jira/${cloudId}/rest/api/3/issuetype`,
|
|
35812
35964
|
headers: { "X-Atlassian-Token": "no-check" },
|
|
35813
35965
|
retries: 3
|
|
@@ -74096,7 +74248,7 @@ function createWorkosTools(options) {
|
|
|
74096
74248
|
* Providers with checked-in HTTP toolsets. MCP-backed providers are discovered
|
|
74097
74249
|
* from the Platform integration catalog at runtime.
|
|
74098
74250
|
*/
|
|
74099
|
-
const
|
|
74251
|
+
const PROVIDERS = [
|
|
74100
74252
|
anthropicProvider,
|
|
74101
74253
|
clerkProvider,
|
|
74102
74254
|
discordProvider,
|
|
@@ -74129,27 +74281,16 @@ const TOOLS = [
|
|
|
74129
74281
|
}
|
|
74130
74282
|
];
|
|
74131
74283
|
/**
|
|
74132
|
-
* @deprecated Use `TOOLS`. This alias is retained for one release cycle so
|
|
74133
|
-
* downstream consumers keep working; it will be removed in the next minor.
|
|
74134
|
-
*/
|
|
74135
|
-
const PROVIDERS = TOOLS;
|
|
74136
|
-
function findRegistration(integrationId) {
|
|
74137
|
-
return TOOLS.find((p) => p.integrationId === integrationId);
|
|
74138
|
-
}
|
|
74139
|
-
/**
|
|
74140
74284
|
* Channel-capable providers with a hand-maintained `channels()` registration.
|
|
74141
74285
|
* The launch ships three (Slack, Telegram, Discord); the provider generator
|
|
74142
74286
|
* grows to cover new entries when a fourth lands.
|
|
74143
74287
|
*/
|
|
74144
74288
|
const CHANNELS = CHANNELS$1;
|
|
74145
|
-
function findChannelRegistration(integrationId) {
|
|
74146
|
-
return CHANNELS.find((c) => c.integrationId === integrationId);
|
|
74147
|
-
}
|
|
74148
74289
|
//#endregion
|
|
74149
74290
|
//#region src/resolution.ts
|
|
74150
74291
|
const INTEGRATION_ID_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
74151
|
-
function
|
|
74152
|
-
for (const integrationId of Object.keys(
|
|
74292
|
+
function validateProviderIds(providers) {
|
|
74293
|
+
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.`);
|
|
74153
74294
|
}
|
|
74154
74295
|
function connectionIdEnvVar(integrationId) {
|
|
74155
74296
|
return `MASTRA_${integrationId.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_CONNECTION_ID`;
|
|
@@ -74164,42 +74305,8 @@ function groupByIntegrationId(connections) {
|
|
|
74164
74305
|
return byIntegrationId;
|
|
74165
74306
|
}
|
|
74166
74307
|
/**
|
|
74167
|
-
* Resolves the connection to use for one provider, per the contract:
|
|
74168
|
-
* option/env var wins; else a single active connection; anything else
|
|
74169
|
-
* (ambiguity, needs_reauth, no usable candidate) warns and skips so one bad
|
|
74170
|
-
* integration never takes down the whole resolution. A needs_reauth
|
|
74171
|
-
* connection is never silently mapped.
|
|
74172
|
-
*/
|
|
74173
|
-
function resolveConnection(request, candidates) {
|
|
74174
|
-
const integrationId = request.integrationId;
|
|
74175
|
-
const directed = request.connectionId?.trim() || process.env[request.envVar]?.trim() || void 0;
|
|
74176
|
-
if (directed) {
|
|
74177
|
-
const match = candidates.find((connection) => connection.id === directed);
|
|
74178
|
-
if (!match) {
|
|
74179
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`);
|
|
74180
|
-
return;
|
|
74181
|
-
}
|
|
74182
|
-
if (match.status === "needs_reauth") {
|
|
74183
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);
|
|
74184
|
-
return;
|
|
74185
|
-
}
|
|
74186
|
-
if (match.status !== "active") {
|
|
74187
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`);
|
|
74188
|
-
return;
|
|
74189
|
-
}
|
|
74190
|
-
return directed;
|
|
74191
|
-
}
|
|
74192
|
-
const active = candidates.filter((connection) => connection.status === "active");
|
|
74193
|
-
if (active.length === 1) return active[0].id;
|
|
74194
|
-
if (active.length === 0) {
|
|
74195
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`);
|
|
74196
|
-
return;
|
|
74197
|
-
}
|
|
74198
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: ${active.length} active connections; pin one with connectionId or ${request.envVar}.`);
|
|
74199
|
-
}
|
|
74200
|
-
/**
|
|
74201
74308
|
* Resolves how to configure one provider given its candidate connections:
|
|
74202
|
-
* - An explicit pin (
|
|
74309
|
+
* - An explicit pin (`connectionId`) forces `single` at that id.
|
|
74203
74310
|
* - Exactly one active connection → `single`.
|
|
74204
74311
|
* - Two or more active connections → `multi`; the caller wraps tools with
|
|
74205
74312
|
* `connection_name` so the agent chooses per call at execute time.
|
|
@@ -74250,12 +74357,12 @@ const FAILURE_COOLDOWN_MS$1 = 3e4;
|
|
|
74250
74357
|
let nextResolverId = 0;
|
|
74251
74358
|
/**
|
|
74252
74359
|
* Returns a live toolset resolver over the project's Platform connections.
|
|
74253
|
-
* HTTP providers are loaded from the shipped `
|
|
74360
|
+
* HTTP providers are loaded from the shipped `PROVIDERS` registry. MCP providers
|
|
74254
74361
|
* are discovered from the Platform integration catalog. Tools from every supported
|
|
74255
74362
|
* provider with a matching project connection are merged into one flat record
|
|
74256
74363
|
* (matched by `integrationId`). The resolver serves a cached snapshot,
|
|
74257
|
-
* revalidating from the platform every `ttlMs`, so
|
|
74258
|
-
* to (or
|
|
74364
|
+
* revalidating from the platform every `ttlMs`, so providers connected
|
|
74365
|
+
* to (or disconnected from) the project are picked up (or dropped) without a
|
|
74259
74366
|
* restart.
|
|
74260
74367
|
*
|
|
74261
74368
|
* Configuration errors (missing project id, bad ttlMs, malformed integration id,
|
|
@@ -74274,11 +74381,14 @@ function tools(options = {}) {
|
|
|
74274
74381
|
const client = resolveClient(options.client);
|
|
74275
74382
|
const resolverId = ++nextResolverId;
|
|
74276
74383
|
const mcpClients = /* @__PURE__ */ new Map();
|
|
74277
|
-
const
|
|
74278
|
-
|
|
74279
|
-
|
|
74280
|
-
|
|
74281
|
-
|
|
74384
|
+
const normalizedProviders = normalizeProviderOverrides(options.providers);
|
|
74385
|
+
const providerOverrides = normalizedProviders.overrides;
|
|
74386
|
+
validateProviderIds(providerOverrides);
|
|
74387
|
+
validateProviderXor(providerOverrides);
|
|
74388
|
+
validateRequireApproval(providerOverrides);
|
|
74389
|
+
rejectRemovedAutoApproveTools(providerOverrides);
|
|
74390
|
+
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.");
|
|
74391
|
+
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.");
|
|
74282
74392
|
let cache;
|
|
74283
74393
|
let inflight;
|
|
74284
74394
|
let closing;
|
|
@@ -74294,15 +74404,17 @@ function tools(options = {}) {
|
|
|
74294
74404
|
const connections = connectionsResult.value;
|
|
74295
74405
|
if (catalogResult.status === "fulfilled") return {
|
|
74296
74406
|
connections,
|
|
74297
|
-
catalog: catalogResult.value
|
|
74407
|
+
catalog: catalogResult.value,
|
|
74408
|
+
catalogAvailable: true
|
|
74298
74409
|
};
|
|
74299
|
-
const checkedIn = new Set(
|
|
74300
|
-
if (connections.some((connection) => connection.status === "active" && !checkedIn.has(connection.integrationId) && !
|
|
74410
|
+
const checkedIn = new Set(PROVIDERS.map((registration) => registration.integrationId));
|
|
74411
|
+
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;
|
|
74301
74412
|
const reason = catalogResult.reason;
|
|
74302
74413
|
console.warn(`[@mastra/connect] Platform catalog unavailable (${reason instanceof Error ? reason.message : String(reason)}); resolving checked-in providers only.`);
|
|
74303
74414
|
return {
|
|
74304
74415
|
connections,
|
|
74305
|
-
catalog: []
|
|
74416
|
+
catalog: [],
|
|
74417
|
+
catalogAvailable: false
|
|
74306
74418
|
};
|
|
74307
74419
|
};
|
|
74308
74420
|
/**
|
|
@@ -74313,8 +74425,8 @@ function tools(options = {}) {
|
|
|
74313
74425
|
if (closing) return closing.then(refresh);
|
|
74314
74426
|
if (!inflight) inflight = (async () => {
|
|
74315
74427
|
try {
|
|
74316
|
-
const { connections, catalog } = await loadSnapshotInputs();
|
|
74317
|
-
const snapshot = await mapTools(connections, buildRequests(
|
|
74428
|
+
const { connections, catalog, catalogAvailable } = await loadSnapshotInputs();
|
|
74429
|
+
const snapshot = await mapTools(connections, buildRequests(normalizedProviders, catalog, catalogAvailable), options, client, mcpClients, resolverId);
|
|
74318
74430
|
cache = {
|
|
74319
74431
|
snapshot,
|
|
74320
74432
|
fetchedAt: Date.now()
|
|
@@ -74344,10 +74456,7 @@ function tools(options = {}) {
|
|
|
74344
74456
|
}
|
|
74345
74457
|
return refresh();
|
|
74346
74458
|
};
|
|
74347
|
-
|
|
74348
|
-
invalidate: () => {
|
|
74349
|
-
cache = void 0;
|
|
74350
|
-
},
|
|
74459
|
+
const resolver = Object.assign(resolve, {
|
|
74351
74460
|
refresh,
|
|
74352
74461
|
disconnect: () => {
|
|
74353
74462
|
closing ??= (async () => {
|
|
@@ -74362,28 +74471,75 @@ function tools(options = {}) {
|
|
|
74362
74471
|
}
|
|
74363
74472
|
})();
|
|
74364
74473
|
return closing;
|
|
74365
|
-
}
|
|
74474
|
+
},
|
|
74475
|
+
with: (extra) => withExtraTools(resolver, extra)
|
|
74366
74476
|
});
|
|
74477
|
+
return resolver;
|
|
74367
74478
|
}
|
|
74368
74479
|
/**
|
|
74369
|
-
*
|
|
74370
|
-
*
|
|
74371
|
-
*
|
|
74372
|
-
* Also rejects malformed inputs early (non-string array entries, duplicates)
|
|
74373
|
-
* so a bad option throws at tools() time rather than at first refresh.
|
|
74480
|
+
* Builds the resolver `.with()` returns: resolutions merge the base
|
|
74481
|
+
* resolver's tools with `extra` (extra wins on key collision), cache handles
|
|
74482
|
+
* delegate to the base, and further `.with()` calls chain.
|
|
74374
74483
|
*/
|
|
74375
|
-
function
|
|
74376
|
-
|
|
74377
|
-
|
|
74378
|
-
const
|
|
74379
|
-
|
|
74380
|
-
|
|
74381
|
-
|
|
74382
|
-
|
|
74383
|
-
|
|
74384
|
-
|
|
74484
|
+
function withExtraTools(base, extra) {
|
|
74485
|
+
const resolveExtra = typeof extra === "function" ? extra : () => extra;
|
|
74486
|
+
const resolve = async (ctx) => {
|
|
74487
|
+
const [baseTools, extraTools] = await Promise.all([base(ctx), resolveExtra(ctx)]);
|
|
74488
|
+
return {
|
|
74489
|
+
...baseTools,
|
|
74490
|
+
...extraTools
|
|
74491
|
+
};
|
|
74492
|
+
};
|
|
74493
|
+
const resolver = Object.assign(resolve, {
|
|
74494
|
+
refresh: async () => {
|
|
74495
|
+
const [baseTools, extraTools] = await Promise.all([base.refresh(), resolveExtra()]);
|
|
74496
|
+
return {
|
|
74497
|
+
...baseTools,
|
|
74498
|
+
...extraTools
|
|
74499
|
+
};
|
|
74500
|
+
},
|
|
74501
|
+
disconnect: () => base.disconnect(),
|
|
74502
|
+
with: (more) => withExtraTools(resolver, more)
|
|
74503
|
+
});
|
|
74504
|
+
return resolver;
|
|
74505
|
+
}
|
|
74506
|
+
/**
|
|
74507
|
+
* Turns the two accepted `providers` shapes into the internal form. The
|
|
74508
|
+
* array form (`["linear", "github"]`) becomes an allowlist with default
|
|
74509
|
+
* options; the record form expands boolean shorthands (`true` → `{}`,
|
|
74510
|
+
* `false` → an internal exclusion marker). Malformed inputs (non-string
|
|
74511
|
+
* array entries, duplicates, non-boolean/non-object record values) throw at
|
|
74512
|
+
* tools() time rather than at first refresh.
|
|
74513
|
+
*/
|
|
74514
|
+
function normalizeProviderOverrides(providers) {
|
|
74515
|
+
if (providers === void 0) return {
|
|
74516
|
+
overrides: {},
|
|
74517
|
+
only: void 0
|
|
74518
|
+
};
|
|
74519
|
+
if (Array.isArray(providers)) {
|
|
74520
|
+
const overrides = {};
|
|
74521
|
+
for (const entry of providers) {
|
|
74522
|
+
if (typeof entry !== "string") throw new MastraConnectError("invalid_options", `Invalid providers entry: expected a string provider id, got ${typeof entry}.`);
|
|
74523
|
+
if (overrides[entry] !== void 0) throw new MastraConnectError("invalid_options", `Duplicate provider '${entry}' in providers array.`);
|
|
74524
|
+
overrides[entry] = {};
|
|
74525
|
+
}
|
|
74526
|
+
return {
|
|
74527
|
+
overrides,
|
|
74528
|
+
only: new Set(Object.keys(overrides))
|
|
74529
|
+
};
|
|
74385
74530
|
}
|
|
74386
|
-
|
|
74531
|
+
const overrides = {};
|
|
74532
|
+
for (const [providerId, value] of Object.entries(providers)) {
|
|
74533
|
+
if (value === void 0) continue;
|
|
74534
|
+
if (value === true) overrides[providerId] = {};
|
|
74535
|
+
else if (value === false) overrides[providerId] = { disabled: true };
|
|
74536
|
+
else if (typeof value === "object" && value !== null && !Array.isArray(value)) overrides[providerId] = value;
|
|
74537
|
+
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}.`);
|
|
74538
|
+
}
|
|
74539
|
+
return {
|
|
74540
|
+
overrides,
|
|
74541
|
+
only: void 0
|
|
74542
|
+
};
|
|
74387
74543
|
}
|
|
74388
74544
|
/**
|
|
74389
74545
|
* Rejects any provider that sets both `allowTools` and `disallowTools`.
|
|
@@ -74391,8 +74547,8 @@ function normalizeIntegrationOverrides(integrations) {
|
|
|
74391
74547
|
* runtime guard catches loosely typed inputs (e.g. built from JSON or a
|
|
74392
74548
|
* `Record<string, unknown>` upstream).
|
|
74393
74549
|
*/
|
|
74394
|
-
function
|
|
74395
|
-
for (const [integrationId, providerOptions] of Object.entries(
|
|
74550
|
+
function validateProviderXor(providers) {
|
|
74551
|
+
for (const [integrationId, providerOptions] of Object.entries(providers)) {
|
|
74396
74552
|
const filters = providerOptions;
|
|
74397
74553
|
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.`);
|
|
74398
74554
|
}
|
|
@@ -74404,8 +74560,8 @@ function validateIntegrationXor(integrations) {
|
|
|
74404
74560
|
* approval on MCP providers silently (and crash the HTTP path into
|
|
74405
74561
|
* warn-and-skip) instead of gating tools as the author intended.
|
|
74406
74562
|
*/
|
|
74407
|
-
function validateRequireApproval(
|
|
74408
|
-
for (const [integrationId, providerOptions] of Object.entries(
|
|
74563
|
+
function validateRequireApproval(providers) {
|
|
74564
|
+
for (const [integrationId, providerOptions] of Object.entries(providers)) {
|
|
74409
74565
|
const requireApproval = providerOptions.requireApproval;
|
|
74410
74566
|
if (requireApproval === void 0 || typeof requireApproval === "boolean") continue;
|
|
74411
74567
|
if (Array.isArray(requireApproval) && requireApproval.every((name) => typeof name === "string")) continue;
|
|
@@ -74418,12 +74574,12 @@ function validateRequireApproval(integrations) {
|
|
|
74418
74574
|
* carrying this key would otherwise be ignored silently and its tools would
|
|
74419
74575
|
* run without the prompts the author expected.
|
|
74420
74576
|
*/
|
|
74421
|
-
function rejectRemovedAutoApproveTools(
|
|
74422
|
-
for (const [integrationId, providerOptions] of Object.entries(
|
|
74577
|
+
function rejectRemovedAutoApproveTools(providers) {
|
|
74578
|
+
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.`);
|
|
74423
74579
|
}
|
|
74424
|
-
function buildRequests(
|
|
74425
|
-
const overrides =
|
|
74426
|
-
const registrations = new Map(
|
|
74580
|
+
function buildRequests(providers, catalog, catalogAvailable) {
|
|
74581
|
+
const { overrides, only } = providers;
|
|
74582
|
+
const registrations = new Map(PROVIDERS.map((registration) => [registration.integrationId, registration]));
|
|
74427
74583
|
const catalogIds = new Set(catalog.map((integration) => integration.id));
|
|
74428
74584
|
for (const integration of catalog) {
|
|
74429
74585
|
if (!integration.capabilities.mcp) continue;
|
|
@@ -74433,9 +74589,14 @@ function buildRequests(integrations, catalog) {
|
|
|
74433
74589
|
transport: "mcp"
|
|
74434
74590
|
});
|
|
74435
74591
|
}
|
|
74436
|
-
|
|
74592
|
+
const unknown = Object.keys(overrides).filter((integrationId) => !registrations.has(integrationId) && !catalogIds.has(integrationId) && !overrides[integrationId]?.disabled);
|
|
74593
|
+
if (unknown.length > 0) {
|
|
74594
|
+
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.`);
|
|
74595
|
+
for (const integrationId of unknown) console.warn(`[@mastra/connect] Platform catalog unavailable; cannot verify provider '${integrationId}'. Skipping it this resolution.`);
|
|
74596
|
+
}
|
|
74437
74597
|
const requests = [];
|
|
74438
74598
|
for (const registration of registrations.values()) {
|
|
74599
|
+
if (only !== void 0 && !only.has(registration.integrationId)) continue;
|
|
74439
74600
|
const providerOptions = overrides[registration.integrationId] ?? {};
|
|
74440
74601
|
if (providerOptions.disabled) continue;
|
|
74441
74602
|
requests.push({
|
|
@@ -74451,6 +74612,7 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
|
|
|
74451
74612
|
const activeMcpKeys = /* @__PURE__ */ new Set();
|
|
74452
74613
|
const result = {};
|
|
74453
74614
|
const toolOwners = /* @__PURE__ */ new Map();
|
|
74615
|
+
const matchedDefaultEntries = /* @__PURE__ */ new Set();
|
|
74454
74616
|
for (const request of requests) {
|
|
74455
74617
|
const integrationId = request.registration.integrationId;
|
|
74456
74618
|
let providerTools;
|
|
@@ -74510,6 +74672,11 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
|
|
|
74510
74672
|
console.warn(`[@mastra/connect] Skipping ${integrationId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
74511
74673
|
continue;
|
|
74512
74674
|
}
|
|
74675
|
+
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);
|
|
74676
|
+
if (request.options.requireApproval === void 0 && options.requireApproval !== void 0) {
|
|
74677
|
+
const policy = options.requireApproval;
|
|
74678
|
+
if (policy !== false) applyDefaultRequireApproval(providerTools, policy, integrationId, matchedDefaultEntries);
|
|
74679
|
+
}
|
|
74513
74680
|
for (const toolKey of Object.keys(providerTools)) {
|
|
74514
74681
|
const existingOwner = toolOwners.get(toolKey);
|
|
74515
74682
|
if (existingOwner) throw new MastraConnectError("invalid_options", `Duplicate tool key '${toolKey}' from providers '${existingOwner}' and '${integrationId}'.`);
|
|
@@ -74517,13 +74684,78 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
|
|
|
74517
74684
|
}
|
|
74518
74685
|
Object.assign(result, providerTools);
|
|
74519
74686
|
}
|
|
74687
|
+
if (Object.keys(result).length > 0) {
|
|
74688
|
+
const defaultEntries = [
|
|
74689
|
+
...options.allowTools ?? [],
|
|
74690
|
+
...options.disallowTools ?? [],
|
|
74691
|
+
...Array.isArray(options.requireApproval) ? options.requireApproval : []
|
|
74692
|
+
];
|
|
74693
|
+
for (const entry of defaultEntries) if (!matchedDefaultEntries.has(entry)) console.warn(`[@mastra/connect] Top-level entry '${entry}' matched no tools on any resolved provider.`);
|
|
74694
|
+
}
|
|
74520
74695
|
const staleClients = Array.from(mcpClients.entries()).filter(([key]) => !activeMcpKeys.has(key));
|
|
74521
74696
|
for (const [key] of staleClients) mcpClients.delete(key);
|
|
74522
74697
|
await Promise.allSettled(staleClients.map(([, entry]) => entry.client.disconnect()));
|
|
74523
74698
|
return result;
|
|
74524
74699
|
}
|
|
74525
74700
|
/**
|
|
74526
|
-
*
|
|
74701
|
+
* Applies the top-level default filter to a provider that set no filter of
|
|
74702
|
+
* its own. Lenient by design: the provider set is live, so a default entry
|
|
74703
|
+
* that matches nothing on this provider simply does not apply here. Entries
|
|
74704
|
+
* that match are recorded in `matchedEntries` so mapTools can warn about
|
|
74705
|
+
* defaults that matched nothing anywhere. The synthetic
|
|
74706
|
+
* `<provider>__list_connections` wrapper is never filtered by defaults,
|
|
74707
|
+
* matching the per-provider multi-connection behavior.
|
|
74708
|
+
*/
|
|
74709
|
+
function applyDefaultFilter(providerTools, defaults, integrationId, matchedEntries) {
|
|
74710
|
+
const listToolKey = listConnectionsToolKey(integrationId);
|
|
74711
|
+
const matchers = (defaults.allowTools ?? defaults.disallowTools ?? []).map((entry) => ({
|
|
74712
|
+
entry,
|
|
74713
|
+
matches: compileToolMatcher([entry])
|
|
74714
|
+
}));
|
|
74715
|
+
const filtered = {};
|
|
74716
|
+
for (const [key, tool] of Object.entries(providerTools)) {
|
|
74717
|
+
if (key === listToolKey) {
|
|
74718
|
+
filtered[key] = tool;
|
|
74719
|
+
continue;
|
|
74720
|
+
}
|
|
74721
|
+
let matchedAny = false;
|
|
74722
|
+
for (const { entry, matches } of matchers) if (matches(key)) {
|
|
74723
|
+
matchedEntries.add(entry);
|
|
74724
|
+
matchedAny = true;
|
|
74725
|
+
}
|
|
74726
|
+
if (defaults.allowTools !== void 0 ? matchedAny : !matchedAny) filtered[key] = tool;
|
|
74727
|
+
}
|
|
74728
|
+
return filtered;
|
|
74729
|
+
}
|
|
74730
|
+
/**
|
|
74731
|
+
* Applies the top-level default `requireApproval` policy to a provider that
|
|
74732
|
+
* set no policy of its own. `true` gates every tool except the synthetic
|
|
74733
|
+
* `<provider>__list_connections` wrapper; an array gates matching keys with
|
|
74734
|
+
* the same lenient semantics as {@link applyDefaultFilter}.
|
|
74735
|
+
*/
|
|
74736
|
+
function applyDefaultRequireApproval(providerTools, policy, integrationId, matchedEntries) {
|
|
74737
|
+
const listToolKey = listConnectionsToolKey(integrationId);
|
|
74738
|
+
if (policy === true) {
|
|
74739
|
+
for (const [key, tool] of Object.entries(providerTools)) {
|
|
74740
|
+
if (key === listToolKey) continue;
|
|
74741
|
+
tool.requireApproval = true;
|
|
74742
|
+
}
|
|
74743
|
+
return;
|
|
74744
|
+
}
|
|
74745
|
+
const matchers = policy.map((entry) => ({
|
|
74746
|
+
entry,
|
|
74747
|
+
matches: compileToolMatcher([entry])
|
|
74748
|
+
}));
|
|
74749
|
+
for (const [key, tool] of Object.entries(providerTools)) {
|
|
74750
|
+
if (key === listToolKey) continue;
|
|
74751
|
+
for (const { entry, matches } of matchers) if (matches(key)) {
|
|
74752
|
+
matchedEntries.add(entry);
|
|
74753
|
+
tool.requireApproval = true;
|
|
74754
|
+
}
|
|
74755
|
+
}
|
|
74756
|
+
}
|
|
74757
|
+
/**
|
|
74758
|
+
* Extracts whichever provider tool filter is set. Since `ToolsProviderOptions`
|
|
74527
74759
|
* is an XOR union and tools() validates co-occurrence up front, at most one
|
|
74528
74760
|
* of the two will ever be defined here.
|
|
74529
74761
|
*/
|
|
@@ -74535,14 +74767,16 @@ function providerFilterOptions(options) {
|
|
|
74535
74767
|
* toolset by marking the selected tools. `true` gates every provider tool
|
|
74536
74768
|
* (but never the synthetic `<provider>__list_connections` wrapper tool,
|
|
74537
74769
|
* matching the MCP paths, where only server-discovered tools are gated);
|
|
74538
|
-
* an array gates the listed keys
|
|
74539
|
-
*
|
|
74540
|
-
*
|
|
74770
|
+
* an array gates the listed keys — entries containing `*` are globs,
|
|
74771
|
+
* expanded against the provider tools minus the synthetic wrapper so a
|
|
74772
|
+
* broad glob behaves like `true` — and rejects unknown names and dead
|
|
74773
|
+
* globs with the same `invalid_options` error MCP discovery raises, so a
|
|
74774
|
+
* typo never silently widens access.
|
|
74541
74775
|
*/
|
|
74542
74776
|
function applyProxyRequireApproval(providerTools, integrationId, requireApproval, excludeKeys = []) {
|
|
74543
74777
|
if (!requireApproval) return;
|
|
74778
|
+
const excluded = new Set(excludeKeys);
|
|
74544
74779
|
if (requireApproval === true) {
|
|
74545
|
-
const excluded = new Set(excludeKeys);
|
|
74546
74780
|
for (const [toolKey, tool] of Object.entries(providerTools)) {
|
|
74547
74781
|
if (excluded.has(toolKey)) continue;
|
|
74548
74782
|
tool.requireApproval = true;
|
|
@@ -74550,13 +74784,14 @@ function applyProxyRequireApproval(providerTools, integrationId, requireApproval
|
|
|
74550
74784
|
return;
|
|
74551
74785
|
}
|
|
74552
74786
|
const known = Object.keys(providerTools);
|
|
74553
|
-
const
|
|
74787
|
+
const expanded = expandToolPatterns(requireApproval, known.filter((key) => !excluded.has(key)), `requireApproval for '${integrationId}'`);
|
|
74788
|
+
const unknown = expanded.filter((name) => !known.includes(name));
|
|
74554
74789
|
if (unknown.length > 0) throw new MastraConnectConfigError(`Unknown tool name(s) in requireApproval for '${integrationId}': ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
|
|
74555
|
-
for (const name of
|
|
74790
|
+
for (const name of expanded) providerTools[name].requireApproval = true;
|
|
74556
74791
|
}
|
|
74557
74792
|
async function discoverMcpTools(input) {
|
|
74558
74793
|
const { registration, connectionId, allowTools, disallowTools, requireApproval, client, mcpClients, resolverId } = input;
|
|
74559
|
-
const requireApprovalFor = Array.isArray(requireApproval) ?
|
|
74794
|
+
const requireApprovalFor = Array.isArray(requireApproval) ? compileToolMatcher(requireApproval) : void 0;
|
|
74560
74795
|
const cacheKey = `${registration.integrationId}::${connectionId}`;
|
|
74561
74796
|
let entry = mcpClients.get(cacheKey);
|
|
74562
74797
|
if (!entry) {
|
|
@@ -74568,7 +74803,7 @@ async function discoverMcpTools(input) {
|
|
|
74568
74803
|
id: `mastra-connect-${resolverId}-${registration.integrationId}-${connectionId}`,
|
|
74569
74804
|
servers: { [registration.integrationId]: {
|
|
74570
74805
|
...transport,
|
|
74571
|
-
...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor
|
|
74806
|
+
...requireApproval === true ? { requireToolApproval: true } : requireApprovalFor ? { requireToolApproval: ({ toolName }) => requireApprovalFor(`${registration.integrationId}_${String(toolName)}`) } : {}
|
|
74572
74807
|
} }
|
|
74573
74808
|
})
|
|
74574
74809
|
};
|
|
@@ -74577,8 +74812,8 @@ async function discoverMcpTools(input) {
|
|
|
74577
74812
|
const discovery = await entry.client.listToolsWithErrors();
|
|
74578
74813
|
const error = discovery.errors[registration.integrationId];
|
|
74579
74814
|
if (error) throw new Error(`MCP tool discovery failed: ${error}`);
|
|
74580
|
-
if (
|
|
74581
|
-
const unknown =
|
|
74815
|
+
if (Array.isArray(requireApproval)) {
|
|
74816
|
+
const unknown = expandToolPatterns(requireApproval, Object.keys(discovery.tools), `requireApproval for '${registration.integrationId}'`).filter((name) => !(name in discovery.tools));
|
|
74582
74817
|
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(", ")}.`);
|
|
74583
74818
|
}
|
|
74584
74819
|
return applyToolFilter(discovery.tools, {
|
|
@@ -74587,23 +74822,6 @@ async function discoverMcpTools(input) {
|
|
|
74587
74822
|
});
|
|
74588
74823
|
}
|
|
74589
74824
|
//#endregion
|
|
74590
|
-
//#region src/connect.ts
|
|
74591
|
-
/**
|
|
74592
|
-
* @deprecated Use `tools` from `@mastra/connect` instead. This module is a
|
|
74593
|
-
* thin re-export retained for one release cycle so downstream consumers keep
|
|
74594
|
-
* working; it will be removed in the next minor.
|
|
74595
|
-
*/
|
|
74596
|
-
/**
|
|
74597
|
-
* @deprecated Renamed to {@link tools}.
|
|
74598
|
-
*
|
|
74599
|
-
* `connect(options)` is a drop-in alias for `tools(options)`. Prefer `tools`
|
|
74600
|
-
* from `@mastra/connect` in new code; this export will be removed in the next
|
|
74601
|
-
* minor version.
|
|
74602
|
-
*/
|
|
74603
|
-
function connect(options = {}) {
|
|
74604
|
-
return tools(options);
|
|
74605
|
-
}
|
|
74606
|
-
//#endregion
|
|
74607
74825
|
//#region src/channels.ts
|
|
74608
74826
|
const DEFAULT_TTL_MS = 3e4;
|
|
74609
74827
|
/** Minimum wait after a failed platform fetch before another background revalidation. */
|
|
@@ -74616,7 +74834,7 @@ const FAILURE_COOLDOWN_MS = 3e4;
|
|
|
74616
74834
|
* Discord) are constructed once, up front and credential-less, so their
|
|
74617
74835
|
* webhook/OAuth routes can mount at Mastra construction. Each resolution
|
|
74618
74836
|
* fetches the project's connections and late-binds credentials into the live
|
|
74619
|
-
* instances:
|
|
74837
|
+
* instances: providers with an active connection appear in the resolved
|
|
74620
74838
|
* map, others don't — so connecting a new channel on the platform takes
|
|
74621
74839
|
* effect without redeploying the app.
|
|
74622
74840
|
*
|
|
@@ -74641,12 +74859,17 @@ async function channels(options = {}) {
|
|
|
74641
74859
|
const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
|
|
74642
74860
|
if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
|
|
74643
74861
|
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.`);
|
|
74644
|
-
|
|
74862
|
+
const { overrides: providerOverrides, only } = normalizeChannelProviders(options.providers);
|
|
74863
|
+
validateProviderIds(providerOverrides);
|
|
74864
|
+
const knownChannelIds = new Set(CHANNELS.map((registration) => registration.integrationId));
|
|
74865
|
+
const unknownChannelIds = Object.keys(providerOverrides).filter((integrationId) => !knownChannelIds.has(integrationId) && !providerOverrides[integrationId]?.disabled);
|
|
74866
|
+
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(", ")}.`);
|
|
74645
74867
|
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
74646
74868
|
const client = resolveClient(options.client);
|
|
74647
74869
|
const states = [];
|
|
74648
74870
|
for (const registration of CHANNELS) {
|
|
74649
|
-
|
|
74871
|
+
if (only !== void 0 && !only.has(registration.integrationId)) continue;
|
|
74872
|
+
const overrides = providerOverrides[registration.integrationId] ?? {};
|
|
74650
74873
|
if (overrides.disabled) continue;
|
|
74651
74874
|
const state = {
|
|
74652
74875
|
registration,
|
|
@@ -74699,7 +74922,7 @@ async function channels(options = {}) {
|
|
|
74699
74922
|
}
|
|
74700
74923
|
for (const state of states) {
|
|
74701
74924
|
const integrationId = state.registration.integrationId;
|
|
74702
|
-
const overrides =
|
|
74925
|
+
const overrides = providerOverrides[integrationId] ?? {};
|
|
74703
74926
|
const candidates = byIntegrationId.get(integrationId) ?? [];
|
|
74704
74927
|
const connectionId = candidates.length === 0 ? void 0 : selectChannelConnection(integrationId, overrides.connectionId, candidates);
|
|
74705
74928
|
state.connectionId = connectionId;
|
|
@@ -74746,9 +74969,6 @@ async function channels(options = {}) {
|
|
|
74746
74969
|
};
|
|
74747
74970
|
const invocable = ((_context) => resolve());
|
|
74748
74971
|
invocable.getRoutes = () => states.flatMap((state) => state.instance.provider.getRoutes());
|
|
74749
|
-
invocable.invalidate = () => {
|
|
74750
|
-
cache = void 0;
|
|
74751
|
-
};
|
|
74752
74972
|
invocable.refresh = refresh;
|
|
74753
74973
|
invocable.disconnect = async () => {
|
|
74754
74974
|
cache = void 0;
|
|
@@ -74756,15 +74976,73 @@ async function channels(options = {}) {
|
|
|
74756
74976
|
return invocable;
|
|
74757
74977
|
}
|
|
74758
74978
|
/**
|
|
74979
|
+
* Friendly channel-key aliases. The Slack channel is keyed `slack-channels`
|
|
74980
|
+
* on the platform (plain `slack` names the Slack tools provider), but inside
|
|
74981
|
+
* a channels() config `slack` is unambiguous — accept it and canonicalize to
|
|
74982
|
+
* the platform key for registration and connection lookup.
|
|
74983
|
+
*/
|
|
74984
|
+
const CHANNEL_KEY_ALIASES = { slack: "slack-channels" };
|
|
74985
|
+
const canonicalChannelId = (id) => CHANNEL_KEY_ALIASES[id] ?? id;
|
|
74986
|
+
/** Throws when two option keys (e.g. `slack` and `slack-channels`) name the same channel. */
|
|
74987
|
+
function rejectAliasCollision(sourceKeys, canonical, key) {
|
|
74988
|
+
const existing = sourceKeys[canonical];
|
|
74989
|
+
if (existing === void 0) return;
|
|
74990
|
+
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.`);
|
|
74991
|
+
}
|
|
74992
|
+
/**
|
|
74993
|
+
* Turns the two accepted `providers` shapes into the internal form, mirroring
|
|
74994
|
+
* `tools()`. The array form becomes an allowlist with default options; the
|
|
74995
|
+
* record form expands boolean shorthands (`true` → `{}`, `false` → an
|
|
74996
|
+
* internal exclusion marker). Alias keys canonicalize first, so `slack` and
|
|
74997
|
+
* `slack-channels` configure the same channel (and collide loudly). Malformed
|
|
74998
|
+
* inputs throw at channels() time.
|
|
74999
|
+
*/
|
|
75000
|
+
function normalizeChannelProviders(providers) {
|
|
75001
|
+
if (providers === void 0) return {
|
|
75002
|
+
overrides: {},
|
|
75003
|
+
only: void 0
|
|
75004
|
+
};
|
|
75005
|
+
const sourceKeys = {};
|
|
75006
|
+
if (Array.isArray(providers)) {
|
|
75007
|
+
const overrides = {};
|
|
75008
|
+
for (const entry of providers) {
|
|
75009
|
+
if (typeof entry !== "string") throw new MastraConnectError("invalid_options", `Invalid providers entry: expected a string channel id, got ${typeof entry}.`);
|
|
75010
|
+
const canonical = canonicalChannelId(entry);
|
|
75011
|
+
rejectAliasCollision(sourceKeys, canonical, entry);
|
|
75012
|
+
sourceKeys[canonical] = entry;
|
|
75013
|
+
overrides[canonical] = {};
|
|
75014
|
+
}
|
|
75015
|
+
return {
|
|
75016
|
+
overrides,
|
|
75017
|
+
only: new Set(Object.keys(overrides))
|
|
75018
|
+
};
|
|
75019
|
+
}
|
|
75020
|
+
const overrides = {};
|
|
75021
|
+
for (const [providerId, value] of Object.entries(providers)) {
|
|
75022
|
+
if (value === void 0) continue;
|
|
75023
|
+
const canonical = canonicalChannelId(providerId);
|
|
75024
|
+
rejectAliasCollision(sourceKeys, canonical, providerId);
|
|
75025
|
+
sourceKeys[canonical] = providerId;
|
|
75026
|
+
if (value === true) overrides[canonical] = {};
|
|
75027
|
+
else if (value === false) overrides[canonical] = { disabled: true };
|
|
75028
|
+
else if (typeof value === "object" && value !== null && !Array.isArray(value)) overrides[canonical] = value;
|
|
75029
|
+
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}.`);
|
|
75030
|
+
}
|
|
75031
|
+
return {
|
|
75032
|
+
overrides,
|
|
75033
|
+
only: void 0
|
|
75034
|
+
};
|
|
75035
|
+
}
|
|
75036
|
+
/**
|
|
74759
75037
|
* Selects the connection `channels()` should use for one provider.
|
|
74760
75038
|
*
|
|
74761
|
-
* - Pinned `connectionId` (from `
|
|
75039
|
+
* - Pinned `connectionId` (from `providers.<id>.connectionId`) wins, but is
|
|
74762
75040
|
* skipped if the pinned id isn't attached to the project or the connection
|
|
74763
75041
|
* isn't `active`.
|
|
74764
75042
|
* - Otherwise, the single active connection is used.
|
|
74765
75043
|
* - When more than one active connection exists, this warns naming the chosen
|
|
74766
75044
|
* id and picks the first active connection. There is no env-var fallback;
|
|
74767
|
-
* pin explicitly with `
|
|
75045
|
+
* pin explicitly with `providers.<id>.connectionId` when the deterministic
|
|
74768
75046
|
* choice matters.
|
|
74769
75047
|
*/
|
|
74770
75048
|
function selectChannelConnection(integrationId, pinnedId, candidates) {
|
|
@@ -74793,7 +75071,7 @@ function selectChannelConnection(integrationId, pinnedId, candidates) {
|
|
|
74793
75071
|
if (active.length === 1) return active[0].id;
|
|
74794
75072
|
const chosen = active[0].id;
|
|
74795
75073
|
const others = active.slice(1).map((connection) => connection.id).join(", ");
|
|
74796
|
-
console.warn(`[@mastra/connect] ${integrationId} channel: found ${active.length} active connections; using ${chosen}. Ignoring ${others}. Pin one with
|
|
75074
|
+
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.`);
|
|
74797
75075
|
return chosen;
|
|
74798
75076
|
}
|
|
74799
75077
|
//#endregion
|
|
@@ -74806,123 +75084,11 @@ async function credential(connectionId, options) {
|
|
|
74806
75084
|
return getCredential(resolveClient(options?.client), connectionId);
|
|
74807
75085
|
}
|
|
74808
75086
|
//#endregion
|
|
74809
|
-
//#region src/environment.ts
|
|
74810
|
-
function credentialToken(credential) {
|
|
74811
|
-
if (credential.type === "oauth2") return credential.accessToken;
|
|
74812
|
-
if (credential.type === "two_step") return credential.token;
|
|
74813
|
-
return credential.apiKey;
|
|
74814
|
-
}
|
|
74815
|
-
/**
|
|
74816
|
-
* Wires git's credential helper to read the token from the environment at use
|
|
74817
|
-
* time, so the secret lives in the process env rather than in git config or
|
|
74818
|
-
* remote URLs. `gh` needs no setup: it honors GH_TOKEN directly.
|
|
74819
|
-
*/
|
|
74820
|
-
const GITHUB_GIT_CREDENTIAL_HELPER = "!f() { echo username=x-access-token; echo \"password=$GH_TOKEN\"; }; f";
|
|
74821
|
-
/** Providers that know how to materialize sandbox credentials. */
|
|
74822
|
-
const ENVIRONMENT_CONTRIBUTORS = [{
|
|
74823
|
-
integrationId: "github",
|
|
74824
|
-
envVar: "MASTRA_GITHUB_CONNECTION_ID",
|
|
74825
|
-
build(credential) {
|
|
74826
|
-
const token = credentialToken(credential);
|
|
74827
|
-
return {
|
|
74828
|
-
env: {
|
|
74829
|
-
GH_TOKEN: token,
|
|
74830
|
-
GITHUB_TOKEN: token
|
|
74831
|
-
},
|
|
74832
|
-
onStart: async ({ sandbox }) => {
|
|
74833
|
-
if (!sandbox.executeCommand) {
|
|
74834
|
-
console.warn("[@mastra/connect] github environment: sandbox does not support executeCommand; skipping git credential setup (gh still works via GH_TOKEN).");
|
|
74835
|
-
return;
|
|
74836
|
-
}
|
|
74837
|
-
await sandbox.executeCommand("git", [
|
|
74838
|
-
"config",
|
|
74839
|
-
"--global",
|
|
74840
|
-
"credential.https://github.com.helper",
|
|
74841
|
-
GITHUB_GIT_CREDENTIAL_HELPER
|
|
74842
|
-
]);
|
|
74843
|
-
}
|
|
74844
|
-
};
|
|
74845
|
-
}
|
|
74846
|
-
}];
|
|
74847
|
-
/**
|
|
74848
|
-
* Resolves the project's Platform connections into sandbox credentials:
|
|
74849
|
-
* environment variables plus a start hook that finishes in-sandbox setup.
|
|
74850
|
-
* Currently GitHub-aware (`GH_TOKEN`/`GITHUB_TOKEN` + git credential helper);
|
|
74851
|
-
* more providers plug in over time.
|
|
74852
|
-
*
|
|
74853
|
-
* Connection resolution matches `connect()`: a per-integration `connectionId`
|
|
74854
|
-
* or the provider's `MASTRA_*_CONNECTION_ID` env var wins, else a single
|
|
74855
|
-
* active connection; ambiguity, needs_reauth, and credential-fetch failures
|
|
74856
|
-
* warn and skip that provider. Configuration errors (missing project id,
|
|
74857
|
-
* malformed integration id) throw here so they surface at startup.
|
|
74858
|
-
*
|
|
74859
|
-
* The returned `env` is a snapshot: call `environment()` again for a fresh
|
|
74860
|
-
* token when starting new sandboxes.
|
|
74861
|
-
*
|
|
74862
|
-
* @example
|
|
74863
|
-
* const { env, onStart } = await environment({ projectId });
|
|
74864
|
-
* const sandbox = new E2BSandbox({ env, onStart });
|
|
74865
|
-
* // in the sandbox: `gh pr list`, `git clone https://github.com/org/repo` just work
|
|
74866
|
-
*/
|
|
74867
|
-
async function environment(options = {}) {
|
|
74868
|
-
const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
|
|
74869
|
-
if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
|
|
74870
|
-
validateIntegrationOverrides(options.integrations);
|
|
74871
|
-
const client = resolveClient(options.client);
|
|
74872
|
-
const byIntegrationId = groupByIntegrationId(await listProjectConnections(client, projectId));
|
|
74873
|
-
const env = {};
|
|
74874
|
-
const hooks = [];
|
|
74875
|
-
for (const contributor of ENVIRONMENT_CONTRIBUTORS) {
|
|
74876
|
-
const integrationId = contributor.integrationId;
|
|
74877
|
-
const overrides = options.integrations?.[integrationId] ?? {};
|
|
74878
|
-
if (overrides.disabled) continue;
|
|
74879
|
-
const candidates = byIntegrationId.get(integrationId) ?? [];
|
|
74880
|
-
if (candidates.length === 0) continue;
|
|
74881
|
-
const connectionId = resolveConnection({
|
|
74882
|
-
integrationId,
|
|
74883
|
-
envVar: contributor.envVar,
|
|
74884
|
-
connectionId: overrides.connectionId
|
|
74885
|
-
}, candidates);
|
|
74886
|
-
if (!connectionId) continue;
|
|
74887
|
-
let credential;
|
|
74888
|
-
try {
|
|
74889
|
-
credential = await getCredential(client, connectionId);
|
|
74890
|
-
} catch (error) {
|
|
74891
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId} environment: ${error instanceof Error ? error.message : String(error)}`);
|
|
74892
|
-
continue;
|
|
74893
|
-
}
|
|
74894
|
-
const contribution = contributor.build(credential);
|
|
74895
|
-
for (const [key, value] of Object.entries(contribution.env)) {
|
|
74896
|
-
if (key in env) console.warn(`[@mastra/connect] ${integrationId} environment overwrites env var '${key}' set by an earlier provider.`);
|
|
74897
|
-
env[key] = value;
|
|
74898
|
-
}
|
|
74899
|
-
if (contribution.onStart) hooks.push({
|
|
74900
|
-
integrationId,
|
|
74901
|
-
hook: contribution.onStart
|
|
74902
|
-
});
|
|
74903
|
-
}
|
|
74904
|
-
const onStart = async (args) => {
|
|
74905
|
-
for (const { hook } of hooks) await hook(args);
|
|
74906
|
-
};
|
|
74907
|
-
return {
|
|
74908
|
-
env,
|
|
74909
|
-
onStart
|
|
74910
|
-
};
|
|
74911
|
-
}
|
|
74912
|
-
//#endregion
|
|
74913
75087
|
exports.CHANNELS = CHANNELS;
|
|
74914
75088
|
exports.MastraConnectError = MastraConnectError;
|
|
74915
75089
|
exports.PROVIDERS = PROVIDERS;
|
|
74916
|
-
exports.TOOLS = TOOLS;
|
|
74917
|
-
exports.applyAllowTools = applyAllowTools;
|
|
74918
75090
|
exports.channels = channels;
|
|
74919
|
-
exports.connect = connect;
|
|
74920
75091
|
exports.credential = credential;
|
|
74921
|
-
exports.defineProxyTool = defineProxyTool;
|
|
74922
|
-
exports.environment = environment;
|
|
74923
|
-
exports.findChannelRegistration = findChannelRegistration;
|
|
74924
|
-
exports.findRegistration = findRegistration;
|
|
74925
|
-
exports.resolveConnectionId = resolveConnectionId;
|
|
74926
75092
|
exports.tools = tools;
|
|
74927
75093
|
|
|
74928
75094
|
//# sourceMappingURL=index.cjs.map
|