@mastra/connect 0.0.0 → 0.1.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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.cjs","names":["z"],"sources":["../src/errors.ts","../src/client.ts","../src/providers/index.ts","../src/registry.ts","../src/connect.ts","../src/credential.ts","../src/toolset.ts"],"sourcesContent":["export type MastraConnectErrorCode =\n | 'missing_access_token'\n | 'missing_project_id'\n | 'missing_connection_id'\n | 'invalid_options'\n | 'connection_not_found'\n | 'unauthorized'\n | 'proxy_error'\n | 'unsupported_credential_type'\n | 'platform_error';\n\nconst MAX_DETAIL_LENGTH = 2000;\n\nexport class MastraConnectError extends Error {\n readonly code: MastraConnectErrorCode;\n readonly status?: number;\n readonly detail?: string;\n\n constructor(code: MastraConnectErrorCode, message: string, options?: { status?: number; detail?: string }) {\n super(message);\n this.name = 'MastraConnectError';\n this.code = code;\n this.status = options?.status;\n this.detail = options?.detail ? truncate(options.detail) : undefined;\n }\n}\n\nfunction truncate(text: string): string {\n return text.length > MAX_DETAIL_LENGTH ? `${text.slice(0, MAX_DETAIL_LENGTH)}…` : text;\n}\n\ninterface ProblemJson {\n title?: string;\n status?: number;\n detail?: string;\n code?: string;\n error?: string;\n}\n\n/**\n * Extracts a human-readable detail string from an RFC-7807 problem JSON body\n * (or a plain `{ error }` body) without echoing anything else from the response.\n * Returns undefined when the body is not parseable JSON.\n */\nexport async function extractProblemDetail(\n response: Response,\n): Promise<{ detail?: string; code?: string; isProblemJson: boolean }> {\n const contentType = response.headers.get('content-type') ?? '';\n // Platform-originated errors are identified strictly by the RFC-7807 content\n // type. A provider error body that merely *looks* problem-shaped (e.g.\n // ASP.NET ProblemDetails served as application/json) must not be\n // misclassified as a platform error.\n const isProblemJson = contentType.includes('application/problem+json');\n try {\n const data = (await response.clone().json()) as ProblemJson;\n if (data && typeof data === 'object') {\n const code = typeof data.code === 'string' ? data.code : undefined;\n for (const field of ['detail', 'title', 'error'] as const) {\n const value = data[field];\n if (typeof value === 'string' && value) {\n return { detail: truncate(value), code, isProblemJson };\n }\n }\n return { code, isProblemJson };\n }\n } catch {\n // Non-JSON body: fall through without echoing it.\n }\n return { isProblemJson };\n}\n","import { z } from 'zod';\n\nimport { extractProblemDetail, MastraConnectError } from './errors.js';\n\n/**\n * Configuration for talking to the Mastra platform integrations service.\n * Every field falls back to an environment variable, so a fully env-configured\n * app can pass nothing at all.\n */\nexport interface ConnectClientOptions {\n /** Platform access token. Falls back to MASTRA_PLATFORM_ACCESS_TOKEN, then MASTRA_PLATFORM_SECRET_KEY. */\n accessToken?: string;\n /** Organization id. Falls back to MASTRA_ORG_ID; the x-organization-id header is omitted when absent. */\n orgId?: string;\n /**\n * Integrations service base URL. Falls back to MASTRA_INTEGRATIONS_API_URL,\n * then the regional default from MASTRA_PLATFORM_REGION ('us' | 'eu'),\n * then https://integrations.mastra.ai.\n */\n baseUrl?: string;\n /** Fetch implementation, for testing or custom transports. Defaults to globalThis.fetch. */\n fetch?: typeof globalThis.fetch;\n}\n\nexport interface ResolvedClient {\n baseUrl: string;\n headers: Record<string, string>;\n fetch: typeof globalThis.fetch;\n accessToken: string;\n}\n\nconst DEFAULT_INTEGRATIONS_URL = 'https://integrations.mastra.ai';\nconst REGIONAL_INTEGRATIONS_URLS: Record<'us' | 'eu', string> = {\n us: 'https://integrations.us.mastra.ai',\n eu: 'https://integrations.eu.mastra.ai',\n};\n\n/**\n * Mirrors the URL conventions of the platform factory api-client\n * (`MASTRA_PLATFORM_REGION` case-insensitive 'us'/'eu'; unknown regions fall\n * through to the global default).\n */\nfunction resolveIntegrationsUrl(): string {\n const region = process.env.MASTRA_PLATFORM_REGION?.trim().toLowerCase();\n if (region === 'us' || region === 'eu') return REGIONAL_INTEGRATIONS_URLS[region];\n return DEFAULT_INTEGRATIONS_URL;\n}\n\n// Written as a loop rather than /\\/+$/ so a hostile baseUrl of many slashes\n// cannot trigger polynomial regex backtracking (CodeQL js/polynomial-redos).\nfunction stripTrailingSlashes(url: string): string {\n let end = url.length;\n while (end > 0 && url[end - 1] === '/') end--;\n return url.slice(0, end);\n}\n\nexport function resolveClient(options?: ConnectClientOptions): ResolvedClient {\n const accessToken =\n options?.accessToken?.trim() ||\n process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() ||\n process.env.MASTRA_PLATFORM_SECRET_KEY?.trim();\n if (!accessToken) {\n throw new MastraConnectError(\n 'missing_access_token',\n 'Missing Mastra platform access token: set MASTRA_PLATFORM_ACCESS_TOKEN (or MASTRA_PLATFORM_SECRET_KEY), or pass client.accessToken.',\n );\n }\n\n const baseUrl = stripTrailingSlashes(\n options?.baseUrl?.trim() || process.env.MASTRA_INTEGRATIONS_API_URL?.trim() || resolveIntegrationsUrl(),\n );\n\n const headers: Record<string, string> = {\n accept: 'application/json',\n authorization: `Bearer ${accessToken}`,\n };\n const orgId = options?.orgId?.trim() || process.env.MASTRA_ORG_ID?.trim();\n if (orgId) {\n headers['x-organization-id'] = orgId;\n }\n\n return { baseUrl, headers, fetch: options?.fetch ?? globalThis.fetch, accessToken };\n}\n\nfunction redact(message: string, accessToken: string): string {\n return message.split(accessToken).join('[REDACTED]');\n}\n\nasync function platformFetch(client: ResolvedClient, path: string, init?: RequestInit): Promise<Response> {\n try {\n return await client.fetch(`${client.baseUrl}${path}`, {\n ...init,\n headers: { ...client.headers, ...(init?.headers as Record<string, string> | undefined) },\n });\n } catch (error) {\n if (error instanceof Error && error.message.includes(client.accessToken)) {\n const redacted = new Error(redact(error.message, client.accessToken));\n redacted.name = error.name;\n throw redacted;\n }\n throw error;\n }\n}\n\n/** Parses a successful platform response body, mapping invalid JSON to the platform_error contract. */\nasync function parsePlatformJson(response: Response, context: string): Promise<unknown> {\n try {\n return await response.json();\n } catch {\n throw new MastraConnectError('platform_error', `Platform returned an unparseable response body while ${context}.`);\n }\n}\n\n/** Maps a non-2xx response from the platform's own endpoints to a typed error. */\nasync function throwPlatformError(response: Response, context: string): Promise<never> {\n const { detail, code } = await extractProblemDetail(response);\n if (response.status === 401 || response.status === 403) {\n throw new MastraConnectError('unauthorized', `Unauthorized while ${context}${detail ? `: ${detail}` : '.'}`, {\n status: response.status,\n detail,\n });\n }\n if (response.status === 404) {\n throw new MastraConnectError('connection_not_found', `Not found while ${context}${detail ? `: ${detail}` : '.'}`, {\n status: response.status,\n detail,\n });\n }\n if (code === 'unsupported_credential_type') {\n throw new MastraConnectError(\n 'unsupported_credential_type',\n `Unsupported credential type while ${context}${detail ? `: ${detail}` : '.'}`,\n { status: response.status, detail },\n );\n }\n throw new MastraConnectError(\n 'platform_error',\n `Platform request failed (${response.status}) while ${context}${detail ? `: ${detail}` : '.'}`,\n { status: response.status, detail },\n );\n}\n\n// —— response schemas (mirroring the platform's http-schemas) ——\n\nexport const connectionSchema = z.object({\n id: z.string(),\n integrationId: z.string(),\n // Lenient on purpose: an unknown future status must not fail the whole\n // connection list — resolution treats anything but 'active'/'needs_reauth'\n // as unusable.\n status: z.string(),\n connectedByUserId: z.string().nullish(),\n connectedAt: z.string().nullish(),\n createdAt: z.string().nullish(),\n accountLabel: z.string().nullish(),\n});\n\nexport type ProjectConnection = z.infer<typeof connectionSchema>;\n\nconst connectionListSchema = z.object({\n connections: z.array(connectionSchema),\n});\n\nexport const credentialSchema = z.discriminatedUnion('type', [\n z.object({ type: z.literal('oauth2'), accessToken: z.string(), expiresAt: z.string().nullable() }),\n z.object({ type: z.literal('api_key'), apiKey: z.string() }),\n]);\n\nexport type ConnectionCredential = z.infer<typeof credentialSchema>;\n\n// —— endpoint functions ——\n\nexport async function listProjectConnections(client: ResolvedClient, projectId: string): Promise<ProjectConnection[]> {\n const response = await platformFetch(client, `/v2/projects/${encodeURIComponent(projectId)}/connections`);\n if (!response.ok) {\n await throwPlatformError(response, `listing connections for project ${projectId}`);\n }\n const parsed = connectionListSchema.safeParse(\n await parsePlatformJson(response, `listing connections for project ${projectId}`),\n );\n if (!parsed.success) {\n throw new MastraConnectError(\n 'platform_error',\n `Platform returned an unexpected connection list shape for project ${projectId}.`,\n );\n }\n return parsed.data.connections;\n}\n\nexport async function getCredential(client: ResolvedClient, connectionId: string): Promise<ConnectionCredential> {\n const response = await platformFetch(client, `/v2/connections/${encodeURIComponent(connectionId)}/credentials`);\n if (!response.ok) {\n await throwPlatformError(response, `fetching credentials for connection ${connectionId}`);\n }\n const parsed = credentialSchema.safeParse(\n await parsePlatformJson(response, `fetching credentials for connection ${connectionId}`),\n );\n if (!parsed.success) {\n throw new MastraConnectError(\n 'unsupported_credential_type',\n `Platform returned an unsupported credential type for connection ${connectionId}.`,\n );\n }\n return parsed.data;\n}\n\nexport interface ProxyRequestOptions {\n method: 'GET' | 'HEAD' | 'POST' | 'PUT' | 'PATCH' | 'DELETE';\n /** Provider-relative path (no leading slash required; dot segments are rejected client-side, absolute URLs by the platform). */\n path: string;\n query?: Record<string, string | number | boolean | undefined>;\n headers?: Record<string, string>;\n body?: unknown;\n}\n\n/**\n * Sends a request through the platform connection proxy and returns the\n * provider's parsed JSON on 2xx.\n *\n * Error-mapping scope: 401/404 map to `unauthorized`/`connection_not_found`\n * only when the response carries a platform RFC-7807 problem body — a\n * provider's own 401/404 passed through the proxy stays `proxy_error` with the\n * provider status attached.\n */\n/**\n * True when any path segment is a literal or percent-encoded dot segment.\n * fetch/URL normalizes `..` (and `%2e%2e` variants) *before* the request is\n * sent, which would let a crafted path escape the connection-proxy prefix and\n * hit an arbitrary platform route with the caller's bearer token.\n */\nfunction hasDotSegment(path: string): boolean {\n return path.split('/').some(segment => {\n let decoded: string;\n try {\n decoded = decodeURIComponent(segment);\n } catch {\n // Malformed percent-encoding: reject rather than guess what the\n // URL parser would do with it.\n return true;\n }\n return decoded === '.' || decoded === '..';\n });\n}\n\nexport async function proxyRequest(\n client: ResolvedClient,\n connectionId: string,\n options: ProxyRequestOptions,\n): Promise<unknown> {\n const cleanPath = options.path.replace(/^\\/+/, '');\n if (hasDotSegment(cleanPath)) {\n throw new MastraConnectError(\n 'invalid_options',\n `Invalid proxy path '${options.path}': dot segments are not allowed.`,\n );\n }\n const search = new URLSearchParams();\n for (const [key, value] of Object.entries(options.query ?? {})) {\n if (value !== undefined) search.set(key, String(value));\n }\n const queryString = search.size > 0 ? `?${search.toString()}` : '';\n const url = `/v2/connections/${encodeURIComponent(connectionId)}/proxy/${cleanPath}${queryString}`;\n\n const headers: Record<string, string> = { ...options.headers };\n const init: RequestInit = { method: options.method, headers };\n if (options.body !== undefined) {\n headers['content-type'] = 'application/json';\n init.body = JSON.stringify(options.body);\n }\n\n const response = await platformFetch(client, url, init);\n\n if (!response.ok) {\n const { detail, isProblemJson } = await extractProblemDetail(response);\n if (isProblemJson) {\n // Platform-originated error (bad path, unknown connection, auth, limits).\n if (response.status === 401 || response.status === 403) {\n throw new MastraConnectError(\n 'unauthorized',\n `Unauthorized calling the connection proxy${detail ? `: ${detail}` : '.'}`,\n { status: response.status, detail },\n );\n }\n if (response.status === 404) {\n throw new MastraConnectError(\n 'connection_not_found',\n `Connection ${connectionId} not found${detail ? `: ${detail}` : '.'}`,\n { status: response.status, detail },\n );\n }\n throw new MastraConnectError(\n 'proxy_error',\n `Proxy request failed (${response.status})${detail ? `: ${detail}` : '.'}`,\n {\n status: response.status,\n detail,\n },\n );\n }\n // Provider-originated error passed through the proxy.\n throw new MastraConnectError(\n 'proxy_error',\n `Provider request failed (${response.status})${detail ? `: ${detail}` : '.'}`,\n { status: response.status, detail },\n );\n }\n\n if (response.status === 204) return null;\n const text = await response.text();\n if (!text) return null;\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n","// AUTO-GENERATED — do not edit by hand.\n// Updated by the maintainer-only add-provider and remove-provider commands.\nimport type { ProviderRegistration } from '../registry.js';\n\nexport const PROVIDERS: readonly ProviderRegistration[] = [];\n","import type { ToolsInput } from '@mastra/core/agent';\n\nimport { PROVIDERS } from './providers/index.js';\nimport type { ProviderToolsOptions } from './toolset.js';\n\n/**\n * A provider registration. One entry per provider directory generated under\n * `packages/connect/src/providers/<integrationId>/` by the maintainer-only\n * `pnpm --filter @mastra/connect add-provider` command.\n *\n * `integrationId` is the Platform catalog id and the provider directory name.\n * Provider matching against project connections is by `integrationId` only;\n * `connect()` merges each matching provider's tools into one flat record.\n *\n * `envVar` is the fallback connection-id env var read at execute time when\n * no `connectionId` override is given and more than one active connection\n * exists on the project for this provider.\n */\nexport interface ProviderRegistration {\n integrationId: string;\n envVar: string;\n createTools: (options?: ProviderToolsOptions) => ToolsInput;\n}\n\n/**\n * Providers with shipped toolsets. The list is assembled declaratively in the\n * generated `src/providers/index.ts` barrel — each generated provider module\n * exports a `ProviderRegistration` const, and the barrel collects them.\n * `connect()` reads this list and merges tools from every matching Platform\n * connection on the project. Providers with no matching connection yet are\n * kept and warned about once, so tools appear automatically once a connection\n * is attached.\n */\nexport { PROVIDERS };\n\nexport function findRegistration(integrationId: string): ProviderRegistration | undefined {\n return PROVIDERS.find(p => p.integrationId === integrationId);\n}\n","import type { ConnectClientOptions, ProjectConnection } from './client.js';\nimport { listProjectConnections, resolveClient } from './client.js';\nimport { MastraConnectError } from './errors.js';\nimport type { ProviderRegistration } from './registry.js';\nimport { PROVIDERS } from './registry.js';\n\nexport interface ConnectIntegrationOptions {\n /** Pin a specific connection id (bypasses env-var fallback and single-active-connection resolution). */\n connectionId?: string;\n /** Restrict the returned toolset to these tool keys. Unknown names throw at build time. */\n allowTools?: string[];\n /** Exclude this provider entirely, even if a connection exists. */\n disabled?: boolean;\n}\n\nexport interface ConnectOptions {\n /** Platform project whose connections to discover. Falls back to MASTRA_PROJECT_ID. */\n projectId?: string;\n /** Optional per-provider overrides keyed by integrationId. */\n integrations?: Record<string, ConnectIntegrationOptions>;\n client?: ConnectClientOptions;\n /** How long a resolved snapshot stays fresh, in milliseconds. Default 30_000. `0` revalidates every resolution. */\n ttlMs?: number;\n}\n\n// Keep the public resolver type structural so linked/local package builds do not\n// bind consumers to the exact @mastra/core type instance used to build Connect.\ntype ResolvedConnectTools = Record<string, { id: string }>;\n\n/**\n * Live tool resolver returned by `connect()`. Pass it straight to an agent's\n * dynamic `tools` argument: Mastra calls it per generate/stream, so project\n * integrations attached or detached on the platform are reflected without\n * restarting the server. Call it directly (`await tools()`) when you need the\n * current flat tool record.\n */\nexport interface ConnectTools {\n (ctx?: { requestContext?: unknown; mastra?: unknown }): Promise<ResolvedConnectTools>;\n /** Drops the cached snapshot; the next resolution fetches fresh from the platform. */\n invalidate(): void;\n /** Fetches tools from the platform now and updates the cache. Rejects if the platform fetch fails. */\n refresh(): Promise<ResolvedConnectTools>;\n}\n\ninterface NormalizedRequest {\n registration: ProviderRegistration;\n options: ConnectIntegrationOptions;\n}\n\nconst DEFAULT_TTL_MS = 30_000;\n/** Minimum wait after a failed platform fetch before another background revalidation. */\nconst FAILURE_COOLDOWN_MS = 30_000;\n\n/**\n * Returns a live toolset resolver over the project's Platform connections.\n * Providers are discovered from the shipped `PROVIDERS` registry. Tools from\n * every registered provider with a matching project connection are merged into\n * one flat record (matched by `integrationId`). The resolver serves a cached snapshot,\n * revalidating from the platform every `ttlMs`, so integrations attached\n * to (or detached from) the project are picked up (or dropped) without a\n * restart.\n *\n * Configuration errors (missing project id, bad ttlMs, unknown provider in\n * `integrations`) throw here — at call time — so they surface at startup.\n * Per-integration problems during resolution (needs re-auth, ambiguity, not\n * attached yet) are downgraded to warn-and-skip so one bad integration never\n * takes down the whole toolset.\n */\nexport function connect(options: ConnectOptions = {}): ConnectTools {\n const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();\n if (!projectId) {\n throw new MastraConnectError('missing_project_id', 'Missing project id: set MASTRA_PROJECT_ID or pass projectId.');\n }\n if (options.ttlMs !== undefined && (!Number.isFinite(options.ttlMs) || options.ttlMs < 0)) {\n throw new MastraConnectError(\n 'invalid_options',\n `Invalid ttlMs (${options.ttlMs}): expected a finite number of milliseconds >= 0.`,\n );\n }\n const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;\n\n const client = resolveClient(options.client);\n const requests = buildRequests(options.integrations);\n\n const warnedMissing = new Set<string>();\n let cache: { snapshot: ResolvedConnectTools; fetchedAt: number } | undefined;\n let inflight: Promise<ResolvedConnectTools> | undefined;\n let lastFailureAt: number | undefined;\n\n /** Fetches a fresh snapshot, deduplicating concurrent calls. Rejects on failure. */\n const refresh = (): Promise<ResolvedConnectTools> => {\n if (!inflight) {\n inflight = (async () => {\n try {\n const connections = await listProjectConnections(client, projectId);\n const snapshot = mapTools(connections, requests, options, warnedMissing);\n cache = { snapshot, fetchedAt: Date.now() };\n lastFailureAt = undefined;\n return snapshot;\n } catch (error) {\n lastFailureAt = Date.now();\n throw error;\n } finally {\n inflight = undefined;\n }\n })();\n }\n return inflight;\n };\n\n const resolve = async (): Promise<ResolvedConnectTools> => {\n if (cache && Date.now() - cache.fetchedAt < ttlMs) {\n return cache.snapshot;\n }\n if (cache) {\n // Stale: serve the snapshot now and revalidate in the background,\n // swallowing (but warning about) fetch failures. During a sustained\n // platform outage the cooldown keeps this to one request per window\n // instead of one per agent call.\n const staleSnapshot = cache.snapshot;\n const inCooldown = lastFailureAt !== undefined && Date.now() - lastFailureAt < FAILURE_COOLDOWN_MS;\n if (!inCooldown && !inflight) {\n const staleFetchedAt = cache.fetchedAt;\n void refresh().catch((error: unknown) => {\n console.warn(\n `[@mastra/connect] Keeping cached tools (fetched ${Date.now() - staleFetchedAt}ms ago); platform refresh failed: ${\n error instanceof Error ? error.message : String(error)\n }`,\n );\n });\n }\n return staleSnapshot;\n }\n return refresh();\n };\n\n return Object.assign(resolve, {\n invalidate: (): void => {\n cache = undefined;\n },\n refresh,\n });\n}\n\nfunction buildRequests(integrations: ConnectOptions['integrations']): NormalizedRequest[] {\n const overrides = integrations ?? {};\n for (const integrationId of Object.keys(overrides)) {\n if (!PROVIDERS.some(p => p.integrationId === integrationId)) {\n throw new MastraConnectError(\n 'invalid_options',\n `Unknown provider '${integrationId}' in integrations option. Known providers: ${\n PROVIDERS.map(p => p.integrationId).join(', ') || '(none shipped in this package build)'\n }.`,\n );\n }\n }\n const requests: NormalizedRequest[] = [];\n for (const registration of PROVIDERS) {\n const options = overrides[registration.integrationId] ?? {};\n if (options.disabled) continue;\n requests.push({ registration, options });\n }\n return requests;\n}\n\n/** Maps one platform connection list snapshot to a flat tool record without allowing ambiguous tool ownership. */\nfunction mapTools(\n connections: ProjectConnection[],\n requests: NormalizedRequest[],\n options: ConnectOptions,\n warnedMissing: Set<string>,\n): ResolvedConnectTools {\n const byIntegrationId = groupByIntegrationId(connections);\n\n const result: ResolvedConnectTools = {};\n const toolOwners = new Map<string, string>();\n for (const request of requests) {\n const integrationId = request.registration.integrationId;\n let providerTools: ReturnType<ProviderRegistration['createTools']> | undefined;\n try {\n const candidates = byIntegrationId.get(integrationId) ?? [];\n if (candidates.length === 0) {\n if (!warnedMissing.has(integrationId)) {\n warnedMissing.add(integrationId);\n console.warn(\n `[@mastra/connect] No ${integrationId} connection in this project yet; its tools will appear automatically once one is attached.`,\n );\n }\n continue;\n }\n const connectionId = resolveProviderConnection(request, candidates);\n if (!connectionId) continue; // warned + skipped\n providerTools = request.registration.createTools({\n connectionId,\n allowTools: request.options.allowTools,\n client: options.client,\n });\n } catch (error) {\n console.warn(\n `[@mastra/connect] Skipping ${integrationId}: ${error instanceof Error ? error.message : String(error)}`,\n );\n continue;\n }\n\n for (const toolKey of Object.keys(providerTools)) {\n const existingOwner = toolOwners.get(toolKey);\n if (existingOwner) {\n throw new MastraConnectError(\n 'invalid_options',\n `Duplicate tool key '${toolKey}' from providers '${existingOwner}' and '${integrationId}'.`,\n );\n }\n toolOwners.set(toolKey, integrationId);\n }\n Object.assign(result, providerTools);\n }\n return result;\n}\n\nfunction groupByIntegrationId(connections: ProjectConnection[]): Map<string, ProjectConnection[]> {\n const byIntegrationId = new Map<string, ProjectConnection[]>();\n for (const connection of connections) {\n const list = byIntegrationId.get(connection.integrationId) ?? [];\n list.push(connection);\n byIntegrationId.set(connection.integrationId, list);\n }\n return byIntegrationId;\n}\n\nfunction readEnvConnectionId(registration: ProviderRegistration): string | undefined {\n return process.env[registration.envVar]?.trim() || undefined;\n}\n\n/**\n * Resolves the connection to use for one provider, per the contract:\n * option/env var wins; else a single active connection; anything else\n * (ambiguity, needs_reauth, no usable candidate) warns and skips so one bad\n * integration never takes down the whole toolset resolution. A needs_reauth\n * connection is never silently mapped.\n */\nfunction resolveProviderConnection(request: NormalizedRequest, candidates: ProjectConnection[]): string | undefined {\n const integrationId = request.registration.integrationId;\n const directed = request.options.connectionId?.trim() || readEnvConnectionId(request.registration);\n if (directed) {\n const match = candidates.find(connection => connection.id === directed);\n if (!match) {\n console.warn(\n `[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`,\n );\n return undefined;\n }\n if (match.status === 'needs_reauth') {\n console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);\n return undefined;\n }\n if (match.status !== 'active') {\n console.warn(\n `[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`,\n );\n return undefined;\n }\n return directed;\n }\n\n const active = candidates.filter(connection => connection.status === 'active');\n if (active.length === 1) return active[0]!.id;\n if (active.length === 0) {\n console.warn(\n `[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`,\n );\n return undefined;\n }\n console.warn(\n `[@mastra/connect] Skipping ${integrationId}: ${active.length} active connections; pin one with connectionId or ${request.registration.envVar}.`,\n );\n return undefined;\n}\n","import type { ConnectClientOptions, ConnectionCredential } from './client.js';\nimport { getCredential, resolveClient } from './client.js';\n\n/**\n * Fetches the raw credential for a connection, for building custom\n * interactions (own SDKs, MCP servers). Never log the result.\n */\nexport async function credential(\n connectionId: string,\n options?: { client?: ConnectClientOptions },\n): Promise<ConnectionCredential> {\n const client = resolveClient(options?.client);\n return getCredential(client, connectionId);\n}\n","import type { ToolsInput } from '@mastra/core/agent';\nimport { createTool } from '@mastra/core/tools';\nimport type { z } from 'zod';\n\nimport type { ConnectClientOptions, ProxyRequestOptions } from './client.js';\nimport { proxyRequest, resolveClient } from './client.js';\nimport { MastraConnectError } from './errors.js';\n\nexport interface ProviderToolsOptions {\n /**\n * Connection to use. `connect()` always supplies one (resolving the\n * registration's env var itself); direct `create<Provider>Tools` callers\n * must pass it or tool calls fail with `missing_connection_id`.\n */\n connectionId?: string;\n /** Restrict the returned toolset to these tool keys. Unknown names throw immediately. */\n allowTools?: string[];\n client?: ConnectClientOptions;\n}\n\n/** Resolves a connection id lazily at execute time: option → env var → typed error naming the env var. */\nexport function resolveConnectionId(envVar: string, connectionId?: string): string {\n const resolved = connectionId?.trim() || process.env[envVar]?.trim();\n if (!resolved) {\n throw new MastraConnectError('missing_connection_id', `Missing connection id: set ${envVar} or pass connectionId.`);\n }\n return resolved;\n}\n\nexport interface ProxyToolConfig<TIn, TOut> {\n id: string;\n description: string;\n inputSchema: z.ZodType<TIn>;\n outputSchema: z.ZodType<TOut>;\n /** Builds the proxy request for a parsed input. */\n request: (input: TIn) => ProxyRequestOptions;\n /** Shapes the provider's raw JSON into the output schema's shape. */\n transform: (raw: unknown, input: TIn) => TOut;\n}\n\nexport interface ProxyToolContext {\n envVar: string;\n options?: ProviderToolsOptions;\n}\n\n/**\n * Wraps `createTool` so the tool executes through the platform connection\n * proxy. Connection id and client config resolve lazily inside execute, so\n * building tools without env vars set never throws.\n */\nexport function defineProxyTool<TIn, TOut>(context: ProxyToolContext, config: ProxyToolConfig<TIn, TOut>) {\n return createTool({\n id: config.id,\n description: config.description,\n inputSchema: config.inputSchema,\n outputSchema: config.outputSchema,\n execute: async input => {\n const connectionId = resolveConnectionId(context.envVar, context.options?.connectionId);\n const client = resolveClient(context.options?.client);\n const raw = await proxyRequest(client, connectionId, config.request(input));\n return config.transform(raw, input);\n },\n });\n}\n\n/**\n * Filters a toolset by tool key. Throws at build time on unknown names so\n * typos in access-limiting config surface immediately.\n */\nexport function applyAllowTools<T extends ToolsInput>(tools: T, allowTools?: string[]): ToolsInput {\n if (!allowTools) return tools;\n const known = Object.keys(tools);\n const unknown = allowTools.filter(name => !known.includes(name));\n if (unknown.length > 0) {\n throw new Error(`Unknown tool name(s) in allowTools: ${unknown.join(', ')}. Known tools: ${known.join(', ')}.`);\n }\n const filtered: ToolsInput = {};\n for (const name of allowTools) {\n filtered[name] = tools[name]!;\n }\n return filtered;\n}\n"],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ export { connect } from './connect.js';
2
+ export type { ConnectOptions, ConnectIntegrationOptions, ConnectTools } from './connect.js';
3
+ export { credential } from './credential.js';
4
+ export { MastraConnectError } from './errors.js';
5
+ export type { MastraConnectErrorCode } from './errors.js';
6
+ export type { ConnectClientOptions, ConnectionCredential, ProjectConnection } from './client.js';
7
+ export type { ProviderToolsOptions, ProxyToolConfig, ProxyToolContext } from './toolset.js';
8
+ export { defineProxyTool, resolveConnectionId, applyAllowTools } from './toolset.js';
9
+ export { PROVIDERS, findRegistration } from './registry.js';
10
+ export type { ProviderRegistration } from './registry.js';
11
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AAC5D,YAAY,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,472 @@
1
+ import { z } from "zod";
2
+ import { createTool } from "@mastra/core/tools";
3
+ //#region src/errors.ts
4
+ const MAX_DETAIL_LENGTH = 2e3;
5
+ var MastraConnectError = class extends Error {
6
+ code;
7
+ status;
8
+ detail;
9
+ constructor(code, message, options) {
10
+ super(message);
11
+ this.name = "MastraConnectError";
12
+ this.code = code;
13
+ this.status = options?.status;
14
+ this.detail = options?.detail ? truncate(options.detail) : void 0;
15
+ }
16
+ };
17
+ function truncate(text) {
18
+ return text.length > MAX_DETAIL_LENGTH ? `${text.slice(0, MAX_DETAIL_LENGTH)}…` : text;
19
+ }
20
+ /**
21
+ * Extracts a human-readable detail string from an RFC-7807 problem JSON body
22
+ * (or a plain `{ error }` body) without echoing anything else from the response.
23
+ * Returns undefined when the body is not parseable JSON.
24
+ */
25
+ async function extractProblemDetail(response) {
26
+ const isProblemJson = (response.headers.get("content-type") ?? "").includes("application/problem+json");
27
+ try {
28
+ const data = await response.clone().json();
29
+ if (data && typeof data === "object") {
30
+ const code = typeof data.code === "string" ? data.code : void 0;
31
+ for (const field of [
32
+ "detail",
33
+ "title",
34
+ "error"
35
+ ]) {
36
+ const value = data[field];
37
+ if (typeof value === "string" && value) return {
38
+ detail: truncate(value),
39
+ code,
40
+ isProblemJson
41
+ };
42
+ }
43
+ return {
44
+ code,
45
+ isProblemJson
46
+ };
47
+ }
48
+ } catch {}
49
+ return { isProblemJson };
50
+ }
51
+ //#endregion
52
+ //#region src/client.ts
53
+ const DEFAULT_INTEGRATIONS_URL = "https://integrations.mastra.ai";
54
+ const REGIONAL_INTEGRATIONS_URLS = {
55
+ us: "https://integrations.us.mastra.ai",
56
+ eu: "https://integrations.eu.mastra.ai"
57
+ };
58
+ /**
59
+ * Mirrors the URL conventions of the platform factory api-client
60
+ * (`MASTRA_PLATFORM_REGION` case-insensitive 'us'/'eu'; unknown regions fall
61
+ * through to the global default).
62
+ */
63
+ function resolveIntegrationsUrl() {
64
+ const region = process.env.MASTRA_PLATFORM_REGION?.trim().toLowerCase();
65
+ if (region === "us" || region === "eu") return REGIONAL_INTEGRATIONS_URLS[region];
66
+ return DEFAULT_INTEGRATIONS_URL;
67
+ }
68
+ function stripTrailingSlashes(url) {
69
+ let end = url.length;
70
+ while (end > 0 && url[end - 1] === "/") end--;
71
+ return url.slice(0, end);
72
+ }
73
+ function resolveClient(options) {
74
+ const accessToken = options?.accessToken?.trim() || process.env.MASTRA_PLATFORM_ACCESS_TOKEN?.trim() || process.env.MASTRA_PLATFORM_SECRET_KEY?.trim();
75
+ if (!accessToken) throw new MastraConnectError("missing_access_token", "Missing Mastra platform access token: set MASTRA_PLATFORM_ACCESS_TOKEN (or MASTRA_PLATFORM_SECRET_KEY), or pass client.accessToken.");
76
+ const baseUrl = stripTrailingSlashes(options?.baseUrl?.trim() || process.env.MASTRA_INTEGRATIONS_API_URL?.trim() || resolveIntegrationsUrl());
77
+ const headers = {
78
+ accept: "application/json",
79
+ authorization: `Bearer ${accessToken}`
80
+ };
81
+ const orgId = options?.orgId?.trim() || process.env.MASTRA_ORG_ID?.trim();
82
+ if (orgId) headers["x-organization-id"] = orgId;
83
+ return {
84
+ baseUrl,
85
+ headers,
86
+ fetch: options?.fetch ?? globalThis.fetch,
87
+ accessToken
88
+ };
89
+ }
90
+ function redact(message, accessToken) {
91
+ return message.split(accessToken).join("[REDACTED]");
92
+ }
93
+ async function platformFetch(client, path, init) {
94
+ try {
95
+ return await client.fetch(`${client.baseUrl}${path}`, {
96
+ ...init,
97
+ headers: {
98
+ ...client.headers,
99
+ ...init?.headers
100
+ }
101
+ });
102
+ } catch (error) {
103
+ if (error instanceof Error && error.message.includes(client.accessToken)) {
104
+ const redacted = new Error(redact(error.message, client.accessToken));
105
+ redacted.name = error.name;
106
+ throw redacted;
107
+ }
108
+ throw error;
109
+ }
110
+ }
111
+ /** Parses a successful platform response body, mapping invalid JSON to the platform_error contract. */
112
+ async function parsePlatformJson(response, context) {
113
+ try {
114
+ return await response.json();
115
+ } catch {
116
+ throw new MastraConnectError("platform_error", `Platform returned an unparseable response body while ${context}.`);
117
+ }
118
+ }
119
+ /** Maps a non-2xx response from the platform's own endpoints to a typed error. */
120
+ async function throwPlatformError(response, context) {
121
+ const { detail, code } = await extractProblemDetail(response);
122
+ if (response.status === 401 || response.status === 403) throw new MastraConnectError("unauthorized", `Unauthorized while ${context}${detail ? `: ${detail}` : "."}`, {
123
+ status: response.status,
124
+ detail
125
+ });
126
+ if (response.status === 404) throw new MastraConnectError("connection_not_found", `Not found while ${context}${detail ? `: ${detail}` : "."}`, {
127
+ status: response.status,
128
+ detail
129
+ });
130
+ if (code === "unsupported_credential_type") throw new MastraConnectError("unsupported_credential_type", `Unsupported credential type while ${context}${detail ? `: ${detail}` : "."}`, {
131
+ status: response.status,
132
+ detail
133
+ });
134
+ throw new MastraConnectError("platform_error", `Platform request failed (${response.status}) while ${context}${detail ? `: ${detail}` : "."}`, {
135
+ status: response.status,
136
+ detail
137
+ });
138
+ }
139
+ const connectionSchema = z.object({
140
+ id: z.string(),
141
+ integrationId: z.string(),
142
+ status: z.string(),
143
+ connectedByUserId: z.string().nullish(),
144
+ connectedAt: z.string().nullish(),
145
+ createdAt: z.string().nullish(),
146
+ accountLabel: z.string().nullish()
147
+ });
148
+ const connectionListSchema = z.object({ connections: z.array(connectionSchema) });
149
+ const credentialSchema = z.discriminatedUnion("type", [z.object({
150
+ type: z.literal("oauth2"),
151
+ accessToken: z.string(),
152
+ expiresAt: z.string().nullable()
153
+ }), z.object({
154
+ type: z.literal("api_key"),
155
+ apiKey: z.string()
156
+ })]);
157
+ async function listProjectConnections(client, projectId) {
158
+ const response = await platformFetch(client, `/v2/projects/${encodeURIComponent(projectId)}/connections`);
159
+ if (!response.ok) await throwPlatformError(response, `listing connections for project ${projectId}`);
160
+ const parsed = connectionListSchema.safeParse(await parsePlatformJson(response, `listing connections for project ${projectId}`));
161
+ if (!parsed.success) throw new MastraConnectError("platform_error", `Platform returned an unexpected connection list shape for project ${projectId}.`);
162
+ return parsed.data.connections;
163
+ }
164
+ async function getCredential(client, connectionId) {
165
+ const response = await platformFetch(client, `/v2/connections/${encodeURIComponent(connectionId)}/credentials`);
166
+ if (!response.ok) await throwPlatformError(response, `fetching credentials for connection ${connectionId}`);
167
+ const parsed = credentialSchema.safeParse(await parsePlatformJson(response, `fetching credentials for connection ${connectionId}`));
168
+ if (!parsed.success) throw new MastraConnectError("unsupported_credential_type", `Platform returned an unsupported credential type for connection ${connectionId}.`);
169
+ return parsed.data;
170
+ }
171
+ /**
172
+ * Sends a request through the platform connection proxy and returns the
173
+ * provider's parsed JSON on 2xx.
174
+ *
175
+ * Error-mapping scope: 401/404 map to `unauthorized`/`connection_not_found`
176
+ * only when the response carries a platform RFC-7807 problem body — a
177
+ * provider's own 401/404 passed through the proxy stays `proxy_error` with the
178
+ * provider status attached.
179
+ */
180
+ /**
181
+ * True when any path segment is a literal or percent-encoded dot segment.
182
+ * fetch/URL normalizes `..` (and `%2e%2e` variants) *before* the request is
183
+ * sent, which would let a crafted path escape the connection-proxy prefix and
184
+ * hit an arbitrary platform route with the caller's bearer token.
185
+ */
186
+ function hasDotSegment(path) {
187
+ return path.split("/").some((segment) => {
188
+ let decoded;
189
+ try {
190
+ decoded = decodeURIComponent(segment);
191
+ } catch {
192
+ return true;
193
+ }
194
+ return decoded === "." || decoded === "..";
195
+ });
196
+ }
197
+ async function proxyRequest(client, connectionId, options) {
198
+ const cleanPath = options.path.replace(/^\/+/, "");
199
+ if (hasDotSegment(cleanPath)) throw new MastraConnectError("invalid_options", `Invalid proxy path '${options.path}': dot segments are not allowed.`);
200
+ const search = new URLSearchParams();
201
+ for (const [key, value] of Object.entries(options.query ?? {})) if (value !== void 0) search.set(key, String(value));
202
+ const queryString = search.size > 0 ? `?${search.toString()}` : "";
203
+ const url = `/v2/connections/${encodeURIComponent(connectionId)}/proxy/${cleanPath}${queryString}`;
204
+ const headers = { ...options.headers };
205
+ const init = {
206
+ method: options.method,
207
+ headers
208
+ };
209
+ if (options.body !== void 0) {
210
+ headers["content-type"] = "application/json";
211
+ init.body = JSON.stringify(options.body);
212
+ }
213
+ const response = await platformFetch(client, url, init);
214
+ if (!response.ok) {
215
+ const { detail, isProblemJson } = await extractProblemDetail(response);
216
+ if (isProblemJson) {
217
+ if (response.status === 401 || response.status === 403) throw new MastraConnectError("unauthorized", `Unauthorized calling the connection proxy${detail ? `: ${detail}` : "."}`, {
218
+ status: response.status,
219
+ detail
220
+ });
221
+ if (response.status === 404) throw new MastraConnectError("connection_not_found", `Connection ${connectionId} not found${detail ? `: ${detail}` : "."}`, {
222
+ status: response.status,
223
+ detail
224
+ });
225
+ throw new MastraConnectError("proxy_error", `Proxy request failed (${response.status})${detail ? `: ${detail}` : "."}`, {
226
+ status: response.status,
227
+ detail
228
+ });
229
+ }
230
+ throw new MastraConnectError("proxy_error", `Provider request failed (${response.status})${detail ? `: ${detail}` : "."}`, {
231
+ status: response.status,
232
+ detail
233
+ });
234
+ }
235
+ if (response.status === 204) return null;
236
+ const text = await response.text();
237
+ if (!text) return null;
238
+ try {
239
+ return JSON.parse(text);
240
+ } catch {
241
+ return text;
242
+ }
243
+ }
244
+ //#endregion
245
+ //#region src/providers/index.ts
246
+ const PROVIDERS = [];
247
+ //#endregion
248
+ //#region src/registry.ts
249
+ function findRegistration(integrationId) {
250
+ return PROVIDERS.find((p) => p.integrationId === integrationId);
251
+ }
252
+ //#endregion
253
+ //#region src/connect.ts
254
+ const DEFAULT_TTL_MS = 3e4;
255
+ /** Minimum wait after a failed platform fetch before another background revalidation. */
256
+ const FAILURE_COOLDOWN_MS = 3e4;
257
+ /**
258
+ * Returns a live toolset resolver over the project's Platform connections.
259
+ * Providers are discovered from the shipped `PROVIDERS` registry. Tools from
260
+ * every registered provider with a matching project connection are merged into
261
+ * one flat record (matched by `integrationId`). The resolver serves a cached snapshot,
262
+ * revalidating from the platform every `ttlMs`, so integrations attached
263
+ * to (or detached from) the project are picked up (or dropped) without a
264
+ * restart.
265
+ *
266
+ * Configuration errors (missing project id, bad ttlMs, unknown provider in
267
+ * `integrations`) throw here — at call time — so they surface at startup.
268
+ * Per-integration problems during resolution (needs re-auth, ambiguity, not
269
+ * attached yet) are downgraded to warn-and-skip so one bad integration never
270
+ * takes down the whole toolset.
271
+ */
272
+ function connect(options = {}) {
273
+ const projectId = options.projectId?.trim() || process.env.MASTRA_PROJECT_ID?.trim();
274
+ if (!projectId) throw new MastraConnectError("missing_project_id", "Missing project id: set MASTRA_PROJECT_ID or pass projectId.");
275
+ 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.`);
276
+ const ttlMs = options.ttlMs ?? DEFAULT_TTL_MS;
277
+ const client = resolveClient(options.client);
278
+ const requests = buildRequests(options.integrations);
279
+ const warnedMissing = /* @__PURE__ */ new Set();
280
+ let cache;
281
+ let inflight;
282
+ let lastFailureAt;
283
+ /** Fetches a fresh snapshot, deduplicating concurrent calls. Rejects on failure. */
284
+ const refresh = () => {
285
+ if (!inflight) inflight = (async () => {
286
+ try {
287
+ const snapshot = mapTools(await listProjectConnections(client, projectId), requests, options, warnedMissing);
288
+ cache = {
289
+ snapshot,
290
+ fetchedAt: Date.now()
291
+ };
292
+ lastFailureAt = void 0;
293
+ return snapshot;
294
+ } catch (error) {
295
+ lastFailureAt = Date.now();
296
+ throw error;
297
+ } finally {
298
+ inflight = void 0;
299
+ }
300
+ })();
301
+ return inflight;
302
+ };
303
+ const resolve = async () => {
304
+ if (cache && Date.now() - cache.fetchedAt < ttlMs) return cache.snapshot;
305
+ if (cache) {
306
+ const staleSnapshot = cache.snapshot;
307
+ if (!(lastFailureAt !== void 0 && Date.now() - lastFailureAt < FAILURE_COOLDOWN_MS) && !inflight) {
308
+ const staleFetchedAt = cache.fetchedAt;
309
+ refresh().catch((error) => {
310
+ console.warn(`[@mastra/connect] Keeping cached tools (fetched ${Date.now() - staleFetchedAt}ms ago); platform refresh failed: ${error instanceof Error ? error.message : String(error)}`);
311
+ });
312
+ }
313
+ return staleSnapshot;
314
+ }
315
+ return refresh();
316
+ };
317
+ return Object.assign(resolve, {
318
+ invalidate: () => {
319
+ cache = void 0;
320
+ },
321
+ refresh
322
+ });
323
+ }
324
+ function buildRequests(integrations) {
325
+ const overrides = integrations ?? {};
326
+ for (const integrationId of Object.keys(overrides)) if (!PROVIDERS.some((p) => p.integrationId === integrationId)) throw new MastraConnectError("invalid_options", `Unknown provider '${integrationId}' in integrations option. Known providers: ${PROVIDERS.map((p) => p.integrationId).join(", ") || "(none shipped in this package build)"}.`);
327
+ const requests = [];
328
+ for (const registration of PROVIDERS) {
329
+ const options = overrides[registration.integrationId] ?? {};
330
+ if (options.disabled) continue;
331
+ requests.push({
332
+ registration,
333
+ options
334
+ });
335
+ }
336
+ return requests;
337
+ }
338
+ /** Maps one platform connection list snapshot to a flat tool record without allowing ambiguous tool ownership. */
339
+ function mapTools(connections, requests, options, warnedMissing) {
340
+ const byIntegrationId = groupByIntegrationId(connections);
341
+ const result = {};
342
+ const toolOwners = /* @__PURE__ */ new Map();
343
+ for (const request of requests) {
344
+ const integrationId = request.registration.integrationId;
345
+ let providerTools;
346
+ try {
347
+ const candidates = byIntegrationId.get(integrationId) ?? [];
348
+ if (candidates.length === 0) {
349
+ if (!warnedMissing.has(integrationId)) {
350
+ warnedMissing.add(integrationId);
351
+ console.warn(`[@mastra/connect] No ${integrationId} connection in this project yet; its tools will appear automatically once one is attached.`);
352
+ }
353
+ continue;
354
+ }
355
+ const connectionId = resolveProviderConnection(request, candidates);
356
+ if (!connectionId) continue;
357
+ providerTools = request.registration.createTools({
358
+ connectionId,
359
+ allowTools: request.options.allowTools,
360
+ client: options.client
361
+ });
362
+ } catch (error) {
363
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: ${error instanceof Error ? error.message : String(error)}`);
364
+ continue;
365
+ }
366
+ for (const toolKey of Object.keys(providerTools)) {
367
+ const existingOwner = toolOwners.get(toolKey);
368
+ if (existingOwner) throw new MastraConnectError("invalid_options", `Duplicate tool key '${toolKey}' from providers '${existingOwner}' and '${integrationId}'.`);
369
+ toolOwners.set(toolKey, integrationId);
370
+ }
371
+ Object.assign(result, providerTools);
372
+ }
373
+ return result;
374
+ }
375
+ function groupByIntegrationId(connections) {
376
+ const byIntegrationId = /* @__PURE__ */ new Map();
377
+ for (const connection of connections) {
378
+ const list = byIntegrationId.get(connection.integrationId) ?? [];
379
+ list.push(connection);
380
+ byIntegrationId.set(connection.integrationId, list);
381
+ }
382
+ return byIntegrationId;
383
+ }
384
+ function readEnvConnectionId(registration) {
385
+ return process.env[registration.envVar]?.trim() || void 0;
386
+ }
387
+ /**
388
+ * Resolves the connection to use for one provider, per the contract:
389
+ * option/env var wins; else a single active connection; anything else
390
+ * (ambiguity, needs_reauth, no usable candidate) warns and skips so one bad
391
+ * integration never takes down the whole toolset resolution. A needs_reauth
392
+ * connection is never silently mapped.
393
+ */
394
+ function resolveProviderConnection(request, candidates) {
395
+ const integrationId = request.registration.integrationId;
396
+ const directed = request.options.connectionId?.trim() || readEnvConnectionId(request.registration);
397
+ if (directed) {
398
+ const match = candidates.find((connection) => connection.id === directed);
399
+ if (!match) {
400
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: pinned connection ${directed} is not attached to this project.`);
401
+ return;
402
+ }
403
+ if (match.status === "needs_reauth") {
404
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} needs re-auth.`);
405
+ return;
406
+ }
407
+ if (match.status !== "active") {
408
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: connection ${directed} is not active (status '${match.status}').`);
409
+ return;
410
+ }
411
+ return directed;
412
+ }
413
+ const active = candidates.filter((connection) => connection.status === "active");
414
+ if (active.length === 1) return active[0].id;
415
+ if (active.length === 0) {
416
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: no active connections (found ${candidates.length} in other states).`);
417
+ return;
418
+ }
419
+ console.warn(`[@mastra/connect] Skipping ${integrationId}: ${active.length} active connections; pin one with connectionId or ${request.registration.envVar}.`);
420
+ }
421
+ //#endregion
422
+ //#region src/credential.ts
423
+ /**
424
+ * Fetches the raw credential for a connection, for building custom
425
+ * interactions (own SDKs, MCP servers). Never log the result.
426
+ */
427
+ async function credential(connectionId, options) {
428
+ return getCredential(resolveClient(options?.client), connectionId);
429
+ }
430
+ //#endregion
431
+ //#region src/toolset.ts
432
+ /** Resolves a connection id lazily at execute time: option → env var → typed error naming the env var. */
433
+ function resolveConnectionId(envVar, connectionId) {
434
+ const resolved = connectionId?.trim() || process.env[envVar]?.trim();
435
+ if (!resolved) throw new MastraConnectError("missing_connection_id", `Missing connection id: set ${envVar} or pass connectionId.`);
436
+ return resolved;
437
+ }
438
+ /**
439
+ * Wraps `createTool` so the tool executes through the platform connection
440
+ * proxy. Connection id and client config resolve lazily inside execute, so
441
+ * building tools without env vars set never throws.
442
+ */
443
+ function defineProxyTool(context, config) {
444
+ return createTool({
445
+ id: config.id,
446
+ description: config.description,
447
+ inputSchema: config.inputSchema,
448
+ outputSchema: config.outputSchema,
449
+ execute: async (input) => {
450
+ const connectionId = resolveConnectionId(context.envVar, context.options?.connectionId);
451
+ const raw = await proxyRequest(resolveClient(context.options?.client), connectionId, config.request(input));
452
+ return config.transform(raw, input);
453
+ }
454
+ });
455
+ }
456
+ /**
457
+ * Filters a toolset by tool key. Throws at build time on unknown names so
458
+ * typos in access-limiting config surface immediately.
459
+ */
460
+ function applyAllowTools(tools, allowTools) {
461
+ if (!allowTools) return tools;
462
+ const known = Object.keys(tools);
463
+ const unknown = allowTools.filter((name) => !known.includes(name));
464
+ if (unknown.length > 0) throw new Error(`Unknown tool name(s) in allowTools: ${unknown.join(", ")}. Known tools: ${known.join(", ")}.`);
465
+ const filtered = {};
466
+ for (const name of allowTools) filtered[name] = tools[name];
467
+ return filtered;
468
+ }
469
+ //#endregion
470
+ export { MastraConnectError, PROVIDERS, applyAllowTools, connect, credential, defineProxyTool, findRegistration, resolveConnectionId };
471
+
472
+ //# sourceMappingURL=index.js.map