@stackone/ai
Advanced tools
+1
-1
| //#region package.json | ||
| var version = "2.8.0"; | ||
| var version = "2.8.1"; | ||
| var peerDependencies = { | ||
@@ -4,0 +4,0 @@ "@anthropic-ai/claude-agent-sdk": "catalog:peer", |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n\t\"name\": \"@stackone/ai\",\n\t\"version\": \"2.8.0\",\n\t\"description\": \"Tools for agents to perform actions on your SaaS\",\n\t\"keywords\": [\n\t\t\"agents\",\n\t\t\"ai\",\n\t\t\"ai sdk\",\n\t\t\"mcp\",\n\t\t\"model context protocol\",\n\t\t\"stackone\",\n\t\t\"tools\"\n\t],\n\t\"homepage\": \"https://github.com/StackOneHQ/stackone-ai-node#readme\",\n\t\"bugs\": \"https://github.com/StackOneHQ/stackone-ai-node/issues\",\n\t\"license\": \"Apache-2.0\",\n\t\"author\": \"StackOne\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/StackOneHQ/stackone-ai-node.git\"\n\t},\n\t\"files\": [\n\t\t\"dist\",\n\t\t\"LICENSE\",\n\t\t\"README.md\",\n\t\t\"src\",\n\t\t\"examples/*.ts\",\n\t\t\"!src/**/*.test-d.ts\",\n\t\t\"!src/**/*.test.ts\"\n\t],\n\t\"type\": \"module\",\n\t\"main\": \"./dist/index.mjs\",\n\t\"module\": \"./dist/index.mjs\",\n\t\"types\": \"./dist/index.d.mts\",\n\t\"exports\": {\n\t\t\".\": \"./src/index.ts\",\n\t\t\"./package.json\": \"./package.json\"\n\t},\n\t\"publishConfig\": {\n\t\t\"exports\": {\n\t\t\t\".\": \"./dist/index.mjs\",\n\t\t\t\"./package.json\": \"./package.json\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"format\": \"pnpm --no-bail --aggregate-output run '/^format:/'\",\n\t\t\"format:oxfmt\": \"oxfmt --no-error-on-unmatched-pattern .\",\n\t\t\"format:oxlint\": \"oxlint --max-warnings=0 --type-aware --type-check --fix\",\n\t\t\"format:knip\": \"knip --fix --no-exit-code\",\n\t\t\"lint\": \"pnpm --aggregate-output run '/^lint:/'\",\n\t\t\"lint:oxfmt\": \"oxfmt --no-error-on-unmatched-pattern --check .\",\n\t\t\"lint:oxlint\": \"oxlint --max-warnings=0 --type-aware --type-check\",\n\t\t\"lint:knip\": \"knip\",\n\t\t\"prepack\": \"npm pkg delete scripts.preinstall && pnpm run build\",\n\t\t\"test\": \"vitest\",\n\t\t\"coverage\": \"vitest run --coverage\",\n\t\t\"run:example\": \"tsx --env-file .env\"\n\t},\n\t\"dependencies\": {\n\t\t\"@modelcontextprotocol/sdk\": \"catalog:prod\",\n\t\t\"@orama/orama\": \"catalog:prod\",\n\t\t\"defu\": \"catalog:prod\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@fast-check/vitest\": \"catalog:dev\",\n\t\t\"@hono/mcp\": \"catalog:dev\",\n\t\t\"@types/node\": \"catalog:dev\",\n\t\t\"@vitest/coverage-v8\": \"catalog:dev\",\n\t\t\"ai\": \"catalog:dev\",\n\t\t\"hono\": \"catalog:dev\",\n\t\t\"knip\": \"catalog:dev\",\n\t\t\"msw\": \"catalog:dev\",\n\t\t\"openai\": \"catalog:peer\",\n\t\t\"publint\": \"catalog:dev\",\n\t\t\"tsdown\": \"catalog:dev\",\n\t\t\"type-fest\": \"catalog:dev\",\n\t\t\"unplugin-unused\": \"catalog:dev\",\n\t\t\"vitest\": \"catalog:dev\",\n\t\t\"zod\": \"catalog:dev\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"@anthropic-ai/claude-agent-sdk\": \"catalog:peer\",\n\t\t\"@anthropic-ai/sdk\": \"catalog:peer\",\n\t\t\"ai\": \"catalog:peer\",\n\t\t\"openai\": \"catalog:peer\",\n\t\t\"zod\": \"catalog:peer\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@anthropic-ai/claude-agent-sdk\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"@anthropic-ai/sdk\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"ai\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"openai\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devEngines\": {\n\t\t\"runtime\": [\n\t\t\t{\n\t\t\t\t\"name\": \"node\",\n\t\t\t\t\"version\": \"^24.11.0\",\n\t\t\t\t\"onFail\": \"download\"\n\t\t\t}\n\t\t]\n\t},\n\t\"engines\": {\n\t\t\"node\": \">=20.19.6\"\n\t},\n\t\"packageManager\": \"pnpm@10.26.0\"\n}\n"],"mappings":";cAEY;uBA+ES;CACnB,kCAAkC;CAClC,qBAAqB;CACrB,MAAM;CACN,UAAU;CACV,OAAO;CACP"} | ||
| {"version":3,"file":"package.mjs","names":[],"sources":["../package.json"],"sourcesContent":["{\n\t\"name\": \"@stackone/ai\",\n\t\"version\": \"2.8.1\",\n\t\"description\": \"Tools for agents to perform actions on your SaaS\",\n\t\"keywords\": [\n\t\t\"agents\",\n\t\t\"ai\",\n\t\t\"ai sdk\",\n\t\t\"mcp\",\n\t\t\"model context protocol\",\n\t\t\"stackone\",\n\t\t\"tools\"\n\t],\n\t\"homepage\": \"https://github.com/StackOneHQ/stackone-ai-node#readme\",\n\t\"bugs\": \"https://github.com/StackOneHQ/stackone-ai-node/issues\",\n\t\"license\": \"Apache-2.0\",\n\t\"author\": \"StackOne\",\n\t\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/StackOneHQ/stackone-ai-node.git\"\n\t},\n\t\"files\": [\n\t\t\"dist\",\n\t\t\"LICENSE\",\n\t\t\"README.md\",\n\t\t\"src\",\n\t\t\"examples/*.ts\",\n\t\t\"!src/**/*.test-d.ts\",\n\t\t\"!src/**/*.test.ts\"\n\t],\n\t\"type\": \"module\",\n\t\"main\": \"./dist/index.mjs\",\n\t\"module\": \"./dist/index.mjs\",\n\t\"types\": \"./dist/index.d.mts\",\n\t\"exports\": {\n\t\t\".\": \"./src/index.ts\",\n\t\t\"./package.json\": \"./package.json\"\n\t},\n\t\"publishConfig\": {\n\t\t\"exports\": {\n\t\t\t\".\": \"./dist/index.mjs\",\n\t\t\t\"./package.json\": \"./package.json\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsdown\",\n\t\t\"format\": \"pnpm --no-bail --aggregate-output run '/^format:/'\",\n\t\t\"format:oxfmt\": \"oxfmt --no-error-on-unmatched-pattern .\",\n\t\t\"format:oxlint\": \"oxlint --max-warnings=0 --type-aware --type-check --fix\",\n\t\t\"format:knip\": \"knip --fix --no-exit-code\",\n\t\t\"lint\": \"pnpm --aggregate-output run '/^lint:/'\",\n\t\t\"lint:oxfmt\": \"oxfmt --no-error-on-unmatched-pattern --check .\",\n\t\t\"lint:oxlint\": \"oxlint --max-warnings=0 --type-aware --type-check\",\n\t\t\"lint:knip\": \"knip\",\n\t\t\"prepack\": \"npm pkg delete scripts.preinstall && pnpm run build\",\n\t\t\"test\": \"vitest\",\n\t\t\"coverage\": \"vitest run --coverage\",\n\t\t\"run:example\": \"tsx --env-file .env\"\n\t},\n\t\"dependencies\": {\n\t\t\"@modelcontextprotocol/sdk\": \"catalog:prod\",\n\t\t\"@orama/orama\": \"catalog:prod\",\n\t\t\"defu\": \"catalog:prod\"\n\t},\n\t\"devDependencies\": {\n\t\t\"@fast-check/vitest\": \"catalog:dev\",\n\t\t\"@hono/mcp\": \"catalog:dev\",\n\t\t\"@types/node\": \"catalog:dev\",\n\t\t\"@vitest/coverage-v8\": \"catalog:dev\",\n\t\t\"ai\": \"catalog:dev\",\n\t\t\"hono\": \"catalog:dev\",\n\t\t\"knip\": \"catalog:dev\",\n\t\t\"msw\": \"catalog:dev\",\n\t\t\"openai\": \"catalog:peer\",\n\t\t\"publint\": \"catalog:dev\",\n\t\t\"tsdown\": \"catalog:dev\",\n\t\t\"type-fest\": \"catalog:dev\",\n\t\t\"unplugin-unused\": \"catalog:dev\",\n\t\t\"vitest\": \"catalog:dev\",\n\t\t\"zod\": \"catalog:dev\"\n\t},\n\t\"peerDependencies\": {\n\t\t\"@anthropic-ai/claude-agent-sdk\": \"catalog:peer\",\n\t\t\"@anthropic-ai/sdk\": \"catalog:peer\",\n\t\t\"ai\": \"catalog:peer\",\n\t\t\"openai\": \"catalog:peer\",\n\t\t\"zod\": \"catalog:peer\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@anthropic-ai/claude-agent-sdk\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"@anthropic-ai/sdk\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"ai\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"openai\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devEngines\": {\n\t\t\"runtime\": [\n\t\t\t{\n\t\t\t\t\"name\": \"node\",\n\t\t\t\t\"version\": \"^24.11.0\",\n\t\t\t\t\"onFail\": \"download\"\n\t\t\t}\n\t\t]\n\t},\n\t\"engines\": {\n\t\t\"node\": \">=20.19.6\"\n\t},\n\t\"packageManager\": \"pnpm@10.26.0\"\n}\n"],"mappings":";cAEY;uBA+ES;CACnB,kCAAkC;CAClC,qBAAqB;CACrB,MAAM;CACN,UAAU;CACV,OAAO;CACP"} |
+13
-17
@@ -605,17 +605,10 @@ import { DEFAULT_BASE_URL } from "./consts.mjs"; | ||
| const toolsPromises = effectiveAccountIds.map(async (accountId) => { | ||
| const headers = { "x-account-id": accountId }; | ||
| const tempHeaders = { | ||
| const requestHeaders = { | ||
| ...this.headers, | ||
| ...headers | ||
| "x-account-id": accountId | ||
| }; | ||
| const originalHeaders = this.headers; | ||
| this.headers = tempHeaders; | ||
| try { | ||
| return (await this.fetchToolsFromMcp()).toArray(); | ||
| } finally { | ||
| this.headers = originalHeaders; | ||
| } | ||
| return (await this.fetchToolsFromMcp(requestHeaders)).toArray(); | ||
| }); | ||
| tools = new Tools((await Promise.all(toolsPromises)).flat()); | ||
| } else tools = await this.fetchToolsFromMcp(); | ||
| } else tools = await this.fetchToolsFromMcp(this.headers); | ||
| const filteredTools = this.filterTools(tools, options); | ||
@@ -628,5 +621,7 @@ const feedbackTool = createFeedbackTool(void 0, this.accountId, this.baseUrl); | ||
| /** | ||
| * Fetch tool definitions from MCP | ||
| * Fetch tool definitions from MCP using the given request headers. | ||
| * Headers are passed in (not read from this.headers) so concurrent callers | ||
| * can each scope their request to a different x-account-id safely. | ||
| */ | ||
| async fetchToolsFromMcp() { | ||
| async fetchToolsFromMcp(requestHeaders) { | ||
| try { | ||
@@ -637,3 +632,3 @@ var _usingCtx$1 = _usingCtx(); | ||
| baseUrl: `${this.baseUrl}/mcp`, | ||
| headers: this.headers | ||
| headers: requestHeaders | ||
| })); | ||
@@ -648,3 +643,4 @@ await clients.client.connect(clients.transport); | ||
| description, | ||
| inputSchema | ||
| inputSchema, | ||
| headers: requestHeaders | ||
| }); | ||
@@ -701,3 +697,3 @@ })); | ||
| } | ||
| createRpcBackedTool({ actionsClient, name, description, inputSchema }) { | ||
| createRpcBackedTool({ actionsClient, name, description, inputSchema, headers }) { | ||
| const executeConfig = { | ||
@@ -719,3 +715,3 @@ kind: "rpc", | ||
| }; | ||
| const tool = new BaseTool(name, description ?? "", toolParameters, executeConfig, this.headers).setExposeExecutionMetadata(false); | ||
| const tool = new BaseTool(name, description ?? "", toolParameters, executeConfig, headers).setExposeExecutionMetadata(false); | ||
| tool.execute = async (inputParams, options) => { | ||
@@ -722,0 +718,0 @@ try { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"toolsets.mjs","names":["result: JsonObject","cachedTools: Awaited<ReturnType<typeof toolset.fetchTools>> | null","authentication: AuthenticationConfig","config: SearchConfig","connectors: string | undefined","connectorsToSearch: Set<string>","client: SemanticSearchClient","allResults: SemanticSearchResult[]","lastError: SemanticSearchError | undefined","actionNames: string[]","availableConnectors: Set<string> | undefined","tools: Tools","rpcBody: JsonObject"],"sources":["../../src/toolsets.ts"],"sourcesContent":["import { defu } from 'defu';\nimport type { MergeExclusive, SimplifyDeep } from 'type-fest';\nimport { z } from 'zod/v4';\nimport { DEFAULT_BASE_URL } from './consts';\nimport { createFeedbackTool } from './feedback';\nimport { type StackOneHeaders, normalizeHeaders, stackOneHeadersSchema } from './headers';\nimport { ToolIndex } from './local-search';\nimport { createMCPClient } from './mcp-client';\nimport { type RpcActionResponse, RpcClient } from './rpc-client';\nimport {\n\tSemanticSearchClient,\n\tSemanticSearchError,\n\ttype SemanticSearchResult,\n} from './semantic-search';\nimport { BaseTool, Tools } from './tool';\nimport type {\n\tExecuteOptions,\n\tJsonObject,\n\tJsonSchemaProperties,\n\tLocalExecuteConfig,\n\tRpcExecuteConfig,\n\tSearchConfig,\n\tToolParameters,\n} from './types';\nimport { StackOneError } from './utils/error-stackone';\nimport { StackOneAPIError } from './utils/error-stackone-api';\nimport { normalizeActionName } from './utils/normalize';\n\n/**\n * Converts RpcActionResponse to JsonObject in a type-safe manner.\n * RpcActionResponse uses z.passthrough() which preserves additional fields,\n * making it structurally compatible with Record<string, JsonValue>.\n */\nfunction rpcResponseToJsonObject(response: RpcActionResponse): JsonObject {\n\t// RpcActionResponse with passthrough() has the shape:\n\t// { next?: string | null, data?: ..., [key: string]: unknown }\n\t// We extract all properties into a plain object\n\tconst result: JsonObject = {};\n\tfor (const [key, value] of Object.entries(response)) {\n\t\tresult[key] = value as JsonObject[string];\n\t}\n\treturn result;\n}\n\ntype ToolInputSchema = Awaited<\n\tReturnType<Awaited<ReturnType<typeof createMCPClient>>['client']['listTools']>\n>['tools'][number]['inputSchema'];\n\n/**\n * Base exception for toolset errors\n */\nexport class ToolSetError extends Error {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetError';\n\t}\n}\n\n/**\n * Raised when there is an error in the toolset configuration\n */\nexport class ToolSetConfigError extends ToolSetError {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetConfigError';\n\t}\n}\n\n/**\n * Raised when there is an error loading tools\n */\nexport class ToolSetLoadError extends ToolSetError {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetLoadError';\n\t}\n}\n\n/**\n * Authentication configuration for toolsets\n */\nexport interface AuthenticationConfig {\n\ttype: 'basic' | 'bearer';\n\tcredentials?: {\n\t\tusername?: string;\n\t\tpassword?: string;\n\t\ttoken?: string;\n\t};\n\theaders?: Record<string, string>;\n}\n\n/**\n * Base configuration for all toolsets\n */\nexport interface BaseToolSetConfig {\n\tbaseUrl?: string;\n\tauthentication?: AuthenticationConfig;\n\theaders?: Record<string, string>;\n\trpcClient?: RpcClient;\n\t/** Request timeout in milliseconds. Default: 60000 (60s). */\n\ttimeout?: number;\n}\n\n/**\n * Configuration with a single account ID\n */\ninterface SingleAccountConfig {\n\t/**\n\t * Single account ID for StackOne API operations\n\t * Use this when working with a single account\n\t */\n\taccountId: string;\n}\n\n/**\n * Configuration with multiple account IDs\n */\ninterface MultipleAccountsConfig {\n\t/**\n\t * Array of account IDs for filtering tools across multiple accounts\n\t * When provided, tools will be fetched for all specified accounts\n\t * @example ['account-1', 'account-2']\n\t */\n\taccountIds: string[];\n}\n\n/**\n * Account configuration options - either single accountId or multiple accountIds, but not both\n */\ntype AccountConfig = SimplifyDeep<MergeExclusive<SingleAccountConfig, MultipleAccountsConfig>>;\n\n/**\n * Execution configuration for the StackOneToolSet constructor.\n * Controls default account scoping for tool execution in tools.\n */\nexport interface ExecuteToolsConfig {\n\t/** Account IDs to scope tool discovery and execution. */\n\taccountIds?: string[];\n\t/** Request timeout in milliseconds. Can also be set as a top-level config param which takes precedence. */\n\ttimeout?: number;\n}\n\n/**\n * Base configuration for StackOne toolset (without account options)\n */\ninterface StackOneToolSetBaseConfig extends BaseToolSetConfig {\n\tapiKey?: string;\n\tstrict?: boolean;\n\t/**\n\t * Search configuration. Controls default search behavior for `searchTools()`,\n\t * `getSearchTool()`, and `searchActionNames()`.\n\t *\n\t * - Omit or pass `undefined` → search disabled (`null`)\n\t * - Pass `null` → search disabled\n\t * - Pass `{}` or `{ method: 'auto' }` → search enabled with defaults\n\t * - Pass `{ method, topK, minSimilarity }` → search enabled with custom defaults\n\t *\n\t * Per-call options always override these defaults.\n\t */\n\tsearch?: SearchConfig | null;\n\t/**\n\t * Execution configuration. Controls default account scoping for tool execution.\n\t * Pass `{ accountIds: ['acc-1'] }` to scope tools to specific accounts.\n\t */\n\texecute?: ExecuteToolsConfig;\n}\n\n/**\n * Configuration for StackOne toolset\n * Accepts either accountId (single) or accountIds (multiple), but not both\n */\nexport type StackOneToolSetConfig = StackOneToolSetBaseConfig & Partial<AccountConfig>;\n\n/**\n * Options for filtering tools when fetching from MCP\n */\ninterface FetchToolsOptions {\n\t/**\n\t * Filter tools by account IDs\n\t * Only tools available on these accounts will be returned\n\t */\n\taccountIds?: string[];\n\n\t/**\n\t * Filter tools by provider names\n\t * Only tools from these providers will be returned\n\t * @example ['hibob', 'bamboohr']\n\t */\n\tproviders?: string[];\n\n\t/**\n\t * Filter tools by action patterns with glob support\n\t * Only tools matching these patterns will be returned\n\t * @example ['*_list_employees', 'hibob_create_employees']\n\t */\n\tactions?: string[];\n}\n\n/**\n * Search mode for tool discovery.\n *\n * - `\"auto\"` (default): try semantic search first, fall back to local BM25+TF-IDF if the API is unavailable\n * - `\"semantic\"`: use only the semantic search API; throws SemanticSearchError on failure\n * - `\"local\"`: use only local BM25+TF-IDF search (no API call to the semantic search endpoint)\n */\nexport type SearchMode = 'auto' | 'semantic' | 'local';\n\n/**\n * Options for searchTools() and SearchTool\n */\nexport interface SearchToolsOptions {\n\t/** Optional provider/connector filter (e.g., \"bamboohr\", \"slack\") */\n\tconnector?: string;\n\t/** Maximum number of tools to return */\n\ttopK?: number;\n\t/** Minimum similarity score threshold 0-1 */\n\tminSimilarity?: number;\n\t/** Optional account IDs (uses setAccounts() if not provided) */\n\taccountIds?: string[];\n\t/** Search backend to use */\n\tsearch?: SearchMode;\n}\n\n/**\n * Options for searchActionNames()\n */\nexport interface SearchActionNamesOptions {\n\t/** Optional provider/connector filter */\n\tconnector?: string;\n\t/** Optional account IDs to scope results */\n\taccountIds?: string[];\n\t/** Maximum number of results */\n\ttopK?: number;\n\t/** Minimum similarity score threshold 0-1 */\n\tminSimilarity?: number;\n}\n\n/**\n * Callable search tool that wraps StackOneToolSet.searchTools().\n *\n * Designed for agent loops — call `search()` with a query to get Tools back.\n *\n * @example\n * ```typescript\n * const toolset = new StackOneToolSet({ apiKey: 'sk-xxx' });\n * const searchTool = toolset.getSearchTool();\n * const tools = await searchTool.search('manage employee records', { accountIds: ['acc-123'] });\n * ```\n */\nexport class SearchTool {\n\tprivate readonly toolset: StackOneToolSet;\n\tprivate readonly defaultConfig: SearchConfig;\n\n\tconstructor(toolset: StackOneToolSet, config: SearchConfig = {}) {\n\t\tthis.toolset = toolset;\n\t\tthis.defaultConfig = config;\n\t}\n\n\t/**\n\t * Search for tools using natural language.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * @param options - Search options (connector, topK, minSimilarity, accountIds, search).\n\t * Per-call options override the defaults from the constructor config.\n\t * @returns Tools collection with matched tools\n\t */\n\tasync search(query: string, options?: SearchToolsOptions): Promise<Tools> {\n\t\treturn this.toolset.searchTools(query, {\n\t\t\t...options,\n\t\t\tsearch: options?.search ?? this.defaultConfig.method,\n\t\t\ttopK: options?.topK ?? this.defaultConfig.topK,\n\t\t\tminSimilarity: options?.minSimilarity ?? this.defaultConfig.minSimilarity,\n\t\t});\n\t}\n}\n\n// --- Internal tool_search + tool_execute ---\n\nconst searchInputSchema = z.object({\n\tquery: z\n\t\t.string()\n\t\t.transform((v) => v.trim())\n\t\t.refine((v) => v.length > 0, { message: 'query must be a non-empty string' }),\n\tconnector: z.string().optional(),\n\ttop_k: z.number().int().min(1).max(50).optional(),\n});\n\nconst searchParameters = {\n\ttype: 'object',\n\tproperties: {\n\t\tquery: {\n\t\t\ttype: 'string',\n\t\t\tdescription:\n\t\t\t\t'Natural language description of what you need (e.g. \"create an employee\", \"list time off requests\")',\n\t\t},\n\t\tconnector: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Optional connector filter (e.g. \"bamboohr\", \"hibob\")',\n\t\t},\n\t\ttop_k: {\n\t\t\ttype: 'integer',\n\t\t\tdescription: 'Max results to return (1-50, default 5)',\n\t\t\tminimum: 1,\n\t\t\tmaximum: 50,\n\t\t},\n\t},\n\trequired: ['query'],\n} as const satisfies ToolParameters;\n\nconst executeInputSchema = z.object({\n\ttool_name: z\n\t\t.string()\n\t\t.transform((v) => v.trim())\n\t\t.refine((v) => v.length > 0, { message: 'tool_name must be a non-empty string' }),\n\tparameters: z.record(z.string(), z.unknown()).optional().default({}),\n});\n\nconst executeParameters = {\n\ttype: 'object',\n\tproperties: {\n\t\ttool_name: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Exact tool name from tool_search results',\n\t\t},\n\t\tparameters: {\n\t\t\ttype: 'object',\n\t\t\tdescription: 'Parameters for the tool. Pass an empty object {} if no parameters are needed.',\n\t\t},\n\t},\n\trequired: ['tool_name'],\n} as const satisfies ToolParameters;\n\nconst localConfig = (id: string): LocalExecuteConfig => ({\n\tkind: 'local',\n\tidentifier: `meta:${id}`,\n});\n\n/** @internal */\nexport function createSearchTool(\n\ttoolset: StackOneToolSet,\n\taccountIds?: string[],\n\tconnectors?: string,\n): BaseTool {\n\tconst connectorLine = connectors ? ` Available connectors: ${connectors}.` : '';\n\tconst tool = new BaseTool(\n\t\t'tool_search',\n\t\t`Search for available tools by describing what you need. Returns matching tool names, descriptions, and parameter schemas. Use the returned parameter schemas to know exactly what to pass when calling tool_execute.${connectorLine}`,\n\t\tsearchParameters,\n\t\tlocalConfig('search'),\n\t);\n\n\ttool.execute = async (inputParams?: JsonObject | string): Promise<JsonObject> => {\n\t\ttry {\n\t\t\tconst raw = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsed = searchInputSchema.parse(raw);\n\n\t\t\tconst searchConfig = toolset.getSearchConfig() ?? {};\n\t\t\tconst results = await toolset.searchTools(parsed.query, {\n\t\t\t\tconnector: parsed.connector,\n\t\t\t\ttopK: parsed.top_k ?? searchConfig.topK,\n\t\t\t\tminSimilarity: searchConfig.minSimilarity,\n\t\t\t\tsearch: searchConfig.method,\n\t\t\t\taccountIds,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\ttools: results.toArray().map((t) => ({\n\t\t\t\t\tname: t.name,\n\t\t\t\t\tdescription: t.description,\n\t\t\t\t\tparameters: t.parameters.properties as unknown as JsonObject,\n\t\t\t\t})),\n\t\t\t\ttotal: results.length,\n\t\t\t\tquery: parsed.query,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneAPIError) {\n\t\t\t\treturn { error: error.message, status_code: error.statusCode };\n\t\t\t}\n\t\t\tif (error instanceof SyntaxError || error instanceof z.ZodError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Invalid input: ${error instanceof z.ZodError ? error.issues.map((i) => i.message).join(', ') : error.message}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\treturn tool;\n}\n\n/** @internal */\nexport function createExecuteTool(\n\ttoolset: StackOneToolSet,\n\taccountIds?: string[],\n\tconnectors?: string,\n): BaseTool {\n\tlet cachedTools: Awaited<ReturnType<typeof toolset.fetchTools>> | null = null;\n\n\tconst connectorLine = connectors ? ` Available connectors: ${connectors}.` : '';\n\tconst tool = new BaseTool(\n\t\t'tool_execute',\n\t\t`Execute a tool by name with the given parameters. Use tool_search first to find available tools. The parameters field must match the parameter schema returned by tool_search. Pass parameters as a nested object matching the schema structure.${connectorLine}`,\n\t\texecuteParameters,\n\t\tlocalConfig('execute'),\n\t);\n\n\ttool.execute = async (\n\t\tinputParams?: JsonObject | string,\n\t\texecuteOptions?: ExecuteOptions,\n\t): Promise<JsonObject> => {\n\t\tlet toolName = 'unknown';\n\t\ttry {\n\t\t\tconst raw = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsed = executeInputSchema.parse(raw);\n\t\t\ttoolName = parsed.tool_name;\n\n\t\t\tif (!cachedTools) {\n\t\t\t\tcachedTools = await toolset.fetchTools({ accountIds });\n\t\t\t}\n\t\t\tconst target = cachedTools.getTool(parsed.tool_name);\n\n\t\t\tif (!target) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Tool \"${parsed.tool_name}\" not found. Use tool_search to find available tools.`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn await target.execute(parsed.parameters as JsonObject, executeOptions);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneAPIError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tstatus_code: error.statusCode,\n\t\t\t\t\tresponse_body: error.responseBody as JsonObject,\n\t\t\t\t\ttool_name: toolName,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (error instanceof SyntaxError || error instanceof z.ZodError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Invalid input: ${error instanceof z.ZodError ? error.issues.map((i) => i.message).join(', ') : error.message}`,\n\t\t\t\t\ttool_name: toolName,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\treturn tool;\n}\n\n/**\n * Class for loading StackOne tools via MCP\n */\nexport class StackOneToolSet {\n\tprivate baseUrl?: string;\n\tprivate authentication?: AuthenticationConfig;\n\tprivate headers: Record<string, string>;\n\tprivate rpcClient?: RpcClient;\n\tprivate readonly timeout: number;\n\tprivate readonly searchConfig: SearchConfig | null;\n\tprivate readonly executeConfig: ExecuteToolsConfig | undefined;\n\n\t/**\n\t * Account ID for StackOne API\n\t */\n\tprivate accountId?: string;\n\tprivate accountIds: string[] = [];\n\n\t/**\n\t * Initialize StackOne toolset with API key and optional account ID(s)\n\t * @param config Configuration object containing API key and optional account ID(s)\n\t */\n\tconstructor(config?: StackOneToolSetConfig) {\n\t\t// Validate mutually exclusive account options\n\t\tif (config?.accountId != null && config?.accountIds != null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Cannot provide both accountId and accountIds. Use accountId for a single account or accountIds for multiple accounts.',\n\t\t\t);\n\t\t}\n\n\t\tconst apiKey = config?.apiKey || process.env.STACKONE_API_KEY;\n\n\t\tif (!apiKey && config?.strict) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'No API key provided. Set STACKONE_API_KEY environment variable or pass apiKey in config.',\n\t\t\t);\n\t\t}\n\n\t\tif (!apiKey) {\n\t\t\tconsole.warn(\n\t\t\t\t'No API key provided. Set STACKONE_API_KEY environment variable or pass apiKey in config.',\n\t\t\t);\n\t\t}\n\n\t\tconst authentication: AuthenticationConfig = {\n\t\t\ttype: 'basic',\n\t\t\tcredentials: {\n\t\t\t\tusername: apiKey || '',\n\t\t\t\tpassword: '',\n\t\t\t},\n\t\t};\n\n\t\tconst accountId = config?.accountId || process.env.STACKONE_ACCOUNT_ID;\n\n\t\tconst configHeaders = {\n\t\t\t...config?.headers,\n\t\t\t...(accountId ? { 'x-account-id': accountId } : {}),\n\t\t};\n\n\t\t// Initialize base properties\n\t\tthis.baseUrl = config?.baseUrl ?? process.env.STACKONE_BASE_URL ?? DEFAULT_BASE_URL;\n\t\tthis.authentication = authentication;\n\t\tthis.headers = configHeaders;\n\t\tthis.rpcClient = config?.rpcClient;\n\t\tthis.timeout = config?.timeout ?? config?.execute?.timeout ?? 60_000;\n\t\tthis.accountId = accountId;\n\t\tthis.accountIds = config?.accountIds ?? [];\n\n\t\t// Resolve search config: undefined/null → disabled, object → custom with defaults\n\t\tthis.searchConfig = config?.search != null ? { method: 'auto', ...config.search } : null;\n\t\tthis.executeConfig = config?.execute;\n\n\t\t// Set Authentication headers if provided\n\t\tif (this.authentication) {\n\t\t\t// Only set auth headers if they don't already exist in custom headers\n\t\t\tconst needsAuthHeader = !('Authorization' in this.headers);\n\n\t\t\tif (needsAuthHeader) {\n\t\t\t\tswitch (this.authentication.type) {\n\t\t\t\t\tcase 'basic':\n\t\t\t\t\t\tif (this.authentication.credentials?.username) {\n\t\t\t\t\t\t\tconst username = this.authentication.credentials.username;\n\t\t\t\t\t\t\tconst password = this.authentication.credentials.password || '';\n\t\t\t\t\t\t\tconst authString = Buffer.from(`${username}:${password}`).toString('base64');\n\t\t\t\t\t\t\tthis.headers.Authorization = `Basic ${authString}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'bearer':\n\t\t\t\t\t\tif (this.authentication.credentials?.token) {\n\t\t\t\t\t\t\tthis.headers.Authorization = `Bearer ${this.authentication.credentials.token}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthis.authentication.type satisfies never;\n\t\t\t\t\t\tthrow new ToolSetError(\n\t\t\t\t\t\t\t`Unsupported authentication type: ${String(this.authentication.type)}`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add any additional headers from authentication config, but don't override existing ones\n\t\t\tif (this.authentication.headers) {\n\t\t\t\tthis.headers = { ...this.authentication.headers, ...this.headers };\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate semanticSearchClient?: SemanticSearchClient;\n\tprivate catalogCache: Map<string, Tools> = new Map();\n\tprivate toolIndexCache?: { tools: Tools; index: ToolIndex };\n\n\t/**\n\t * Set account IDs for filtering tools\n\t * @param accountIds Array of account IDs to filter tools by\n\t * @returns This toolset instance for chaining\n\t */\n\tsetAccounts(accountIds: string[]): this {\n\t\tthis.accountIds = accountIds;\n\t\tthis.clearCatalogCache();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Invalidate cached tool catalog and local search index.\n\t *\n\t * Call when linked accounts change outside of {@link setAccounts} or when\n\t * you need to force a fresh fetch from the StackOne MCP endpoint.\n\t */\n\tclearCatalogCache(): void {\n\t\tthis.catalogCache.clear();\n\t\tthis.toolIndexCache = undefined;\n\t}\n\n\t/**\n\t * Get or lazily create the semantic search client.\n\t */\n\tprivate getSemanticClient(): SemanticSearchClient {\n\t\tif (!this.semanticSearchClient) {\n\t\t\tconst apiKey = this.getApiKey();\n\t\t\tthis.semanticSearchClient = new SemanticSearchClient({\n\t\t\t\tapiKey,\n\t\t\t\tbaseUrl: this.baseUrl,\n\t\t\t});\n\t\t}\n\t\treturn this.semanticSearchClient;\n\t}\n\n\t/**\n\t * Get the current search config.\n\t */\n\tgetSearchConfig(): SearchConfig | null {\n\t\treturn this.searchConfig;\n\t}\n\n\t/**\n\t * Extract the API key from authentication config.\n\t */\n\tprivate getApiKey(): string {\n\t\tconst credentials = this.authentication?.credentials ?? {};\n\t\tconst apiKeyFromAuth =\n\t\t\tthis.authentication?.type === 'basic'\n\t\t\t\t? credentials.username\n\t\t\t\t: this.authentication?.type === 'bearer'\n\t\t\t\t\t? credentials.token\n\t\t\t\t\t: credentials.username;\n\n\t\tconst apiKey = apiKeyFromAuth || process.env.STACKONE_API_KEY;\n\t\tif (!apiKey) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'API key is required for semantic search. Provide apiKey in config or set STACKONE_API_KEY environment variable.',\n\t\t\t);\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\t/**\n\t * Get a callable search tool that returns Tools collections.\n\t *\n\t * Returns a SearchTool instance that wraps `searchTools()` for use in agent loops.\n\t *\n\t * @param options - Options including the default search mode\n\t * @returns SearchTool instance\n\t *\n\t * @example\n\t * ```typescript\n\t * const toolset = new StackOneToolSet({ apiKey: 'sk-xxx' });\n\t * const searchTool = toolset.getSearchTool();\n\t * const tools = await searchTool.search('manage employee records', { accountIds: ['acc-123'] });\n\t * ```\n\t */\n\tgetSearchTool(options?: { search?: SearchMode }): SearchTool {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst config: SearchConfig = options?.search\n\t\t\t? { ...this.searchConfig, method: options.search }\n\t\t\t: this.searchConfig;\n\n\t\treturn new SearchTool(this, config);\n\t}\n\n\t/**\n\t * Get tool_search + tool_execute for agent-driven discovery.\n\t *\n\t * Returns a Tools collection with two tools that let the LLM\n\t * discover and execute tools on-demand.\n\t *\n\t * @param options - Options to scope tool discovery\n\t * @returns Tools collection containing tool_search and tool_execute\n\t */\n\tgetTools(options?: { accountIds?: string[] }): Tools {\n\t\tconst accountIds =\n\t\t\toptions?.accountIds ??\n\t\t\tthis.executeConfig?.accountIds ??\n\t\t\t(this.accountIds.length > 0 ? this.accountIds : undefined);\n\t\treturn this.buildTools(accountIds);\n\t}\n\n\t/**\n\t * Build tool_search + tool_execute tools scoped to this toolset.\n\t */\n\tprivate buildTools(accountIds?: string[], connectors?: string): Tools {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst searchTool = createSearchTool(this, accountIds, connectors);\n\t\tconst executeTool = createExecuteTool(this, accountIds, connectors);\n\t\treturn new Tools([searchTool, executeTool]);\n\t}\n\n\t/**\n\t * Get tools in OpenAI function calling format.\n\t *\n\t * @param options - Options\n\t * @param options.mode - Tool mode.\n\t * `undefined` (default): fetch all tools and convert to OpenAI format.\n\t * `\"search_and_execute\"`: return two tools (tool_search + tool_execute)\n\t * that let the LLM discover and execute tools on-demand.\n\t * @param options.accountIds - Account IDs to scope tools. Overrides the `execute`\n\t * config from the constructor.\n\t * @returns List of tool definitions in OpenAI function format.\n\t *\n\t * @example\n\t * ```typescript\n\t * // All tools\n\t * const toolset = new StackOneToolSet();\n\t * const tools = await toolset.openai();\n\t *\n\t * // Search and execute for agent-driven discovery\n\t * const toolset = new StackOneToolSet({ search: {} });\n\t * const tools = await toolset.openai({ mode: 'search_and_execute' });\n\t * ```\n\t */\n\tasync openai(options?: {\n\t\tmode?: 'search_and_execute';\n\t\taccountIds?: string[];\n\t}): Promise<ReturnType<Tools['toOpenAI']>> {\n\t\tconst effectiveAccountIds = options?.accountIds ?? this.executeConfig?.accountIds;\n\n\t\tif (options?.mode === 'search_and_execute') {\n\t\t\t// Discover available connectors for dynamic descriptions\n\t\t\tlet connectors: string | undefined;\n\t\t\ttry {\n\t\t\t\tconst allTools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\t\t\tconst connectorSet = allTools.getConnectors();\n\t\t\t\tif (connectorSet.size > 0) {\n\t\t\t\t\tconnectors = Array.from(connectorSet).sort().join(', ');\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Best-effort: if discovery fails, use generic descriptions\n\t\t\t}\n\t\t\treturn this.buildTools(effectiveAccountIds, connectors).toOpenAI();\n\t\t}\n\n\t\tconst tools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\treturn tools.toOpenAI();\n\t}\n\n\t/**\n\t * Search for and fetch tools using semantic or local search.\n\t *\n\t * This method discovers relevant tools based on natural language queries.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * (e.g., \"create employee\", \"send a message\")\n\t * @param options - Search options\n\t * @returns Tools collection with matched tools from linked accounts\n\t * @throws SemanticSearchError if the API call fails and search is \"semantic\"\n\t *\n\t * @example\n\t * ```typescript\n\t * // Semantic search (default with local fallback)\n\t * const tools = await toolset.searchTools('manage employee records', { topK: 5 });\n\t *\n\t * // Explicit semantic search\n\t * const tools = await toolset.searchTools('manage employees', { search: 'semantic' });\n\t *\n\t * // Local BM25+TF-IDF search\n\t * const tools = await toolset.searchTools('manage employees', { search: 'local' });\n\t *\n\t * // Filter by connector\n\t * const tools = await toolset.searchTools('create time off request', {\n\t * connector: 'bamboohr',\n\t * search: 'semantic',\n\t * });\n\t * ```\n\t */\n\tasync searchTools(query: string, options?: SearchToolsOptions): Promise<Tools> {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst search = options?.search ?? this.searchConfig.method ?? 'auto';\n\t\tconst topK = options?.topK ?? this.searchConfig.topK;\n\t\tconst minSimilarity = options?.minSimilarity ?? this.searchConfig.minSimilarity;\n\t\tconst mergedOptions = { ...options, search, topK, minSimilarity };\n\n\t\tconst allTools = await this.fetchTools({ accountIds: mergedOptions.accountIds });\n\t\tconst availableConnectors = allTools.getConnectors();\n\n\t\tif (availableConnectors.size === 0) {\n\t\t\treturn new Tools([]);\n\t\t}\n\n\t\t// Local-only search — skip semantic API entirely\n\t\tif (search === 'local') {\n\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t}\n\n\t\ttry {\n\t\t\t// Determine which connectors to search\n\t\t\tlet connectorsToSearch: Set<string>;\n\t\t\tif (mergedOptions.connector) {\n\t\t\t\tconst connectorLower = mergedOptions.connector.toLowerCase();\n\t\t\t\tconnectorsToSearch = availableConnectors.has(connectorLower)\n\t\t\t\t\t? new Set([connectorLower])\n\t\t\t\t\t: new Set();\n\t\t\t\tif (connectorsToSearch.size === 0) {\n\t\t\t\t\treturn new Tools([]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconnectorsToSearch = availableConnectors;\n\t\t\t}\n\n\t\t\t// Search each connector in parallel — in auto mode, treat missing\n\t\t\t// API key as \"semantic unavailable\" and fall back to local search.\n\t\t\tlet client: SemanticSearchClient;\n\t\t\ttry {\n\t\t\t\tclient = this.getSemanticClient();\n\t\t\t} catch (error) {\n\t\t\t\tif (search === 'auto' && error instanceof ToolSetConfigError) {\n\t\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst allResults: SemanticSearchResult[] = [];\n\t\t\tlet lastError: SemanticSearchError | undefined;\n\n\t\t\tconst searchPromises = [...connectorsToSearch].map(async (connector) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\t\tconnector,\n\t\t\t\t\t\ttopK: mergedOptions.topK,\n\t\t\t\t\t\tminSimilarity: mergedOptions.minSimilarity,\n\t\t\t\t\t});\n\t\t\t\t\treturn response.results;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\t\t\tlastError = error;\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst resultArrays = await Promise.all(searchPromises);\n\t\t\tfor (const results of resultArrays) {\n\t\t\t\tallResults.push(...results);\n\t\t\t}\n\n\t\t\t// If ALL connector searches failed, re-raise to trigger fallback\n\t\t\tif (allResults.length === 0 && lastError) {\n\t\t\t\tthrow lastError;\n\t\t\t}\n\n\t\t\t// Sort by score, apply topK\n\t\t\tallResults.sort((a, b) => b.similarityScore - a.similarityScore);\n\t\t\tconst topResults =\n\t\t\t\tmergedOptions.topK != null ? allResults.slice(0, mergedOptions.topK) : allResults;\n\n\t\t\tif (topResults.length === 0) {\n\t\t\t\treturn new Tools([]);\n\t\t\t}\n\n\t\t\t// 1. Parse composite IDs to MCP-format action names, deduplicate\n\t\t\tconst seenNames = new Set<string>();\n\t\t\tconst actionNames: string[] = [];\n\t\t\tfor (const result of topResults) {\n\t\t\t\tconst name = normalizeActionName(result.id);\n\t\t\t\tif (seenNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenNames.add(name);\n\t\t\t\tactionNames.push(name);\n\t\t\t}\n\n\t\t\tif (actionNames.length === 0) {\n\t\t\t\treturn new Tools([]);\n\t\t\t}\n\n\t\t\t// 2. Use MCP tools (already fetched) — schemas come from the source of truth\n\t\t\t// 3. Filter to only the tools search found, preserving search relevance order\n\t\t\tconst actionOrder = new Map(actionNames.map((name, i) => [name, i]));\n\t\t\tconst matchedTools = allTools.toArray().filter((t) => seenNames.has(t.name));\n\t\t\tmatchedTools.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\t(actionOrder.get(a.name) ?? Number.POSITIVE_INFINITY) -\n\t\t\t\t\t(actionOrder.get(b.name) ?? Number.POSITIVE_INFINITY),\n\t\t\t);\n\n\t\t\t// Auto mode: if semantic returned results but none matched MCP tools, fall back to local\n\t\t\tif (search === 'auto' && matchedTools.length === 0) {\n\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t}\n\n\t\t\treturn new Tools(matchedTools);\n\t\t} catch (error) {\n\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\tif (search === 'semantic') {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\t// Auto mode: silently fall back to local search\n\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Search for action names without fetching tools.\n\t *\n\t * Useful when you need to inspect search results before fetching,\n\t * or when building custom filtering logic.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * @param options - Search options\n\t * @returns List of SemanticSearchResult with action names, scores, and metadata\n\t *\n\t * @example\n\t * ```typescript\n\t * // Lightweight: inspect results before fetching\n\t * const results = await toolset.searchActionNames('manage employees');\n\t * for (const r of results) {\n\t * console.log(`${r.id}: ${r.similarityScore.toFixed(2)}`);\n\t * }\n\t *\n\t * // Then fetch specific high-scoring actions\n\t * const selected = results\n\t * .filter(r => r.similarityScore > 0.7)\n\t * .map(r => r.id);\n\t * const tools = await toolset.fetchTools({ actions: selected });\n\t * ```\n\t */\n\tasync searchActionNames(\n\t\tquery: string,\n\t\toptions?: SearchActionNamesOptions,\n\t): Promise<SemanticSearchResult[]> {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst effectiveTopK = options?.topK ?? this.searchConfig.topK;\n\t\tconst effectiveMinSimilarity = options?.minSimilarity ?? this.searchConfig.minSimilarity;\n\n\t\t// Resolve available connectors from account IDs\n\t\tlet availableConnectors: Set<string> | undefined;\n\t\tconst effectiveAccountIds = options?.accountIds || this.accountIds;\n\t\tif (effectiveAccountIds.length > 0) {\n\t\t\tconst allTools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\t\tavailableConnectors = allTools.getConnectors();\n\t\t\tif (availableConnectors.size === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst client = this.getSemanticClient();\n\t\t\tlet allResults: SemanticSearchResult[] = [];\n\n\t\t\tif (availableConnectors) {\n\t\t\t\t// Parallel per-connector search (only user's connectors)\n\t\t\t\tlet connectorsToSearch: Set<string>;\n\t\t\t\tif (options?.connector) {\n\t\t\t\t\tconst connectorLower = options.connector.toLowerCase();\n\t\t\t\t\tconnectorsToSearch = availableConnectors.has(connectorLower)\n\t\t\t\t\t\t? new Set([connectorLower])\n\t\t\t\t\t\t: new Set();\n\t\t\t\t} else {\n\t\t\t\t\tconnectorsToSearch = availableConnectors;\n\t\t\t\t}\n\n\t\t\t\tconst searchPromises = [...connectorsToSearch].map(async (connector) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\t\t\tconnector,\n\t\t\t\t\t\t\ttopK: effectiveTopK,\n\t\t\t\t\t\t\tminSimilarity: effectiveMinSimilarity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn response.results;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tconst resultArrays = await Promise.all(searchPromises);\n\t\t\t\tfor (const results of resultArrays) {\n\t\t\t\t\tallResults.push(...results);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// No account filtering — single global search\n\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\tconnector: options?.connector,\n\t\t\t\t\ttopK: effectiveTopK,\n\t\t\t\t\tminSimilarity: effectiveMinSimilarity,\n\t\t\t\t});\n\t\t\t\tallResults = response.results;\n\t\t\t}\n\n\t\t\t// Sort by score — return raw results (consumers can normalize the composite ID if needed)\n\t\t\tallResults.sort((a, b) => b.similarityScore - a.similarityScore);\n\n\t\t\treturn effectiveTopK != null ? allResults.slice(0, effectiveTopK) : allResults;\n\t\t} catch (error) {\n\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Run local BM25+TF-IDF search over already-fetched tools.\n\t */\n\tprivate async localSearch(\n\t\tquery: string,\n\t\tallTools: Tools,\n\t\toptions?: Pick<SearchToolsOptions, 'connector' | 'topK' | 'minSimilarity'>,\n\t): Promise<Tools> {\n\t\tconst availableConnectors = allTools.getConnectors();\n\t\tif (availableConnectors.size === 0) {\n\t\t\treturn new Tools([]);\n\t\t}\n\n\t\tif (!this.toolIndexCache || this.toolIndexCache.tools !== allTools) {\n\t\t\tthis.toolIndexCache = { tools: allTools, index: new ToolIndex(allTools.toArray()) };\n\t\t}\n\t\tconst index = this.toolIndexCache.index;\n\t\tconst results = await index.search(query, options?.topK ?? 5, options?.minSimilarity ?? 0.0);\n\n\t\tconst matchedNames = results.map((r) => r.name);\n\t\tconst toolMap = new Map(allTools.toArray().map((t) => [t.name, t]));\n\t\tconst filterConnectors = options?.connector\n\t\t\t? new Set([options.connector.toLowerCase()])\n\t\t\t: availableConnectors;\n\n\t\tconst matchedTools = matchedNames\n\t\t\t.filter((name) => toolMap.has(name))\n\t\t\t.map((name) => toolMap.get(name)!)\n\t\t\t.filter((tool) => tool.connector && filterConnectors.has(tool.connector));\n\n\t\treturn new Tools(options?.topK != null ? matchedTools.slice(0, options.topK) : matchedTools);\n\t}\n\n\t/**\n\t * Fetch tools from MCP with optional filtering\n\t * @param options Optional filtering options for account IDs, providers, and actions\n\t * @returns Collection of tools matching the filter criteria\n\t */\n\tasync fetchTools(options?: FetchToolsOptions): Promise<Tools> {\n\t\t// Use account IDs from options, or fall back to instance state\n\t\tconst effectiveAccountIds = options?.accountIds || this.accountIds;\n\n\t\tconst cacheKey = JSON.stringify({\n\t\t\taccountIds: [...effectiveAccountIds].sort(),\n\t\t\tproviders: options?.providers?.length ? [...options.providers].sort() : null,\n\t\t\tactions: options?.actions?.length ? [...options.actions].sort() : null,\n\t\t});\n\t\tconst cached = this.catalogCache.get(cacheKey);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\t// Fetch tools (with account filtering if needed)\n\t\tlet tools: Tools;\n\t\tif (effectiveAccountIds.length > 0) {\n\t\t\tconst toolsPromises = effectiveAccountIds.map(async (accountId) => {\n\t\t\t\tconst headers = { 'x-account-id': accountId };\n\t\t\t\tconst mergedHeaders = { ...this.headers, ...headers };\n\n\t\t\t\t// Create a temporary toolset instance with the account-specific headers\n\t\t\t\tconst tempHeaders = mergedHeaders;\n\t\t\t\tconst originalHeaders = this.headers;\n\t\t\t\tthis.headers = tempHeaders;\n\n\t\t\t\ttry {\n\t\t\t\t\tconst tools = await this.fetchToolsFromMcp();\n\t\t\t\t\treturn tools.toArray();\n\t\t\t\t} finally {\n\t\t\t\t\t// Restore original headers\n\t\t\t\t\tthis.headers = originalHeaders;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst toolArrays = await Promise.all(toolsPromises);\n\t\t\tconst allTools = toolArrays.flat();\n\t\t\ttools = new Tools(allTools);\n\t\t} else {\n\t\t\t// No account filtering - fetch all tools\n\t\t\ttools = await this.fetchToolsFromMcp();\n\t\t}\n\n\t\t// Apply provider and action filters\n\t\tconst filteredTools = this.filterTools(tools, options);\n\n\t\t// Add feedback tool\n\t\tconst feedbackTool = createFeedbackTool(undefined, this.accountId, this.baseUrl);\n\t\tconst toolsWithFeedback = new Tools([...filteredTools.toArray(), feedbackTool]);\n\n\t\tthis.catalogCache.set(cacheKey, toolsWithFeedback);\n\t\treturn toolsWithFeedback;\n\t}\n\n\t/**\n\t * Fetch tool definitions from MCP\n\t */\n\tprivate async fetchToolsFromMcp(): Promise<Tools> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new ToolSetConfigError('baseUrl is required to fetch MCP tools');\n\t\t}\n\n\t\tawait using clients = await createMCPClient({\n\t\t\tbaseUrl: `${this.baseUrl}/mcp`,\n\t\t\theaders: this.headers,\n\t\t});\n\n\t\tawait clients.client.connect(clients.transport);\n\t\tconst listToolsResult = await clients.client.listTools();\n\t\tconst actionsClient = this.getActionsClient();\n\n\t\tconst tools = listToolsResult.tools.map(({ name, description, inputSchema }) => {\n\t\t\treturn this.createRpcBackedTool({\n\t\t\t\tactionsClient,\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\tinputSchema,\n\t\t\t});\n\t\t});\n\n\t\treturn new Tools(tools);\n\t}\n\n\t/**\n\t * Filter tools by providers and actions\n\t * @param tools Tools collection to filter\n\t * @param options Filtering options\n\t * @returns Filtered tools collection\n\t */\n\tprivate filterTools(tools: Tools, options?: FetchToolsOptions): Tools {\n\t\tlet filteredTools = tools.toArray();\n\n\t\t// Filter by providers if specified\n\t\tif (options?.providers && options.providers.length > 0) {\n\t\t\tconst providerSet = new Set(options.providers.map((p) => p.toLowerCase()));\n\t\t\tfilteredTools = filteredTools.filter((tool) => {\n\t\t\t\treturn tool.connector && providerSet.has(tool.connector);\n\t\t\t});\n\t\t}\n\n\t\t// Filter by actions if specified (with glob support)\n\t\tif (options?.actions && options.actions.length > 0) {\n\t\t\tfilteredTools = filteredTools.filter((tool) =>\n\t\t\t\toptions.actions?.some((pattern) => this.matchGlob(tool.name, pattern)),\n\t\t\t);\n\t\t}\n\n\t\treturn new Tools(filteredTools);\n\t}\n\n\t/**\n\t * Check if a string matches a glob pattern\n\t * @param str String to check\n\t * @param pattern Glob pattern\n\t * @returns True if the string matches the pattern\n\t */\n\tprivate matchGlob(str: string, pattern: string): boolean {\n\t\t// Convert glob pattern to regex\n\t\tconst regexPattern = pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '.*').replace(/\\?/g, '.');\n\n\t\t// Create regex with start and end anchors\n\t\tconst regex = new RegExp(`^${regexPattern}$`);\n\n\t\t// Test if the string matches the pattern\n\t\treturn regex.test(str);\n\t}\n\n\tprivate getActionsClient(): RpcClient {\n\t\tif (this.rpcClient) {\n\t\t\treturn this.rpcClient;\n\t\t}\n\n\t\tconst credentials = this.authentication?.credentials ?? {};\n\t\tconst apiKeyFromAuth =\n\t\t\tthis.authentication?.type === 'basic'\n\t\t\t\t? credentials.username\n\t\t\t\t: this.authentication?.type === 'bearer'\n\t\t\t\t\t? credentials.token\n\t\t\t\t\t: credentials.username;\n\n\t\tconst apiKey = apiKeyFromAuth || process.env.STACKONE_API_KEY;\n\t\tconst password = this.authentication?.type === 'basic' ? (credentials.password ?? '') : '';\n\n\t\tif (!apiKey) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'StackOne API key is required to create an actions client. Provide rpcClient, configure authentication credentials, or set the STACKONE_API_KEY environment variable.',\n\t\t\t);\n\t\t}\n\n\t\tthis.rpcClient = new RpcClient({\n\t\t\tserverURL: this.baseUrl,\n\t\t\tsecurity: {\n\t\t\t\tusername: apiKey,\n\t\t\t\tpassword,\n\t\t\t},\n\t\t\ttimeout: this.timeout,\n\t\t});\n\n\t\treturn this.rpcClient;\n\t}\n\n\tprivate createRpcBackedTool({\n\t\tactionsClient,\n\t\tname,\n\t\tdescription,\n\t\tinputSchema,\n\t}: {\n\t\tactionsClient: RpcClient;\n\t\tname: string;\n\t\tdescription?: string;\n\t\tinputSchema: ToolInputSchema;\n\t}): BaseTool {\n\t\tconst executeConfig = {\n\t\t\tkind: 'rpc',\n\t\t\tmethod: 'POST',\n\t\t\turl: `${this.baseUrl}/actions/rpc`,\n\t\t\tpayloadKeys: {\n\t\t\t\taction: 'action',\n\t\t\t\tbody: 'body',\n\t\t\t\theaders: 'headers',\n\t\t\t\tpath: 'path',\n\t\t\t\tquery: 'query',\n\t\t\t},\n\t\t} as const satisfies RpcExecuteConfig; // Mirrors StackOne RPC payload layout so metadata/debug stays in sync.\n\n\t\tconst toolParameters = {\n\t\t\t...inputSchema,\n\n\t\t\t// properties are not well typed in MCP spec\n\t\t\tproperties: inputSchema?.properties as JsonSchemaProperties,\n\t\t} satisfies ToolParameters;\n\n\t\tconst tool = new BaseTool(\n\t\t\tname,\n\t\t\tdescription ?? '',\n\t\t\ttoolParameters,\n\t\t\texecuteConfig,\n\t\t\tthis.headers,\n\t\t).setExposeExecutionMetadata(false);\n\n\t\ttool.execute = async (\n\t\t\tinputParams?: JsonObject | string,\n\t\t\toptions?: ExecuteOptions,\n\t\t): Promise<JsonObject> => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\tinputParams !== undefined &&\n\t\t\t\t\ttypeof inputParams !== 'object' &&\n\t\t\t\t\ttypeof inputParams !== 'string'\n\t\t\t\t) {\n\t\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(inputParams)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst parsedParams =\n\t\t\t\t\ttypeof inputParams === 'string' ? JSON.parse(inputParams) : (inputParams ?? {});\n\n\t\t\t\tconst currentHeaders = tool.getHeaders();\n\t\t\t\tconst baseHeaders = this.buildActionHeaders(currentHeaders);\n\n\t\t\t\tconst pathParams = this.extractRecord(parsedParams, 'path');\n\t\t\t\tconst queryParams = this.extractRecord(parsedParams, 'query');\n\t\t\t\tconst additionalHeaders = this.extractRecord(parsedParams, 'headers');\n\t\t\t\tconst extraHeaders = normalizeHeaders(additionalHeaders);\n\t\t\t\t// defu merges extraHeaders into baseHeaders, both are already branded types\n\t\t\t\tconst actionHeaders = defu(extraHeaders, baseHeaders);\n\n\t\t\t\tconst bodyPayload = this.extractRecord(parsedParams, 'body');\n\t\t\t\tconst rpcBody: JsonObject = bodyPayload ? { ...bodyPayload } : {};\n\t\t\t\tfor (const [key, value] of Object.entries(parsedParams)) {\n\t\t\t\t\tif (key === 'body' || key === 'headers' || key === 'path' || key === 'query') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\trpcBody[key] = value as JsonObject[string];\n\t\t\t\t}\n\n\t\t\t\tif (options?.dryRun) {\n\t\t\t\t\tconst requestPayload = {\n\t\t\t\t\t\taction: name,\n\t\t\t\t\t\tbody: rpcBody,\n\t\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\t\tpath: pathParams ?? undefined,\n\t\t\t\t\t\tquery: queryParams ?? undefined,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: executeConfig.url,\n\t\t\t\t\t\tmethod: executeConfig.method,\n\t\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\t\tbody: JSON.stringify(requestPayload),\n\t\t\t\t\t\tmappedParams: parsedParams,\n\t\t\t\t\t} satisfies JsonObject;\n\t\t\t\t}\n\n\t\t\t\tconst response = await actionsClient.actions.rpcAction({\n\t\t\t\t\taction: name,\n\t\t\t\t\tbody: rpcBody,\n\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\tpath: pathParams ?? undefined,\n\t\t\t\t\tquery: queryParams ?? undefined,\n\t\t\t\t});\n\n\t\t\t\treturn rpcResponseToJsonObject(response);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof StackOneError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tthrow new StackOneError(`Error executing RPC action ${name}`, { cause: error });\n\t\t\t}\n\t\t};\n\n\t\treturn tool;\n\t}\n\n\tprivate buildActionHeaders(headers: Record<string, string>): StackOneHeaders {\n\t\tconst sanitizedEntries = Object.entries(headers).filter(\n\t\t\t([key]) => key.toLowerCase() !== 'authorization',\n\t\t);\n\n\t\treturn stackOneHeadersSchema.parse(\n\t\t\tObject.fromEntries(sanitizedEntries.map(([key, value]) => [key, String(value)])),\n\t\t);\n\t}\n\n\tprivate extractRecord(\n\t\tparams: JsonObject,\n\t\tkey: 'body' | 'headers' | 'path' | 'query',\n\t): JsonObject | undefined {\n\t\tconst value = params[key];\n\t\tif (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n\t\t\treturn value as JsonObject;\n\t\t}\n\t\treturn undefined;\n\t}\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"toolsets.mjs","names":["result: JsonObject","cachedTools: Awaited<ReturnType<typeof toolset.fetchTools>> | null","authentication: AuthenticationConfig","config: SearchConfig","connectors: string | undefined","connectorsToSearch: Set<string>","client: SemanticSearchClient","allResults: SemanticSearchResult[]","lastError: SemanticSearchError | undefined","actionNames: string[]","availableConnectors: Set<string> | undefined","tools: Tools","rpcBody: JsonObject"],"sources":["../../src/toolsets.ts"],"sourcesContent":["import { defu } from 'defu';\nimport type { MergeExclusive, SimplifyDeep } from 'type-fest';\nimport { z } from 'zod/v4';\nimport { DEFAULT_BASE_URL } from './consts';\nimport { createFeedbackTool } from './feedback';\nimport { type StackOneHeaders, normalizeHeaders, stackOneHeadersSchema } from './headers';\nimport { ToolIndex } from './local-search';\nimport { createMCPClient } from './mcp-client';\nimport { type RpcActionResponse, RpcClient } from './rpc-client';\nimport {\n\tSemanticSearchClient,\n\tSemanticSearchError,\n\ttype SemanticSearchResult,\n} from './semantic-search';\nimport { BaseTool, Tools } from './tool';\nimport type {\n\tExecuteOptions,\n\tJsonObject,\n\tJsonSchemaProperties,\n\tLocalExecuteConfig,\n\tRpcExecuteConfig,\n\tSearchConfig,\n\tToolParameters,\n} from './types';\nimport { StackOneError } from './utils/error-stackone';\nimport { StackOneAPIError } from './utils/error-stackone-api';\nimport { normalizeActionName } from './utils/normalize';\n\n/**\n * Converts RpcActionResponse to JsonObject in a type-safe manner.\n * RpcActionResponse uses z.passthrough() which preserves additional fields,\n * making it structurally compatible with Record<string, JsonValue>.\n */\nfunction rpcResponseToJsonObject(response: RpcActionResponse): JsonObject {\n\t// RpcActionResponse with passthrough() has the shape:\n\t// { next?: string | null, data?: ..., [key: string]: unknown }\n\t// We extract all properties into a plain object\n\tconst result: JsonObject = {};\n\tfor (const [key, value] of Object.entries(response)) {\n\t\tresult[key] = value as JsonObject[string];\n\t}\n\treturn result;\n}\n\ntype ToolInputSchema = Awaited<\n\tReturnType<Awaited<ReturnType<typeof createMCPClient>>['client']['listTools']>\n>['tools'][number]['inputSchema'];\n\n/**\n * Base exception for toolset errors\n */\nexport class ToolSetError extends Error {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetError';\n\t}\n}\n\n/**\n * Raised when there is an error in the toolset configuration\n */\nexport class ToolSetConfigError extends ToolSetError {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetConfigError';\n\t}\n}\n\n/**\n * Raised when there is an error loading tools\n */\nexport class ToolSetLoadError extends ToolSetError {\n\tconstructor(message: string, options?: ErrorOptions) {\n\t\tsuper(message, options);\n\t\tthis.name = 'ToolSetLoadError';\n\t}\n}\n\n/**\n * Authentication configuration for toolsets\n */\nexport interface AuthenticationConfig {\n\ttype: 'basic' | 'bearer';\n\tcredentials?: {\n\t\tusername?: string;\n\t\tpassword?: string;\n\t\ttoken?: string;\n\t};\n\theaders?: Record<string, string>;\n}\n\n/**\n * Base configuration for all toolsets\n */\nexport interface BaseToolSetConfig {\n\tbaseUrl?: string;\n\tauthentication?: AuthenticationConfig;\n\theaders?: Record<string, string>;\n\trpcClient?: RpcClient;\n\t/** Request timeout in milliseconds. Default: 60000 (60s). */\n\ttimeout?: number;\n}\n\n/**\n * Configuration with a single account ID\n */\ninterface SingleAccountConfig {\n\t/**\n\t * Single account ID for StackOne API operations\n\t * Use this when working with a single account\n\t */\n\taccountId: string;\n}\n\n/**\n * Configuration with multiple account IDs\n */\ninterface MultipleAccountsConfig {\n\t/**\n\t * Array of account IDs for filtering tools across multiple accounts\n\t * When provided, tools will be fetched for all specified accounts\n\t * @example ['account-1', 'account-2']\n\t */\n\taccountIds: string[];\n}\n\n/**\n * Account configuration options - either single accountId or multiple accountIds, but not both\n */\ntype AccountConfig = SimplifyDeep<MergeExclusive<SingleAccountConfig, MultipleAccountsConfig>>;\n\n/**\n * Execution configuration for the StackOneToolSet constructor.\n * Controls default account scoping for tool execution in tools.\n */\nexport interface ExecuteToolsConfig {\n\t/** Account IDs to scope tool discovery and execution. */\n\taccountIds?: string[];\n\t/** Request timeout in milliseconds. Can also be set as a top-level config param which takes precedence. */\n\ttimeout?: number;\n}\n\n/**\n * Base configuration for StackOne toolset (without account options)\n */\ninterface StackOneToolSetBaseConfig extends BaseToolSetConfig {\n\tapiKey?: string;\n\tstrict?: boolean;\n\t/**\n\t * Search configuration. Controls default search behavior for `searchTools()`,\n\t * `getSearchTool()`, and `searchActionNames()`.\n\t *\n\t * - Omit or pass `undefined` → search disabled (`null`)\n\t * - Pass `null` → search disabled\n\t * - Pass `{}` or `{ method: 'auto' }` → search enabled with defaults\n\t * - Pass `{ method, topK, minSimilarity }` → search enabled with custom defaults\n\t *\n\t * Per-call options always override these defaults.\n\t */\n\tsearch?: SearchConfig | null;\n\t/**\n\t * Execution configuration. Controls default account scoping for tool execution.\n\t * Pass `{ accountIds: ['acc-1'] }` to scope tools to specific accounts.\n\t */\n\texecute?: ExecuteToolsConfig;\n}\n\n/**\n * Configuration for StackOne toolset\n * Accepts either accountId (single) or accountIds (multiple), but not both\n */\nexport type StackOneToolSetConfig = StackOneToolSetBaseConfig & Partial<AccountConfig>;\n\n/**\n * Options for filtering tools when fetching from MCP\n */\ninterface FetchToolsOptions {\n\t/**\n\t * Filter tools by account IDs\n\t * Only tools available on these accounts will be returned\n\t */\n\taccountIds?: string[];\n\n\t/**\n\t * Filter tools by provider names\n\t * Only tools from these providers will be returned\n\t * @example ['hibob', 'bamboohr']\n\t */\n\tproviders?: string[];\n\n\t/**\n\t * Filter tools by action patterns with glob support\n\t * Only tools matching these patterns will be returned\n\t * @example ['*_list_employees', 'hibob_create_employees']\n\t */\n\tactions?: string[];\n}\n\n/**\n * Search mode for tool discovery.\n *\n * - `\"auto\"` (default): try semantic search first, fall back to local BM25+TF-IDF if the API is unavailable\n * - `\"semantic\"`: use only the semantic search API; throws SemanticSearchError on failure\n * - `\"local\"`: use only local BM25+TF-IDF search (no API call to the semantic search endpoint)\n */\nexport type SearchMode = 'auto' | 'semantic' | 'local';\n\n/**\n * Options for searchTools() and SearchTool\n */\nexport interface SearchToolsOptions {\n\t/** Optional provider/connector filter (e.g., \"bamboohr\", \"slack\") */\n\tconnector?: string;\n\t/** Maximum number of tools to return */\n\ttopK?: number;\n\t/** Minimum similarity score threshold 0-1 */\n\tminSimilarity?: number;\n\t/** Optional account IDs (uses setAccounts() if not provided) */\n\taccountIds?: string[];\n\t/** Search backend to use */\n\tsearch?: SearchMode;\n}\n\n/**\n * Options for searchActionNames()\n */\nexport interface SearchActionNamesOptions {\n\t/** Optional provider/connector filter */\n\tconnector?: string;\n\t/** Optional account IDs to scope results */\n\taccountIds?: string[];\n\t/** Maximum number of results */\n\ttopK?: number;\n\t/** Minimum similarity score threshold 0-1 */\n\tminSimilarity?: number;\n}\n\n/**\n * Callable search tool that wraps StackOneToolSet.searchTools().\n *\n * Designed for agent loops — call `search()` with a query to get Tools back.\n *\n * @example\n * ```typescript\n * const toolset = new StackOneToolSet({ apiKey: 'sk-xxx' });\n * const searchTool = toolset.getSearchTool();\n * const tools = await searchTool.search('manage employee records', { accountIds: ['acc-123'] });\n * ```\n */\nexport class SearchTool {\n\tprivate readonly toolset: StackOneToolSet;\n\tprivate readonly defaultConfig: SearchConfig;\n\n\tconstructor(toolset: StackOneToolSet, config: SearchConfig = {}) {\n\t\tthis.toolset = toolset;\n\t\tthis.defaultConfig = config;\n\t}\n\n\t/**\n\t * Search for tools using natural language.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * @param options - Search options (connector, topK, minSimilarity, accountIds, search).\n\t * Per-call options override the defaults from the constructor config.\n\t * @returns Tools collection with matched tools\n\t */\n\tasync search(query: string, options?: SearchToolsOptions): Promise<Tools> {\n\t\treturn this.toolset.searchTools(query, {\n\t\t\t...options,\n\t\t\tsearch: options?.search ?? this.defaultConfig.method,\n\t\t\ttopK: options?.topK ?? this.defaultConfig.topK,\n\t\t\tminSimilarity: options?.minSimilarity ?? this.defaultConfig.minSimilarity,\n\t\t});\n\t}\n}\n\n// --- Internal tool_search + tool_execute ---\n\nconst searchInputSchema = z.object({\n\tquery: z\n\t\t.string()\n\t\t.transform((v) => v.trim())\n\t\t.refine((v) => v.length > 0, { message: 'query must be a non-empty string' }),\n\tconnector: z.string().optional(),\n\ttop_k: z.number().int().min(1).max(50).optional(),\n});\n\nconst searchParameters = {\n\ttype: 'object',\n\tproperties: {\n\t\tquery: {\n\t\t\ttype: 'string',\n\t\t\tdescription:\n\t\t\t\t'Natural language description of what you need (e.g. \"create an employee\", \"list time off requests\")',\n\t\t},\n\t\tconnector: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Optional connector filter (e.g. \"bamboohr\", \"hibob\")',\n\t\t},\n\t\ttop_k: {\n\t\t\ttype: 'integer',\n\t\t\tdescription: 'Max results to return (1-50, default 5)',\n\t\t\tminimum: 1,\n\t\t\tmaximum: 50,\n\t\t},\n\t},\n\trequired: ['query'],\n} as const satisfies ToolParameters;\n\nconst executeInputSchema = z.object({\n\ttool_name: z\n\t\t.string()\n\t\t.transform((v) => v.trim())\n\t\t.refine((v) => v.length > 0, { message: 'tool_name must be a non-empty string' }),\n\tparameters: z.record(z.string(), z.unknown()).optional().default({}),\n});\n\nconst executeParameters = {\n\ttype: 'object',\n\tproperties: {\n\t\ttool_name: {\n\t\t\ttype: 'string',\n\t\t\tdescription: 'Exact tool name from tool_search results',\n\t\t},\n\t\tparameters: {\n\t\t\ttype: 'object',\n\t\t\tdescription: 'Parameters for the tool. Pass an empty object {} if no parameters are needed.',\n\t\t},\n\t},\n\trequired: ['tool_name'],\n} as const satisfies ToolParameters;\n\nconst localConfig = (id: string): LocalExecuteConfig => ({\n\tkind: 'local',\n\tidentifier: `meta:${id}`,\n});\n\n/** @internal */\nexport function createSearchTool(\n\ttoolset: StackOneToolSet,\n\taccountIds?: string[],\n\tconnectors?: string,\n): BaseTool {\n\tconst connectorLine = connectors ? ` Available connectors: ${connectors}.` : '';\n\tconst tool = new BaseTool(\n\t\t'tool_search',\n\t\t`Search for available tools by describing what you need. Returns matching tool names, descriptions, and parameter schemas. Use the returned parameter schemas to know exactly what to pass when calling tool_execute.${connectorLine}`,\n\t\tsearchParameters,\n\t\tlocalConfig('search'),\n\t);\n\n\ttool.execute = async (inputParams?: JsonObject | string): Promise<JsonObject> => {\n\t\ttry {\n\t\t\tconst raw = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsed = searchInputSchema.parse(raw);\n\n\t\t\tconst searchConfig = toolset.getSearchConfig() ?? {};\n\t\t\tconst results = await toolset.searchTools(parsed.query, {\n\t\t\t\tconnector: parsed.connector,\n\t\t\t\ttopK: parsed.top_k ?? searchConfig.topK,\n\t\t\t\tminSimilarity: searchConfig.minSimilarity,\n\t\t\t\tsearch: searchConfig.method,\n\t\t\t\taccountIds,\n\t\t\t});\n\n\t\t\treturn {\n\t\t\t\ttools: results.toArray().map((t) => ({\n\t\t\t\t\tname: t.name,\n\t\t\t\t\tdescription: t.description,\n\t\t\t\t\tparameters: t.parameters.properties as unknown as JsonObject,\n\t\t\t\t})),\n\t\t\t\ttotal: results.length,\n\t\t\t\tquery: parsed.query,\n\t\t\t};\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneAPIError) {\n\t\t\t\treturn { error: error.message, status_code: error.statusCode };\n\t\t\t}\n\t\t\tif (error instanceof SyntaxError || error instanceof z.ZodError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Invalid input: ${error instanceof z.ZodError ? error.issues.map((i) => i.message).join(', ') : error.message}`,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\treturn tool;\n}\n\n/** @internal */\nexport function createExecuteTool(\n\ttoolset: StackOneToolSet,\n\taccountIds?: string[],\n\tconnectors?: string,\n): BaseTool {\n\tlet cachedTools: Awaited<ReturnType<typeof toolset.fetchTools>> | null = null;\n\n\tconst connectorLine = connectors ? ` Available connectors: ${connectors}.` : '';\n\tconst tool = new BaseTool(\n\t\t'tool_execute',\n\t\t`Execute a tool by name with the given parameters. Use tool_search first to find available tools. The parameters field must match the parameter schema returned by tool_search. Pass parameters as a nested object matching the schema structure.${connectorLine}`,\n\t\texecuteParameters,\n\t\tlocalConfig('execute'),\n\t);\n\n\ttool.execute = async (\n\t\tinputParams?: JsonObject | string,\n\t\texecuteOptions?: ExecuteOptions,\n\t): Promise<JsonObject> => {\n\t\tlet toolName = 'unknown';\n\t\ttry {\n\t\t\tconst raw = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsed = executeInputSchema.parse(raw);\n\t\t\ttoolName = parsed.tool_name;\n\n\t\t\tif (!cachedTools) {\n\t\t\t\tcachedTools = await toolset.fetchTools({ accountIds });\n\t\t\t}\n\t\t\tconst target = cachedTools.getTool(parsed.tool_name);\n\n\t\t\tif (!target) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Tool \"${parsed.tool_name}\" not found. Use tool_search to find available tools.`,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\treturn await target.execute(parsed.parameters as JsonObject, executeOptions);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneAPIError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: error.message,\n\t\t\t\t\tstatus_code: error.statusCode,\n\t\t\t\t\tresponse_body: error.responseBody as JsonObject,\n\t\t\t\t\ttool_name: toolName,\n\t\t\t\t};\n\t\t\t}\n\t\t\tif (error instanceof SyntaxError || error instanceof z.ZodError) {\n\t\t\t\treturn {\n\t\t\t\t\terror: `Invalid input: ${error instanceof z.ZodError ? error.issues.map((i) => i.message).join(', ') : error.message}`,\n\t\t\t\t\ttool_name: toolName,\n\t\t\t\t};\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t};\n\n\treturn tool;\n}\n\n/**\n * Class for loading StackOne tools via MCP\n */\nexport class StackOneToolSet {\n\tprivate baseUrl?: string;\n\tprivate authentication?: AuthenticationConfig;\n\tprivate headers: Record<string, string>;\n\tprivate rpcClient?: RpcClient;\n\tprivate readonly timeout: number;\n\tprivate readonly searchConfig: SearchConfig | null;\n\tprivate readonly executeConfig: ExecuteToolsConfig | undefined;\n\n\t/**\n\t * Account ID for StackOne API\n\t */\n\tprivate accountId?: string;\n\tprivate accountIds: string[] = [];\n\n\t/**\n\t * Initialize StackOne toolset with API key and optional account ID(s)\n\t * @param config Configuration object containing API key and optional account ID(s)\n\t */\n\tconstructor(config?: StackOneToolSetConfig) {\n\t\t// Validate mutually exclusive account options\n\t\tif (config?.accountId != null && config?.accountIds != null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Cannot provide both accountId and accountIds. Use accountId for a single account or accountIds for multiple accounts.',\n\t\t\t);\n\t\t}\n\n\t\tconst apiKey = config?.apiKey || process.env.STACKONE_API_KEY;\n\n\t\tif (!apiKey && config?.strict) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'No API key provided. Set STACKONE_API_KEY environment variable or pass apiKey in config.',\n\t\t\t);\n\t\t}\n\n\t\tif (!apiKey) {\n\t\t\tconsole.warn(\n\t\t\t\t'No API key provided. Set STACKONE_API_KEY environment variable or pass apiKey in config.',\n\t\t\t);\n\t\t}\n\n\t\tconst authentication: AuthenticationConfig = {\n\t\t\ttype: 'basic',\n\t\t\tcredentials: {\n\t\t\t\tusername: apiKey || '',\n\t\t\t\tpassword: '',\n\t\t\t},\n\t\t};\n\n\t\tconst accountId = config?.accountId || process.env.STACKONE_ACCOUNT_ID;\n\n\t\tconst configHeaders = {\n\t\t\t...config?.headers,\n\t\t\t...(accountId ? { 'x-account-id': accountId } : {}),\n\t\t};\n\n\t\t// Initialize base properties\n\t\tthis.baseUrl = config?.baseUrl ?? process.env.STACKONE_BASE_URL ?? DEFAULT_BASE_URL;\n\t\tthis.authentication = authentication;\n\t\tthis.headers = configHeaders;\n\t\tthis.rpcClient = config?.rpcClient;\n\t\tthis.timeout = config?.timeout ?? config?.execute?.timeout ?? 60_000;\n\t\tthis.accountId = accountId;\n\t\tthis.accountIds = config?.accountIds ?? [];\n\n\t\t// Resolve search config: undefined/null → disabled, object → custom with defaults\n\t\tthis.searchConfig = config?.search != null ? { method: 'auto', ...config.search } : null;\n\t\tthis.executeConfig = config?.execute;\n\n\t\t// Set Authentication headers if provided\n\t\tif (this.authentication) {\n\t\t\t// Only set auth headers if they don't already exist in custom headers\n\t\t\tconst needsAuthHeader = !('Authorization' in this.headers);\n\n\t\t\tif (needsAuthHeader) {\n\t\t\t\tswitch (this.authentication.type) {\n\t\t\t\t\tcase 'basic':\n\t\t\t\t\t\tif (this.authentication.credentials?.username) {\n\t\t\t\t\t\t\tconst username = this.authentication.credentials.username;\n\t\t\t\t\t\t\tconst password = this.authentication.credentials.password || '';\n\t\t\t\t\t\t\tconst authString = Buffer.from(`${username}:${password}`).toString('base64');\n\t\t\t\t\t\t\tthis.headers.Authorization = `Basic ${authString}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'bearer':\n\t\t\t\t\t\tif (this.authentication.credentials?.token) {\n\t\t\t\t\t\t\tthis.headers.Authorization = `Bearer ${this.authentication.credentials.token}`;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tbreak;\n\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tthis.authentication.type satisfies never;\n\t\t\t\t\t\tthrow new ToolSetError(\n\t\t\t\t\t\t\t`Unsupported authentication type: ${String(this.authentication.type)}`,\n\t\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Add any additional headers from authentication config, but don't override existing ones\n\t\t\tif (this.authentication.headers) {\n\t\t\t\tthis.headers = { ...this.authentication.headers, ...this.headers };\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate semanticSearchClient?: SemanticSearchClient;\n\tprivate catalogCache: Map<string, Tools> = new Map();\n\tprivate toolIndexCache?: { tools: Tools; index: ToolIndex };\n\n\t/**\n\t * Set account IDs for filtering tools\n\t * @param accountIds Array of account IDs to filter tools by\n\t * @returns This toolset instance for chaining\n\t */\n\tsetAccounts(accountIds: string[]): this {\n\t\tthis.accountIds = accountIds;\n\t\tthis.clearCatalogCache();\n\t\treturn this;\n\t}\n\n\t/**\n\t * Invalidate cached tool catalog and local search index.\n\t *\n\t * Call when linked accounts change outside of {@link setAccounts} or when\n\t * you need to force a fresh fetch from the StackOne MCP endpoint.\n\t */\n\tclearCatalogCache(): void {\n\t\tthis.catalogCache.clear();\n\t\tthis.toolIndexCache = undefined;\n\t}\n\n\t/**\n\t * Get or lazily create the semantic search client.\n\t */\n\tprivate getSemanticClient(): SemanticSearchClient {\n\t\tif (!this.semanticSearchClient) {\n\t\t\tconst apiKey = this.getApiKey();\n\t\t\tthis.semanticSearchClient = new SemanticSearchClient({\n\t\t\t\tapiKey,\n\t\t\t\tbaseUrl: this.baseUrl,\n\t\t\t});\n\t\t}\n\t\treturn this.semanticSearchClient;\n\t}\n\n\t/**\n\t * Get the current search config.\n\t */\n\tgetSearchConfig(): SearchConfig | null {\n\t\treturn this.searchConfig;\n\t}\n\n\t/**\n\t * Extract the API key from authentication config.\n\t */\n\tprivate getApiKey(): string {\n\t\tconst credentials = this.authentication?.credentials ?? {};\n\t\tconst apiKeyFromAuth =\n\t\t\tthis.authentication?.type === 'basic'\n\t\t\t\t? credentials.username\n\t\t\t\t: this.authentication?.type === 'bearer'\n\t\t\t\t\t? credentials.token\n\t\t\t\t\t: credentials.username;\n\n\t\tconst apiKey = apiKeyFromAuth || process.env.STACKONE_API_KEY;\n\t\tif (!apiKey) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'API key is required for semantic search. Provide apiKey in config or set STACKONE_API_KEY environment variable.',\n\t\t\t);\n\t\t}\n\t\treturn apiKey;\n\t}\n\n\t/**\n\t * Get a callable search tool that returns Tools collections.\n\t *\n\t * Returns a SearchTool instance that wraps `searchTools()` for use in agent loops.\n\t *\n\t * @param options - Options including the default search mode\n\t * @returns SearchTool instance\n\t *\n\t * @example\n\t * ```typescript\n\t * const toolset = new StackOneToolSet({ apiKey: 'sk-xxx' });\n\t * const searchTool = toolset.getSearchTool();\n\t * const tools = await searchTool.search('manage employee records', { accountIds: ['acc-123'] });\n\t * ```\n\t */\n\tgetSearchTool(options?: { search?: SearchMode }): SearchTool {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst config: SearchConfig = options?.search\n\t\t\t? { ...this.searchConfig, method: options.search }\n\t\t\t: this.searchConfig;\n\n\t\treturn new SearchTool(this, config);\n\t}\n\n\t/**\n\t * Get tool_search + tool_execute for agent-driven discovery.\n\t *\n\t * Returns a Tools collection with two tools that let the LLM\n\t * discover and execute tools on-demand.\n\t *\n\t * @param options - Options to scope tool discovery\n\t * @returns Tools collection containing tool_search and tool_execute\n\t */\n\tgetTools(options?: { accountIds?: string[] }): Tools {\n\t\tconst accountIds =\n\t\t\toptions?.accountIds ??\n\t\t\tthis.executeConfig?.accountIds ??\n\t\t\t(this.accountIds.length > 0 ? this.accountIds : undefined);\n\t\treturn this.buildTools(accountIds);\n\t}\n\n\t/**\n\t * Build tool_search + tool_execute tools scoped to this toolset.\n\t */\n\tprivate buildTools(accountIds?: string[], connectors?: string): Tools {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst searchTool = createSearchTool(this, accountIds, connectors);\n\t\tconst executeTool = createExecuteTool(this, accountIds, connectors);\n\t\treturn new Tools([searchTool, executeTool]);\n\t}\n\n\t/**\n\t * Get tools in OpenAI function calling format.\n\t *\n\t * @param options - Options\n\t * @param options.mode - Tool mode.\n\t * `undefined` (default): fetch all tools and convert to OpenAI format.\n\t * `\"search_and_execute\"`: return two tools (tool_search + tool_execute)\n\t * that let the LLM discover and execute tools on-demand.\n\t * @param options.accountIds - Account IDs to scope tools. Overrides the `execute`\n\t * config from the constructor.\n\t * @returns List of tool definitions in OpenAI function format.\n\t *\n\t * @example\n\t * ```typescript\n\t * // All tools\n\t * const toolset = new StackOneToolSet();\n\t * const tools = await toolset.openai();\n\t *\n\t * // Search and execute for agent-driven discovery\n\t * const toolset = new StackOneToolSet({ search: {} });\n\t * const tools = await toolset.openai({ mode: 'search_and_execute' });\n\t * ```\n\t */\n\tasync openai(options?: {\n\t\tmode?: 'search_and_execute';\n\t\taccountIds?: string[];\n\t}): Promise<ReturnType<Tools['toOpenAI']>> {\n\t\tconst effectiveAccountIds = options?.accountIds ?? this.executeConfig?.accountIds;\n\n\t\tif (options?.mode === 'search_and_execute') {\n\t\t\t// Discover available connectors for dynamic descriptions\n\t\t\tlet connectors: string | undefined;\n\t\t\ttry {\n\t\t\t\tconst allTools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\t\t\tconst connectorSet = allTools.getConnectors();\n\t\t\t\tif (connectorSet.size > 0) {\n\t\t\t\t\tconnectors = Array.from(connectorSet).sort().join(', ');\n\t\t\t\t}\n\t\t\t} catch {\n\t\t\t\t// Best-effort: if discovery fails, use generic descriptions\n\t\t\t}\n\t\t\treturn this.buildTools(effectiveAccountIds, connectors).toOpenAI();\n\t\t}\n\n\t\tconst tools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\treturn tools.toOpenAI();\n\t}\n\n\t/**\n\t * Search for and fetch tools using semantic or local search.\n\t *\n\t * This method discovers relevant tools based on natural language queries.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * (e.g., \"create employee\", \"send a message\")\n\t * @param options - Search options\n\t * @returns Tools collection with matched tools from linked accounts\n\t * @throws SemanticSearchError if the API call fails and search is \"semantic\"\n\t *\n\t * @example\n\t * ```typescript\n\t * // Semantic search (default with local fallback)\n\t * const tools = await toolset.searchTools('manage employee records', { topK: 5 });\n\t *\n\t * // Explicit semantic search\n\t * const tools = await toolset.searchTools('manage employees', { search: 'semantic' });\n\t *\n\t * // Local BM25+TF-IDF search\n\t * const tools = await toolset.searchTools('manage employees', { search: 'local' });\n\t *\n\t * // Filter by connector\n\t * const tools = await toolset.searchTools('create time off request', {\n\t * connector: 'bamboohr',\n\t * search: 'semantic',\n\t * });\n\t * ```\n\t */\n\tasync searchTools(query: string, options?: SearchToolsOptions): Promise<Tools> {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst search = options?.search ?? this.searchConfig.method ?? 'auto';\n\t\tconst topK = options?.topK ?? this.searchConfig.topK;\n\t\tconst minSimilarity = options?.minSimilarity ?? this.searchConfig.minSimilarity;\n\t\tconst mergedOptions = { ...options, search, topK, minSimilarity };\n\n\t\tconst allTools = await this.fetchTools({ accountIds: mergedOptions.accountIds });\n\t\tconst availableConnectors = allTools.getConnectors();\n\n\t\tif (availableConnectors.size === 0) {\n\t\t\treturn new Tools([]);\n\t\t}\n\n\t\t// Local-only search — skip semantic API entirely\n\t\tif (search === 'local') {\n\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t}\n\n\t\ttry {\n\t\t\t// Determine which connectors to search\n\t\t\tlet connectorsToSearch: Set<string>;\n\t\t\tif (mergedOptions.connector) {\n\t\t\t\tconst connectorLower = mergedOptions.connector.toLowerCase();\n\t\t\t\tconnectorsToSearch = availableConnectors.has(connectorLower)\n\t\t\t\t\t? new Set([connectorLower])\n\t\t\t\t\t: new Set();\n\t\t\t\tif (connectorsToSearch.size === 0) {\n\t\t\t\t\treturn new Tools([]);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tconnectorsToSearch = availableConnectors;\n\t\t\t}\n\n\t\t\t// Search each connector in parallel — in auto mode, treat missing\n\t\t\t// API key as \"semantic unavailable\" and fall back to local search.\n\t\t\tlet client: SemanticSearchClient;\n\t\t\ttry {\n\t\t\t\tclient = this.getSemanticClient();\n\t\t\t} catch (error) {\n\t\t\t\tif (search === 'auto' && error instanceof ToolSetConfigError) {\n\t\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tconst allResults: SemanticSearchResult[] = [];\n\t\t\tlet lastError: SemanticSearchError | undefined;\n\n\t\t\tconst searchPromises = [...connectorsToSearch].map(async (connector) => {\n\t\t\t\ttry {\n\t\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\t\tconnector,\n\t\t\t\t\t\ttopK: mergedOptions.topK,\n\t\t\t\t\t\tminSimilarity: mergedOptions.minSimilarity,\n\t\t\t\t\t});\n\t\t\t\t\treturn response.results;\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\t\t\tlastError = error;\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst resultArrays = await Promise.all(searchPromises);\n\t\t\tfor (const results of resultArrays) {\n\t\t\t\tallResults.push(...results);\n\t\t\t}\n\n\t\t\t// If ALL connector searches failed, re-raise to trigger fallback\n\t\t\tif (allResults.length === 0 && lastError) {\n\t\t\t\tthrow lastError;\n\t\t\t}\n\n\t\t\t// Sort by score, apply topK\n\t\t\tallResults.sort((a, b) => b.similarityScore - a.similarityScore);\n\t\t\tconst topResults =\n\t\t\t\tmergedOptions.topK != null ? allResults.slice(0, mergedOptions.topK) : allResults;\n\n\t\t\tif (topResults.length === 0) {\n\t\t\t\treturn new Tools([]);\n\t\t\t}\n\n\t\t\t// 1. Parse composite IDs to MCP-format action names, deduplicate\n\t\t\tconst seenNames = new Set<string>();\n\t\t\tconst actionNames: string[] = [];\n\t\t\tfor (const result of topResults) {\n\t\t\t\tconst name = normalizeActionName(result.id);\n\t\t\t\tif (seenNames.has(name)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tseenNames.add(name);\n\t\t\t\tactionNames.push(name);\n\t\t\t}\n\n\t\t\tif (actionNames.length === 0) {\n\t\t\t\treturn new Tools([]);\n\t\t\t}\n\n\t\t\t// 2. Use MCP tools (already fetched) — schemas come from the source of truth\n\t\t\t// 3. Filter to only the tools search found, preserving search relevance order\n\t\t\tconst actionOrder = new Map(actionNames.map((name, i) => [name, i]));\n\t\t\tconst matchedTools = allTools.toArray().filter((t) => seenNames.has(t.name));\n\t\t\tmatchedTools.sort(\n\t\t\t\t(a, b) =>\n\t\t\t\t\t(actionOrder.get(a.name) ?? Number.POSITIVE_INFINITY) -\n\t\t\t\t\t(actionOrder.get(b.name) ?? Number.POSITIVE_INFINITY),\n\t\t\t);\n\n\t\t\t// Auto mode: if semantic returned results but none matched MCP tools, fall back to local\n\t\t\tif (search === 'auto' && matchedTools.length === 0) {\n\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t}\n\n\t\t\treturn new Tools(matchedTools);\n\t\t} catch (error) {\n\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\tif (search === 'semantic') {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\n\t\t\t\t// Auto mode: silently fall back to local search\n\t\t\t\treturn this.localSearch(query, allTools, mergedOptions);\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Search for action names without fetching tools.\n\t *\n\t * Useful when you need to inspect search results before fetching,\n\t * or when building custom filtering logic.\n\t *\n\t * @param query - Natural language description of needed functionality\n\t * @param options - Search options\n\t * @returns List of SemanticSearchResult with action names, scores, and metadata\n\t *\n\t * @example\n\t * ```typescript\n\t * // Lightweight: inspect results before fetching\n\t * const results = await toolset.searchActionNames('manage employees');\n\t * for (const r of results) {\n\t * console.log(`${r.id}: ${r.similarityScore.toFixed(2)}`);\n\t * }\n\t *\n\t * // Then fetch specific high-scoring actions\n\t * const selected = results\n\t * .filter(r => r.similarityScore > 0.7)\n\t * .map(r => r.id);\n\t * const tools = await toolset.fetchTools({ actions: selected });\n\t * ```\n\t */\n\tasync searchActionNames(\n\t\tquery: string,\n\t\toptions?: SearchActionNamesOptions,\n\t): Promise<SemanticSearchResult[]> {\n\t\tif (this.searchConfig === null) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'Search is disabled. Initialize StackOneToolSet with a search config to enable.',\n\t\t\t);\n\t\t}\n\n\t\tconst effectiveTopK = options?.topK ?? this.searchConfig.topK;\n\t\tconst effectiveMinSimilarity = options?.minSimilarity ?? this.searchConfig.minSimilarity;\n\n\t\t// Resolve available connectors from account IDs\n\t\tlet availableConnectors: Set<string> | undefined;\n\t\tconst effectiveAccountIds = options?.accountIds || this.accountIds;\n\t\tif (effectiveAccountIds.length > 0) {\n\t\t\tconst allTools = await this.fetchTools({ accountIds: effectiveAccountIds });\n\t\t\tavailableConnectors = allTools.getConnectors();\n\t\t\tif (availableConnectors.size === 0) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t}\n\n\t\ttry {\n\t\t\tconst client = this.getSemanticClient();\n\t\t\tlet allResults: SemanticSearchResult[] = [];\n\n\t\t\tif (availableConnectors) {\n\t\t\t\t// Parallel per-connector search (only user's connectors)\n\t\t\t\tlet connectorsToSearch: Set<string>;\n\t\t\t\tif (options?.connector) {\n\t\t\t\t\tconst connectorLower = options.connector.toLowerCase();\n\t\t\t\t\tconnectorsToSearch = availableConnectors.has(connectorLower)\n\t\t\t\t\t\t? new Set([connectorLower])\n\t\t\t\t\t\t: new Set();\n\t\t\t\t} else {\n\t\t\t\t\tconnectorsToSearch = availableConnectors;\n\t\t\t\t}\n\n\t\t\t\tconst searchPromises = [...connectorsToSearch].map(async (connector) => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\t\t\tconnector,\n\t\t\t\t\t\t\ttopK: effectiveTopK,\n\t\t\t\t\t\t\tminSimilarity: effectiveMinSimilarity,\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn response.results;\n\t\t\t\t\t} catch {\n\t\t\t\t\t\treturn [];\n\t\t\t\t\t}\n\t\t\t\t});\n\n\t\t\t\tconst resultArrays = await Promise.all(searchPromises);\n\t\t\t\tfor (const results of resultArrays) {\n\t\t\t\t\tallResults.push(...results);\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// No account filtering — single global search\n\t\t\t\tconst response = await client.search(query, {\n\t\t\t\t\tconnector: options?.connector,\n\t\t\t\t\ttopK: effectiveTopK,\n\t\t\t\t\tminSimilarity: effectiveMinSimilarity,\n\t\t\t\t});\n\t\t\t\tallResults = response.results;\n\t\t\t}\n\n\t\t\t// Sort by score — return raw results (consumers can normalize the composite ID if needed)\n\t\t\tallResults.sort((a, b) => b.similarityScore - a.similarityScore);\n\n\t\t\treturn effectiveTopK != null ? allResults.slice(0, effectiveTopK) : allResults;\n\t\t} catch (error) {\n\t\t\tif (error instanceof SemanticSearchError) {\n\t\t\t\treturn [];\n\t\t\t}\n\t\t\tthrow error;\n\t\t}\n\t}\n\n\t/**\n\t * Run local BM25+TF-IDF search over already-fetched tools.\n\t */\n\tprivate async localSearch(\n\t\tquery: string,\n\t\tallTools: Tools,\n\t\toptions?: Pick<SearchToolsOptions, 'connector' | 'topK' | 'minSimilarity'>,\n\t): Promise<Tools> {\n\t\tconst availableConnectors = allTools.getConnectors();\n\t\tif (availableConnectors.size === 0) {\n\t\t\treturn new Tools([]);\n\t\t}\n\n\t\tif (!this.toolIndexCache || this.toolIndexCache.tools !== allTools) {\n\t\t\tthis.toolIndexCache = { tools: allTools, index: new ToolIndex(allTools.toArray()) };\n\t\t}\n\t\tconst index = this.toolIndexCache.index;\n\t\tconst results = await index.search(query, options?.topK ?? 5, options?.minSimilarity ?? 0.0);\n\n\t\tconst matchedNames = results.map((r) => r.name);\n\t\tconst toolMap = new Map(allTools.toArray().map((t) => [t.name, t]));\n\t\tconst filterConnectors = options?.connector\n\t\t\t? new Set([options.connector.toLowerCase()])\n\t\t\t: availableConnectors;\n\n\t\tconst matchedTools = matchedNames\n\t\t\t.filter((name) => toolMap.has(name))\n\t\t\t.map((name) => toolMap.get(name)!)\n\t\t\t.filter((tool) => tool.connector && filterConnectors.has(tool.connector));\n\n\t\treturn new Tools(options?.topK != null ? matchedTools.slice(0, options.topK) : matchedTools);\n\t}\n\n\t/**\n\t * Fetch tools from MCP with optional filtering\n\t * @param options Optional filtering options for account IDs, providers, and actions\n\t * @returns Collection of tools matching the filter criteria\n\t */\n\tasync fetchTools(options?: FetchToolsOptions): Promise<Tools> {\n\t\t// Use account IDs from options, or fall back to instance state\n\t\tconst effectiveAccountIds = options?.accountIds || this.accountIds;\n\n\t\tconst cacheKey = JSON.stringify({\n\t\t\taccountIds: [...effectiveAccountIds].sort(),\n\t\t\tproviders: options?.providers?.length ? [...options.providers].sort() : null,\n\t\t\tactions: options?.actions?.length ? [...options.actions].sort() : null,\n\t\t});\n\t\tconst cached = this.catalogCache.get(cacheKey);\n\t\tif (cached) {\n\t\t\treturn cached;\n\t\t}\n\n\t\t// Fetch tools (with account filtering if needed)\n\t\t// Headers are threaded as parameters per request — never mutate this.headers,\n\t\t// since concurrent callers would clobber each other's x-account-id.\n\t\tlet tools: Tools;\n\t\tif (effectiveAccountIds.length > 0) {\n\t\t\tconst toolsPromises = effectiveAccountIds.map(async (accountId) => {\n\t\t\t\tconst requestHeaders = { ...this.headers, 'x-account-id': accountId };\n\t\t\t\tconst accountTools = await this.fetchToolsFromMcp(requestHeaders);\n\t\t\t\treturn accountTools.toArray();\n\t\t\t});\n\n\t\t\tconst toolArrays = await Promise.all(toolsPromises);\n\t\t\tconst allTools = toolArrays.flat();\n\t\t\ttools = new Tools(allTools);\n\t\t} else {\n\t\t\t// No account filtering - fetch all tools\n\t\t\ttools = await this.fetchToolsFromMcp(this.headers);\n\t\t}\n\n\t\t// Apply provider and action filters\n\t\tconst filteredTools = this.filterTools(tools, options);\n\n\t\t// Add feedback tool\n\t\tconst feedbackTool = createFeedbackTool(undefined, this.accountId, this.baseUrl);\n\t\tconst toolsWithFeedback = new Tools([...filteredTools.toArray(), feedbackTool]);\n\n\t\tthis.catalogCache.set(cacheKey, toolsWithFeedback);\n\t\treturn toolsWithFeedback;\n\t}\n\n\t/**\n\t * Fetch tool definitions from MCP using the given request headers.\n\t * Headers are passed in (not read from this.headers) so concurrent callers\n\t * can each scope their request to a different x-account-id safely.\n\t */\n\tprivate async fetchToolsFromMcp(requestHeaders: Record<string, string>): Promise<Tools> {\n\t\tif (!this.baseUrl) {\n\t\t\tthrow new ToolSetConfigError('baseUrl is required to fetch MCP tools');\n\t\t}\n\n\t\tawait using clients = await createMCPClient({\n\t\t\tbaseUrl: `${this.baseUrl}/mcp`,\n\t\t\theaders: requestHeaders,\n\t\t});\n\n\t\tawait clients.client.connect(clients.transport);\n\t\tconst listToolsResult = await clients.client.listTools();\n\t\tconst actionsClient = this.getActionsClient();\n\n\t\tconst tools = listToolsResult.tools.map(({ name, description, inputSchema }) => {\n\t\t\treturn this.createRpcBackedTool({\n\t\t\t\tactionsClient,\n\t\t\t\tname,\n\t\t\t\tdescription,\n\t\t\t\tinputSchema,\n\t\t\t\theaders: requestHeaders,\n\t\t\t});\n\t\t});\n\n\t\treturn new Tools(tools);\n\t}\n\n\t/**\n\t * Filter tools by providers and actions\n\t * @param tools Tools collection to filter\n\t * @param options Filtering options\n\t * @returns Filtered tools collection\n\t */\n\tprivate filterTools(tools: Tools, options?: FetchToolsOptions): Tools {\n\t\tlet filteredTools = tools.toArray();\n\n\t\t// Filter by providers if specified\n\t\tif (options?.providers && options.providers.length > 0) {\n\t\t\tconst providerSet = new Set(options.providers.map((p) => p.toLowerCase()));\n\t\t\tfilteredTools = filteredTools.filter((tool) => {\n\t\t\t\treturn tool.connector && providerSet.has(tool.connector);\n\t\t\t});\n\t\t}\n\n\t\t// Filter by actions if specified (with glob support)\n\t\tif (options?.actions && options.actions.length > 0) {\n\t\t\tfilteredTools = filteredTools.filter((tool) =>\n\t\t\t\toptions.actions?.some((pattern) => this.matchGlob(tool.name, pattern)),\n\t\t\t);\n\t\t}\n\n\t\treturn new Tools(filteredTools);\n\t}\n\n\t/**\n\t * Check if a string matches a glob pattern\n\t * @param str String to check\n\t * @param pattern Glob pattern\n\t * @returns True if the string matches the pattern\n\t */\n\tprivate matchGlob(str: string, pattern: string): boolean {\n\t\t// Convert glob pattern to regex\n\t\tconst regexPattern = pattern.replace(/\\./g, '\\\\.').replace(/\\*/g, '.*').replace(/\\?/g, '.');\n\n\t\t// Create regex with start and end anchors\n\t\tconst regex = new RegExp(`^${regexPattern}$`);\n\n\t\t// Test if the string matches the pattern\n\t\treturn regex.test(str);\n\t}\n\n\tprivate getActionsClient(): RpcClient {\n\t\tif (this.rpcClient) {\n\t\t\treturn this.rpcClient;\n\t\t}\n\n\t\tconst credentials = this.authentication?.credentials ?? {};\n\t\tconst apiKeyFromAuth =\n\t\t\tthis.authentication?.type === 'basic'\n\t\t\t\t? credentials.username\n\t\t\t\t: this.authentication?.type === 'bearer'\n\t\t\t\t\t? credentials.token\n\t\t\t\t\t: credentials.username;\n\n\t\tconst apiKey = apiKeyFromAuth || process.env.STACKONE_API_KEY;\n\t\tconst password = this.authentication?.type === 'basic' ? (credentials.password ?? '') : '';\n\n\t\tif (!apiKey) {\n\t\t\tthrow new ToolSetConfigError(\n\t\t\t\t'StackOne API key is required to create an actions client. Provide rpcClient, configure authentication credentials, or set the STACKONE_API_KEY environment variable.',\n\t\t\t);\n\t\t}\n\n\t\tthis.rpcClient = new RpcClient({\n\t\t\tserverURL: this.baseUrl,\n\t\t\tsecurity: {\n\t\t\t\tusername: apiKey,\n\t\t\t\tpassword,\n\t\t\t},\n\t\t\ttimeout: this.timeout,\n\t\t});\n\n\t\treturn this.rpcClient;\n\t}\n\n\tprivate createRpcBackedTool({\n\t\tactionsClient,\n\t\tname,\n\t\tdescription,\n\t\tinputSchema,\n\t\theaders,\n\t}: {\n\t\tactionsClient: RpcClient;\n\t\tname: string;\n\t\tdescription?: string;\n\t\tinputSchema: ToolInputSchema;\n\t\theaders: Record<string, string>;\n\t}): BaseTool {\n\t\tconst executeConfig = {\n\t\t\tkind: 'rpc',\n\t\t\tmethod: 'POST',\n\t\t\turl: `${this.baseUrl}/actions/rpc`,\n\t\t\tpayloadKeys: {\n\t\t\t\taction: 'action',\n\t\t\t\tbody: 'body',\n\t\t\t\theaders: 'headers',\n\t\t\t\tpath: 'path',\n\t\t\t\tquery: 'query',\n\t\t\t},\n\t\t} as const satisfies RpcExecuteConfig; // Mirrors StackOne RPC payload layout so metadata/debug stays in sync.\n\n\t\tconst toolParameters = {\n\t\t\t...inputSchema,\n\n\t\t\t// properties are not well typed in MCP spec\n\t\t\tproperties: inputSchema?.properties as JsonSchemaProperties,\n\t\t} satisfies ToolParameters;\n\n\t\tconst tool = new BaseTool(\n\t\t\tname,\n\t\t\tdescription ?? '',\n\t\t\ttoolParameters,\n\t\t\texecuteConfig,\n\t\t\theaders,\n\t\t).setExposeExecutionMetadata(false);\n\n\t\ttool.execute = async (\n\t\t\tinputParams?: JsonObject | string,\n\t\t\toptions?: ExecuteOptions,\n\t\t): Promise<JsonObject> => {\n\t\t\ttry {\n\t\t\t\tif (\n\t\t\t\t\tinputParams !== undefined &&\n\t\t\t\t\ttypeof inputParams !== 'object' &&\n\t\t\t\t\ttypeof inputParams !== 'string'\n\t\t\t\t) {\n\t\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(inputParams)}`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\tconst parsedParams =\n\t\t\t\t\ttypeof inputParams === 'string' ? JSON.parse(inputParams) : (inputParams ?? {});\n\n\t\t\t\tconst currentHeaders = tool.getHeaders();\n\t\t\t\tconst baseHeaders = this.buildActionHeaders(currentHeaders);\n\n\t\t\t\tconst pathParams = this.extractRecord(parsedParams, 'path');\n\t\t\t\tconst queryParams = this.extractRecord(parsedParams, 'query');\n\t\t\t\tconst additionalHeaders = this.extractRecord(parsedParams, 'headers');\n\t\t\t\tconst extraHeaders = normalizeHeaders(additionalHeaders);\n\t\t\t\t// defu merges extraHeaders into baseHeaders, both are already branded types\n\t\t\t\tconst actionHeaders = defu(extraHeaders, baseHeaders);\n\n\t\t\t\tconst bodyPayload = this.extractRecord(parsedParams, 'body');\n\t\t\t\tconst rpcBody: JsonObject = bodyPayload ? { ...bodyPayload } : {};\n\t\t\t\tfor (const [key, value] of Object.entries(parsedParams)) {\n\t\t\t\t\tif (key === 'body' || key === 'headers' || key === 'path' || key === 'query') {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\t\t\t\t\trpcBody[key] = value as JsonObject[string];\n\t\t\t\t}\n\n\t\t\t\tif (options?.dryRun) {\n\t\t\t\t\tconst requestPayload = {\n\t\t\t\t\t\taction: name,\n\t\t\t\t\t\tbody: rpcBody,\n\t\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\t\tpath: pathParams ?? undefined,\n\t\t\t\t\t\tquery: queryParams ?? undefined,\n\t\t\t\t\t};\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\turl: executeConfig.url,\n\t\t\t\t\t\tmethod: executeConfig.method,\n\t\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\t\tbody: JSON.stringify(requestPayload),\n\t\t\t\t\t\tmappedParams: parsedParams,\n\t\t\t\t\t} satisfies JsonObject;\n\t\t\t\t}\n\n\t\t\t\tconst response = await actionsClient.actions.rpcAction({\n\t\t\t\t\taction: name,\n\t\t\t\t\tbody: rpcBody,\n\t\t\t\t\theaders: actionHeaders,\n\t\t\t\t\tpath: pathParams ?? undefined,\n\t\t\t\t\tquery: queryParams ?? undefined,\n\t\t\t\t});\n\n\t\t\t\treturn rpcResponseToJsonObject(response);\n\t\t\t} catch (error) {\n\t\t\t\tif (error instanceof StackOneError) {\n\t\t\t\t\tthrow error;\n\t\t\t\t}\n\t\t\t\tthrow new StackOneError(`Error executing RPC action ${name}`, { cause: error });\n\t\t\t}\n\t\t};\n\n\t\treturn tool;\n\t}\n\n\tprivate buildActionHeaders(headers: Record<string, string>): StackOneHeaders {\n\t\tconst sanitizedEntries = Object.entries(headers).filter(\n\t\t\t([key]) => key.toLowerCase() !== 'authorization',\n\t\t);\n\n\t\treturn stackOneHeadersSchema.parse(\n\t\t\tObject.fromEntries(sanitizedEntries.map(([key, value]) => [key, String(value)])),\n\t\t);\n\t}\n\n\tprivate extractRecord(\n\t\tparams: JsonObject,\n\t\tkey: 'body' | 'headers' | 'path' | 'query',\n\t): JsonObject | undefined {\n\t\tconst value = params[key];\n\t\tif (typeof value === 'object' && value !== null && !Array.isArray(value)) {\n\t\t\treturn value as JsonObject;\n\t\t}\n\t\treturn undefined;\n\t}\n}\n"],"mappings":"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"} |
+1
-1
| { | ||
| "name": "@stackone/ai", | ||
| "version": "2.8.0", | ||
| "version": "2.8.1", | ||
| "description": "Tools for agents to perform actions on your SaaS", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
+15
-20
@@ -1056,20 +1056,10 @@ import { defu } from 'defu'; | ||
| // Fetch tools (with account filtering if needed) | ||
| // Headers are threaded as parameters per request — never mutate this.headers, | ||
| // since concurrent callers would clobber each other's x-account-id. | ||
| let tools: Tools; | ||
| if (effectiveAccountIds.length > 0) { | ||
| const toolsPromises = effectiveAccountIds.map(async (accountId) => { | ||
| const headers = { 'x-account-id': accountId }; | ||
| const mergedHeaders = { ...this.headers, ...headers }; | ||
| // Create a temporary toolset instance with the account-specific headers | ||
| const tempHeaders = mergedHeaders; | ||
| const originalHeaders = this.headers; | ||
| this.headers = tempHeaders; | ||
| try { | ||
| const tools = await this.fetchToolsFromMcp(); | ||
| return tools.toArray(); | ||
| } finally { | ||
| // Restore original headers | ||
| this.headers = originalHeaders; | ||
| } | ||
| const requestHeaders = { ...this.headers, 'x-account-id': accountId }; | ||
| const accountTools = await this.fetchToolsFromMcp(requestHeaders); | ||
| return accountTools.toArray(); | ||
| }); | ||
@@ -1082,3 +1072,3 @@ | ||
| // No account filtering - fetch all tools | ||
| tools = await this.fetchToolsFromMcp(); | ||
| tools = await this.fetchToolsFromMcp(this.headers); | ||
| } | ||
@@ -1098,5 +1088,7 @@ | ||
| /** | ||
| * Fetch tool definitions from MCP | ||
| * Fetch tool definitions from MCP using the given request headers. | ||
| * Headers are passed in (not read from this.headers) so concurrent callers | ||
| * can each scope their request to a different x-account-id safely. | ||
| */ | ||
| private async fetchToolsFromMcp(): Promise<Tools> { | ||
| private async fetchToolsFromMcp(requestHeaders: Record<string, string>): Promise<Tools> { | ||
| if (!this.baseUrl) { | ||
@@ -1108,3 +1100,3 @@ throw new ToolSetConfigError('baseUrl is required to fetch MCP tools'); | ||
| baseUrl: `${this.baseUrl}/mcp`, | ||
| headers: this.headers, | ||
| headers: requestHeaders, | ||
| }); | ||
@@ -1122,2 +1114,3 @@ | ||
| inputSchema, | ||
| headers: requestHeaders, | ||
| }); | ||
@@ -1212,2 +1205,3 @@ }); | ||
| inputSchema, | ||
| headers, | ||
| }: { | ||
@@ -1218,2 +1212,3 @@ actionsClient: RpcClient; | ||
| inputSchema: ToolInputSchema; | ||
| headers: Record<string, string>; | ||
| }): BaseTool { | ||
@@ -1245,3 +1240,3 @@ const executeConfig = { | ||
| executeConfig, | ||
| this.headers, | ||
| headers, | ||
| ).setExposeExecutionMetadata(false); | ||
@@ -1248,0 +1243,0 @@ |
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 5 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
551775
0.05%52
-3.7%6512
-0.11%