@mastra/connect 0.3.1-alpha.0 → 0.4.0-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +22 -0
- package/dist/channels.d.ts +127 -0
- package/dist/channels.d.ts.map +1 -0
- package/dist/connect.d.ts +15 -59
- package/dist/connect.d.ts.map +1 -1
- package/dist/environment.d.ts +48 -0
- package/dist/environment.d.ts.map +1 -0
- package/dist/index.cjs +455 -43
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +450 -44
- package/dist/index.js.map +1 -1
- package/dist/providers/channel-provider.d.ts +32 -0
- package/dist/providers/channel-provider.d.ts.map +1 -0
- package/dist/providers/channels.d.ts +30 -0
- package/dist/providers/channels.d.ts.map +1 -0
- package/dist/registry.d.ts +13 -0
- package/dist/registry.d.ts.map +1 -1
- package/dist/resolution.d.ts +21 -0
- package/dist/resolution.d.ts.map +1 -0
- package/dist/tools.d.ts +66 -0
- package/dist/tools.d.ts.map +1 -0
- package/package.json +23 -6
- package/dist/connect.test-d.d.ts +0 -2
- package/dist/connect.test-d.d.ts.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
+
export { tools } from './tools.js';
|
|
2
|
+
export type { ToolsOptions, ToolsIntegrationOptions, ToolsResolver } from './tools.js';
|
|
1
3
|
export { connect } from './connect.js';
|
|
2
4
|
export type { ConnectOptions, ConnectIntegrationOptions, ConnectTools } from './connect.js';
|
|
5
|
+
export { channels } from './channels.js';
|
|
6
|
+
export type { ChannelsOptions, ChannelsIntegrationOptions, ChannelsResolver, ChannelsResolverContext, ResolvedChannels, } from './channels.js';
|
|
7
|
+
export type { ChannelProviderRegistration, ChannelBuildContext } from './providers/channel-provider.js';
|
|
3
8
|
export { credential } from './credential.js';
|
|
9
|
+
export { environment } from './environment.js';
|
|
10
|
+
export type { ConnectEnvironment, EnvironmentIntegrationOptions, EnvironmentOptions } from './environment.js';
|
|
4
11
|
export { MastraConnectError } from './errors.js';
|
|
5
12
|
export type { MastraConnectErrorCode } from './errors.js';
|
|
6
13
|
export type { ConnectClientOptions, ConnectionCredential, ProjectConnection } from './client.js';
|
|
7
14
|
export type { ProviderToolsOptions, ProxyToolConfig, ProxyToolContext } from './toolset.js';
|
|
8
15
|
export { defineProxyTool, resolveConnectionId, applyAllowTools } from './toolset.js';
|
|
9
|
-
export { PROVIDERS, findRegistration } from './registry.js';
|
|
16
|
+
export { TOOLS, PROVIDERS, CHANNELS, findRegistration, findChannelRegistration } from './registry.js';
|
|
10
17
|
export type { McpProviderRegistration, ProviderRegistration, ProxyProviderRegistration } from './registry.js';
|
|
11
18
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,SAAS,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AACvF,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AACvC,YAAY,EAAE,cAAc,EAAE,yBAAyB,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,YAAY,EACV,eAAe,EACf,0BAA0B,EAC1B,gBAAgB,EAChB,uBAAuB,EACvB,gBAAgB,GACjB,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,2BAA2B,EAAE,mBAAmB,EAAE,MAAM,iCAAiC,CAAC;AACxG,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,YAAY,EAAE,kBAAkB,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAC9G,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,YAAY,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC;AAC1D,YAAY,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACjG,YAAY,EAAE,oBAAoB,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAC5F,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,gBAAgB,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AACtG,YAAY,EAAE,uBAAuB,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -342,6 +342,106 @@ async function proxyRequestWithResponse(client, connectionId, options) {
|
|
|
342
342
|
}
|
|
343
343
|
}
|
|
344
344
|
//#endregion
|
|
345
|
+
//#region src/providers/channels.ts
|
|
346
|
+
function credentialToken$1(credential) {
|
|
347
|
+
return credential.type === "oauth2" ? credential.accessToken : credential.apiKey;
|
|
348
|
+
}
|
|
349
|
+
function missingPeerError(integrationId, packageName, error) {
|
|
350
|
+
return new MastraConnectError("invalid_options", `channels() cannot build '${integrationId}' provider: install '${packageName}' as a dependency of your app (${error instanceof Error ? error.message : String(error)}).`);
|
|
351
|
+
}
|
|
352
|
+
const RESERVED_OPTION_KEYS = {
|
|
353
|
+
slack: [
|
|
354
|
+
"baseUrl",
|
|
355
|
+
"refreshToken",
|
|
356
|
+
"token",
|
|
357
|
+
"encryptionKey"
|
|
358
|
+
],
|
|
359
|
+
telegram: [
|
|
360
|
+
"baseUrl",
|
|
361
|
+
"apiBaseUrl",
|
|
362
|
+
"botToken",
|
|
363
|
+
"encryptionKey"
|
|
364
|
+
],
|
|
365
|
+
discord: ["baseUrl", "encryptionKey"]
|
|
366
|
+
};
|
|
367
|
+
function stripReservedOptions(integrationId, options) {
|
|
368
|
+
if (!options) return options;
|
|
369
|
+
const reserved = RESERVED_OPTION_KEYS[integrationId];
|
|
370
|
+
if (!reserved) return options;
|
|
371
|
+
const stripped = [];
|
|
372
|
+
const filtered = {};
|
|
373
|
+
for (const [key, value] of Object.entries(options)) {
|
|
374
|
+
if (reserved.includes(key)) {
|
|
375
|
+
stripped.push(key);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
378
|
+
filtered[key] = value;
|
|
379
|
+
}
|
|
380
|
+
if (stripped.length > 0) console.warn(`[@mastra/connect] ${integrationId} channel: ignoring reserved providerOptions field(s): ${stripped.map((k) => `'${k}'`).join(", ")}. These are managed by the connection credential or the Mastra server config.`);
|
|
381
|
+
return filtered;
|
|
382
|
+
}
|
|
383
|
+
const CHANNELS$1 = [
|
|
384
|
+
{
|
|
385
|
+
integrationId: "slack",
|
|
386
|
+
async build(credential, options) {
|
|
387
|
+
const refreshToken = credentialToken$1(credential);
|
|
388
|
+
let mod;
|
|
389
|
+
try {
|
|
390
|
+
mod = await import("@mastra/slack");
|
|
391
|
+
} catch (error) {
|
|
392
|
+
throw missingPeerError("slack", "@mastra/slack", error);
|
|
393
|
+
}
|
|
394
|
+
const safeOptions = stripReservedOptions("slack", options);
|
|
395
|
+
return new mod.SlackProvider({
|
|
396
|
+
refreshToken,
|
|
397
|
+
...safeOptions ?? {}
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
},
|
|
401
|
+
{
|
|
402
|
+
integrationId: "telegram",
|
|
403
|
+
async build(credential, options) {
|
|
404
|
+
const botToken = credentialToken$1(credential);
|
|
405
|
+
let mod;
|
|
406
|
+
try {
|
|
407
|
+
mod = await import("@mastra/telegram");
|
|
408
|
+
} catch (error) {
|
|
409
|
+
throw missingPeerError("telegram", "@mastra/telegram", error);
|
|
410
|
+
}
|
|
411
|
+
const safeOptions = stripReservedOptions("telegram", options);
|
|
412
|
+
return new mod.TelegramProvider({
|
|
413
|
+
botToken,
|
|
414
|
+
...safeOptions ?? {}
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
{
|
|
419
|
+
integrationId: "discord",
|
|
420
|
+
async build(credential, options, context) {
|
|
421
|
+
const botToken = credentialToken$1(credential);
|
|
422
|
+
const metadata = context?.context?.metadata ?? {};
|
|
423
|
+
const applicationId = options?.applicationId ?? (typeof metadata.applicationId === "string" ? metadata.applicationId : void 0) ?? (typeof metadata.application_id === "string" ? metadata.application_id : void 0);
|
|
424
|
+
const publicKey = options?.publicKey ?? (typeof metadata.publicKey === "string" ? metadata.publicKey : void 0) ?? (typeof metadata.public_key === "string" ? metadata.public_key : void 0);
|
|
425
|
+
let mod;
|
|
426
|
+
try {
|
|
427
|
+
mod = await import("@mastra/discord");
|
|
428
|
+
} catch (error) {
|
|
429
|
+
throw missingPeerError("discord", "@mastra/discord", error);
|
|
430
|
+
}
|
|
431
|
+
if (!applicationId || !publicKey) console.warn(`[@mastra/connect] discord channel: missing ${[!applicationId && "applicationId", !publicKey && "publicKey"].filter(Boolean).join(" + ")} on the connection. Store them on the connection's metadata or pass them via integrations.discord.providerOptions.`);
|
|
432
|
+
const { applicationId: _optAppId, publicKey: _optPubKey, ...rest } = options ?? {};
|
|
433
|
+
return new mod.DiscordProvider({
|
|
434
|
+
app: {
|
|
435
|
+
botToken,
|
|
436
|
+
applicationId,
|
|
437
|
+
publicKey
|
|
438
|
+
},
|
|
439
|
+
...rest
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
];
|
|
444
|
+
//#endregion
|
|
345
445
|
//#region src/runtime/platform-proxy.ts
|
|
346
446
|
/** Maps the platform credential to the field names templates are written against. */
|
|
347
447
|
function toTemplateCredentials(credential) {
|
|
@@ -65717,7 +65817,7 @@ function createWorkosTools(options) {
|
|
|
65717
65817
|
* Providers with checked-in HTTP toolsets. MCP-backed providers are discovered
|
|
65718
65818
|
* from the Platform integration catalog at runtime.
|
|
65719
65819
|
*/
|
|
65720
|
-
const
|
|
65820
|
+
const TOOLS = [
|
|
65721
65821
|
anthropicProvider,
|
|
65722
65822
|
clerkProvider,
|
|
65723
65823
|
discordProvider,
|
|
@@ -65744,18 +65844,84 @@ const PROVIDERS = [
|
|
|
65744
65844
|
createTools: createWorkosTools
|
|
65745
65845
|
}
|
|
65746
65846
|
];
|
|
65847
|
+
/**
|
|
65848
|
+
* @deprecated Use `TOOLS`. This alias is retained for one release cycle so
|
|
65849
|
+
* downstream consumers keep working; it will be removed in the next minor.
|
|
65850
|
+
*/
|
|
65851
|
+
const PROVIDERS = TOOLS;
|
|
65747
65852
|
function findRegistration(integrationId) {
|
|
65748
|
-
return
|
|
65853
|
+
return TOOLS.find((p) => p.integrationId === integrationId);
|
|
65854
|
+
}
|
|
65855
|
+
/**
|
|
65856
|
+
* Channel-capable providers with a hand-maintained `channels()` registration.
|
|
65857
|
+
* The launch ships three (Slack, Telegram, Discord); the provider generator
|
|
65858
|
+
* grows to cover new entries when a fourth lands.
|
|
65859
|
+
*/
|
|
65860
|
+
const CHANNELS = CHANNELS$1;
|
|
65861
|
+
function findChannelRegistration(integrationId) {
|
|
65862
|
+
return CHANNELS.find((c) => c.integrationId === integrationId);
|
|
65749
65863
|
}
|
|
65750
65864
|
//#endregion
|
|
65751
|
-
//#region src/
|
|
65752
|
-
const
|
|
65865
|
+
//#region src/resolution.ts
|
|
65866
|
+
const INTEGRATION_ID_PATTERN = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
65867
|
+
function validateIntegrationOverrides(integrations) {
|
|
65868
|
+
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.`);
|
|
65869
|
+
}
|
|
65870
|
+
function connectionIdEnvVar(integrationId) {
|
|
65871
|
+
return `MASTRA_${integrationId.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_CONNECTION_ID`;
|
|
65872
|
+
}
|
|
65873
|
+
function groupByIntegrationId(connections) {
|
|
65874
|
+
const byIntegrationId = /* @__PURE__ */ new Map();
|
|
65875
|
+
for (const connection of connections) {
|
|
65876
|
+
const list = byIntegrationId.get(connection.integrationId) ?? [];
|
|
65877
|
+
list.push(connection);
|
|
65878
|
+
byIntegrationId.set(connection.integrationId, list);
|
|
65879
|
+
}
|
|
65880
|
+
return byIntegrationId;
|
|
65881
|
+
}
|
|
65882
|
+
/**
|
|
65883
|
+
* Resolves the connection to use for one provider, per the contract:
|
|
65884
|
+
* option/env var wins; else a single active connection; anything else
|
|
65885
|
+
* (ambiguity, needs_reauth, no usable candidate) warns and skips so one bad
|
|
65886
|
+
* integration never takes down the whole resolution. A needs_reauth
|
|
65887
|
+
* connection is never silently mapped.
|
|
65888
|
+
*/
|
|
65889
|
+
function resolveConnection(request, candidates) {
|
|
65890
|
+
const integrationId = request.integrationId;
|
|
65891
|
+
const directed = request.connectionId?.trim() || process.env[request.envVar]?.trim() || void 0;
|
|
65892
|
+
if (directed) {
|
|
65893
|
+
const match = candidates.find((connection) => connection.id === directed);
|
|
65894
|
+
if (!match) {
|
|
65895
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`);
|
|
65896
|
+
return;
|
|
65897
|
+
}
|
|
65898
|
+
if (match.status === "needs_reauth") {
|
|
65899
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);
|
|
65900
|
+
return;
|
|
65901
|
+
}
|
|
65902
|
+
if (match.status !== "active") {
|
|
65903
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`);
|
|
65904
|
+
return;
|
|
65905
|
+
}
|
|
65906
|
+
return directed;
|
|
65907
|
+
}
|
|
65908
|
+
const active = candidates.filter((connection) => connection.status === "active");
|
|
65909
|
+
if (active.length === 1) return active[0].id;
|
|
65910
|
+
if (active.length === 0) {
|
|
65911
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`);
|
|
65912
|
+
return;
|
|
65913
|
+
}
|
|
65914
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId}: ${active.length} active connections; pin one with connectionId or ${request.envVar}.`);
|
|
65915
|
+
}
|
|
65916
|
+
//#endregion
|
|
65917
|
+
//#region src/tools.ts
|
|
65918
|
+
const DEFAULT_TTL_MS$1 = 3e4;
|
|
65753
65919
|
/** Minimum wait after a failed platform fetch before another background revalidation. */
|
|
65754
|
-
const FAILURE_COOLDOWN_MS = 3e4;
|
|
65920
|
+
const FAILURE_COOLDOWN_MS$1 = 3e4;
|
|
65755
65921
|
let nextResolverId = 0;
|
|
65756
65922
|
/**
|
|
65757
65923
|
* Returns a live toolset resolver over the project's Platform connections.
|
|
65758
|
-
* HTTP providers are loaded from the shipped `
|
|
65924
|
+
* HTTP providers are loaded from the shipped `TOOLS` registry. MCP providers
|
|
65759
65925
|
* are discovered from the Platform integration catalog. Tools from every supported
|
|
65760
65926
|
* provider with a matching project connection are merged into one flat record
|
|
65761
65927
|
* (matched by `integrationId`). The resolver serves a cached snapshot,
|
|
@@ -65769,11 +65935,11 @@ let nextResolverId = 0;
|
|
|
65769
65935
|
* problems during resolution (needs re-auth or ambiguity) are downgraded to
|
|
65770
65936
|
* warn-and-skip so one bad integration never takes down the whole toolset.
|
|
65771
65937
|
*/
|
|
65772
|
-
function
|
|
65938
|
+
function tools(options = {}) {
|
|
65773
65939
|
const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
|
|
65774
65940
|
if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
|
|
65775
65941
|
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.`);
|
|
65776
|
-
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
65942
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS$1;
|
|
65777
65943
|
const client = resolveClient(options.client);
|
|
65778
65944
|
const resolverId = ++nextResolverId;
|
|
65779
65945
|
const mcpClients = /* @__PURE__ */ new Map();
|
|
@@ -65795,7 +65961,7 @@ function connect(options = {}) {
|
|
|
65795
65961
|
connections,
|
|
65796
65962
|
catalog: catalogResult.value
|
|
65797
65963
|
};
|
|
65798
|
-
const checkedIn = new Set(
|
|
65964
|
+
const checkedIn = new Set(TOOLS.map((registration) => registration.integrationId));
|
|
65799
65965
|
if (connections.some((connection) => connection.status === "active" && !checkedIn.has(connection.integrationId) && !options.integrations?.[connection.integrationId]?.disabled)) throw catalogResult.reason;
|
|
65800
65966
|
const reason = catalogResult.reason;
|
|
65801
65967
|
console.warn(`[@mastra/connect] Platform catalog unavailable (${reason instanceof Error ? reason.message : String(reason)}); resolving checked-in providers only.`);
|
|
@@ -65833,7 +65999,7 @@ function connect(options = {}) {
|
|
|
65833
65999
|
if (cache && Date.now() - cache.fetchedAt < ttlMs) return cache.snapshot;
|
|
65834
66000
|
if (cache) {
|
|
65835
66001
|
const staleSnapshot = cache.snapshot;
|
|
65836
|
-
if (!(lastFailureAt !== void 0 && Date.now() - lastFailureAt < FAILURE_COOLDOWN_MS) && !inflight && !closing) {
|
|
66002
|
+
if (!(lastFailureAt !== void 0 && Date.now() - lastFailureAt < FAILURE_COOLDOWN_MS$1) && !inflight && !closing) {
|
|
65837
66003
|
const staleFetchedAt = cache.fetchedAt;
|
|
65838
66004
|
refresh().catch((error) => {
|
|
65839
66005
|
console.warn(`[@mastra/connect] Keeping cached tools (fetched ${Date.now() - staleFetchedAt}ms ago); platform refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
@@ -65864,16 +66030,9 @@ function connect(options = {}) {
|
|
|
65864
66030
|
}
|
|
65865
66031
|
});
|
|
65866
66032
|
}
|
|
65867
|
-
function validateIntegrationOverrides(integrations) {
|
|
65868
|
-
const integrationIdPattern = /^[a-zA-Z0-9_-]{1,128}$/;
|
|
65869
|
-
for (const integrationId of Object.keys(integrations ?? {})) if (!integrationIdPattern.test(integrationId)) throw new MastraConnectError("invalid_options", `Invalid provider '${integrationId}' in integrations option: expected 1-128 letters, numbers, underscores, or hyphens.`);
|
|
65870
|
-
}
|
|
65871
|
-
function connectionIdEnvVar(integrationId) {
|
|
65872
|
-
return `MASTRA_${integrationId.toUpperCase().replace(/[^A-Z0-9]/g, "_")}_CONNECTION_ID`;
|
|
65873
|
-
}
|
|
65874
66033
|
function buildRequests(integrations, catalog) {
|
|
65875
66034
|
const overrides = integrations ?? {};
|
|
65876
|
-
const registrations = new Map(
|
|
66035
|
+
const registrations = new Map(TOOLS.map((registration) => [registration.integrationId, registration]));
|
|
65877
66036
|
const catalogIds = new Set(catalog.map((integration) => integration.id));
|
|
65878
66037
|
for (const integration of catalog) {
|
|
65879
66038
|
if (!integration.capabilities.mcp) continue;
|
|
@@ -65907,7 +66066,11 @@ async function mapTools(connections, requests, options, client, mcpClients, reso
|
|
|
65907
66066
|
try {
|
|
65908
66067
|
const candidates = byIntegrationId.get(integrationId) ?? [];
|
|
65909
66068
|
if (candidates.length === 0) continue;
|
|
65910
|
-
const connectionId =
|
|
66069
|
+
const connectionId = resolveConnection({
|
|
66070
|
+
integrationId,
|
|
66071
|
+
envVar: request.registration.envVar,
|
|
66072
|
+
connectionId: request.options.connectionId
|
|
66073
|
+
}, candidates);
|
|
65911
66074
|
if (!connectionId) continue;
|
|
65912
66075
|
if (request.registration.transport === "mcp") {
|
|
65913
66076
|
activeMcpIntegrations.add(integrationId);
|
|
@@ -65967,51 +66130,192 @@ async function discoverMcpTools(input) {
|
|
|
65967
66130
|
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(", ")}.`);
|
|
65968
66131
|
return applyAllowTools(discovery.tools, allowTools);
|
|
65969
66132
|
}
|
|
65970
|
-
|
|
65971
|
-
|
|
65972
|
-
|
|
65973
|
-
|
|
65974
|
-
|
|
65975
|
-
|
|
65976
|
-
|
|
65977
|
-
|
|
66133
|
+
//#endregion
|
|
66134
|
+
//#region src/connect.ts
|
|
66135
|
+
/**
|
|
66136
|
+
* @deprecated Use `tools` from `@mastra/connect` instead. This module is a
|
|
66137
|
+
* thin re-export retained for one release cycle so downstream consumers keep
|
|
66138
|
+
* working; it will be removed in the next minor.
|
|
66139
|
+
*/
|
|
66140
|
+
/**
|
|
66141
|
+
* @deprecated Renamed to {@link tools}.
|
|
66142
|
+
*
|
|
66143
|
+
* `connect(options)` is a drop-in alias for `tools(options)`. Prefer `tools`
|
|
66144
|
+
* from `@mastra/connect` in new code; this export will be removed in the next
|
|
66145
|
+
* minor version.
|
|
66146
|
+
*/
|
|
66147
|
+
function connect(options = {}) {
|
|
66148
|
+
return tools(options);
|
|
65978
66149
|
}
|
|
65979
|
-
|
|
65980
|
-
|
|
66150
|
+
//#endregion
|
|
66151
|
+
//#region src/channels.ts
|
|
66152
|
+
const DEFAULT_TTL_MS = 3e4;
|
|
66153
|
+
/** Minimum wait after a failed platform fetch before another background revalidation. */
|
|
66154
|
+
const FAILURE_COOLDOWN_MS = 3e4;
|
|
66155
|
+
/**
|
|
66156
|
+
* Returns a live channels resolver over the project's Platform connections.
|
|
66157
|
+
* Channel-capable integrations (Slack, Telegram, Discord) with an active
|
|
66158
|
+
* project connection are materialized into `ChannelProvider` instances ready
|
|
66159
|
+
* to hand to `new Mastra({ channels: await channels({...}) })`.
|
|
66160
|
+
*
|
|
66161
|
+
* Configuration errors (missing project id, bad ttlMs, malformed integration
|
|
66162
|
+
* id) throw at call time so they surface at startup. Actionable per-integration
|
|
66163
|
+
* problems during resolution (needs re-auth, ambiguity, missing peer package)
|
|
66164
|
+
* are downgraded to warn-and-skip so one bad integration never takes down the
|
|
66165
|
+
* whole map.
|
|
66166
|
+
*
|
|
66167
|
+
* @example
|
|
66168
|
+
* ```ts
|
|
66169
|
+
* import { Mastra } from '@mastra/core/mastra';
|
|
66170
|
+
* import { channels } from '@mastra/connect';
|
|
66171
|
+
*
|
|
66172
|
+
* export const mastra = new Mastra({
|
|
66173
|
+
* agents: { pat },
|
|
66174
|
+
* channels: await channels({ projectId }),
|
|
66175
|
+
* });
|
|
66176
|
+
* ```
|
|
66177
|
+
*/
|
|
66178
|
+
function channels(options = {}) {
|
|
66179
|
+
const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
|
|
66180
|
+
if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
|
|
66181
|
+
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.`);
|
|
66182
|
+
validateIntegrationOverrides(options.integrations);
|
|
66183
|
+
const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
|
|
66184
|
+
const client = resolveClient(options.client);
|
|
66185
|
+
let cache;
|
|
66186
|
+
let inflight;
|
|
66187
|
+
let lastFailureAt;
|
|
66188
|
+
const buildSnapshot = async () => {
|
|
66189
|
+
let connections;
|
|
66190
|
+
try {
|
|
66191
|
+
connections = await listProjectConnections(client, projectId);
|
|
66192
|
+
} catch (error) {
|
|
66193
|
+
lastFailureAt = Date.now();
|
|
66194
|
+
throw error;
|
|
66195
|
+
}
|
|
66196
|
+
const byIntegrationId = groupByIntegrationId(connections);
|
|
66197
|
+
const providers = {};
|
|
66198
|
+
for (const registration of CHANNELS) {
|
|
66199
|
+
const integrationId = registration.integrationId;
|
|
66200
|
+
const overrides = options.integrations?.[integrationId] ?? {};
|
|
66201
|
+
if (overrides.disabled) continue;
|
|
66202
|
+
const candidates = byIntegrationId.get(integrationId) ?? [];
|
|
66203
|
+
if (candidates.length === 0) continue;
|
|
66204
|
+
const connectionId = selectChannelConnection(integrationId, overrides.connectionId, candidates);
|
|
66205
|
+
if (!connectionId) continue;
|
|
66206
|
+
const provider = await buildProvider(registration, connectionId, overrides, client);
|
|
66207
|
+
if (!provider) continue;
|
|
66208
|
+
providers[integrationId] = provider;
|
|
66209
|
+
}
|
|
66210
|
+
lastFailureAt = void 0;
|
|
66211
|
+
return providers;
|
|
66212
|
+
};
|
|
66213
|
+
const refresh = () => {
|
|
66214
|
+
if (!inflight) inflight = (async () => {
|
|
66215
|
+
try {
|
|
66216
|
+
const providers = await buildSnapshot();
|
|
66217
|
+
cache = {
|
|
66218
|
+
providers,
|
|
66219
|
+
fetchedAt: Date.now()
|
|
66220
|
+
};
|
|
66221
|
+
return providers;
|
|
66222
|
+
} finally {
|
|
66223
|
+
inflight = void 0;
|
|
66224
|
+
}
|
|
66225
|
+
})();
|
|
66226
|
+
return inflight;
|
|
66227
|
+
};
|
|
66228
|
+
const resolve = async () => {
|
|
66229
|
+
if (cache && Date.now() - cache.fetchedAt < ttlMs) return cache.providers;
|
|
66230
|
+
if (cache) {
|
|
66231
|
+
const stale = cache.providers;
|
|
66232
|
+
if (!(lastFailureAt !== void 0 && Date.now() - lastFailureAt < FAILURE_COOLDOWN_MS) && !inflight) {
|
|
66233
|
+
const staleFetchedAt = cache.fetchedAt;
|
|
66234
|
+
refresh().catch((error) => {
|
|
66235
|
+
console.warn(`[@mastra/connect] Keeping cached channels (fetched ${Date.now() - staleFetchedAt}ms ago); platform refresh failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
66236
|
+
});
|
|
66237
|
+
}
|
|
66238
|
+
return stale;
|
|
66239
|
+
}
|
|
66240
|
+
return refresh();
|
|
66241
|
+
};
|
|
66242
|
+
const invocable = ((_context) => resolve());
|
|
66243
|
+
invocable.invalidate = () => {
|
|
66244
|
+
cache = void 0;
|
|
66245
|
+
};
|
|
66246
|
+
invocable.refresh = refresh;
|
|
66247
|
+
invocable.disconnect = async () => {
|
|
66248
|
+
cache = void 0;
|
|
66249
|
+
};
|
|
66250
|
+
invocable.then = ((onfulfilled, onrejected) => resolve().then(onfulfilled, onrejected));
|
|
66251
|
+
return invocable;
|
|
66252
|
+
}
|
|
66253
|
+
async function buildProvider(registration, connectionId, overrides, client) {
|
|
66254
|
+
let credential;
|
|
66255
|
+
try {
|
|
66256
|
+
credential = await getCredential(client, connectionId);
|
|
66257
|
+
} catch (error) {
|
|
66258
|
+
console.warn(`[@mastra/connect] Skipping ${registration.integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
|
|
66259
|
+
return;
|
|
66260
|
+
}
|
|
66261
|
+
let context = {
|
|
66262
|
+
connectionId,
|
|
66263
|
+
client
|
|
66264
|
+
};
|
|
66265
|
+
try {
|
|
66266
|
+
context = {
|
|
66267
|
+
connectionId,
|
|
66268
|
+
client,
|
|
66269
|
+
context: await getConnectionContext(client, connectionId)
|
|
66270
|
+
};
|
|
66271
|
+
} catch {}
|
|
66272
|
+
try {
|
|
66273
|
+
return await registration.build(credential, overrides.providerOptions ?? {}, context);
|
|
66274
|
+
} catch (error) {
|
|
66275
|
+
console.warn(`[@mastra/connect] Skipping ${registration.integrationId} channel: ${error instanceof Error ? error.message : String(error)}`);
|
|
66276
|
+
return;
|
|
66277
|
+
}
|
|
65981
66278
|
}
|
|
65982
66279
|
/**
|
|
65983
|
-
*
|
|
65984
|
-
*
|
|
65985
|
-
*
|
|
65986
|
-
*
|
|
65987
|
-
*
|
|
66280
|
+
* Selects the connection `channels()` should use for one provider.
|
|
66281
|
+
*
|
|
66282
|
+
* - Pinned `connectionId` (from `integrations.<id>.connectionId`) wins, but is
|
|
66283
|
+
* skipped if the pinned id isn't attached to the project or the connection
|
|
66284
|
+
* isn't `active`.
|
|
66285
|
+
* - Otherwise, the single active connection is used.
|
|
66286
|
+
* - When more than one active connection exists, this warns naming the chosen
|
|
66287
|
+
* id and picks the first active connection. There is no env-var fallback;
|
|
66288
|
+
* pin explicitly with `integrations.<id>.connectionId` when the deterministic
|
|
66289
|
+
* choice matters.
|
|
65988
66290
|
*/
|
|
65989
|
-
function
|
|
65990
|
-
const
|
|
65991
|
-
const directed = request.options.connectionId?.trim() || readEnvConnectionId(request.registration);
|
|
66291
|
+
function selectChannelConnection(integrationId, pinnedId, candidates) {
|
|
66292
|
+
const directed = pinnedId?.trim() || void 0;
|
|
65992
66293
|
if (directed) {
|
|
65993
66294
|
const match = candidates.find((connection) => connection.id === directed);
|
|
65994
66295
|
if (!match) {
|
|
65995
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`);
|
|
66296
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId} channel: pinned connection ${directed} is not attached to this project.`);
|
|
65996
66297
|
return;
|
|
65997
66298
|
}
|
|
65998
66299
|
if (match.status === "needs_reauth") {
|
|
65999
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);
|
|
66300
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId} channel: connection ${directed} needs re-auth.`);
|
|
66000
66301
|
return;
|
|
66001
66302
|
}
|
|
66002
66303
|
if (match.status !== "active") {
|
|
66003
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`);
|
|
66304
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId} channel: connection ${directed} is not active (status '${match.status}').`);
|
|
66004
66305
|
return;
|
|
66005
66306
|
}
|
|
66006
66307
|
return directed;
|
|
66007
66308
|
}
|
|
66008
66309
|
const active = candidates.filter((connection) => connection.status === "active");
|
|
66009
|
-
if (active.length === 1) return active[0].id;
|
|
66010
66310
|
if (active.length === 0) {
|
|
66011
|
-
console.warn(`[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`);
|
|
66311
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId} channel: no active connections (found ${candidates.length} in other states).`);
|
|
66012
66312
|
return;
|
|
66013
66313
|
}
|
|
66014
|
-
|
|
66314
|
+
if (active.length === 1) return active[0].id;
|
|
66315
|
+
const chosen = active[0].id;
|
|
66316
|
+
const others = active.slice(1).map((connection) => connection.id).join(", ");
|
|
66317
|
+
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.`);
|
|
66318
|
+
return chosen;
|
|
66015
66319
|
}
|
|
66016
66320
|
//#endregion
|
|
66017
66321
|
//#region src/credential.ts
|
|
@@ -66023,6 +66327,108 @@ async function credential(connectionId, options) {
|
|
|
66023
66327
|
return getCredential(resolveClient(options?.client), connectionId);
|
|
66024
66328
|
}
|
|
66025
66329
|
//#endregion
|
|
66026
|
-
|
|
66330
|
+
//#region src/environment.ts
|
|
66331
|
+
function credentialToken(credential) {
|
|
66332
|
+
return credential.type === "oauth2" ? credential.accessToken : credential.apiKey;
|
|
66333
|
+
}
|
|
66334
|
+
/**
|
|
66335
|
+
* Wires git's credential helper to read the token from the environment at use
|
|
66336
|
+
* time, so the secret lives in the process env rather than in git config or
|
|
66337
|
+
* remote URLs. `gh` needs no setup: it honors GH_TOKEN directly.
|
|
66338
|
+
*/
|
|
66339
|
+
const GITHUB_GIT_CREDENTIAL_HELPER = "!f() { echo username=x-access-token; echo \"password=$GH_TOKEN\"; }; f";
|
|
66340
|
+
/** Providers that know how to materialize sandbox credentials. */
|
|
66341
|
+
const ENVIRONMENT_CONTRIBUTORS = [{
|
|
66342
|
+
integrationId: "github",
|
|
66343
|
+
envVar: "MASTRA_GITHUB_CONNECTION_ID",
|
|
66344
|
+
build(credential) {
|
|
66345
|
+
const token = credentialToken(credential);
|
|
66346
|
+
return {
|
|
66347
|
+
env: {
|
|
66348
|
+
GH_TOKEN: token,
|
|
66349
|
+
GITHUB_TOKEN: token
|
|
66350
|
+
},
|
|
66351
|
+
onStart: async ({ sandbox }) => {
|
|
66352
|
+
if (!sandbox.executeCommand) {
|
|
66353
|
+
console.warn("[@mastra/connect] github environment: sandbox does not support executeCommand; skipping git credential setup (gh still works via GH_TOKEN).");
|
|
66354
|
+
return;
|
|
66355
|
+
}
|
|
66356
|
+
await sandbox.executeCommand("git", [
|
|
66357
|
+
"config",
|
|
66358
|
+
"--global",
|
|
66359
|
+
"credential.https://github.com.helper",
|
|
66360
|
+
GITHUB_GIT_CREDENTIAL_HELPER
|
|
66361
|
+
]);
|
|
66362
|
+
}
|
|
66363
|
+
};
|
|
66364
|
+
}
|
|
66365
|
+
}];
|
|
66366
|
+
/**
|
|
66367
|
+
* Resolves the project's Platform connections into sandbox credentials:
|
|
66368
|
+
* environment variables plus a start hook that finishes in-sandbox setup.
|
|
66369
|
+
* Currently GitHub-aware (`GH_TOKEN`/`GITHUB_TOKEN` + git credential helper);
|
|
66370
|
+
* more providers plug in over time.
|
|
66371
|
+
*
|
|
66372
|
+
* Connection resolution matches `connect()`: a per-integration `connectionId`
|
|
66373
|
+
* or the provider's `MASTRA_*_CONNECTION_ID` env var wins, else a single
|
|
66374
|
+
* active connection; ambiguity, needs_reauth, and credential-fetch failures
|
|
66375
|
+
* warn and skip that provider. Configuration errors (missing project id,
|
|
66376
|
+
* malformed integration id) throw here so they surface at startup.
|
|
66377
|
+
*
|
|
66378
|
+
* The returned `env` is a snapshot: call `environment()` again for a fresh
|
|
66379
|
+
* token when starting new sandboxes.
|
|
66380
|
+
*
|
|
66381
|
+
* @example
|
|
66382
|
+
* const { env, onStart } = await environment({ projectId });
|
|
66383
|
+
* const sandbox = new E2BSandbox({ env, onStart });
|
|
66384
|
+
* // in the sandbox: `gh pr list`, `git clone https://github.com/org/repo` just work
|
|
66385
|
+
*/
|
|
66386
|
+
async function environment(options = {}) {
|
|
66387
|
+
const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
|
|
66388
|
+
if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
|
|
66389
|
+
validateIntegrationOverrides(options.integrations);
|
|
66390
|
+
const client = resolveClient(options.client);
|
|
66391
|
+
const byIntegrationId = groupByIntegrationId(await listProjectConnections(client, projectId));
|
|
66392
|
+
const env = {};
|
|
66393
|
+
const hooks = [];
|
|
66394
|
+
for (const contributor of ENVIRONMENT_CONTRIBUTORS) {
|
|
66395
|
+
const integrationId = contributor.integrationId;
|
|
66396
|
+
const overrides = options.integrations?.[integrationId] ?? {};
|
|
66397
|
+
if (overrides.disabled) continue;
|
|
66398
|
+
const candidates = byIntegrationId.get(integrationId) ?? [];
|
|
66399
|
+
if (candidates.length === 0) continue;
|
|
66400
|
+
const connectionId = resolveConnection({
|
|
66401
|
+
integrationId,
|
|
66402
|
+
envVar: contributor.envVar,
|
|
66403
|
+
connectionId: overrides.connectionId
|
|
66404
|
+
}, candidates);
|
|
66405
|
+
if (!connectionId) continue;
|
|
66406
|
+
let credential;
|
|
66407
|
+
try {
|
|
66408
|
+
credential = await getCredential(client, connectionId);
|
|
66409
|
+
} catch (error) {
|
|
66410
|
+
console.warn(`[@mastra/connect] Skipping ${integrationId} environment: ${error instanceof Error ? error.message : String(error)}`);
|
|
66411
|
+
continue;
|
|
66412
|
+
}
|
|
66413
|
+
const contribution = contributor.build(credential);
|
|
66414
|
+
for (const [key, value] of Object.entries(contribution.env)) {
|
|
66415
|
+
if (key in env) console.warn(`[@mastra/connect] ${integrationId} environment overwrites env var '${key}' set by an earlier provider.`);
|
|
66416
|
+
env[key] = value;
|
|
66417
|
+
}
|
|
66418
|
+
if (contribution.onStart) hooks.push({
|
|
66419
|
+
integrationId,
|
|
66420
|
+
hook: contribution.onStart
|
|
66421
|
+
});
|
|
66422
|
+
}
|
|
66423
|
+
const onStart = async (args) => {
|
|
66424
|
+
for (const { hook } of hooks) await hook(args);
|
|
66425
|
+
};
|
|
66426
|
+
return {
|
|
66427
|
+
env,
|
|
66428
|
+
onStart
|
|
66429
|
+
};
|
|
66430
|
+
}
|
|
66431
|
+
//#endregion
|
|
66432
|
+
export { CHANNELS, MastraConnectError, PROVIDERS, TOOLS, applyAllowTools, channels, connect, credential, defineProxyTool, environment, findChannelRegistration, findRegistration, resolveConnectionId, tools };
|
|
66027
66433
|
|
|
66028
66434
|
//# sourceMappingURL=index.js.map
|