UNPKG

@mastra/core

Version:
1 lines 42.8 kB
{"version":3,"sources":["../../src/tool-provider/types.ts","../../src/tool-provider/base.ts","../../src/tool-provider/runtime.ts","../../src/tool-provider/errors.ts"],"names":["MASTRA_RESOURCE_ID_KEY"],"mappings":"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","file":"index.cjs","sourcesContent":["import type { StorageToolConfig } from '../storage/types';\nimport type { ToolAction } from '../tools/types';\n\n/**\n * Metadata about a tool provider.\n */\nexport interface ToolProviderInfo {\n /** Unique identifier for this provider (e.g., 'composio') */\n id: string;\n /** Human-readable name */\n name: string;\n /** Short description of the provider */\n description?: string;\n}\n\n/**\n * A toolkit (group of related tools) from a tool provider.\n *\n * Composio calls this a \"toolkit\"; the neutral term keeps additional\n * providers (e.g. Arcade) from having to adopt vendor vocabulary.\n */\nexport interface ToolProviderToolkit {\n /** Unique slug for this toolkit (e.g., 'gmail', 'slack') */\n slug: string;\n /** Human-readable name */\n name: string;\n /** Description of the toolkit */\n description?: string;\n /** Icon URL or identifier */\n icon?: string;\n}\n\n/**\n * A tool listing entry from a tool provider.\n * Used for UI discovery — does not include the full executable tool.\n */\nexport interface ToolProviderToolInfo {\n /** Fully-qualified tool slug (e.g., 'gmail.fetch_emails') */\n slug: string;\n /** Human-readable name */\n name: string;\n /** Description of what this tool does */\n description?: string;\n /** Toolkit (tool service) this tool belongs to */\n toolkit?: string;\n}\n\n/**\n * Options for listing tools from a provider.\n */\nexport interface ListToolProviderToolsOptions {\n /** Filter by toolkit slug */\n toolkit?: string;\n /** Search query for filtering tools */\n search?: string;\n /** Pagination cursor or page (1-indexed) */\n page?: number;\n /** Number of tools per page */\n perPage?: number;\n}\n\n/**\n * Paginated result from tool provider list operations.\n */\nexport interface ToolProviderListResult<T> {\n data: T[];\n pagination?: {\n total?: number;\n page?: number;\n perPage?: number;\n hasMore: boolean;\n };\n}\n\n/**\n * Options for resolving executable tools at agent runtime.\n */\nexport interface ResolveToolProviderToolsOptions {\n /** User ID for user-scoped tool execution (e.g., Composio) */\n userId?: string;\n /** Per-request context (e.g., user-specific API keys, tenant IDs) */\n requestContext?: Record<string, unknown>;\n /** Additional provider-specific options */\n [key: string]: unknown;\n}\n\n// ── Capabilities ────────────────────────────────────────────────────────\n\n/**\n * Per-provider capability flags. Lets callers branch on optional features\n * without instanceof / subclass checks.\n */\nexport interface ToolProviderCapabilities {\n /** Provider supports multiple connections (OAuth buckets) on the same toolkit. */\n multipleConnectionsPerToolkit: boolean;\n /** Provider can answer `getConnectionStatus` for many items in one call. */\n batchConnectionStatus: boolean;\n /** Re-authorizing a connection reuses the same `connectionId` (token refresh in place). */\n reauthorizeReusesConnectionId: boolean;\n /**\n * Provider supports revoking a connection at the underlying service (true) vs.\n * only unpinning it locally (false). UI hides the \"Disconnect\" affordance\n * when this is falsy.\n */\n supportsRevoke?: boolean;\n}\n\n// ── Connections + scope ─────────────────────────────────────────────────\n\n/**\n * Identity bucketing for a pinned connection.\n *\n * - `'per-author'` (default) — bucketed under the caller's resolved authorId.\n * - `'shared'` — bucketed under {@link SHARED_BUCKET_ID}, visible to every caller.\n * - `'caller-supplied'` — bucketed under the host app's `resourceId` forwarded\n * via request context. Used for multi-tenant SaaS deployments where the\n * host app authenticates the end-user upstream. Falls back to a shared\n * `'default'` bucket when the resource id is missing; multi-tenant\n * deployments should wire the resource id explicitly (e.g. via\n * `authConfig.mapUserToResourceId`) to avoid cross-tenant bucket sharing.\n */\nexport type ToolProviderConnectionScope = 'shared' | 'per-author' | 'caller-supplied';\n\n/**\n * Constant authorId used to bucket {@link ToolProviderConnection}s with\n * `scope: 'shared'`.\n */\nexport const SHARED_BUCKET_ID = 'shared';\n\n/**\n * A single OAuth bucket bound to one toolkit on one agent.\n */\nexport interface ToolProviderConnection {\n /**\n * Identity binding kind.\n *\n * - `'author'` — uses the agent author's connection (v1 default).\n * - `'invoker'` — uses the end-user's connection (reserved).\n * - `'platform'` — uses a shared platform account (reserved).\n */\n kind: 'author' | 'invoker' | 'platform';\n /** Parent toolkit slug. Denormalized for callsite clarity. */\n toolkit: string;\n /**\n * Provider-opaque identifier for the OAuth bucket.\n *\n * Required for `'author'` and `'platform'`; reserved (empty) for `'invoker'`.\n */\n connectionId: string;\n /**\n * Display label and LLM disambiguator. Optional when this is the only\n * connection for a `toolkit`; required (non-empty, ≤ 32 chars,\n * `[A-Za-z0-9 _-]+`, case-insensitively unique) once there are ≥ 2\n * connections sharing the same `toolkit`.\n */\n label?: string;\n /**\n * Identity bucketing. Optional for back-compat with pre-scope pins;\n * treated as `'per-author'` when missing.\n */\n scope?: ToolProviderConnectionScope;\n}\n\n/**\n * Per-tool override stored alongside the selected tool slug.\n */\nexport interface ToolProviderToolMeta {\n /**\n * Toolkit this slug belongs to. Required for the runtime to group\n * selected tools by toolkit when fanning out across connections.\n * Optional only for backward-compat with pre-fix stored data; new writes\n * must include it.\n */\n toolkit?: string;\n /** Optional description override surfaced to the LLM. */\n description?: string;\n}\n\n/**\n * Stored shape for one provider's configuration on one agent.\n */\nexport interface ToolProviderConfig {\n /** Selected tool slugs and their per-agent overrides. Key = tool slug. */\n tools: Record<string, ToolProviderToolMeta>;\n /** Connections grouped by toolkit slug. */\n connections: Record<string, ToolProviderConnection[]>;\n}\n\n/**\n * The full tool-providers shape on an agent: keyed by provider id.\n */\nexport type ToolProviders = Record<string /* providerId */, ToolProviderConfig>;\n\n// ── List / auth / health surface ─────────────────────────────────────────\n\n/**\n * Options for `ToolProvider.listToolsVNext`. All fields are optional.\n */\nexport interface ListToolsOpts {\n /** Restrict results to one toolkit slug. */\n toolkit?: string;\n /** Free-text search across tool slugs / names / descriptions. */\n search?: string;\n /** 1-indexed page number for paginated listings. */\n page?: number;\n /** Page size. Adapters may clamp to a sane upper bound. */\n perPage?: number;\n}\n\n/**\n * Wrapped pagination envelope returned by `listToolsVNext`. `hasMore` is the\n * only forward-progress signal — adapters are not required to surface a\n * total count.\n */\nexport interface ListToolsResult {\n data: ToolProviderToolInfo[];\n pagination: {\n page: number;\n perPage?: number;\n hasMore: boolean;\n };\n}\n\n/** Wrapped result returned by `listToolkitsVNext`. */\nexport interface ListToolkitsResult {\n data: ToolProviderToolkit[];\n}\n\n/**\n * Options for `ToolProvider.resolveToolsVNext`.\n *\n * The runtime fan-out calls this **once per connection** — providers never\n * see fan-out logic or tool-name suffixes.\n */\nexport interface ResolveToolsOpts {\n /** Original tool slugs to materialise. */\n toolSlugs: string[];\n /** Per-tool overrides (description, etc.) keyed by tool slug. */\n toolMeta: Record<string, ToolProviderToolMeta>;\n /** Provider-opaque OAuth bucket identifier. */\n connectionId: string;\n /**\n * For `kind: 'author'` connections, the agent author's user id. The runtime\n * uses this as the provider's user bucket so the pin works for any\n * invoker, not just the author. Falsy = fall back to request context.\n */\n authorId?: string;\n /** Per-request context (auth, tenant, currentUser, ...). */\n requestContext?: Record<string, unknown>;\n}\n\n/**\n * Options for `ToolProvider.authorize`.\n */\nexport interface AuthorizeOpts {\n /** Toolkit slug being authorized. */\n toolkit: string;\n /**\n * Existing or newly-minted connection bucket id.\n *\n * Providers with `reauthorizeReusesConnectionId: true` refresh the token\n * in place when a known id is supplied.\n */\n connectionId: string;\n /** Optional tool slug — some providers scope authorize per tool. */\n toolName?: string;\n /**\n * Provider-specific custom fields collected from the user before the\n * OAuth flow starts (e.g. Confluence `{ subdomain: 'mycorp' }`).\n */\n config?: Record<string, unknown>;\n}\n\n/**\n * A single user-supplied field required to open a new connection on a\n * toolkit. Surfaced by {@link ToolProvider.listConnectionFields} and\n * rendered inline in the connection picker.\n */\nexport interface ConnectionField {\n /** Programmatic name (e.g. `'subdomain'`). Sent back as a key in `AuthorizeOpts.config`. */\n name: string;\n /** Human-readable label (e.g. `'Your Subdomain'`). */\n displayName: string;\n /** Optional helper text rendered under the input. */\n description?: string;\n /** Storage / input type. Adapters coerce richer types into one of these. */\n type: 'string' | 'number' | 'boolean';\n /** Whether the user must provide a value before authorize can run. */\n required: boolean;\n /** Provider-suggested default (rendered as the input's initial value). */\n default?: string | null;\n}\n\n/**\n * Health summary returned by `ToolProvider.getHealth`.\n */\nexport interface ToolProviderHealth {\n ok: boolean;\n /** Short, user-facing message. */\n message?: string;\n /** Free-form per-provider diagnostics. */\n details?: Record<string, unknown>;\n}\n\n/**\n * Async OAuth flow status as observed by `getAuthStatus`.\n */\nexport type AuthFlowStatus = 'pending' | 'completed' | 'failed';\n\n/**\n * Options for `ToolProvider.listConnections`.\n *\n * Use `userIds[]` to list across multiple buckets in one call (admin\n * cross-author listing). `userId` is the single-bucket convenience and is\n * normalized to `[userId]` internally.\n */\nexport interface ListConnectionsOpts {\n /** Toolkit slug. */\n toolkit: string;\n /** Multi-bucket lookup. Preferred when present. */\n userIds?: string[];\n /** Single-bucket convenience. */\n userId?: string;\n /** 1-indexed page number for paginated listings. */\n page?: number;\n /** Page size. Adapters clamp to a sane upper bound (default 50, max 200). */\n perPage?: number;\n}\n\n/** One existing connection on the underlying provider. */\nexport interface ExistingConnection {\n /** Provider-issued connection identifier (e.g. Composio `ca_xxx`). */\n connectionId: string;\n /** Connection state: `'active'` is safe to pin. */\n status: 'active' | 'inactive' | 'failed' | 'pending';\n /** When the connection was created, if the provider reports it. */\n createdAt?: string;\n /**\n * Bucket owner this connection belongs to. Mirrors the `userId` /\n * `authorId` the connection was created under. Surfaced for admin\n * cross-author UI.\n */\n authorId?: string;\n}\n\nexport interface ListConnectionsResult {\n items: ExistingConnection[];\n /** Pagination envelope. Adapters are not required to surface a total count. */\n pagination: {\n page: number;\n perPage?: number;\n hasMore: boolean;\n total?: number;\n };\n}\n\n/**\n * Interface for tool providers (e.g., Composio) that supply tools to agents.\n *\n * Tool providers serve two purposes:\n * 1. **Discovery** — UI uses `listToolkits()` / `listToolkitsVNext()` / `listTools()` / `listToolsVNext()` to browse available tools\n * 2. **Runtime** — Agent hydration uses `resolveTools()` / `resolveToolsVNext()` to get executable tools for selected tool slugs\n *\n * The VNext surface (`listToolkitsVNext` / `listToolsVNext` / `resolveToolsVNext` and the\n * auth/health methods below) is opt-in: providers can keep using the legacy\n * `listTools()` / `resolveTools()` pair for static, code-config use cases.\n */\nexport interface ToolProvider {\n /** Provider metadata */\n readonly info: ToolProviderInfo;\n\n /**\n * Optional human-readable display name surfaced in the picker. Falls back\n * to `info.name` when absent.\n */\n readonly displayName?: string;\n\n /**\n * Static capability flags. Required when the v2 surface is implemented;\n * legacy providers may omit it.\n */\n readonly capabilities?: ToolProviderCapabilities;\n\n // ── Legacy surface (kept for back-compat) ─────────────────────────────\n\n /**\n * List available toolkits from this provider.\n * Used by UI for browsing.\n */\n listToolkits?(): Promise<ToolProviderListResult<ToolProviderToolkit>>;\n\n /**\n * List available tools, optionally filtered by toolkit or search query.\n * Used by UI for browsing/selecting tools.\n */\n listTools(options?: ListToolProviderToolsOptions): Promise<ToolProviderListResult<ToolProviderToolInfo>>;\n\n /**\n * Get the JSON schema for a specific tool's input.\n * Used by UI to display tool details.\n */\n getToolSchema?(toolSlug: string): Promise<Record<string, unknown> | null>;\n\n /**\n * Resolve executable tools for the given slugs (legacy signature).\n * Called during agent hydration to resolve `integrationTools` references.\n */\n resolveTools(\n toolSlugs: string[],\n toolConfigs?: Record<string, StorageToolConfig>,\n options?: ResolveToolProviderToolsOptions,\n ): Promise<Record<string, ToolAction<any, any, any>>>;\n\n // ── VNext surface (opt-in, used by Agent Builder + editor UI) ────────\n\n /** List allowed toolkits, wrapped in a result envelope. */\n listToolkitsVNext?(): Promise<ListToolkitsResult>;\n\n /**\n * List allowed tools (wrapped envelope). With no options, lists across\n * every toolkit. Pass `toolkit` to scope; pass `search` to filter; pass\n * `page` / `perPage` to paginate.\n */\n listToolsVNext?(opts?: ListToolsOpts): Promise<ListToolsResult>;\n\n /**\n * Materialise executable Mastra tools for one (toolSlugs × connection)\n * call. Runtime fan-out invokes this once per connection and applies\n * naming/suffix logic on top.\n */\n resolveToolsVNext?(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>>;\n\n /** Start an OAuth flow; returns the redirect URL and an opaque auth handle. */\n authorize?(opts: AuthorizeOpts): Promise<{ url: string; authId: string }>;\n\n /**\n * List provider-specific custom fields the user must supply when starting\n * a fresh OAuth flow for `toolkit` (e.g. Confluence subdomain).\n *\n * Returning `[]` means the provider can authorize without extra input.\n * The picker UI shows an inline form when this is non-empty.\n */\n listConnectionFields?(opts: { toolkit: string }): Promise<ConnectionField[]>;\n\n /** Poll the OAuth flow status by `authId`. */\n getAuthStatus?(authId: string): Promise<AuthFlowStatus>;\n\n /**\n * Batch-check whether a set of `(connectionId, toolkit)` tuples are still\n * connected (lazy revocation detection). Result keyed by `connectionId`.\n */\n getConnectionStatus?(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>>;\n\n /**\n * List the underlying provider's existing connections for a given user +\n * toolkit. Used by the picker UI to surface already-authorized accounts\n * so authors can pin them onto an agent without re-running OAuth.\n */\n listConnections?(opts: ListConnectionsOpts): Promise<ListConnectionsResult>;\n\n /** Provider-level health (config, reachability, etc.). */\n getHealth?(): Promise<ToolProviderHealth>;\n\n /**\n * Revoke an existing connection at the provider. Only meaningful when\n * `capabilities.supportsRevoke` is true. Implementations should treat a\n * missing connection (already revoked / never existed) as a success.\n */\n revokeConnection?(connectionId: string): Promise<void>;\n}\n","import type { StorageToolConfig } from '../storage/types';\nimport type { ToolAction } from '../tools/types';\nimport type {\n AuthFlowStatus,\n AuthorizeOpts,\n ConnectionField,\n ListConnectionsOpts,\n ListConnectionsResult,\n ListToolkitsResult,\n ListToolsOpts,\n ListToolsResult,\n ResolveToolsOpts,\n ToolProvider,\n ToolProviderCapabilities,\n ToolProviderHealth,\n ToolProviderInfo,\n ToolProviderListResult,\n ToolProviderToolInfo,\n ToolProviderToolkit,\n} from './types';\n\n/**\n * Constructor options shared by every {@link BaseToolProvider} subclass.\n *\n * Allowlists are matched against the **provider-opaque slugs** returned by\n * `listAllToolkits()` / `listAllTools()`. Two forms are supported:\n *\n * - exact slug match: `'gmail'`\n * - suffix wildcard: `'gmail.*'`\n *\n * `allowedTools` is keyed by toolkit slug. If a toolkit is listed in\n * `allowedToolkits` but **not** present in `allowedTools`, all of its\n * tools are included. An explicit empty array (`allowedTools: { gmail: [] }`)\n * filters everything out for that toolkit.\n */\nexport interface BaseToolProviderOptions {\n /** When set, `listToolkitsVNext()` keeps only toolkits whose slug matches. */\n allowedToolkits?: readonly string[];\n /**\n * Per-toolkit tool allowlist. Keys are toolkit slugs; values are patterns\n * matched against tool slugs (exact or `prefix*`). Omitting a toolkit\n * leaves its tools unfiltered.\n */\n allowedTools?: Readonly<Record<string, readonly string[]>>;\n}\n\n/**\n * Shared base class for concrete {@link ToolProvider} implementations.\n *\n * Subclasses implement the SDK-specific `listAllToolkits` and\n * `listAllTools` methods (and the runtime / auth methods); the base class\n * layers admin allowlist filtering on top so every adapter behaves the\n * same way.\n */\nexport abstract class BaseToolProvider implements ToolProvider {\n abstract readonly info: ToolProviderInfo;\n abstract readonly capabilities: ToolProviderCapabilities;\n\n protected readonly allowedToolkits: readonly string[];\n protected readonly allowedTools: Readonly<Record<string, readonly string[]>>;\n\n constructor(options: BaseToolProviderOptions = {}) {\n this.allowedToolkits = options.allowedToolkits ?? [];\n this.allowedTools = options.allowedTools ?? {};\n }\n\n // ── VNext catalog (filtered) ──────────────────────────────────────────\n\n async listToolkitsVNext(): Promise<ListToolkitsResult> {\n const all = await this.listAllToolkits();\n const data =\n this.allowedToolkits.length === 0 ? all : all.filter(toolkit => matchesAny(toolkit.slug, this.allowedToolkits));\n return { data };\n }\n\n async listToolsVNext(opts: ListToolsOpts = {}): Promise<ListToolsResult> {\n // Deny toolkits not in the allowlist before touching the SDK.\n if (\n opts.toolkit !== undefined &&\n this.allowedToolkits.length > 0 &&\n !matchesAny(opts.toolkit, this.allowedToolkits)\n ) {\n return {\n data: [],\n pagination: { page: opts.page ?? 1, perPage: opts.perPage, hasMore: false },\n };\n }\n const result = await this.listAllTools(opts);\n if (Object.keys(this.allowedTools).length === 0) return result;\n return {\n ...result,\n data: result.data.filter(tool => {\n const toolkit = tool.toolkit;\n if (!toolkit) return true;\n const patterns = this.allowedTools[toolkit];\n if (patterns === undefined) return true;\n return matchesAny(tool.slug, patterns);\n }),\n };\n }\n\n // ── legacy surface (default forwards to VNext) ───────────────────────\n\n async listToolkits(): Promise<ToolProviderListResult<ToolProviderToolkit>> {\n const result = await this.listToolkitsVNext();\n return { data: result.data };\n }\n\n async listTools(options: ListToolsOpts = {}): Promise<ToolProviderListResult<ToolProviderToolInfo>> {\n const result = await this.listToolsVNext(options);\n return { data: result.data, pagination: result.pagination };\n }\n\n /**\n * Legacy `resolveTools` shim — subclasses that opt into the VNext surface\n * normally implement `resolveToolsVNext` instead; the legacy signature\n * delegates so existing callers keep working.\n */\n async resolveTools(\n toolSlugs: string[],\n toolConfigs?: Record<string, StorageToolConfig>,\n options?: { userId?: string; requestContext?: Record<string, unknown>; [key: string]: unknown },\n ): Promise<Record<string, ToolAction<any, any, any>>> {\n return this.resolveToolsVNext({\n toolSlugs,\n toolMeta: Object.fromEntries(\n Object.entries(toolConfigs ?? {}).map(([slug, cfg]) => [slug, { description: cfg?.description }]),\n ),\n connectionId: '',\n authorId: options?.userId,\n requestContext: options?.requestContext,\n });\n }\n\n // ── SDK hooks subclasses implement ────────────────────────────────────\n\n protected abstract listAllToolkits(): Promise<ToolProviderToolkit[]>;\n protected abstract listAllTools(opts: ListToolsOpts): Promise<ListToolsResult>;\n\n abstract resolveToolsVNext(opts: ResolveToolsOpts): Promise<Record<string, ToolAction<any, any, any>>>;\n\n abstract authorize(opts: AuthorizeOpts): Promise<{ url: string; authId: string }>;\n abstract getAuthStatus(authId: string): Promise<AuthFlowStatus>;\n abstract getConnectionStatus(opts: {\n items: Array<{ connectionId: string; toolkit: string }>;\n }): Promise<Record<string, { connected: boolean }>>;\n abstract listConnections(opts: ListConnectionsOpts): Promise<ListConnectionsResult>;\n\n /**\n * Default connection-fields implementation — returns `[]`. Subclasses\n * whose underlying provider requires user-supplied custom fields at\n * authorize time (e.g. Confluence subdomain) should override.\n */\n async listConnectionFields(_opts: { toolkit: string }): Promise<ConnectionField[]> {\n return [];\n }\n\n /**\n * Default health implementation — returns `{ ok: true }`. Subclasses that\n * need to probe SDK reachability or configuration should override.\n */\n async getHealth(): Promise<ToolProviderHealth> {\n return { ok: true };\n }\n}\n\n/**\n * Matches `slug` against an allowlist entry. Supports exact match and a\n * `prefix*` suffix wildcard.\n */\nfunction matchesAny(slug: string, patterns: readonly string[]): boolean {\n for (const pattern of patterns) {\n if (pattern === slug) return true;\n if (pattern.endsWith('*')) {\n const prefix = pattern.slice(0, -1);\n if (slug.startsWith(prefix)) return true;\n }\n }\n return false;\n}\n","import type { IMastraLogger } from '../logger';\nimport { MASTRA_RESOURCE_ID_KEY } from '../request-context';\nimport type { ToolAction } from '../tools/types';\nimport type { ToolProvider, ToolProviderConnection, ToolProviders } from './types';\nimport { SHARED_BUCKET_ID } from './types';\n\n/**\n * Lookup function the runtime uses to resolve a registered provider by id.\n */\nexport type ToolProviderLookup = (providerId: string) => ToolProvider;\n\nexport interface ResolveStoredToolProvidersOpts {\n /** Per-request context plumbed to each `provider.resolveToolsVNext` call. */\n requestContext?: Record<string, unknown>;\n /**\n * Agent author's user id. Used as the provider user bucket for\n * `kind: 'author'` connections so pinned credentials work for any invoker.\n */\n authorId?: string;\n /** Optional logger for non-fatal per-connection warnings. */\n logger?: IMastraLogger;\n}\n\n/**\n * Sanitize a connection label into the suffix segment appended to a tool slug\n * (`__<SUFFIX>`).\n *\n * Rules:\n * - Uppercase.\n * - Non-`[A-Z0-9_]` characters become `_`.\n * - On collision with `usedSuffixes`, append `_2`, `_3`, ... until unique.\n * - The returned suffix is added to `usedSuffixes` in place.\n */\nexport function buildConnectionSuffix(label: string | undefined, usedSuffixes: Set<string>): string {\n // Single linear pass over the input — no regex with quantifiers on\n // user-controlled data (CodeQL js/polynomial-redos).\n const raw = (label ?? '').toUpperCase();\n let base = '';\n let prevWasUnderscore = true; // start true → skips leading underscores\n for (let i = 0; i < raw.length; i++) {\n const c = raw.charCodeAt(i);\n const isAllowed =\n (c >= 0x41 && c <= 0x5a) || // A-Z\n (c >= 0x30 && c <= 0x39); // 0-9\n if (isAllowed) {\n base += raw[i];\n prevWasUnderscore = false;\n } else if (!prevWasUnderscore) {\n base += '_';\n prevWasUnderscore = true;\n }\n }\n if (base.endsWith('_')) base = base.slice(0, -1);\n if (!base) base = 'CONN';\n\n let candidate = base;\n let n = 2;\n while (usedSuffixes.has(candidate)) {\n candidate = `${base}_${n}`;\n n += 1;\n }\n usedSuffixes.add(candidate);\n return candidate;\n}\n\n/**\n * Provider-agnostic runtime fan-out.\n *\n * For every stored `toolProviders[providerId].connections[toolkit]`\n * entry, calls `provider.resolveToolsVNext` once per connection, then renames\n * the resulting tools with a `__<LABEL>` suffix when more than one\n * connection is bound to the same toolkit. Single-connection toolkits keep\n * the natural slug.\n *\n * Each renamed tool also gets a routing hint appended to its description so\n * the LLM can disambiguate between connections.\n *\n * Errors from one connection do **not** poison sibling connections — they are\n * logged and skipped.\n */\nexport async function resolveStoredToolProviders(\n toolProviders: ToolProviders | undefined,\n lookup: ToolProviderLookup,\n opts: ResolveStoredToolProvidersOpts = {},\n): Promise<Record<string, ToolAction<any, any, any>>> {\n const { requestContext, authorId, logger } = opts;\n const out: Record<string, ToolAction<any, any, any>> = {};\n logger?.debug(`[resolveStoredToolProviders] called`, {\n providerIds: Object.keys(toolProviders ?? {}),\n authorId,\n });\n if (!toolProviders || Object.keys(toolProviders).length === 0) {\n logger?.debug(`[resolveStoredToolProviders] no toolProviders on agent — returning {}`);\n return out;\n }\n\n for (const [providerId, cfg] of Object.entries(toolProviders)) {\n let provider: ToolProvider;\n try {\n provider = lookup(providerId);\n } catch (error) {\n logger?.warn(`[resolveStoredToolProviders] Unknown provider \"${providerId}\"`, { error });\n continue;\n }\n\n if (!provider.resolveToolsVNext) {\n logger?.warn(`[resolveStoredToolProviders] Provider \"${providerId}\" does not implement resolveToolsVNext`);\n continue;\n }\n\n const tools = cfg.tools ?? {};\n const connectionsByToolkit = cfg.connections ?? {};\n\n for (const [toolkit, connections] of Object.entries(connectionsByToolkit)) {\n if (!connections || connections.length === 0) {\n logger?.debug(\n `[resolveStoredToolProviders] toolkit \"${toolkit}\" on provider \"${providerId}\" has no pinned connections — skipping`,\n );\n continue;\n }\n\n if (connections.length > 1 && !provider.capabilities?.multipleConnectionsPerToolkit) {\n logger?.warn(\n `[resolveStoredToolProviders] provider \"${providerId}\" does not support multiple ` +\n `connections per toolkit but received ${connections.length} for \"${toolkit}\" — skipping`,\n );\n continue;\n }\n\n // Group selected slugs by ToolProviderToolMeta.toolkit. Falls back to a\n // slug-prefix match (`<toolkit>.<tool>`) for providers that follow the\n // dot convention but didn't write toolkit on the meta entry.\n const slugsForToolkit = Object.entries(tools)\n .filter(([slug, meta]) => (meta?.toolkit ? meta.toolkit === toolkit : slug.startsWith(`${toolkit}.`)))\n .map(([slug]) => slug);\n if (slugsForToolkit.length === 0) {\n logger?.debug(\n `[resolveStoredToolProviders] toolkit \"${toolkit}\" on provider \"${providerId}\" has connections but no matching tool slugs — skipping`,\n { availableSlugs: Object.keys(tools) },\n );\n continue;\n }\n logger?.debug(`[resolveStoredToolProviders] resolving tools for ${providerId}/${toolkit}`, {\n slugs: slugsForToolkit,\n connectionCount: connections.length,\n });\n\n const skipSuffix = connections.length === 1;\n const usedSuffixes = new Set<string>();\n\n for (const connection of connections) {\n const suffix = skipSuffix ? '' : `__${buildConnectionSuffix(connection.label, usedSuffixes)}`;\n\n const resolvedAuthorId = resolveConnectionAuthorId(connection, authorId, requestContext, logger);\n\n let resolved: Record<string, ToolAction<any, any, any>>;\n try {\n resolved = await provider.resolveToolsVNext({\n toolSlugs: slugsForToolkit,\n toolMeta: cfg.tools ?? {},\n connectionId: connection.connectionId,\n authorId: resolvedAuthorId,\n requestContext,\n });\n } catch (error) {\n logger?.warn(\n `[resolveStoredToolProviders] Failed to resolve tools for ${providerId}/${toolkit} ` +\n `connection ${connection.connectionId}`,\n { error },\n );\n continue;\n }\n\n for (const [slug, tool] of Object.entries(resolved)) {\n const renamedSlug = `${slug}${suffix}`;\n const baseDescription = tool.description ?? '';\n const description = skipSuffix ? baseDescription : appendRoutingHint(baseDescription, connection);\n\n out[renamedSlug] = {\n ...tool,\n id: renamedSlug,\n description,\n } as ToolAction<any, any, any>;\n }\n }\n }\n }\n\n return out;\n}\n\n// Emit a single warn per process when the connection-owner fallback fires.\n// Multi-tenant deployments that forget to wire `mapUserToResourceId` silently\n// funnel every `caller-supplied` pin into one shared OAuth account — surface\n// that misconfiguration once.\nlet defaultBucketWarned = false;\nfunction warnDefaultBucketFallback(logger: IMastraLogger | undefined): void {\n if (defaultBucketWarned) return;\n defaultBucketWarned = true;\n logger?.warn(\n '[resolveStoredToolProviders] caller-supplied scope falling back to shared \"default\" bucket — ' +\n 'wire authConfig.mapUserToResourceId to avoid cross-tenant OAuth sharing',\n );\n}\n\n/**\n * Resolve the provider user bucket for a pinned connection.\n *\n * - `kind !== 'author'` → undefined (invoker/platform are reserved for later phases).\n * - `scope === 'shared'` → {@link SHARED_BUCKET_ID}.\n * - `scope === 'caller-supplied'` → `requestContext[MASTRA_RESOURCE_ID_KEY]` when\n * present, otherwise falls back to the shared `'default'` bucket (matching legacy\n * `ComposioToolProvider` semantics on main). Multi-tenant deployments should wire\n * `authConfig.mapUserToResourceId` to avoid cross-user bucket sharing.\n * - otherwise → the caller's resolved authorId.\n */\nfunction resolveConnectionAuthorId(\n connection: ToolProviderConnection,\n callerAuthorId: string | undefined,\n requestContext: Record<string, unknown> | undefined,\n logger: IMastraLogger | undefined,\n): string | undefined {\n if (connection.kind !== 'author') return undefined;\n if (connection.scope === 'shared') return SHARED_BUCKET_ID;\n if (connection.scope === 'caller-supplied') {\n const resourceId = requestContext?.[MASTRA_RESOURCE_ID_KEY];\n if (typeof resourceId === 'string' && resourceId.length > 0) return resourceId;\n // Match legacy ComposioToolProvider behavior: when the host app has not\n // wired requestContext[MASTRA_RESOURCE_ID_KEY] (e.g. via\n // authConfig.mapUserToResourceId), fall back to a shared 'default' bucket\n // so tools still resolve. Multi-tenant deployments must wire the resource\n // id explicitly to avoid cross-user bucket sharing.\n warnDefaultBucketFallback(logger);\n return 'default';\n }\n return callerAuthorId;\n}\n\nfunction appendRoutingHint(description: string, connection: ToolProviderConnection): string {\n const hint = `Routes through connection: ${connection.label ?? connection.connectionId}`;\n if (!description) return hint;\n return `${description}\\n\\n${hint}`;\n}\n","/**\n * Thrown when two {@link ToolProvider} entries share the same `id` during\n * editor / Mastra construction.\n */\nexport class DuplicateToolProviderError extends Error {\n readonly ids: readonly string[];\n\n constructor(ids: readonly string[]) {\n super(`Duplicate tool provider ids: ${ids.join(', ')}`);\n this.name = 'DuplicateToolProviderError';\n this.ids = ids;\n }\n}\n\n/**\n * Thrown when no registered tool provider matches the requested id.\n */\nexport class UnknownToolProviderError extends Error {\n readonly id: string;\n readonly knownIds: readonly string[];\n\n constructor(id: string, knownIds: readonly string[]) {\n super(`Unknown tool provider \"${id}\". Known ids: ${knownIds.length ? knownIds.join(', ') : '(none)'}`);\n this.name = 'UnknownToolProviderError';\n this.id = id;\n this.knownIds = knownIds;\n }\n}\n"]}