@mastra/core
Version:
1 lines • 44 kB
Source Map (JSON)
{"version":3,"file":"index.cjs","names":["MASTRA_RESOURCE_ID_KEY"],"sources":["../../src/tool-provider/types.ts","../../src/tool-provider/base.ts","../../src/tool-provider/runtime.ts","../../src/tool-provider/errors.ts"],"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 /**\n * Identity bucketing for this connection. Providers may use it to decide\n * whether to pin a specific account or let the backend auto-resolve within\n * the user bucket. Absent = treat as `'per-author'` (back-compat).\n */\n scope?: ToolProviderConnectionScope;\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 /**\n * Default identity-bucketing scope for connections authorized against this\n * provider. Set by the app author at construction time — this is a tenancy\n * architecture decision, not an end-user choice. The authorize flow uses it\n * when the request does not specify a `scope`, so a provider configured with\n * `defaultScope: 'caller-supplied'` produces per-tenant connections without\n * any UI control. Falls back to `'per-author'` when absent.\n */\n readonly defaultScope?: ToolProviderConnectionScope;\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 ToolProviderConnectionScope,\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 * Default identity-bucketing scope for connections authorized against this\n * provider. This is the app author's tenancy decision (e.g.\n * `'caller-supplied'` for per-tenant OAuth). The authorize flow applies it\n * whenever a request doesn't override `scope`. Defaults to `'per-author'`.\n */\n defaultScope?: ToolProviderConnectionScope;\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 readonly defaultScope?: ToolProviderConnectionScope;\n\n constructor(options: BaseToolProviderOptions = {}) {\n this.allowedToolkits = options.allowedToolkits ?? [];\n this.allowedTools = options.allowedTools ?? {};\n this.defaultScope = options.defaultScope;\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. A provider\n * whose default scope is `caller-supplied` is also called once for each\n * selected toolkit without a pinned connection, using the request resource id\n * as its dynamic connection bucket. This lets connection-management and other\n * connectionless tools bootstrap a caller's first OAuth connection.\n *\n * Tools resolved through multiple pinned connections are renamed with a\n * `__<LABEL>` suffix. Single-connection and unpinned caller-supplied toolkits\n * keep 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 // `caller-supplied` providers resolve within the current request's user\n // bucket, so they do not need a persisted account pin to materialise a\n // selected toolkit. This is required for bootstrap tools such as\n // COMPOSIO_MANAGE_CONNECTIONS, whose own toolkit intentionally has no\n // OAuth connection.\n if (provider.defaultScope === 'caller-supplied') {\n const unpinnedSlugsByToolkit = new Map<string, string[]>();\n for (const [slug, meta] of Object.entries(tools)) {\n const separatorIndex = slug.indexOf('.');\n const toolkit = meta?.toolkit ?? (separatorIndex > 0 ? slug.slice(0, separatorIndex) : undefined);\n if (!toolkit || connectionsByToolkit[toolkit]?.length) continue;\n const slugs = unpinnedSlugsByToolkit.get(toolkit) ?? [];\n slugs.push(slug);\n unpinnedSlugsByToolkit.set(toolkit, slugs);\n }\n\n if (unpinnedSlugsByToolkit.size > 0) {\n const resolvedAuthorId = resolveCallerSuppliedAuthorId(requestContext, logger);\n for (const [toolkit, toolSlugs] of unpinnedSlugsByToolkit) {\n logger?.debug(\n `[resolveStoredToolProviders] resolving unpinned caller-supplied tools for ${providerId}/${toolkit}`,\n { slugs: toolSlugs },\n );\n\n try {\n const resolved = await provider.resolveToolsVNext({\n toolSlugs,\n toolMeta: tools,\n connectionId: resolvedAuthorId,\n authorId: resolvedAuthorId,\n scope: 'caller-supplied',\n requestContext,\n });\n\n for (const [slug, tool] of Object.entries(resolved)) {\n out[slug] = { ...tool, id: slug } as ToolAction<any, any, any>;\n }\n } catch (error) {\n logger?.warn(\n `[resolveStoredToolProviders] Failed to resolve unpinned caller-supplied tools for ${providerId}/${toolkit}`,\n { error },\n );\n }\n }\n }\n }\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 scope: connection.scope,\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 return resolveCallerSuppliedAuthorId(requestContext, logger);\n }\n return callerAuthorId;\n}\n\nfunction resolveCallerSuppliedAuthorId(\n requestContext: Record<string, unknown> | undefined,\n logger: IMastraLogger | undefined,\n): string {\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\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"],"mappings":"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"}