@stackone/ai
Advanced tools
| /** | ||
| * This example demonstrates how to use utility tools for dynamic tool discovery and execution. | ||
| * Utility tools allow AI agents to search for relevant tools based on natural language queries | ||
| * and execute them dynamically without hardcoding tool names. | ||
| * | ||
| * @beta Utility tools are in beta and may change in future versions | ||
| */ | ||
| import process from 'node:process'; | ||
| import { openai } from '@ai-sdk/openai'; | ||
| import { type JsonObject, StackOneToolSet, Tools } from '@stackone/ai'; | ||
| import { generateText, stepCountIs } from 'ai'; | ||
| const apiKey = process.env.STACKONE_API_KEY; | ||
| if (!apiKey) { | ||
| console.error('STACKONE_API_KEY environment variable is required'); | ||
| process.exit(1); | ||
| } | ||
| // Replace with your actual account ID from StackOne dashboard | ||
| const accountId = 'your-bamboohr-account-id'; | ||
| /** | ||
| * Example 1: Using utility tools with AI SDK for dynamic tool discovery | ||
| */ | ||
| const utilityToolsWithAISDK = async (): Promise<void> => { | ||
| console.log('š Example 1: Dynamic tool discovery with AI SDK\n'); | ||
| // Initialize StackOne toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch all available tools via MCP | ||
| const allTools = await toolset.fetchTools(); | ||
| // Get utility tools for dynamic discovery and execution | ||
| const utilityTools = await allTools.utilityTools(); | ||
| const aiSdkUtilityTools = await utilityTools.toAISDK(); | ||
| // Use utility tools to dynamically find and execute relevant tools | ||
| const { text, toolCalls } = await generateText({ | ||
| model: openai('gpt-5.1'), | ||
| tools: aiSdkUtilityTools, | ||
| prompt: `I need to create a time off request for an employee. | ||
| First, find the right tool for this task, then use it to create a time off request | ||
| for employee ID "emp_123" from January 15, 2024 to January 19, 2024.`, | ||
| stopWhen: stepCountIs(3), // Allow multiple tool calls | ||
| }); | ||
| console.log('AI Response:', text); | ||
| console.log('\nTool calls made:', toolCalls?.map((call) => call.toolName).join(', ')); | ||
| }; | ||
| /** | ||
| * Example 2: Using utility tools with OpenAI for HR assistant | ||
| */ | ||
| const utilityToolsWithOpenAI = async (): Promise<void> => { | ||
| console.log('\nš¤ Example 2: HR Assistant with OpenAI\n'); | ||
| const { OpenAI } = await import('openai'); | ||
| const openaiClient = new OpenAI({ | ||
| apiKey: process.env.OPENAI_API_KEY, | ||
| }); | ||
| // Initialize StackOne toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch BambooHR tools via MCP | ||
| const bamboohrTools = await toolset.fetchTools({ | ||
| actions: ['bamboohr_*'], | ||
| }); | ||
| // Get utility tools | ||
| const utilityTools = await bamboohrTools.utilityTools(); | ||
| const openAIUtilityTools = utilityTools.toOpenAI(); | ||
| // Create an HR assistant that can discover and use tools dynamically | ||
| const response = await openaiClient.chat.completions.create({ | ||
| model: 'gpt-5.1', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are an HR assistant with access to various HR tools. | ||
| Use the tool_search to find appropriate tools for user requests, | ||
| then use tool_execute to execute them.`, | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: | ||
| 'Can you help me find tools for managing employee records and then list current employees?', | ||
| }, | ||
| ], | ||
| tools: openAIUtilityTools, | ||
| tool_choice: 'auto', | ||
| }); | ||
| console.log('Assistant response:', response.choices[0].message.content); | ||
| // Handle tool calls if any | ||
| if (response.choices[0].message.tool_calls) { | ||
| console.log('\nTool calls:'); | ||
| for (const toolCall of response.choices[0].message.tool_calls) { | ||
| if (toolCall.type === 'function') { | ||
| console.log(`- ${toolCall.function.name}: ${toolCall.function.arguments}`); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| /** | ||
| * Example 3: Direct usage of utility tools without AI | ||
| */ | ||
| const directUtilityToolUsage = async (): Promise<void> => { | ||
| console.log('\nš ļø Example 3: Direct utility tool usage\n'); | ||
| // Initialize toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch all available tools via MCP | ||
| const allTools = await toolset.fetchTools(); | ||
| console.log(`Total available tools: ${allTools.length}`); | ||
| // Get utility tools | ||
| const utilityTools = await allTools.utilityTools(); | ||
| // Step 1: Search for relevant tools | ||
| const filterTool = utilityTools.getTool('tool_search'); | ||
| if (!filterTool) throw new Error('tool_search not found'); | ||
| const searchResult = await filterTool.execute({ | ||
| query: 'employee management create update list', | ||
| limit: 5, | ||
| minScore: 0.3, | ||
| }); | ||
| console.log('Found relevant tools:'); | ||
| const foundTools = searchResult.tools as Array<{ | ||
| name: string; | ||
| description: string; | ||
| score: number; | ||
| }>; | ||
| for (const tool of foundTools) { | ||
| console.log(`- ${tool.name} (score: ${tool.score.toFixed(2)}): ${tool.description}`); | ||
| } | ||
| // Step 2: Execute one of the found tools | ||
| if (foundTools.length > 0) { | ||
| const executeTool = utilityTools.getTool('tool_execute'); | ||
| if (!executeTool) throw new Error('tool_execute not found'); | ||
| const firstTool = foundTools[0]; | ||
| console.log(`\nExecuting ${firstTool.name}...`); | ||
| try { | ||
| // Prepare parameters based on the tool's schema | ||
| let params = {} satisfies JsonObject; | ||
| if (firstTool.name === 'bamboohr_list_employees') { | ||
| params = { limit: 5 }; | ||
| } else if (firstTool.name === 'bamboohr_create_employee') { | ||
| params = { | ||
| name: 'John Doe', | ||
| email: 'john.doe@example.com', | ||
| title: 'Software Engineer', | ||
| }; | ||
| } | ||
| const result = await executeTool.execute({ | ||
| toolName: firstTool.name, | ||
| params, | ||
| }); | ||
| console.log('Execution result:', JSON.stringify(result, null, 2)); | ||
| } catch (error) { | ||
| console.error('Execution failed:', error); | ||
| } | ||
| } | ||
| }; | ||
| /** | ||
| * Example 4: Building a dynamic tool router | ||
| */ | ||
| const dynamicToolRouter = async (): Promise<void> => { | ||
| console.log('\nš Example 4: Dynamic tool router\n'); | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch tools from multiple integrations via MCP | ||
| const bamboohrTools = await toolset.fetchTools({ | ||
| actions: ['bamboohr_*'], | ||
| }); | ||
| const workdayTools = await toolset.fetchTools({ | ||
| actions: ['workday_*'], | ||
| }); | ||
| // Combine tools | ||
| const combinedTools = new Tools([...bamboohrTools.toArray(), ...workdayTools.toArray()]); | ||
| // Get utility tools for the combined set | ||
| const utilityTools = await combinedTools.utilityTools(); | ||
| // Create a router function that finds and executes tools based on intent | ||
| const routeAndExecute = async (intent: string, params: JsonObject = {}) => { | ||
| const filterTool = utilityTools.getTool('tool_search'); | ||
| const executeTool = utilityTools.getTool('tool_execute'); | ||
| if (!filterTool || !executeTool) throw new Error('Utility tools not found'); | ||
| // Find relevant tools | ||
| const searchResult = await filterTool.execute({ | ||
| query: intent, | ||
| limit: 1, | ||
| minScore: 0.5, | ||
| }); | ||
| const tools = searchResult.tools; | ||
| if (!Array.isArray(tools) || tools.length === 0) { | ||
| return { error: 'No relevant tools found for the given intent' }; | ||
| } | ||
| const selectedTool = tools[0] as { name: string; score: number }; | ||
| console.log(`Routing to: ${selectedTool.name} (score: ${selectedTool.score.toFixed(2)})`); | ||
| // Execute the selected tool | ||
| return await executeTool.execute({ | ||
| toolName: selectedTool.name, | ||
| params, | ||
| }); | ||
| }; | ||
| // Test the router with different intents | ||
| const intents = [ | ||
| { intent: 'I want to see all employees', params: { limit: 10 } }, | ||
| { | ||
| intent: 'Create a new job candidate', | ||
| params: { name: 'Jane Smith', email: 'jane@example.com' }, | ||
| }, | ||
| { intent: 'Find recruitment candidates', params: { status: 'active' } }, | ||
| ] as const satisfies { intent: string; params: JsonObject }[]; | ||
| for (const { intent, params } of intents) { | ||
| console.log(`\nIntent: "${intent}"`); | ||
| const result = await routeAndExecute(intent, params); | ||
| console.log('Result:', JSON.stringify(result, null, 2)); | ||
| } | ||
| }; | ||
| // Main execution | ||
| const main = async () => { | ||
| try { | ||
| // Run examples based on environment setup | ||
| if (process.env.OPENAI_API_KEY) { | ||
| await utilityToolsWithAISDK(); | ||
| await utilityToolsWithOpenAI(); | ||
| } else { | ||
| console.log('ā ļø OPENAI_API_KEY not found, skipping AI examples\n'); | ||
| } | ||
| // These examples work without AI | ||
| await directUtilityToolUsage(); | ||
| await dynamicToolRouter(); | ||
| } catch (error) { | ||
| console.error('Error running examples:', error); | ||
| } | ||
| }; | ||
| // Run if this file is executed directly | ||
| if (import.meta.main) { | ||
| await main(); | ||
| } | ||
| export { utilityToolsWithAISDK, utilityToolsWithOpenAI, directUtilityToolUsage, dynamicToolRouter }; |
+1
-1
| //#region package.json | ||
| var version = "2.2.0"; | ||
| var version = "2.3.0"; | ||
| 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.2.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\"repository\": {\n\t\t\"type\": \"git\",\n\t\t\"url\": \"git+https://github.com/StackOneHQ/stackone-ai-node.git\"\n\t},\n\t\"author\": \"StackOne\",\n\t\"license\": \"Apache-2.0\",\n\t\"type\": \"module\",\n\t\"main\": \"./dist/index.mjs\",\n\t\"exports\": {\n\t\t\".\": \"./src/index.ts\",\n\t\t\"./package.json\": \"./package.json\"\n\t},\n\t\"module\": \"./dist/index.mjs\",\n\t\"types\": \"./dist/index.d.mts\",\n\t\"files\": [\n\t\t\"!examples/*.test.ts\",\n\t\t\"!src/**/*.test-d.ts\",\n\t\t\"!src/**/*.test.ts\",\n\t\t\"LICENSE\",\n\t\t\"README.md\",\n\t\t\"dist\",\n\t\t\"examples/*.ts\",\n\t\t\"src\"\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},\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\"@typescript/native-preview\": \"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\"lefthook\": \"catalog:dev\",\n\t\t\"msw\": \"catalog:dev\",\n\t\t\"openai\": \"catalog:peer\",\n\t\t\"oxfmt\": \"catalog:dev\",\n\t\t\"oxlint\": \"catalog:dev\",\n\t\t\"oxlint-tsgolint\": \"catalog:dev\",\n\t\t\"publint\": \"catalog:dev\",\n\t\t\"tsdown\": \"catalog:dev\",\n\t\t\"type-fest\": \"catalog:dev\",\n\t\t\"typescript\": \"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\"packageManager\": \"pnpm@10.26.0\",\n\t\"engines\": {\n\t\t\"node\": \">=20.19.6\"\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\"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}\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.3.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/*.test.ts\",\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},\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"} |
@@ -19,3 +19,3 @@ import { DEFAULT_BASE_URL } from "./consts.mjs"; | ||
| }; | ||
| const name = "meta_collect_tool_feedback"; | ||
| const name = "tool_feedback"; | ||
| const description = "Collects user feedback on StackOne tool performance. First ask the user, \"Are you ok with sending feedback to StackOne?\" and mention that the LLM will take care of sending it. Call this tool only when the user explicitly answers yes."; | ||
@@ -22,0 +22,0 @@ const parameters = { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"feedback.mjs","names":["options: FeedbackToolOptions","authHeaders: Record<string, string>","accountId","results: Array<{ account_id: string; status: number; response: JsonValue }>","errors: Array<{ account_id: string; status?: number; error: string }>","response","parsed: JsonValue"],"sources":["../../src/feedback.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { DEFAULT_BASE_URL } from './consts';\nimport { BaseTool } from './tool';\nimport type { ExecuteConfig, ExecuteOptions, JsonObject, JsonValue, ToolParameters } from './types';\nimport { StackOneError } from './utils/error-stackone';\n\ninterface FeedbackToolOptions {\n\tbaseUrl?: string;\n\tapiKey?: string;\n\taccountId?: string;\n}\n\nconst createNonEmptyTrimmedStringSchema = (fieldName: string) =>\n\tz\n\t\t.string()\n\t\t.transform((value) => value.trim())\n\t\t.refine((value) => value.length > 0, {\n\t\t\tmessage: `${fieldName} must be a non-empty string.`,\n\t\t});\n\nconst feedbackInputSchema = z.object({\n\tfeedback: createNonEmptyTrimmedStringSchema('Feedback'),\n\taccount_id: z\n\t\t.union([\n\t\t\tcreateNonEmptyTrimmedStringSchema('Account ID'),\n\t\t\tz\n\t\t\t\t.array(createNonEmptyTrimmedStringSchema('Account ID'))\n\t\t\t\t.min(1, 'At least one account ID is required'),\n\t\t])\n\t\t.transform((value) => (Array.isArray(value) ? value : [value])),\n\ttool_names: z\n\t\t.array(z.string())\n\t\t.min(1, 'At least one tool name is required')\n\t\t.transform((value) => value.map((item) => item.trim()).filter((item) => item.length > 0))\n\t\t.refine((value) => value.length > 0, {\n\t\t\tmessage: 'Tool names must contain at least one non-empty string',\n\t\t}),\n});\n\nexport function createFeedbackTool(\n\tapiKey?: string,\n\taccountId?: string,\n\tbaseUrl = DEFAULT_BASE_URL,\n): BaseTool {\n\tconst options: FeedbackToolOptions = {\n\t\tapiKey,\n\t\taccountId,\n\t\tbaseUrl,\n\t};\n\tconst name = 'meta_collect_tool_feedback' as const;\n\tconst description =\n\t\t'Collects user feedback on StackOne tool performance. First ask the user, \"Are you ok with sending feedback to StackOne?\" and mention that the LLM will take care of sending it. Call this tool only when the user explicitly answers yes.';\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\taccount_id: {\n\t\t\t\toneOf: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdescription: 'Single account identifier (e.g., \"acc_123456\")',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'array',\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdescription: 'Array of account identifiers (e.g., [\"acc_123456\", \"acc_789012\"])',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdescription: 'Account identifier(s) - can be a single string or array of strings',\n\t\t\t},\n\t\t\tfeedback: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Verbatim feedback from the user about their experience with StackOne tools.',\n\t\t\t},\n\t\t\ttool_names: {\n\t\t\t\ttype: 'array',\n\t\t\t\titems: {\n\t\t\t\t\ttype: 'string',\n\t\t\t\t},\n\t\t\t\tdescription: 'Array of tool names being reviewed',\n\t\t\t},\n\t\t},\n\t\trequired: ['feedback', 'account_id', 'tool_names'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'http',\n\t\tmethod: 'POST',\n\t\turl: '/ai/tool-feedback',\n\t\tbodyType: 'json',\n\t\tparams: [],\n\t} as const satisfies ExecuteConfig;\n\n\t// Get API key from environment or options\n\tconst resolvedApiKey = options.apiKey || process.env.STACKONE_API_KEY;\n\n\t// Create authentication headers\n\tconst authHeaders: Record<string, string> = {};\n\tif (resolvedApiKey) {\n\t\tconst authString = Buffer.from(`${resolvedApiKey}:`).toString('base64');\n\t\tauthHeaders.Authorization = `Basic ${authString}`;\n\t}\n\n\tconst tool = new BaseTool(name, description, parameters, executeConfig, authHeaders);\n\tconst resolvedBaseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n\n\ttool.execute = async function (\n\t\tthis: BaseTool,\n\t\tinputParams?: JsonObject | string,\n\t\texecuteOptions?: ExecuteOptions,\n\t): Promise<JsonObject> {\n\t\ttry {\n\t\t\tconst rawParams =\n\t\t\t\ttypeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsedParams = feedbackInputSchema.parse(rawParams);\n\n\t\t\tconst headers = {\n\t\t\t\tAccept: 'application/json',\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t...this.getHeaders(),\n\t\t\t};\n\n\t\t\t// Handle dry run - show what would be sent to each account\n\t\t\tif (executeOptions?.dryRun) {\n\t\t\t\tconst dryRunResults = parsedParams.account_id.map((accountId: string) => ({\n\t\t\t\t\turl: `${resolvedBaseUrl}${executeConfig.url}`,\n\t\t\t\t\tmethod: executeConfig.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: {\n\t\t\t\t\t\tfeedback: parsedParams.feedback,\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\ttool_names: parsedParams.tool_names,\n\t\t\t\t\t},\n\t\t\t\t}));\n\n\t\t\t\treturn {\n\t\t\t\t\tmultiple_requests: dryRunResults,\n\t\t\t\t\ttotal_accounts: parsedParams.account_id.length,\n\t\t\t\t} satisfies JsonObject;\n\t\t\t}\n\n\t\t\t// Send feedback to each account individually\n\t\t\tconst results: Array<{ account_id: string; status: number; response: JsonValue }> = [];\n\t\t\tconst errors: Array<{ account_id: string; status?: number; error: string }> = [];\n\n\t\t\tfor (const accountId of parsedParams.account_id) {\n\t\t\t\ttry {\n\t\t\t\t\tconst requestBody = {\n\t\t\t\t\t\tfeedback: parsedParams.feedback,\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\ttool_names: parsedParams.tool_names,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst response = await fetch(`${resolvedBaseUrl}${executeConfig.url}`, {\n\t\t\t\t\t\tmethod: executeConfig.method satisfies 'POST',\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\t\t});\n\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\tlet parsed: JsonValue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed = text ? (JSON.parse(text) satisfies JsonValue) : {};\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tparsed = { raw: text };\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\ttypeof parsed === 'object' && parsed !== null\n\t\t\t\t\t\t\t\t\t? JSON.stringify(parsed)\n\t\t\t\t\t\t\t\t\t: String(parsed),\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.push({\n\t\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\tresponse: parsed,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return summary of all submissions in Python SDK format\n\t\t\tconst response = {\n\t\t\t\tmessage: `Feedback sent to ${parsedParams.account_id.length} account(s)`,\n\t\t\t\ttotal_accounts: parsedParams.account_id.length,\n\t\t\t\tsuccessful: results.length,\n\t\t\t\tfailed: errors.length,\n\t\t\t\tresults: [\n\t\t\t\t\t...results.map((r) => ({\n\t\t\t\t\t\taccount_id: r.account_id,\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tresult: r.response,\n\t\t\t\t\t})),\n\t\t\t\t\t...errors.map((e) => ({\n\t\t\t\t\t\taccount_id: e.account_id,\n\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\terror: e.error,\n\t\t\t\t\t})),\n\t\t\t\t],\n\t\t\t} satisfies JsonObject;\n\n\t\t\t// If all submissions failed, throw an error\n\t\t\tif (errors.length > 0 && results.length === 0) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Failed to submit feedback to any account. Errors: ${JSON.stringify(errors)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn response;\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError('Error executing tool', { cause: error });\n\t\t}\n\t};\n\n\treturn tool;\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"feedback.mjs","names":["options: FeedbackToolOptions","authHeaders: Record<string, string>","accountId","results: Array<{ account_id: string; status: number; response: JsonValue }>","errors: Array<{ account_id: string; status?: number; error: string }>","response","parsed: JsonValue"],"sources":["../../src/feedback.ts"],"sourcesContent":["import { z } from 'zod/v4';\nimport { DEFAULT_BASE_URL } from './consts';\nimport { BaseTool } from './tool';\nimport type { ExecuteConfig, ExecuteOptions, JsonObject, JsonValue, ToolParameters } from './types';\nimport { StackOneError } from './utils/error-stackone';\n\ninterface FeedbackToolOptions {\n\tbaseUrl?: string;\n\tapiKey?: string;\n\taccountId?: string;\n}\n\nconst createNonEmptyTrimmedStringSchema = (fieldName: string) =>\n\tz\n\t\t.string()\n\t\t.transform((value) => value.trim())\n\t\t.refine((value) => value.length > 0, {\n\t\t\tmessage: `${fieldName} must be a non-empty string.`,\n\t\t});\n\nconst feedbackInputSchema = z.object({\n\tfeedback: createNonEmptyTrimmedStringSchema('Feedback'),\n\taccount_id: z\n\t\t.union([\n\t\t\tcreateNonEmptyTrimmedStringSchema('Account ID'),\n\t\t\tz\n\t\t\t\t.array(createNonEmptyTrimmedStringSchema('Account ID'))\n\t\t\t\t.min(1, 'At least one account ID is required'),\n\t\t])\n\t\t.transform((value) => (Array.isArray(value) ? value : [value])),\n\ttool_names: z\n\t\t.array(z.string())\n\t\t.min(1, 'At least one tool name is required')\n\t\t.transform((value) => value.map((item) => item.trim()).filter((item) => item.length > 0))\n\t\t.refine((value) => value.length > 0, {\n\t\t\tmessage: 'Tool names must contain at least one non-empty string',\n\t\t}),\n});\n\nexport function createFeedbackTool(\n\tapiKey?: string,\n\taccountId?: string,\n\tbaseUrl = DEFAULT_BASE_URL,\n): BaseTool {\n\tconst options: FeedbackToolOptions = {\n\t\tapiKey,\n\t\taccountId,\n\t\tbaseUrl,\n\t};\n\tconst name = 'tool_feedback' as const;\n\tconst description =\n\t\t'Collects user feedback on StackOne tool performance. First ask the user, \"Are you ok with sending feedback to StackOne?\" and mention that the LLM will take care of sending it. Call this tool only when the user explicitly answers yes.';\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\taccount_id: {\n\t\t\t\toneOf: [\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\tdescription: 'Single account identifier (e.g., \"acc_123456\")',\n\t\t\t\t\t},\n\t\t\t\t\t{\n\t\t\t\t\t\ttype: 'array',\n\t\t\t\t\t\titems: {\n\t\t\t\t\t\t\ttype: 'string',\n\t\t\t\t\t\t},\n\t\t\t\t\t\tdescription: 'Array of account identifiers (e.g., [\"acc_123456\", \"acc_789012\"])',\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\tdescription: 'Account identifier(s) - can be a single string or array of strings',\n\t\t\t},\n\t\t\tfeedback: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Verbatim feedback from the user about their experience with StackOne tools.',\n\t\t\t},\n\t\t\ttool_names: {\n\t\t\t\ttype: 'array',\n\t\t\t\titems: {\n\t\t\t\t\ttype: 'string',\n\t\t\t\t},\n\t\t\t\tdescription: 'Array of tool names being reviewed',\n\t\t\t},\n\t\t},\n\t\trequired: ['feedback', 'account_id', 'tool_names'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'http',\n\t\tmethod: 'POST',\n\t\turl: '/ai/tool-feedback',\n\t\tbodyType: 'json',\n\t\tparams: [],\n\t} as const satisfies ExecuteConfig;\n\n\t// Get API key from environment or options\n\tconst resolvedApiKey = options.apiKey || process.env.STACKONE_API_KEY;\n\n\t// Create authentication headers\n\tconst authHeaders: Record<string, string> = {};\n\tif (resolvedApiKey) {\n\t\tconst authString = Buffer.from(`${resolvedApiKey}:`).toString('base64');\n\t\tauthHeaders.Authorization = `Basic ${authString}`;\n\t}\n\n\tconst tool = new BaseTool(name, description, parameters, executeConfig, authHeaders);\n\tconst resolvedBaseUrl = options.baseUrl ?? DEFAULT_BASE_URL;\n\n\ttool.execute = async function (\n\t\tthis: BaseTool,\n\t\tinputParams?: JsonObject | string,\n\t\texecuteOptions?: ExecuteOptions,\n\t): Promise<JsonObject> {\n\t\ttry {\n\t\t\tconst rawParams =\n\t\t\t\ttypeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst parsedParams = feedbackInputSchema.parse(rawParams);\n\n\t\t\tconst headers = {\n\t\t\t\tAccept: 'application/json',\n\t\t\t\t'Content-Type': 'application/json',\n\t\t\t\t...this.getHeaders(),\n\t\t\t};\n\n\t\t\t// Handle dry run - show what would be sent to each account\n\t\t\tif (executeOptions?.dryRun) {\n\t\t\t\tconst dryRunResults = parsedParams.account_id.map((accountId: string) => ({\n\t\t\t\t\turl: `${resolvedBaseUrl}${executeConfig.url}`,\n\t\t\t\t\tmethod: executeConfig.method,\n\t\t\t\t\theaders,\n\t\t\t\t\tbody: {\n\t\t\t\t\t\tfeedback: parsedParams.feedback,\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\ttool_names: parsedParams.tool_names,\n\t\t\t\t\t},\n\t\t\t\t}));\n\n\t\t\t\treturn {\n\t\t\t\t\tmultiple_requests: dryRunResults,\n\t\t\t\t\ttotal_accounts: parsedParams.account_id.length,\n\t\t\t\t} satisfies JsonObject;\n\t\t\t}\n\n\t\t\t// Send feedback to each account individually\n\t\t\tconst results: Array<{ account_id: string; status: number; response: JsonValue }> = [];\n\t\t\tconst errors: Array<{ account_id: string; status?: number; error: string }> = [];\n\n\t\t\tfor (const accountId of parsedParams.account_id) {\n\t\t\t\ttry {\n\t\t\t\t\tconst requestBody = {\n\t\t\t\t\t\tfeedback: parsedParams.feedback,\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\ttool_names: parsedParams.tool_names,\n\t\t\t\t\t};\n\n\t\t\t\t\tconst response = await fetch(`${resolvedBaseUrl}${executeConfig.url}`, {\n\t\t\t\t\t\tmethod: executeConfig.method satisfies 'POST',\n\t\t\t\t\t\theaders,\n\t\t\t\t\t\tbody: JSON.stringify(requestBody),\n\t\t\t\t\t});\n\n\t\t\t\t\tconst text = await response.text();\n\t\t\t\t\tlet parsed: JsonValue;\n\t\t\t\t\ttry {\n\t\t\t\t\t\tparsed = text ? (JSON.parse(text) satisfies JsonValue) : {};\n\t\t\t\t\t} catch {\n\t\t\t\t\t\tparsed = { raw: text };\n\t\t\t\t\t}\n\n\t\t\t\t\tif (!response.ok) {\n\t\t\t\t\t\terrors.push({\n\t\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\terror:\n\t\t\t\t\t\t\t\ttypeof parsed === 'object' && parsed !== null\n\t\t\t\t\t\t\t\t\t? JSON.stringify(parsed)\n\t\t\t\t\t\t\t\t\t: String(parsed),\n\t\t\t\t\t\t});\n\t\t\t\t\t} else {\n\t\t\t\t\t\tresults.push({\n\t\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\t\tstatus: response.status,\n\t\t\t\t\t\t\tresponse: parsed,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\terrors.push({\n\t\t\t\t\t\taccount_id: accountId,\n\t\t\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Return summary of all submissions in Python SDK format\n\t\t\tconst response = {\n\t\t\t\tmessage: `Feedback sent to ${parsedParams.account_id.length} account(s)`,\n\t\t\t\ttotal_accounts: parsedParams.account_id.length,\n\t\t\t\tsuccessful: results.length,\n\t\t\t\tfailed: errors.length,\n\t\t\t\tresults: [\n\t\t\t\t\t...results.map((r) => ({\n\t\t\t\t\t\taccount_id: r.account_id,\n\t\t\t\t\t\tstatus: 'success',\n\t\t\t\t\t\tresult: r.response,\n\t\t\t\t\t})),\n\t\t\t\t\t...errors.map((e) => ({\n\t\t\t\t\t\taccount_id: e.account_id,\n\t\t\t\t\t\tstatus: 'error',\n\t\t\t\t\t\terror: e.error,\n\t\t\t\t\t})),\n\t\t\t\t],\n\t\t\t} satisfies JsonObject;\n\n\t\t\t// If all submissions failed, throw an error\n\t\t\tif (errors.length > 0 && results.length === 0) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Failed to submit feedback to any account. Errors: ${JSON.stringify(errors)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn response;\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError('Error executing tool', { cause: error });\n\t\t}\n\t};\n\n\treturn tool;\n}\n"],"mappings":"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"} |
@@ -194,7 +194,7 @@ import { JsonObject } from "../node_modules/.pnpm/type-fest@4.41.0/node_modules/type-fest/source/basic.mjs"; | ||
| /** | ||
| * Return meta tools for tool discovery and execution | ||
| * Return utility tools for tool discovery and execution | ||
| * @beta This feature is in beta and may change in future versions | ||
| * @param hybridAlpha - Weight for BM25 in hybrid search (0-1). If not provided, uses DEFAULT_HYBRID_ALPHA (0.2). | ||
| */ | ||
| metaTools(hybridAlpha?: number): Promise<Tools>; | ||
| utilityTools(hybridAlpha?: number): Promise<Tools>; | ||
| /** | ||
@@ -201,0 +201,0 @@ * Iterator implementation |
@@ -343,8 +343,8 @@ import { peerDependencies } from "../package.mjs"; | ||
| /** | ||
| * Return meta tools for tool discovery and execution | ||
| * Return utility tools for tool discovery and execution | ||
| * @beta This feature is in beta and may change in future versions | ||
| * @param hybridAlpha - Weight for BM25 in hybrid search (0-1). If not provided, uses DEFAULT_HYBRID_ALPHA (0.2). | ||
| */ | ||
| async metaTools(hybridAlpha = DEFAULT_HYBRID_ALPHA) { | ||
| return new Tools([metaSearchTools(await initializeOramaDb(this.tools), initializeTfidfIndex(this.tools), this.tools, hybridAlpha), metaExecuteTool(this)]); | ||
| async utilityTools(hybridAlpha = DEFAULT_HYBRID_ALPHA) { | ||
| return new Tools([toolSearch(await initializeOramaDb(this.tools), initializeTfidfIndex(this.tools), this.tools, hybridAlpha), toolExecute(this)]); | ||
| } | ||
@@ -455,4 +455,4 @@ /** | ||
| } | ||
| function metaSearchTools(oramaDb, tfidfIndex, allTools, hybridAlpha = DEFAULT_HYBRID_ALPHA) { | ||
| const name = "meta_search_tools"; | ||
| function toolSearch(oramaDb, tfidfIndex, allTools, hybridAlpha = DEFAULT_HYBRID_ALPHA) { | ||
| const name = "tool_search"; | ||
| const tool = new BaseTool(name, `Searches for relevant tools based on a natural language query using hybrid BM25 + TF-IDF search (alpha=${hybridAlpha}). This tool should be called first to discover available tools before executing them.`, { | ||
@@ -541,5 +541,5 @@ type: "object", | ||
| } | ||
| function metaExecuteTool(tools) { | ||
| const name = "meta_execute_tool"; | ||
| const tool = new BaseTool(name, "Executes a specific tool by name with the provided parameters. Use this after discovering tools with meta_search_tools.", { | ||
| function toolExecute(tools) { | ||
| const name = "tool_execute"; | ||
| const tool = new BaseTool(name, "Executes a specific tool by name with the provided parameters. Use this after discovering tools with tool_search.", { | ||
| type: "object", | ||
@@ -546,0 +546,0 @@ properties: { |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"tool.mjs","names":["#headers","#exposeExecutionMetadata","result: AISDKToolResult","fused: Array<{ name: string; score: number }>","name","tool"],"sources":["../../src/tool.ts"],"sourcesContent":["import { type JSONSchema7 as AISDKJSONSchema, jsonSchema } from 'ai';\nimport type { Tool as AnthropicTool } from '@anthropic-ai/sdk/resources';\nimport type { McpSdkServerConfigWithInstance } from '@anthropic-ai/claude-agent-sdk';\nimport * as orama from '@orama/orama';\nimport type { ChatCompletionFunctionTool } from 'openai/resources/chat/completions';\nimport type { FunctionTool as OpenAIResponsesFunctionTool } from 'openai/resources/responses/responses';\nimport type { OverrideProperties } from 'type-fest';\nimport { peerDependencies } from '../package.json';\nimport { DEFAULT_HYBRID_ALPHA } from './consts';\nimport { RequestBuilder } from './requestBuilder';\nimport type {\n\tAISDKToolDefinition,\n\tAISDKToolResult,\n\tClaudeAgentSdkOptions,\n\tExecuteConfig,\n\tExecuteOptions,\n\tHttpExecuteConfig,\n\tJsonObject,\n\tJSONSchema,\n\tLocalExecuteConfig,\n\tRpcExecuteConfig,\n\tToolExecution,\n\tToolParameters,\n} from './types';\n\nimport { StackOneError } from './utils/error-stackone';\nimport { TfidfIndex } from './utils/tfidf-index';\nimport { tryImport } from './utils/try-import';\n\n/**\n * JSON Schema with type narrowed to 'object'\n * Used for tool parameter schemas which are always objects\n */\ntype ObjectJSONSchema = OverrideProperties<JSONSchema, { type: 'object' }>;\n\n/**\n * Base class for all tools. Provides common functionality for executing API calls\n * and converting to various formats (OpenAI, AI SDK)\n */\nexport class BaseTool {\n\tname: string;\n\tdescription: string;\n\tparameters: ToolParameters;\n\texecuteConfig: ExecuteConfig;\n\tprotected requestBuilder?: RequestBuilder;\n\t#exposeExecutionMetadata = true;\n\t#headers: Record<string, string>;\n\n\tprivate createExecutionMetadata(): ToolExecution {\n\t\tconst config = (() => {\n\t\t\tswitch (this.executeConfig.kind) {\n\t\t\t\tcase 'http':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'http',\n\t\t\t\t\t\tmethod: this.executeConfig.method,\n\t\t\t\t\t\turl: this.executeConfig.url,\n\t\t\t\t\t\tbodyType: this.executeConfig.bodyType,\n\t\t\t\t\t\tparams: this.executeConfig.params.map((param) => ({\n\t\t\t\t\t\t\t...param,\n\t\t\t\t\t\t})),\n\t\t\t\t\t} satisfies HttpExecuteConfig;\n\t\t\t\tcase 'rpc':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'rpc',\n\t\t\t\t\t\tmethod: this.executeConfig.method,\n\t\t\t\t\t\turl: this.executeConfig.url,\n\t\t\t\t\t\tpayloadKeys: { ...this.executeConfig.payloadKeys },\n\t\t\t\t\t} satisfies RpcExecuteConfig;\n\t\t\t\tcase 'local':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'local',\n\t\t\t\t\t\tidentifier: this.executeConfig.identifier,\n\t\t\t\t\t\tdescription: this.executeConfig.description,\n\t\t\t\t\t} satisfies LocalExecuteConfig;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.executeConfig satisfies never;\n\t\t\t\t\tthrow new StackOneError('Unsupported executeConfig kind');\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tconfig,\n\t\t\theaders: this.getHeaders(),\n\t\t};\n\t}\n\n\tconstructor(\n\t\tname: string,\n\t\tdescription: string,\n\t\tparameters: ToolParameters,\n\t\texecuteConfig: ExecuteConfig,\n\t\theaders?: Record<string, string>,\n\t) {\n\t\tthis.name = name;\n\t\tthis.description = description;\n\t\tthis.parameters = parameters;\n\t\tthis.executeConfig = executeConfig;\n\t\tthis.#headers = { ...headers };\n\t\tif (executeConfig.kind === 'http') {\n\t\t\tthis.requestBuilder = new RequestBuilder(executeConfig, this.#headers);\n\t\t}\n\t}\n\n\t/**\n\t * Set headers for this tool\n\t */\n\tsetHeaders(headers: Record<string, string>): BaseTool {\n\t\tthis.#headers = { ...this.#headers, ...headers };\n\t\tif (this.requestBuilder) {\n\t\t\tthis.requestBuilder.setHeaders(headers);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get the current headers\n\t */\n\tgetHeaders(): Record<string, string> {\n\t\tif (this.requestBuilder) {\n\t\t\tconst currentHeaders = this.requestBuilder.getHeaders();\n\t\t\tthis.#headers = { ...currentHeaders };\n\t\t\treturn currentHeaders;\n\t\t}\n\t\treturn { ...this.#headers };\n\t}\n\n\t/**\n\t * Control whether execution metadata should be exposed in AI SDK conversions.\n\t */\n\tsetExposeExecutionMetadata(expose: boolean): this {\n\t\tthis.#exposeExecutionMetadata = expose;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Execute the tool with the provided parameters\n\t */\n\tasync execute(inputParams?: JsonObject | string, options?: ExecuteOptions): Promise<JsonObject> {\n\t\ttry {\n\t\t\tif (!this.requestBuilder || this.executeConfig.kind !== 'http') {\n\t\t\t\t// Non-HTTP tools provide their own execute override (e.g. RPC, local meta tools).\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t'BaseTool.execute is only available for HTTP-backed tools. Provide a custom execute implementation for non-HTTP tools.',\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\n\t\t\t// Execute the request directly with parameters\n\t\t\treturn await this.requestBuilder.execute(params, options);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Convert the tool parameters to a pure JSON Schema format\n\t * This is framework-agnostic and can be used with any LLM that accepts JSON Schema\n\t */\n\ttoJsonSchema(): ObjectJSONSchema {\n\t\treturn {\n\t\t\ttype: 'object',\n\t\t\tproperties: this.parameters.properties,\n\t\t\trequired: this.parameters.required,\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to OpenAI Chat Completions API format\n\t */\n\ttoOpenAI(): ChatCompletionFunctionTool {\n\t\treturn {\n\t\t\ttype: 'function',\n\t\t\tfunction: {\n\t\t\t\tname: this.name,\n\t\t\t\tdescription: this.description,\n\t\t\t\tparameters: this.toJsonSchema(),\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to Anthropic format\n\t * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use\n\t */\n\ttoAnthropic(): AnthropicTool {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tinput_schema: this.toJsonSchema(),\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to OpenAI Responses API format\n\t * @see https://platform.openai.com/docs/api-reference/responses\n\t */\n\ttoOpenAIResponses(options: { strict?: boolean } = {}): OpenAIResponsesFunctionTool {\n\t\tconst { strict = true } = options;\n\t\treturn {\n\t\t\ttype: 'function',\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tstrict,\n\t\t\tparameters: {\n\t\t\t\t...this.toJsonSchema(),\n\t\t\t\t...(strict ? { additionalProperties: false } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to Claude Agent SDK format.\n\t * Returns a tool definition compatible with the Claude Agent SDK's tool() function.\n\t *\n\t * @see https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk\n\t */\n\tasync toClaudeAgentSdkTool(): Promise<{\n\t\tname: string;\n\t\tdescription: string;\n\t\tinputSchema: Record<string, unknown>;\n\t\thandler: (\n\t\t\targs: Record<string, unknown>,\n\t\t) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n\t}> {\n\t\tconst inputSchema = jsonSchema(this.toJsonSchema());\n\t\tconst execute = this.execute.bind(this);\n\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tinputSchema,\n\t\t\thandler: async (args: Record<string, unknown>) => {\n\t\t\t\tconst result = await execute(args as JsonObject);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: 'text' as const, text: JSON.stringify(result) }],\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to AI SDK format\n\t */\n\tasync toAISDK(\n\t\toptions: { executable?: boolean; execution?: ToolExecution | false } = {\n\t\t\texecutable: true,\n\t\t},\n\t): Promise<AISDKToolResult> {\n\t\tconst schema = {\n\t\t\t...this.toJsonSchema(),\n\t\t\tadditionalProperties: false,\n\t\t} satisfies AISDKJSONSchema;\n\n\t\t/** AI SDK is optional dependency, import only when needed */\n\t\tconst ai = await tryImport<typeof import('ai')>(\n\t\t\t'ai',\n\t\t\t`npm install ai (requires ${peerDependencies.ai})`,\n\t\t);\n\t\tconst schemaObject = ai.jsonSchema(schema);\n\n\t\tconst executionOption =\n\t\t\toptions.execution !== undefined\n\t\t\t\t? options.execution\n\t\t\t\t: this.#exposeExecutionMetadata\n\t\t\t\t\t? this.createExecutionMetadata()\n\t\t\t\t\t: false;\n\n\t\tconst toolDefinition = {\n\t\t\tinputSchema: schemaObject,\n\t\t\tdescription: this.description,\n\t\t\texecution: executionOption !== false ? executionOption : undefined,\n\t\t\texecute:\n\t\t\t\t(options.executable ?? true)\n\t\t\t\t\t? async (args: Record<string, unknown>) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn await this.execute(args as JsonObject);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\treturn `Error executing tool: ${\n\t\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t} satisfies AISDKToolDefinition;\n\n\t\treturn {\n\t\t\t[this.name]: toolDefinition,\n\t\t} satisfies AISDKToolResult;\n\t}\n}\n\n/**\n * StackOne-specific tool class with additional functionality\n */\nexport class StackOneTool extends BaseTool {\n\t/**\n\t * Get the current account ID\n\t */\n\tgetAccountId(): string | undefined {\n\t\treturn this.getHeaders()['x-account-id'];\n\t}\n\n\t/**\n\t * Set the account ID for this tool\n\t */\n\tsetAccountId(accountId: string): StackOneTool {\n\t\tthis.setHeaders({ 'x-account-id': accountId });\n\t\treturn this;\n\t}\n}\n\n/**\n * Collection of tools with utility methods\n */\nexport class Tools implements Iterable<BaseTool> {\n\tprivate tools: BaseTool[];\n\n\tconstructor(tools: BaseTool[]) {\n\t\tthis.tools = tools;\n\t}\n\n\t/**\n\t * Get the number of tools in the collection\n\t */\n\tget length(): number {\n\t\treturn this.tools.length;\n\t}\n\n\t/**\n\t * Get a tool by name\n\t */\n\tgetTool(name: string): BaseTool | undefined {\n\t\treturn this.tools.find((tool) => tool.name === name);\n\t}\n\n\t/**\n\t * Get a StackOne tool by name\n\t */\n\tgetStackOneTool(name: string): StackOneTool {\n\t\tconst tool = this.getTool(name);\n\t\tif (tool instanceof StackOneTool) {\n\t\t\treturn tool;\n\t\t}\n\t\tthrow new StackOneError(`Tool ${name} is not a StackOne tool`);\n\t}\n\n\t/**\n\t * Check if a tool is a StackOne tool\n\t */\n\tisStackOneTool(tool: BaseTool): tool is StackOneTool {\n\t\treturn tool instanceof StackOneTool;\n\t}\n\n\t/**\n\t * Get all StackOne tools in the collection\n\t */\n\tgetStackOneTools(): StackOneTool[] {\n\t\treturn this.tools.filter((tool): tool is StackOneTool => tool instanceof StackOneTool);\n\t}\n\n\t/**\n\t * Convert all tools to pure JSON Schema format\n\t * Returns an array of objects with name, description, and schema\n\t */\n\ttoJsonSchema(): Array<{ name: string; description: string; parameters: JSONSchema }> {\n\t\treturn this.tools.map((tool) => ({\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tparameters: tool.toJsonSchema(),\n\t\t}));\n\t}\n\n\t/**\n\t * Convert all tools to OpenAI Chat Completions API format\n\t */\n\ttoOpenAI(): ChatCompletionFunctionTool[] {\n\t\treturn this.tools.map((tool) => tool.toOpenAI());\n\t}\n\n\t/**\n\t * Convert all tools to Anthropic format\n\t * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use\n\t */\n\ttoAnthropic(): AnthropicTool[] {\n\t\treturn this.tools.map((tool) => tool.toAnthropic());\n\t}\n\n\t/**\n\t * Convert all tools to OpenAI Responses API format\n\t * @see https://platform.openai.com/docs/api-reference/responses\n\t */\n\ttoOpenAIResponses(options: { strict?: boolean } = {}): OpenAIResponsesFunctionTool[] {\n\t\treturn this.tools.map((tool) => tool.toOpenAIResponses(options));\n\t}\n\n\t/**\n\t * Convert all tools to AI SDK format\n\t */\n\tasync toAISDK(\n\t\toptions: { executable?: boolean; execution?: ToolExecution | false } = {\n\t\t\texecutable: true,\n\t\t},\n\t): Promise<AISDKToolResult> {\n\t\tconst result: AISDKToolResult = {};\n\t\tfor (const tool of this.tools) {\n\t\t\tObject.assign(result, await tool.toAISDK(options));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convert all tools to Claude Agent SDK format.\n\t * Returns an MCP server configuration that can be passed to the\n\t * Claude Agent SDK query() function's mcpServers option.\n\t *\n\t * @example\n\t * ```typescript\n\t * const tools = await toolset.fetchTools();\n\t * const mcpServer = await tools.toClaudeAgentSdk();\n\t *\n\t * const result = query({\n\t * prompt: 'Get employee info',\n\t * options: {\n\t * model: 'claude-sonnet-4-5-20250929',\n\t * mcpServers: {\n\t * 'stackone-tools': mcpServer,\n\t * },\n\t * },\n\t * });\n\t * ```\n\t *\n\t * @see https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk\n\t */\n\tasync toClaudeAgentSdk(\n\t\toptions: ClaudeAgentSdkOptions = {},\n\t): Promise<McpSdkServerConfigWithInstance> {\n\t\tconst { serverName = 'stackone-tools', serverVersion = '1.0.0' } = options;\n\n\t\t// Import the Claude Agent SDK dynamically\n\t\tconst claudeAgentSdk = await tryImport<typeof import('@anthropic-ai/claude-agent-sdk')>(\n\t\t\t'@anthropic-ai/claude-agent-sdk',\n\t\t\t`npm install @anthropic-ai/claude-agent-sdk (requires ${peerDependencies['@anthropic-ai/claude-agent-sdk']})`,\n\t\t);\n\n\t\t// Convert all tools to Claude Agent SDK format\n\t\t// We use type assertions here because the Zod types from our dynamic import\n\t\t// don't perfectly match the Claude Agent SDK's expected types at compile time\n\t\tconst sdkTools = await Promise.all(\n\t\t\tthis.tools.map(async (baseTool) => {\n\t\t\t\tconst toolDef = await baseTool.toClaudeAgentSdkTool();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Dynamic Zod schema types\n\t\t\t\treturn claudeAgentSdk.tool(\n\t\t\t\t\ttoolDef.name,\n\t\t\t\t\ttoolDef.description,\n\t\t\t\t\ttoolDef.inputSchema as any,\n\t\t\t\t\ttoolDef.handler as any,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\n\t\t// Create and return the MCP server\n\t\treturn claudeAgentSdk.createSdkMcpServer({\n\t\t\tname: serverName,\n\t\t\tversion: serverVersion,\n\t\t\ttools: sdkTools,\n\t\t});\n\t}\n\n\t/**\n\t * Filter tools by a predicate function\n\t */\n\tfilter(predicate: (tool: BaseTool) => boolean): Tools {\n\t\treturn new Tools(this.tools.filter(predicate));\n\t}\n\n\t/**\n\t * Return meta tools for tool discovery and execution\n\t * @beta This feature is in beta and may change in future versions\n\t * @param hybridAlpha - Weight for BM25 in hybrid search (0-1). If not provided, uses DEFAULT_HYBRID_ALPHA (0.2).\n\t */\n\tasync metaTools(hybridAlpha = DEFAULT_HYBRID_ALPHA): Promise<Tools> {\n\t\tconst oramaDb = await initializeOramaDb(this.tools);\n\t\tconst tfidfIndex = initializeTfidfIndex(this.tools);\n\t\tconst baseTools = [\n\t\t\tmetaSearchTools(oramaDb, tfidfIndex, this.tools, hybridAlpha),\n\t\t\tmetaExecuteTool(this),\n\t\t];\n\t\tconst tools = new Tools(baseTools);\n\t\treturn tools;\n\t}\n\n\t/**\n\t * Iterator implementation\n\t */\n\t[Symbol.iterator](): Iterator<BaseTool> {\n\t\tlet index = 0;\n\t\tconst tools = this.tools;\n\n\t\treturn {\n\t\t\tnext(): IteratorResult<BaseTool> {\n\t\t\t\tif (index < tools.length) {\n\t\t\t\t\treturn { value: tools[index++], done: false };\n\t\t\t\t}\n\t\t\t\treturn { value: undefined as unknown as BaseTool, done: true };\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert to array\n\t */\n\ttoArray(): BaseTool[] {\n\t\treturn [...this.tools];\n\t}\n\n\t/**\n\t * Map tools to a new array\n\t */\n\tmap<T>(mapper: (tool: BaseTool) => T): T[] {\n\t\treturn this.tools.map(mapper);\n\t}\n\n\t/**\n\t * Execute a function for each tool\n\t */\n\tforEach(callback: (tool: BaseTool) => void): void {\n\t\tthis.tools.forEach(callback);\n\t}\n}\n\n/**\n * Result from meta_search_tools\n */\nexport interface MetaToolSearchResult {\n\tname: string;\n\tdescription: string;\n\tparameters: ToolParameters;\n\tscore: number;\n}\n\ntype OramaDb = ReturnType<typeof orama.create>;\n\n/**\n * Initialize TF-IDF index for tool search\n */\nfunction initializeTfidfIndex(tools: BaseTool[]): TfidfIndex {\n\tconst index = new TfidfIndex();\n\tconst corpus = tools.map((tool) => {\n\t\t// Extract integration from tool name (e.g., 'bamboohr_create_employee' -> 'bamboohr')\n\t\tconst parts = tool.name.split('_');\n\t\tconst integration = parts[0];\n\n\t\t// Extract action type\n\t\tconst actionTypes = ['create', 'update', 'delete', 'get', 'list', 'search'];\n\t\tconst actions = parts.filter((p) => actionTypes.includes(p));\n\n\t\t// Build text corpus for TF-IDF (similar weighting strategy as in tool-calling-evals)\n\t\tconst text = [\n\t\t\t`${tool.name} ${tool.name} ${tool.name}`, // boost name\n\t\t\t`${integration} ${actions.join(' ')}`,\n\t\t\ttool.description,\n\t\t\tparts.join(' '),\n\t\t].join(' ');\n\n\t\treturn { id: tool.name, text };\n\t});\n\n\tindex.build(corpus);\n\treturn index;\n}\n\n/**\n * Initialize Orama database with BM25 algorithm for tool search\n * Using Orama's BM25 scoring algorithm for relevance ranking\n * @see https://docs.orama.com/open-source/usage/create\n * @see https://docs.orama.com/open-source/usage/search/bm25-algorithm/\n */\nasync function initializeOramaDb(tools: BaseTool[]): Promise<OramaDb> {\n\t// Create Orama database schema with BM25 scoring algorithm\n\t// BM25 provides better relevance ranking for natural language queries\n\tconst oramaDb = orama.create({\n\t\tschema: {\n\t\t\tname: 'string' as const,\n\t\t\tdescription: 'string' as const,\n\t\t\tintegration: 'string' as const,\n\t\t\ttags: 'string[]' as const,\n\t\t},\n\t\tcomponents: {\n\t\t\ttokenizer: {\n\t\t\t\tstemming: true,\n\t\t\t},\n\t\t},\n\t});\n\n\t// Index all tools\n\tfor (const tool of tools) {\n\t\t// Extract integration from tool name (e.g., 'bamboohr_create_employee' -> 'bamboohr')\n\t\tconst parts = tool.name.split('_');\n\t\tconst integration = parts[0];\n\n\t\t// Extract action type\n\t\tconst actionTypes = ['create', 'update', 'delete', 'get', 'list', 'search'];\n\t\tconst actions = parts.filter((p) => actionTypes.includes(p));\n\n\t\tawait orama.insert(oramaDb, {\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tintegration: integration,\n\t\t\ttags: [...parts, ...actions],\n\t\t});\n\t}\n\n\treturn oramaDb;\n}\n\nfunction metaSearchTools(\n\toramaDb: OramaDb,\n\ttfidfIndex: TfidfIndex,\n\tallTools: BaseTool[],\n\thybridAlpha = DEFAULT_HYBRID_ALPHA,\n): BaseTool {\n\tconst name = 'meta_search_tools' as const;\n\tconst description =\n\t\t`Searches for relevant tools based on a natural language query using hybrid BM25 + TF-IDF search (alpha=${hybridAlpha}). This tool should be called first to discover available tools before executing them.` as const;\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tquery: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription:\n\t\t\t\t\t'Natural language query describing what tools you need (e.g., \"tools for managing employees\", \"create time off request\")',\n\t\t\t},\n\t\t\tlimit: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Maximum number of tools to return (default: 5)',\n\t\t\t\tdefault: 5,\n\t\t\t},\n\t\t\tminScore: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Minimum relevance score (0-1) for results (default: 0.3)',\n\t\t\t\tdefault: 0.3,\n\t\t\t},\n\t\t},\n\t\trequired: ['query'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'local',\n\t\tidentifier: name,\n\t\tdescription: 'local://get-relevant-tools',\n\t} as const satisfies LocalExecuteConfig;\n\n\tconst tool = new BaseTool(name, description, parameters, executeConfig);\n\ttool.execute = async (inputParams?: JsonObject | string): Promise<JsonObject> => {\n\t\ttry {\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst limit = params.limit || 5;\n\t\t\tconst minScore = params.minScore ?? 0.3;\n\t\t\tconst query = params.query || '';\n\n\t\t\t// Hybrid: BM25 + TF-IDF fusion\n\t\t\tconst alpha = Math.max(0, Math.min(1, hybridAlpha));\n\n\t\t\t// Get results from both algorithms\n\t\t\tconst [bm25Results, tfidfResults] = await Promise.all([\n\t\t\t\torama.search(oramaDb, {\n\t\t\t\t\tterm: query,\n\t\t\t\t\tlimit: Math.max(50, limit),\n\t\t\t\t} as Parameters<typeof orama.search>[1]),\n\t\t\t\tPromise.resolve(tfidfIndex.search(query, Math.max(50, limit))),\n\t\t\t]);\n\n\t\t\t// Build score map\n\t\t\tconst scoreMap = new Map<string, { bm25?: number; tfidf?: number }>();\n\n\t\t\tfor (const hit of bm25Results.hits) {\n\t\t\t\tconst doc = hit.document as { name: string };\n\t\t\t\tscoreMap.set(doc.name, {\n\t\t\t\t\t...scoreMap.get(doc.name),\n\t\t\t\t\tbm25: clamp01(hit.score),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const r of tfidfResults) {\n\t\t\t\tscoreMap.set(r.id, {\n\t\t\t\t\t...scoreMap.get(r.id),\n\t\t\t\t\ttfidf: clamp01(r.score),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Fuse scores\n\t\t\tconst fused: Array<{ name: string; score: number }> = [];\n\t\t\tfor (const [name, scores] of scoreMap) {\n\t\t\t\tconst bm25 = scores.bm25 ?? 0;\n\t\t\t\tconst tfidf = scores.tfidf ?? 0;\n\t\t\t\tconst score = alpha * bm25 + (1 - alpha) * tfidf;\n\t\t\t\tfused.push({ name, score });\n\t\t\t}\n\n\t\t\tfused.sort((a, b) => b.score - a.score);\n\n\t\t\tconst toolConfigs = fused\n\t\t\t\t.filter((r) => r.score >= minScore)\n\t\t\t\t.map((r) => {\n\t\t\t\t\tconst tool = allTools.find((t) => t.name === r.name);\n\t\t\t\t\tif (!tool) return null;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tparameters: tool.parameters,\n\t\t\t\t\t\tscore: r.score,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((t): t is MetaToolSearchResult => t !== null)\n\t\t\t\t.slice(0, limit);\n\n\t\t\t// Convert to JSON-serialisable format (removes undefined values)\n\t\t\treturn JSON.parse(JSON.stringify({ tools: toolConfigs })) satisfies JsonObject;\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t};\n\treturn tool;\n}\n\n/**\n * Clamp value to [0, 1]\n */\nfunction clamp01(x: number): number {\n\treturn x < 0 ? 0 : x > 1 ? 1 : x;\n}\n\nfunction metaExecuteTool(tools: Tools): BaseTool {\n\tconst name = 'meta_execute_tool' as const;\n\tconst description =\n\t\t'Executes a specific tool by name with the provided parameters. Use this after discovering tools with meta_search_tools.' as const;\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\ttoolName: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Name of the tool to execute',\n\t\t\t},\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: 'Parameters to pass to the tool',\n\t\t\t},\n\t\t},\n\t\trequired: ['toolName', 'params'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'local',\n\t\tidentifier: name,\n\t\tdescription: 'local://execute-tool',\n\t} as const satisfies LocalExecuteConfig;\n\n\t// Create the tool instance\n\tconst tool = new BaseTool(name, description, parameters, executeConfig);\n\n\t// Override the execute method to handle tool execution\n\t// receives tool name and parameters and executes the tool\n\ttool.execute = async (\n\t\tinputParams?: JsonObject | string,\n\t\toptions?: ExecuteOptions,\n\t): Promise<JsonObject> => {\n\t\ttry {\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\n\t\t\t// Extract tool name and parameters\n\t\t\tconst { toolName, params: toolParams } = params;\n\n\t\t\t// Find the tool by name\n\t\t\tconst toolToExecute = tools.getTool(toolName);\n\t\t\tif (!toolToExecute) {\n\t\t\t\tthrow new StackOneError(`Tool ${toolName} not found`);\n\t\t\t}\n\n\t\t\t// Execute the tool with the provided parameters\n\t\t\treturn await toolToExecute.execute(toolParams, options);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t};\n\treturn tool;\n}\n"],"mappings":"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"} | ||
| {"version":3,"file":"tool.mjs","names":["#headers","#exposeExecutionMetadata","result: AISDKToolResult","fused: Array<{ name: string; score: number }>","name","tool"],"sources":["../../src/tool.ts"],"sourcesContent":["import { type JSONSchema7 as AISDKJSONSchema, jsonSchema } from 'ai';\nimport type { Tool as AnthropicTool } from '@anthropic-ai/sdk/resources';\nimport type { McpSdkServerConfigWithInstance } from '@anthropic-ai/claude-agent-sdk';\nimport * as orama from '@orama/orama';\nimport type { ChatCompletionFunctionTool } from 'openai/resources/chat/completions';\nimport type { FunctionTool as OpenAIResponsesFunctionTool } from 'openai/resources/responses/responses';\nimport type { OverrideProperties } from 'type-fest';\nimport { peerDependencies } from '../package.json';\nimport { DEFAULT_HYBRID_ALPHA } from './consts';\nimport { RequestBuilder } from './requestBuilder';\nimport type {\n\tAISDKToolDefinition,\n\tAISDKToolResult,\n\tClaudeAgentSdkOptions,\n\tExecuteConfig,\n\tExecuteOptions,\n\tHttpExecuteConfig,\n\tJsonObject,\n\tJSONSchema,\n\tLocalExecuteConfig,\n\tRpcExecuteConfig,\n\tToolExecution,\n\tToolParameters,\n} from './types';\n\nimport { StackOneError } from './utils/error-stackone';\nimport { TfidfIndex } from './utils/tfidf-index';\nimport { tryImport } from './utils/try-import';\n\n/**\n * JSON Schema with type narrowed to 'object'\n * Used for tool parameter schemas which are always objects\n */\ntype ObjectJSONSchema = OverrideProperties<JSONSchema, { type: 'object' }>;\n\n/**\n * Base class for all tools. Provides common functionality for executing API calls\n * and converting to various formats (OpenAI, AI SDK)\n */\nexport class BaseTool {\n\tname: string;\n\tdescription: string;\n\tparameters: ToolParameters;\n\texecuteConfig: ExecuteConfig;\n\tprotected requestBuilder?: RequestBuilder;\n\t#exposeExecutionMetadata = true;\n\t#headers: Record<string, string>;\n\n\tprivate createExecutionMetadata(): ToolExecution {\n\t\tconst config = (() => {\n\t\t\tswitch (this.executeConfig.kind) {\n\t\t\t\tcase 'http':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'http',\n\t\t\t\t\t\tmethod: this.executeConfig.method,\n\t\t\t\t\t\turl: this.executeConfig.url,\n\t\t\t\t\t\tbodyType: this.executeConfig.bodyType,\n\t\t\t\t\t\tparams: this.executeConfig.params.map((param) => ({\n\t\t\t\t\t\t\t...param,\n\t\t\t\t\t\t})),\n\t\t\t\t\t} satisfies HttpExecuteConfig;\n\t\t\t\tcase 'rpc':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'rpc',\n\t\t\t\t\t\tmethod: this.executeConfig.method,\n\t\t\t\t\t\turl: this.executeConfig.url,\n\t\t\t\t\t\tpayloadKeys: { ...this.executeConfig.payloadKeys },\n\t\t\t\t\t} satisfies RpcExecuteConfig;\n\t\t\t\tcase 'local':\n\t\t\t\t\treturn {\n\t\t\t\t\t\tkind: 'local',\n\t\t\t\t\t\tidentifier: this.executeConfig.identifier,\n\t\t\t\t\t\tdescription: this.executeConfig.description,\n\t\t\t\t\t} satisfies LocalExecuteConfig;\n\t\t\t\tdefault:\n\t\t\t\t\tthis.executeConfig satisfies never;\n\t\t\t\t\tthrow new StackOneError('Unsupported executeConfig kind');\n\t\t\t}\n\t\t})();\n\n\t\treturn {\n\t\t\tconfig,\n\t\t\theaders: this.getHeaders(),\n\t\t};\n\t}\n\n\tconstructor(\n\t\tname: string,\n\t\tdescription: string,\n\t\tparameters: ToolParameters,\n\t\texecuteConfig: ExecuteConfig,\n\t\theaders?: Record<string, string>,\n\t) {\n\t\tthis.name = name;\n\t\tthis.description = description;\n\t\tthis.parameters = parameters;\n\t\tthis.executeConfig = executeConfig;\n\t\tthis.#headers = { ...headers };\n\t\tif (executeConfig.kind === 'http') {\n\t\t\tthis.requestBuilder = new RequestBuilder(executeConfig, this.#headers);\n\t\t}\n\t}\n\n\t/**\n\t * Set headers for this tool\n\t */\n\tsetHeaders(headers: Record<string, string>): BaseTool {\n\t\tthis.#headers = { ...this.#headers, ...headers };\n\t\tif (this.requestBuilder) {\n\t\t\tthis.requestBuilder.setHeaders(headers);\n\t\t}\n\t\treturn this;\n\t}\n\n\t/**\n\t * Get the current headers\n\t */\n\tgetHeaders(): Record<string, string> {\n\t\tif (this.requestBuilder) {\n\t\t\tconst currentHeaders = this.requestBuilder.getHeaders();\n\t\t\tthis.#headers = { ...currentHeaders };\n\t\t\treturn currentHeaders;\n\t\t}\n\t\treturn { ...this.#headers };\n\t}\n\n\t/**\n\t * Control whether execution metadata should be exposed in AI SDK conversions.\n\t */\n\tsetExposeExecutionMetadata(expose: boolean): this {\n\t\tthis.#exposeExecutionMetadata = expose;\n\t\treturn this;\n\t}\n\n\t/**\n\t * Execute the tool with the provided parameters\n\t */\n\tasync execute(inputParams?: JsonObject | string, options?: ExecuteOptions): Promise<JsonObject> {\n\t\ttry {\n\t\t\tif (!this.requestBuilder || this.executeConfig.kind !== 'http') {\n\t\t\t\t// Non-HTTP tools provide their own execute override (e.g. RPC, local meta tools).\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t'BaseTool.execute is only available for HTTP-backed tools. Provide a custom execute implementation for non-HTTP tools.',\n\t\t\t\t);\n\t\t\t}\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\n\t\t\t// Execute the request directly with parameters\n\t\t\treturn await this.requestBuilder.execute(params, options);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Convert the tool parameters to a pure JSON Schema format\n\t * This is framework-agnostic and can be used with any LLM that accepts JSON Schema\n\t */\n\ttoJsonSchema(): ObjectJSONSchema {\n\t\treturn {\n\t\t\ttype: 'object',\n\t\t\tproperties: this.parameters.properties,\n\t\t\trequired: this.parameters.required,\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to OpenAI Chat Completions API format\n\t */\n\ttoOpenAI(): ChatCompletionFunctionTool {\n\t\treturn {\n\t\t\ttype: 'function',\n\t\t\tfunction: {\n\t\t\t\tname: this.name,\n\t\t\t\tdescription: this.description,\n\t\t\t\tparameters: this.toJsonSchema(),\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to Anthropic format\n\t * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use\n\t */\n\ttoAnthropic(): AnthropicTool {\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tinput_schema: this.toJsonSchema(),\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to OpenAI Responses API format\n\t * @see https://platform.openai.com/docs/api-reference/responses\n\t */\n\ttoOpenAIResponses(options: { strict?: boolean } = {}): OpenAIResponsesFunctionTool {\n\t\tconst { strict = true } = options;\n\t\treturn {\n\t\t\ttype: 'function',\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tstrict,\n\t\t\tparameters: {\n\t\t\t\t...this.toJsonSchema(),\n\t\t\t\t...(strict ? { additionalProperties: false } : {}),\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to Claude Agent SDK format.\n\t * Returns a tool definition compatible with the Claude Agent SDK's tool() function.\n\t *\n\t * @see https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk\n\t */\n\tasync toClaudeAgentSdkTool(): Promise<{\n\t\tname: string;\n\t\tdescription: string;\n\t\tinputSchema: Record<string, unknown>;\n\t\thandler: (\n\t\t\targs: Record<string, unknown>,\n\t\t) => Promise<{ content: Array<{ type: 'text'; text: string }> }>;\n\t}> {\n\t\tconst inputSchema = jsonSchema(this.toJsonSchema());\n\t\tconst execute = this.execute.bind(this);\n\n\t\treturn {\n\t\t\tname: this.name,\n\t\t\tdescription: this.description,\n\t\t\tinputSchema,\n\t\t\thandler: async (args: Record<string, unknown>) => {\n\t\t\t\tconst result = await execute(args as JsonObject);\n\t\t\t\treturn {\n\t\t\t\t\tcontent: [{ type: 'text' as const, text: JSON.stringify(result) }],\n\t\t\t\t};\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert the tool to AI SDK format\n\t */\n\tasync toAISDK(\n\t\toptions: { executable?: boolean; execution?: ToolExecution | false } = {\n\t\t\texecutable: true,\n\t\t},\n\t): Promise<AISDKToolResult> {\n\t\tconst schema = {\n\t\t\t...this.toJsonSchema(),\n\t\t\tadditionalProperties: false,\n\t\t} satisfies AISDKJSONSchema;\n\n\t\t/** AI SDK is optional dependency, import only when needed */\n\t\tconst ai = await tryImport<typeof import('ai')>(\n\t\t\t'ai',\n\t\t\t`npm install ai (requires ${peerDependencies.ai})`,\n\t\t);\n\t\tconst schemaObject = ai.jsonSchema(schema);\n\n\t\tconst executionOption =\n\t\t\toptions.execution !== undefined\n\t\t\t\t? options.execution\n\t\t\t\t: this.#exposeExecutionMetadata\n\t\t\t\t\t? this.createExecutionMetadata()\n\t\t\t\t\t: false;\n\n\t\tconst toolDefinition = {\n\t\t\tinputSchema: schemaObject,\n\t\t\tdescription: this.description,\n\t\t\texecution: executionOption !== false ? executionOption : undefined,\n\t\t\texecute:\n\t\t\t\t(options.executable ?? true)\n\t\t\t\t\t? async (args: Record<string, unknown>) => {\n\t\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t\treturn await this.execute(args as JsonObject);\n\t\t\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\t\t\treturn `Error executing tool: ${\n\t\t\t\t\t\t\t\t\terror instanceof Error ? error.message : String(error)\n\t\t\t\t\t\t\t\t}`;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t} satisfies AISDKToolDefinition;\n\n\t\treturn {\n\t\t\t[this.name]: toolDefinition,\n\t\t} satisfies AISDKToolResult;\n\t}\n}\n\n/**\n * StackOne-specific tool class with additional functionality\n */\nexport class StackOneTool extends BaseTool {\n\t/**\n\t * Get the current account ID\n\t */\n\tgetAccountId(): string | undefined {\n\t\treturn this.getHeaders()['x-account-id'];\n\t}\n\n\t/**\n\t * Set the account ID for this tool\n\t */\n\tsetAccountId(accountId: string): StackOneTool {\n\t\tthis.setHeaders({ 'x-account-id': accountId });\n\t\treturn this;\n\t}\n}\n\n/**\n * Collection of tools with utility methods\n */\nexport class Tools implements Iterable<BaseTool> {\n\tprivate tools: BaseTool[];\n\n\tconstructor(tools: BaseTool[]) {\n\t\tthis.tools = tools;\n\t}\n\n\t/**\n\t * Get the number of tools in the collection\n\t */\n\tget length(): number {\n\t\treturn this.tools.length;\n\t}\n\n\t/**\n\t * Get a tool by name\n\t */\n\tgetTool(name: string): BaseTool | undefined {\n\t\treturn this.tools.find((tool) => tool.name === name);\n\t}\n\n\t/**\n\t * Get a StackOne tool by name\n\t */\n\tgetStackOneTool(name: string): StackOneTool {\n\t\tconst tool = this.getTool(name);\n\t\tif (tool instanceof StackOneTool) {\n\t\t\treturn tool;\n\t\t}\n\t\tthrow new StackOneError(`Tool ${name} is not a StackOne tool`);\n\t}\n\n\t/**\n\t * Check if a tool is a StackOne tool\n\t */\n\tisStackOneTool(tool: BaseTool): tool is StackOneTool {\n\t\treturn tool instanceof StackOneTool;\n\t}\n\n\t/**\n\t * Get all StackOne tools in the collection\n\t */\n\tgetStackOneTools(): StackOneTool[] {\n\t\treturn this.tools.filter((tool): tool is StackOneTool => tool instanceof StackOneTool);\n\t}\n\n\t/**\n\t * Convert all tools to pure JSON Schema format\n\t * Returns an array of objects with name, description, and schema\n\t */\n\ttoJsonSchema(): Array<{ name: string; description: string; parameters: JSONSchema }> {\n\t\treturn this.tools.map((tool) => ({\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tparameters: tool.toJsonSchema(),\n\t\t}));\n\t}\n\n\t/**\n\t * Convert all tools to OpenAI Chat Completions API format\n\t */\n\ttoOpenAI(): ChatCompletionFunctionTool[] {\n\t\treturn this.tools.map((tool) => tool.toOpenAI());\n\t}\n\n\t/**\n\t * Convert all tools to Anthropic format\n\t * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use\n\t */\n\ttoAnthropic(): AnthropicTool[] {\n\t\treturn this.tools.map((tool) => tool.toAnthropic());\n\t}\n\n\t/**\n\t * Convert all tools to OpenAI Responses API format\n\t * @see https://platform.openai.com/docs/api-reference/responses\n\t */\n\ttoOpenAIResponses(options: { strict?: boolean } = {}): OpenAIResponsesFunctionTool[] {\n\t\treturn this.tools.map((tool) => tool.toOpenAIResponses(options));\n\t}\n\n\t/**\n\t * Convert all tools to AI SDK format\n\t */\n\tasync toAISDK(\n\t\toptions: { executable?: boolean; execution?: ToolExecution | false } = {\n\t\t\texecutable: true,\n\t\t},\n\t): Promise<AISDKToolResult> {\n\t\tconst result: AISDKToolResult = {};\n\t\tfor (const tool of this.tools) {\n\t\t\tObject.assign(result, await tool.toAISDK(options));\n\t\t}\n\t\treturn result;\n\t}\n\n\t/**\n\t * Convert all tools to Claude Agent SDK format.\n\t * Returns an MCP server configuration that can be passed to the\n\t * Claude Agent SDK query() function's mcpServers option.\n\t *\n\t * @example\n\t * ```typescript\n\t * const tools = await toolset.fetchTools();\n\t * const mcpServer = await tools.toClaudeAgentSdk();\n\t *\n\t * const result = query({\n\t * prompt: 'Get employee info',\n\t * options: {\n\t * model: 'claude-sonnet-4-5-20250929',\n\t * mcpServers: {\n\t * 'stackone-tools': mcpServer,\n\t * },\n\t * },\n\t * });\n\t * ```\n\t *\n\t * @see https://docs.anthropic.com/en/docs/agents-and-tools/claude-agent-sdk\n\t */\n\tasync toClaudeAgentSdk(\n\t\toptions: ClaudeAgentSdkOptions = {},\n\t): Promise<McpSdkServerConfigWithInstance> {\n\t\tconst { serverName = 'stackone-tools', serverVersion = '1.0.0' } = options;\n\n\t\t// Import the Claude Agent SDK dynamically\n\t\tconst claudeAgentSdk = await tryImport<typeof import('@anthropic-ai/claude-agent-sdk')>(\n\t\t\t'@anthropic-ai/claude-agent-sdk',\n\t\t\t`npm install @anthropic-ai/claude-agent-sdk (requires ${peerDependencies['@anthropic-ai/claude-agent-sdk']})`,\n\t\t);\n\n\t\t// Convert all tools to Claude Agent SDK format\n\t\t// We use type assertions here because the Zod types from our dynamic import\n\t\t// don't perfectly match the Claude Agent SDK's expected types at compile time\n\t\tconst sdkTools = await Promise.all(\n\t\t\tthis.tools.map(async (baseTool) => {\n\t\t\t\tconst toolDef = await baseTool.toClaudeAgentSdkTool();\n\t\t\t\t// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Dynamic Zod schema types\n\t\t\t\treturn claudeAgentSdk.tool(\n\t\t\t\t\ttoolDef.name,\n\t\t\t\t\ttoolDef.description,\n\t\t\t\t\ttoolDef.inputSchema as any,\n\t\t\t\t\ttoolDef.handler as any,\n\t\t\t\t);\n\t\t\t}),\n\t\t);\n\n\t\t// Create and return the MCP server\n\t\treturn claudeAgentSdk.createSdkMcpServer({\n\t\t\tname: serverName,\n\t\t\tversion: serverVersion,\n\t\t\ttools: sdkTools,\n\t\t});\n\t}\n\n\t/**\n\t * Filter tools by a predicate function\n\t */\n\tfilter(predicate: (tool: BaseTool) => boolean): Tools {\n\t\treturn new Tools(this.tools.filter(predicate));\n\t}\n\n\t/**\n\t * Return utility tools for tool discovery and execution\n\t * @beta This feature is in beta and may change in future versions\n\t * @param hybridAlpha - Weight for BM25 in hybrid search (0-1). If not provided, uses DEFAULT_HYBRID_ALPHA (0.2).\n\t */\n\tasync utilityTools(hybridAlpha = DEFAULT_HYBRID_ALPHA): Promise<Tools> {\n\t\tconst oramaDb = await initializeOramaDb(this.tools);\n\t\tconst tfidfIndex = initializeTfidfIndex(this.tools);\n\t\tconst baseTools = [toolSearch(oramaDb, tfidfIndex, this.tools, hybridAlpha), toolExecute(this)];\n\t\tconst tools = new Tools(baseTools);\n\t\treturn tools;\n\t}\n\n\t/**\n\t * Iterator implementation\n\t */\n\t[Symbol.iterator](): Iterator<BaseTool> {\n\t\tlet index = 0;\n\t\tconst tools = this.tools;\n\n\t\treturn {\n\t\t\tnext(): IteratorResult<BaseTool> {\n\t\t\t\tif (index < tools.length) {\n\t\t\t\t\treturn { value: tools[index++], done: false };\n\t\t\t\t}\n\t\t\t\treturn { value: undefined as unknown as BaseTool, done: true };\n\t\t\t},\n\t\t};\n\t}\n\n\t/**\n\t * Convert to array\n\t */\n\ttoArray(): BaseTool[] {\n\t\treturn [...this.tools];\n\t}\n\n\t/**\n\t * Map tools to a new array\n\t */\n\tmap<T>(mapper: (tool: BaseTool) => T): T[] {\n\t\treturn this.tools.map(mapper);\n\t}\n\n\t/**\n\t * Execute a function for each tool\n\t */\n\tforEach(callback: (tool: BaseTool) => void): void {\n\t\tthis.tools.forEach(callback);\n\t}\n}\n\n/**\n * Result from tool_search\n */\nexport interface ToolSearchResult {\n\tname: string;\n\tdescription: string;\n\tparameters: ToolParameters;\n\tscore: number;\n}\n\ntype OramaDb = ReturnType<typeof orama.create>;\n\n/**\n * Initialize TF-IDF index for tool search\n */\nfunction initializeTfidfIndex(tools: BaseTool[]): TfidfIndex {\n\tconst index = new TfidfIndex();\n\tconst corpus = tools.map((tool) => {\n\t\t// Extract integration from tool name (e.g., 'bamboohr_create_employee' -> 'bamboohr')\n\t\tconst parts = tool.name.split('_');\n\t\tconst integration = parts[0];\n\n\t\t// Extract action type\n\t\tconst actionTypes = ['create', 'update', 'delete', 'get', 'list', 'search'];\n\t\tconst actions = parts.filter((p) => actionTypes.includes(p));\n\n\t\t// Build text corpus for TF-IDF (similar weighting strategy as in tool-calling-evals)\n\t\tconst text = [\n\t\t\t`${tool.name} ${tool.name} ${tool.name}`, // boost name\n\t\t\t`${integration} ${actions.join(' ')}`,\n\t\t\ttool.description,\n\t\t\tparts.join(' '),\n\t\t].join(' ');\n\n\t\treturn { id: tool.name, text };\n\t});\n\n\tindex.build(corpus);\n\treturn index;\n}\n\n/**\n * Initialize Orama database with BM25 algorithm for tool search\n * Using Orama's BM25 scoring algorithm for relevance ranking\n * @see https://docs.orama.com/open-source/usage/create\n * @see https://docs.orama.com/open-source/usage/search/bm25-algorithm/\n */\nasync function initializeOramaDb(tools: BaseTool[]): Promise<OramaDb> {\n\t// Create Orama database schema with BM25 scoring algorithm\n\t// BM25 provides better relevance ranking for natural language queries\n\tconst oramaDb = orama.create({\n\t\tschema: {\n\t\t\tname: 'string' as const,\n\t\t\tdescription: 'string' as const,\n\t\t\tintegration: 'string' as const,\n\t\t\ttags: 'string[]' as const,\n\t\t},\n\t\tcomponents: {\n\t\t\ttokenizer: {\n\t\t\t\tstemming: true,\n\t\t\t},\n\t\t},\n\t});\n\n\t// Index all tools\n\tfor (const tool of tools) {\n\t\t// Extract integration from tool name (e.g., 'bamboohr_create_employee' -> 'bamboohr')\n\t\tconst parts = tool.name.split('_');\n\t\tconst integration = parts[0];\n\n\t\t// Extract action type\n\t\tconst actionTypes = ['create', 'update', 'delete', 'get', 'list', 'search'];\n\t\tconst actions = parts.filter((p) => actionTypes.includes(p));\n\n\t\tawait orama.insert(oramaDb, {\n\t\t\tname: tool.name,\n\t\t\tdescription: tool.description,\n\t\t\tintegration: integration,\n\t\t\ttags: [...parts, ...actions],\n\t\t});\n\t}\n\n\treturn oramaDb;\n}\n\nfunction toolSearch(\n\toramaDb: OramaDb,\n\ttfidfIndex: TfidfIndex,\n\tallTools: BaseTool[],\n\thybridAlpha = DEFAULT_HYBRID_ALPHA,\n): BaseTool {\n\tconst name = 'tool_search' as const;\n\tconst description =\n\t\t`Searches for relevant tools based on a natural language query using hybrid BM25 + TF-IDF search (alpha=${hybridAlpha}). This tool should be called first to discover available tools before executing them.` as const;\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\tquery: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription:\n\t\t\t\t\t'Natural language query describing what tools you need (e.g., \"tools for managing employees\", \"create time off request\")',\n\t\t\t},\n\t\t\tlimit: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Maximum number of tools to return (default: 5)',\n\t\t\t\tdefault: 5,\n\t\t\t},\n\t\t\tminScore: {\n\t\t\t\ttype: 'number',\n\t\t\t\tdescription: 'Minimum relevance score (0-1) for results (default: 0.3)',\n\t\t\t\tdefault: 0.3,\n\t\t\t},\n\t\t},\n\t\trequired: ['query'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'local',\n\t\tidentifier: name,\n\t\tdescription: 'local://get-relevant-tools',\n\t} as const satisfies LocalExecuteConfig;\n\n\tconst tool = new BaseTool(name, description, parameters, executeConfig);\n\ttool.execute = async (inputParams?: JsonObject | string): Promise<JsonObject> => {\n\t\ttry {\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\t\t\tconst limit = params.limit || 5;\n\t\t\tconst minScore = params.minScore ?? 0.3;\n\t\t\tconst query = params.query || '';\n\n\t\t\t// Hybrid: BM25 + TF-IDF fusion\n\t\t\tconst alpha = Math.max(0, Math.min(1, hybridAlpha));\n\n\t\t\t// Get results from both algorithms\n\t\t\tconst [bm25Results, tfidfResults] = await Promise.all([\n\t\t\t\torama.search(oramaDb, {\n\t\t\t\t\tterm: query,\n\t\t\t\t\tlimit: Math.max(50, limit),\n\t\t\t\t} as Parameters<typeof orama.search>[1]),\n\t\t\t\tPromise.resolve(tfidfIndex.search(query, Math.max(50, limit))),\n\t\t\t]);\n\n\t\t\t// Build score map\n\t\t\tconst scoreMap = new Map<string, { bm25?: number; tfidf?: number }>();\n\n\t\t\tfor (const hit of bm25Results.hits) {\n\t\t\t\tconst doc = hit.document as { name: string };\n\t\t\t\tscoreMap.set(doc.name, {\n\t\t\t\t\t...scoreMap.get(doc.name),\n\t\t\t\t\tbm25: clamp01(hit.score),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tfor (const r of tfidfResults) {\n\t\t\t\tscoreMap.set(r.id, {\n\t\t\t\t\t...scoreMap.get(r.id),\n\t\t\t\t\ttfidf: clamp01(r.score),\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Fuse scores\n\t\t\tconst fused: Array<{ name: string; score: number }> = [];\n\t\t\tfor (const [name, scores] of scoreMap) {\n\t\t\t\tconst bm25 = scores.bm25 ?? 0;\n\t\t\t\tconst tfidf = scores.tfidf ?? 0;\n\t\t\t\tconst score = alpha * bm25 + (1 - alpha) * tfidf;\n\t\t\t\tfused.push({ name, score });\n\t\t\t}\n\n\t\t\tfused.sort((a, b) => b.score - a.score);\n\n\t\t\tconst toolConfigs = fused\n\t\t\t\t.filter((r) => r.score >= minScore)\n\t\t\t\t.map((r) => {\n\t\t\t\t\tconst tool = allTools.find((t) => t.name === r.name);\n\t\t\t\t\tif (!tool) return null;\n\n\t\t\t\t\treturn {\n\t\t\t\t\t\tname: tool.name,\n\t\t\t\t\t\tdescription: tool.description,\n\t\t\t\t\t\tparameters: tool.parameters,\n\t\t\t\t\t\tscore: r.score,\n\t\t\t\t\t};\n\t\t\t\t})\n\t\t\t\t.filter((t): t is ToolSearchResult => t !== null)\n\t\t\t\t.slice(0, limit);\n\n\t\t\t// Convert to JSON-serialisable format (removes undefined values)\n\t\t\treturn JSON.parse(JSON.stringify({ tools: toolConfigs })) satisfies JsonObject;\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t};\n\treturn tool;\n}\n\n/**\n * Clamp value to [0, 1]\n */\nfunction clamp01(x: number): number {\n\treturn x < 0 ? 0 : x > 1 ? 1 : x;\n}\n\nfunction toolExecute(tools: Tools): BaseTool {\n\tconst name = 'tool_execute' as const;\n\tconst description =\n\t\t'Executes a specific tool by name with the provided parameters. Use this after discovering tools with tool_search.' as const;\n\tconst parameters = {\n\t\ttype: 'object',\n\t\tproperties: {\n\t\t\ttoolName: {\n\t\t\t\ttype: 'string',\n\t\t\t\tdescription: 'Name of the tool to execute',\n\t\t\t},\n\t\t\tparams: {\n\t\t\t\ttype: 'object',\n\t\t\t\tdescription: 'Parameters to pass to the tool',\n\t\t\t},\n\t\t},\n\t\trequired: ['toolName', 'params'],\n\t} as const satisfies ToolParameters;\n\n\tconst executeConfig = {\n\t\tkind: 'local',\n\t\tidentifier: name,\n\t\tdescription: 'local://execute-tool',\n\t} as const satisfies LocalExecuteConfig;\n\n\t// Create the tool instance\n\tconst tool = new BaseTool(name, description, parameters, executeConfig);\n\n\t// Override the execute method to handle tool execution\n\t// receives tool name and parameters and executes the tool\n\ttool.execute = async (\n\t\tinputParams?: JsonObject | string,\n\t\toptions?: ExecuteOptions,\n\t): Promise<JsonObject> => {\n\t\ttry {\n\t\t\t// Validate params is either undefined, string, or object\n\t\t\tif (\n\t\t\t\tinputParams !== undefined &&\n\t\t\t\ttypeof inputParams !== 'string' &&\n\t\t\t\ttypeof inputParams !== 'object'\n\t\t\t) {\n\t\t\t\tthrow new StackOneError(\n\t\t\t\t\t`Invalid parameters type. Expected object or string, got ${typeof inputParams}. Parameters: ${JSON.stringify(\n\t\t\t\t\t\tinputParams,\n\t\t\t\t\t)}`,\n\t\t\t\t);\n\t\t\t}\n\n\t\t\t// Convert string params to object\n\t\t\tconst params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {};\n\n\t\t\t// Extract tool name and parameters\n\t\t\tconst { toolName, params: toolParams } = params;\n\n\t\t\t// Find the tool by name\n\t\t\tconst toolToExecute = tools.getTool(toolName);\n\t\t\tif (!toolToExecute) {\n\t\t\t\tthrow new StackOneError(`Tool ${toolName} not found`);\n\t\t\t}\n\n\t\t\t// Execute the tool with the provided parameters\n\t\t\treturn await toolToExecute.execute(toolParams, options);\n\t\t} catch (error) {\n\t\t\tif (error instanceof StackOneError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t\tthrow new StackOneError(\n\t\t\t\t`Error executing tool: ${error instanceof Error ? error.message : String(error)}`,\n\t\t\t);\n\t\t}\n\t};\n\treturn tool;\n}\n"],"mappings":"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"} |
@@ -69,3 +69,3 @@ /** | ||
| expect( | ||
| providerToolNames.every((name) => name.startsWith('bamboohr_') || name.startsWith('meta_')), | ||
| providerToolNames.every((name) => name.startsWith('bamboohr_') || name.startsWith('tool_')), | ||
| ).toBe(true); | ||
@@ -89,3 +89,3 @@ | ||
| .toArray() | ||
| .filter((t) => !t.name.startsWith('meta_')) | ||
| .filter((t) => !t.name.startsWith('tool_')) | ||
| .map((t) => t.name); | ||
@@ -92,0 +92,0 @@ expect(globToolNames).toContain('bamboohr_list_employees'); |
+20
-26
| { | ||
| "name": "@stackone/ai", | ||
| "version": "2.2.0", | ||
| "version": "2.3.0", | ||
| "description": "Tools for agents to perform actions on your SaaS", | ||
@@ -16,2 +16,4 @@ "keywords": [ | ||
| "bugs": "https://github.com/StackOneHQ/stackone-ai-node/issues", | ||
| "license": "Apache-2.0", | ||
| "author": "StackOne", | ||
| "repository": { | ||
@@ -21,6 +23,16 @@ "type": "git", | ||
| }, | ||
| "author": "StackOne", | ||
| "license": "Apache-2.0", | ||
| "files": [ | ||
| "dist", | ||
| "LICENSE", | ||
| "README.md", | ||
| "src", | ||
| "!examples/*.test.ts", | ||
| "examples/*.ts", | ||
| "!src/**/*.test-d.ts", | ||
| "!src/**/*.test.ts" | ||
| ], | ||
| "type": "module", | ||
| "main": "./dist/index.mjs", | ||
| "module": "./dist/index.mjs", | ||
| "types": "./dist/index.d.mts", | ||
| "exports": { | ||
@@ -30,14 +42,2 @@ ".": "./dist/index.mjs", | ||
| }, | ||
| "module": "./dist/index.mjs", | ||
| "types": "./dist/index.d.mts", | ||
| "files": [ | ||
| "!examples/*.test.ts", | ||
| "!src/**/*.test-d.ts", | ||
| "!src/**/*.test.ts", | ||
| "LICENSE", | ||
| "README.md", | ||
| "dist", | ||
| "examples/*.ts", | ||
| "src" | ||
| ], | ||
| "dependencies": { | ||
@@ -52,3 +52,2 @@ "@modelcontextprotocol/sdk": "^1.24.3", | ||
| "@types/node": "^22.13.5", | ||
| "@typescript/native-preview": "^7.0.0-dev.20251209.1", | ||
| "@vitest/coverage-v8": "^4.0.15", | ||
@@ -58,12 +57,7 @@ "ai": "^6.0.7", | ||
| "knip": "^5.72.0", | ||
| "lefthook": "^2.0.8", | ||
| "msw": "^2.10.4", | ||
| "openai": "^6.2.0", | ||
| "oxfmt": "^0.18.0", | ||
| "oxlint": "^1.32.0", | ||
| "oxlint-tsgolint": "^0.8.6", | ||
| "publint": "^0.3.12", | ||
| "tsdown": "^0.17.2", | ||
| "type-fest": "^4.41.0", | ||
| "typescript": "^5.8.3", | ||
| "unplugin-unused": "^0.5.4", | ||
@@ -75,4 +69,4 @@ "vitest": "^4.0.15", | ||
| "peerDependencies": { | ||
| "@anthropic-ai/claude-agent-sdk": "^0.1.67", | ||
| "@anthropic-ai/sdk": "^0.52.0", | ||
| "@anthropic-ai/claude-agent-sdk": "^0.2.12", | ||
| "@anthropic-ai/sdk": "^0.71.2", | ||
| "ai": ">=5.0.108 <7.0.0", | ||
@@ -96,5 +90,2 @@ "openai": "^6.2.0", | ||
| }, | ||
| "engines": { | ||
| "node": ">=20.19.6" | ||
| }, | ||
| "devEngines": { | ||
@@ -109,2 +100,5 @@ "runtime": [ | ||
| }, | ||
| "engines": { | ||
| "node": ">=20.19.6" | ||
| }, | ||
| "scripts": { | ||
@@ -111,0 +105,0 @@ "build": "tsdown", |
+147
-150
@@ -34,11 +34,11 @@ # StackOne AI SDK | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
| const tools = await toolset.fetchTools(); | ||
| const employeeTool = tools.getTool("bamboohr_list_employees"); | ||
| const employeeTool = tools.getTool('bamboohr_list_employees'); | ||
| const employees = await employeeTool.execute(); | ||
@@ -62,6 +62,6 @@ ``` | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| // Single account - simplest approach | ||
| const toolset = new StackOneToolSet({ accountId: "your-bamboohr-account" }); | ||
| const toolset = new StackOneToolSet({ accountId: 'your-bamboohr-account' }); | ||
| const tools = await toolset.fetchTools(); | ||
@@ -72,3 +72,3 @@ | ||
| const allTools = await multiAccountToolset.fetchTools({ | ||
| accountIds: ["bamboohr-account-123", "workday-account-456"], | ||
| accountIds: ['bamboohr-account-123', 'workday-account-456'], | ||
| }); | ||
@@ -78,8 +78,8 @@ | ||
| const bamboohrOnly = await multiAccountToolset.fetchTools({ | ||
| accountIds: ["bamboohr-account-123", "workday-account-456"], | ||
| actions: ["bamboohr_*"], // Only BambooHR tools | ||
| accountIds: ['bamboohr-account-123', 'workday-account-456'], | ||
| actions: ['bamboohr_*'], // Only BambooHR tools | ||
| }); | ||
| // Set directly on a tool instance | ||
| tools.setAccountId("direct-account-id"); | ||
| tools.setAccountId('direct-account-id'); | ||
| const currentAccountId = tools.getAccountId(); // Get the current account ID | ||
@@ -100,8 +100,8 @@ ``` | ||
| ```typescript | ||
| import { OpenAI } from "openai"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { OpenAI } from 'openai'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
@@ -112,14 +112,14 @@ | ||
| await openai.chat.completions.create({ | ||
| model: "gpt-5.1", | ||
| messages: [ | ||
| { | ||
| role: "system", | ||
| content: "You are a helpful HR assistant using BambooHR.", | ||
| }, | ||
| { | ||
| role: "user", | ||
| content: "Create a time-off request for employee id cxIQ5764hj2", | ||
| }, | ||
| ], | ||
| tools: tools.toOpenAI(), | ||
| model: 'gpt-5.1', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: 'You are a helpful HR assistant using BambooHR.', | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: 'Create a time-off request for employee id cxIQ5764hj2', | ||
| }, | ||
| ], | ||
| tools: tools.toOpenAI(), | ||
| }); | ||
@@ -140,8 +140,8 @@ ``` | ||
| ```typescript | ||
| import OpenAI from "openai"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import OpenAI from 'openai'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
@@ -154,6 +154,6 @@ | ||
| await openai.responses.create({ | ||
| model: "gpt-5.1", | ||
| instructions: "You are a helpful HR assistant.", | ||
| input: "What is the phone number for employee c28xIQ?", | ||
| tools: tools.toOpenAIResponses(), | ||
| model: 'gpt-5.1', | ||
| instructions: 'You are a helpful HR assistant.', | ||
| input: 'What is the phone number for employee c28xIQ?', | ||
| tools: tools.toOpenAIResponses(), | ||
| }); | ||
@@ -174,8 +174,8 @@ ``` | ||
| ```typescript | ||
| import Anthropic from "@anthropic-ai/sdk"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import Anthropic from '@anthropic-ai/sdk'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
@@ -188,12 +188,12 @@ | ||
| await anthropic.messages.create({ | ||
| model: "claude-haiku-4-5-20241022", | ||
| max_tokens: 1024, | ||
| system: "You are a helpful HR assistant.", | ||
| messages: [ | ||
| { | ||
| role: "user", | ||
| content: "What is the phone number for employee c28xIQ?", | ||
| }, | ||
| ], | ||
| tools: tools.toAnthropic(), | ||
| model: 'claude-haiku-4-5-20241022', | ||
| max_tokens: 1024, | ||
| system: 'You are a helpful HR assistant.', | ||
| messages: [ | ||
| { | ||
| role: 'user', | ||
| content: 'What is the phone number for employee c28xIQ?', | ||
| }, | ||
| ], | ||
| tools: tools.toAnthropic(), | ||
| }); | ||
@@ -214,9 +214,9 @@ ``` | ||
| ```typescript | ||
| import { openai } from "@ai-sdk/openai"; | ||
| import { generateText } from "ai"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { openai } from '@ai-sdk/openai'; | ||
| import { generateText } from 'ai'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
@@ -227,5 +227,5 @@ | ||
| await generateText({ | ||
| model: openai("gpt-5.1"), | ||
| tools: await tools.toAISDK(), | ||
| maxSteps: 3, | ||
| model: openai('gpt-5.1'), | ||
| tools: await tools.toAISDK(), | ||
| maxSteps: 3, | ||
| }); | ||
@@ -246,25 +246,25 @@ ``` | ||
| ```typescript | ||
| import { chat } from "@tanstack/ai"; | ||
| import { openai } from "@tanstack/ai-openai"; | ||
| import { z } from "zod"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { chat } from '@tanstack/ai'; | ||
| import { openai } from '@tanstack/ai-openai'; | ||
| import { z } from 'zod'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
| const tools = await toolset.fetchTools(); | ||
| const employeeTool = tools.getTool("bamboohr_get_employee"); | ||
| const employeeTool = tools.getTool('bamboohr_get_employee'); | ||
| // TanStack AI requires Zod schemas for tool input validation | ||
| const getEmployeeTool = { | ||
| name: employeeTool.name, | ||
| description: employeeTool.description, | ||
| inputSchema: z.object({ | ||
| id: z.string().describe("The employee ID"), | ||
| }), | ||
| execute: async (args: { id: string }) => { | ||
| return employeeTool.execute(args); | ||
| }, | ||
| name: employeeTool.name, | ||
| description: employeeTool.description, | ||
| inputSchema: z.object({ | ||
| id: z.string().describe('The employee ID'), | ||
| }), | ||
| execute: async (args: { id: string }) => { | ||
| return employeeTool.execute(args); | ||
| }, | ||
| }; | ||
@@ -274,10 +274,10 @@ | ||
| const stream = chat({ | ||
| adapter, | ||
| model: "gpt-5.1", | ||
| messages: [{ role: "user", content: "Get employee with id: abc123" }], | ||
| tools: [getEmployeeTool], | ||
| adapter, | ||
| model: 'gpt-5.1', | ||
| messages: [{ role: 'user', content: 'Get employee with id: abc123' }], | ||
| tools: [getEmployeeTool], | ||
| }); | ||
| for await (const chunk of stream) { | ||
| // Process streaming chunks | ||
| // Process streaming chunks | ||
| } | ||
@@ -298,8 +298,8 @@ ``` | ||
| ```typescript | ||
| import { query } from "@anthropic-ai/claude-agent-sdk"; | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { query } from '@anthropic-ai/claude-agent-sdk'; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| accountId: "your-account-id", | ||
| baseUrl: 'https://api.stackone.com', | ||
| accountId: 'your-account-id', | ||
| }); | ||
@@ -313,13 +313,13 @@ | ||
| const result = query({ | ||
| prompt: "Get the employee with id: abc123", | ||
| options: { | ||
| model: "claude-sonnet-4-5-20250929", | ||
| mcpServers: { "stackone-tools": mcpServer }, | ||
| tools: [], // Disable built-in tools | ||
| maxTurns: 3, | ||
| }, | ||
| prompt: 'Get the employee with id: abc123', | ||
| options: { | ||
| model: 'claude-sonnet-4-5-20250929', | ||
| mcpServers: { 'stackone-tools': mcpServer }, | ||
| tools: [], // Disable built-in tools | ||
| maxTurns: 3, | ||
| }, | ||
| }); | ||
| for await (const message of result) { | ||
| // Process streaming messages | ||
| // Process streaming messages | ||
| } | ||
@@ -340,25 +340,25 @@ ``` | ||
| // Filter by account IDs | ||
| toolset.setAccounts(["account-123", "account-456"]); | ||
| toolset.setAccounts(['account-123', 'account-456']); | ||
| const tools = await toolset.fetchTools(); | ||
| // OR | ||
| const tools = await toolset.fetchTools({ | ||
| accountIds: ["account-123", "account-456"], | ||
| accountIds: ['account-123', 'account-456'], | ||
| }); | ||
| // Filter by providers | ||
| const tools = await toolset.fetchTools({ providers: ["hibob", "bamboohr"] }); | ||
| const tools = await toolset.fetchTools({ providers: ['hibob', 'bamboohr'] }); | ||
| // Filter by actions with exact match | ||
| const tools = await toolset.fetchTools({ | ||
| actions: ["hibob_list_employees", "hibob_create_employees"], | ||
| actions: ['hibob_list_employees', 'hibob_create_employees'], | ||
| }); | ||
| // Filter by actions with glob patterns | ||
| const tools = await toolset.fetchTools({ actions: ["*_list_employees"] }); | ||
| const tools = await toolset.fetchTools({ actions: ['*_list_employees'] }); | ||
| // Combine multiple filters | ||
| const tools = await toolset.fetchTools({ | ||
| accountIds: ["account-123"], | ||
| providers: ["hibob"], | ||
| actions: ["*_list_*"], | ||
| accountIds: ['account-123'], | ||
| providers: ['hibob'], | ||
| actions: ['*_list_*'], | ||
| }); | ||
@@ -375,14 +375,14 @@ ``` | ||
| ### Meta Tools (Beta) | ||
| ### Utility Tools (Beta) | ||
| Meta tools enable dynamic tool discovery and execution, allowing AI agents to search for relevant tools based on natural language queries without hardcoding tool names. | ||
| Utility tools enable dynamic tool discovery and execution, allowing AI agents to search for relevant tools based on natural language queries without hardcoding tool names. | ||
| > **Beta Feature**: Meta tools are currently in beta and the API may change in future versions. | ||
| > **Beta Feature**: Utility tools are currently in beta and the API may change in future versions. | ||
| #### How Meta Tools Work | ||
| #### How Utility Tools Work | ||
| Meta tools provide two core capabilities: | ||
| Utility tools provide two core capabilities: | ||
| 1. **Tool Discovery** (`meta_search_tools`): Search for tools using natural language queries | ||
| 2. **Tool Execution** (`meta_execute_tool`): Execute discovered tools dynamically | ||
| 1. **Tool Discovery** (`tool_search`): Search for tools using natural language queries | ||
| 2. **Tool Execution** (`tool_execute`): Execute discovered tools dynamically | ||
@@ -392,17 +392,17 @@ #### Basic Usage | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| baseUrl: 'https://api.stackone.com', | ||
| }); | ||
| const tools = await toolset.fetchTools(); | ||
| // Get meta tools for dynamic discovery | ||
| const metaTools = await tools.metaTools(); | ||
| // Get utility tools for dynamic discovery | ||
| const utilityTools = await tools.utilityTools(); | ||
| // Use with OpenAI | ||
| const openAITools = metaTools.toOpenAI(); | ||
| const openAITools = utilityTools.toOpenAI(); | ||
| // Use with AI SDK | ||
| const aiSdkTools = await metaTools.toAISDK(); | ||
| const aiSdkTools = await utilityTools.toAISDK(); | ||
| ``` | ||
@@ -413,10 +413,10 @@ | ||
| ```typescript | ||
| import { generateText } from "ai"; | ||
| import { openai } from "@ai-sdk/openai"; | ||
| import { generateText } from 'ai'; | ||
| import { openai } from '@ai-sdk/openai'; | ||
| const { text } = await generateText({ | ||
| model: openai("gpt-5.1"), | ||
| tools: aiSdkTools, | ||
| prompt: "Find tools for managing employees and create a time off request", | ||
| maxSteps: 3, // Allow multiple tool calls | ||
| model: openai('gpt-5.1'), | ||
| tools: aiSdkTools, | ||
| prompt: 'Find tools for managing employees and create a time off request', | ||
| maxSteps: 3, // Allow multiple tool calls | ||
| }); | ||
@@ -429,22 +429,22 @@ ``` | ||
| // Step 1: Discover relevant tools | ||
| const filterTool = metaTools.getTool("meta_search_tools"); | ||
| const filterTool = utilityTools.getTool('tool_search'); | ||
| const searchResult = await filterTool.execute({ | ||
| query: "employee time off vacation", | ||
| limit: 5, | ||
| minScore: 0.3, // Minimum relevance score (0-1) | ||
| query: 'employee time off vacation', | ||
| limit: 5, | ||
| minScore: 0.3, // Minimum relevance score (0-1) | ||
| }); | ||
| // Step 2: Execute a discovered tool | ||
| const executeTool = metaTools.getTool("meta_execute_tool"); | ||
| const executeTool = utilityTools.getTool('tool_execute'); | ||
| const result = await executeTool.execute({ | ||
| toolName: "bamboohr_create_time_off", | ||
| params: { | ||
| employeeId: "emp_123", | ||
| startDate: "2024-01-15", | ||
| endDate: "2024-01-19", | ||
| }, | ||
| toolName: 'bamboohr_create_time_off', | ||
| params: { | ||
| employeeId: 'emp_123', | ||
| startDate: '2024-01-15', | ||
| endDate: '2024-01-19', | ||
| }, | ||
| }); | ||
| ``` | ||
| [View full example](examples/meta-tools.ts) | ||
| [View full example](examples/utility-tools.ts) | ||
@@ -454,5 +454,5 @@ ### Custom Base URL | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ baseUrl: "https://api.example-dev.com" }); | ||
| const toolset = new StackOneToolSet({ baseUrl: 'https://api.example-dev.com' }); | ||
| ``` | ||
@@ -465,17 +465,14 @@ | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| // Initialize the toolset | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| baseUrl: 'https://api.stackone.com', | ||
| }); | ||
| const tools = await toolset.fetchTools(); | ||
| const employeeTool = tools.getTool("bamboohr_list_employees"); | ||
| const employeeTool = tools.getTool('bamboohr_list_employees'); | ||
| // Use dryRun to see the request details | ||
| const dryRunResult = await employeeTool.execute( | ||
| { query: { limit: 5 } }, | ||
| { dryRun: true } | ||
| ); | ||
| const dryRunResult = await employeeTool.execute({ query: { limit: 5 } }, { dryRun: true }); | ||
@@ -502,3 +499,3 @@ console.log(dryRunResult); | ||
| The StackOne AI SDK includes a built-in feedback collection tool (`meta_collect_tool_feedback`) that allows users to provide feedback on their experience with StackOne tools. This tool is automatically included when using `fetchTools()` and helps improve the SDK based on user input. | ||
| The StackOne AI SDK includes a built-in feedback collection tool (`tool_feedback`) that allows users to provide feedback on their experience with StackOne tools. This tool is automatically included when using `fetchTools()` and helps improve the SDK based on user input. | ||
@@ -520,6 +517,6 @@ #### How It Works | ||
| ```typescript | ||
| import { StackOneToolSet } from "@stackone/ai"; | ||
| import { StackOneToolSet } from '@stackone/ai'; | ||
| const toolset = new StackOneToolSet({ | ||
| baseUrl: "https://api.stackone.com", | ||
| baseUrl: 'https://api.stackone.com', | ||
| }); | ||
@@ -529,3 +526,3 @@ const tools = await toolset.fetchTools(); | ||
| // The feedback tool is automatically included | ||
| const feedbackTool = tools.getTool("meta_collect_tool_feedback"); | ||
| const feedbackTool = tools.getTool('tool_feedback'); | ||
@@ -544,9 +541,9 @@ // Use with AI agents - they will ask for user consent first | ||
| // Get the feedback tool | ||
| const feedbackTool = tools.getTool("meta_collect_tool_feedback"); | ||
| const feedbackTool = tools.getTool('tool_feedback'); | ||
| // Submit feedback (after getting user consent) | ||
| const result = await feedbackTool.execute({ | ||
| feedback: "The tools worked great! Very easy to use.", | ||
| account_id: "acc_123456", | ||
| tool_names: ["bamboohr_list_employees", "bamboohr_create_time_off"], | ||
| feedback: 'The tools worked great! Very easy to use.', | ||
| account_id: 'acc_123456', | ||
| tool_names: ['bamboohr_list_employees', 'bamboohr_create_time_off'], | ||
| }); | ||
@@ -562,5 +559,5 @@ ``` | ||
| await feedbackTool.execute({ | ||
| feedback: "The tools worked great! Very easy to use.", | ||
| account_id: "acc_123456", | ||
| tool_names: ["bamboohr_list_employees", "bamboohr_create_time_off"], | ||
| feedback: 'The tools worked great! Very easy to use.', | ||
| account_id: 'acc_123456', | ||
| tool_names: ['bamboohr_list_employees', 'bamboohr_create_time_off'], | ||
| }); | ||
@@ -570,5 +567,5 @@ | ||
| await feedbackTool.execute({ | ||
| feedback: "The tools worked great! Very easy to use.", | ||
| account_id: ["acc_123456", "acc_789012"], | ||
| tool_names: ["bamboohr_list_employees", "bamboohr_create_time_off"], | ||
| feedback: 'The tools worked great! Very easy to use.', | ||
| account_id: ['acc_123456', 'acc_789012'], | ||
| tool_names: ['bamboohr_list_employees', 'bamboohr_create_time_off'], | ||
| }); | ||
@@ -575,0 +572,0 @@ ``` |
+1
-1
@@ -50,3 +50,3 @@ import { z } from 'zod/v4'; | ||
| }; | ||
| const name = 'meta_collect_tool_feedback' as const; | ||
| const name = 'tool_feedback' as const; | ||
| const description = | ||
@@ -53,0 +53,0 @@ 'Collects user feedback on StackOne tool performance. First ask the user, "Are you ok with sending feedback to StackOne?" and mention that the LLM will take care of sending it. Call this tool only when the user explicitly answers yes.'; |
+11
-14
@@ -496,13 +496,10 @@ import { type JSONSchema7 as AISDKJSONSchema, jsonSchema } from 'ai'; | ||
| /** | ||
| * Return meta tools for tool discovery and execution | ||
| * Return utility tools for tool discovery and execution | ||
| * @beta This feature is in beta and may change in future versions | ||
| * @param hybridAlpha - Weight for BM25 in hybrid search (0-1). If not provided, uses DEFAULT_HYBRID_ALPHA (0.2). | ||
| */ | ||
| async metaTools(hybridAlpha = DEFAULT_HYBRID_ALPHA): Promise<Tools> { | ||
| async utilityTools(hybridAlpha = DEFAULT_HYBRID_ALPHA): Promise<Tools> { | ||
| const oramaDb = await initializeOramaDb(this.tools); | ||
| const tfidfIndex = initializeTfidfIndex(this.tools); | ||
| const baseTools = [ | ||
| metaSearchTools(oramaDb, tfidfIndex, this.tools, hybridAlpha), | ||
| metaExecuteTool(this), | ||
| ]; | ||
| const baseTools = [toolSearch(oramaDb, tfidfIndex, this.tools, hybridAlpha), toolExecute(this)]; | ||
| const tools = new Tools(baseTools); | ||
@@ -552,5 +549,5 @@ return tools; | ||
| /** | ||
| * Result from meta_search_tools | ||
| * Result from tool_search | ||
| */ | ||
| export interface MetaToolSearchResult { | ||
| export interface ToolSearchResult { | ||
| name: string; | ||
@@ -637,3 +634,3 @@ description: string; | ||
| function metaSearchTools( | ||
| function toolSearch( | ||
| oramaDb: OramaDb, | ||
@@ -644,3 +641,3 @@ tfidfIndex: TfidfIndex, | ||
| ): BaseTool { | ||
| const name = 'meta_search_tools' as const; | ||
| const name = 'tool_search' as const; | ||
| const description = | ||
@@ -752,3 +749,3 @@ `Searches for relevant tools based on a natural language query using hybrid BM25 + TF-IDF search (alpha=${hybridAlpha}). This tool should be called first to discover available tools before executing them.` as const; | ||
| }) | ||
| .filter((t): t is MetaToolSearchResult => t !== null) | ||
| .filter((t): t is ToolSearchResult => t !== null) | ||
| .slice(0, limit); | ||
@@ -777,6 +774,6 @@ | ||
| function metaExecuteTool(tools: Tools): BaseTool { | ||
| const name = 'meta_execute_tool' as const; | ||
| function toolExecute(tools: Tools): BaseTool { | ||
| const name = 'tool_execute' as const; | ||
| const description = | ||
| 'Executes a specific tool by name with the provided parameters. Use this after discovering tools with meta_search_tools.' as const; | ||
| 'Executes a specific tool by name with the provided parameters. Use this after discovering tools with tool_search.' as const; | ||
| const parameters = { | ||
@@ -783,0 +780,0 @@ type: 'object', |
| /** | ||
| * This example demonstrates how to use meta tools for dynamic tool discovery and execution. | ||
| * Meta tools allow AI agents to search for relevant tools based on natural language queries | ||
| * and execute them dynamically without hardcoding tool names. | ||
| * | ||
| * @beta Meta tools are in beta and may change in future versions | ||
| */ | ||
| import process from 'node:process'; | ||
| import { openai } from '@ai-sdk/openai'; | ||
| import { type JsonObject, StackOneToolSet, Tools } from '@stackone/ai'; | ||
| import { generateText, stepCountIs } from 'ai'; | ||
| const apiKey = process.env.STACKONE_API_KEY; | ||
| if (!apiKey) { | ||
| console.error('STACKONE_API_KEY environment variable is required'); | ||
| process.exit(1); | ||
| } | ||
| // Replace with your actual account ID from StackOne dashboard | ||
| const accountId = 'your-bamboohr-account-id'; | ||
| /** | ||
| * Example 1: Using meta tools with AI SDK for dynamic tool discovery | ||
| */ | ||
| const metaToolsWithAISDK = async (): Promise<void> => { | ||
| console.log('š Example 1: Dynamic tool discovery with AI SDK\n'); | ||
| // Initialize StackOne toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch all available tools via MCP | ||
| const allTools = await toolset.fetchTools(); | ||
| // Get meta tools for dynamic discovery and execution | ||
| const metaTools = await allTools.metaTools(); | ||
| const aiSdkMetaTools = await metaTools.toAISDK(); | ||
| // Use meta tools to dynamically find and execute relevant tools | ||
| const { text, toolCalls } = await generateText({ | ||
| model: openai('gpt-5.1'), | ||
| tools: aiSdkMetaTools, | ||
| prompt: `I need to create a time off request for an employee. | ||
| First, find the right tool for this task, then use it to create a time off request | ||
| for employee ID "emp_123" from January 15, 2024 to January 19, 2024.`, | ||
| stopWhen: stepCountIs(3), // Allow multiple tool calls | ||
| }); | ||
| console.log('AI Response:', text); | ||
| console.log('\nTool calls made:', toolCalls?.map((call) => call.toolName).join(', ')); | ||
| }; | ||
| /** | ||
| * Example 2: Using meta tools with OpenAI for HR assistant | ||
| */ | ||
| const metaToolsWithOpenAI = async (): Promise<void> => { | ||
| console.log('\nš¤ Example 2: HR Assistant with OpenAI\n'); | ||
| const { OpenAI } = await import('openai'); | ||
| const openaiClient = new OpenAI({ | ||
| apiKey: process.env.OPENAI_API_KEY, | ||
| }); | ||
| // Initialize StackOne toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch BambooHR tools via MCP | ||
| const bamboohrTools = await toolset.fetchTools({ | ||
| actions: ['bamboohr_*'], | ||
| }); | ||
| // Get meta tools | ||
| const metaTools = await bamboohrTools.metaTools(); | ||
| const openAIMetaTools = metaTools.toOpenAI(); | ||
| // Create an HR assistant that can discover and use tools dynamically | ||
| const response = await openaiClient.chat.completions.create({ | ||
| model: 'gpt-5.1', | ||
| messages: [ | ||
| { | ||
| role: 'system', | ||
| content: `You are an HR assistant with access to various HR tools. | ||
| Use the meta_search_tools to find appropriate tools for user requests, | ||
| then use meta_execute_tool to execute them.`, | ||
| }, | ||
| { | ||
| role: 'user', | ||
| content: | ||
| 'Can you help me find tools for managing employee records and then list current employees?', | ||
| }, | ||
| ], | ||
| tools: openAIMetaTools, | ||
| tool_choice: 'auto', | ||
| }); | ||
| console.log('Assistant response:', response.choices[0].message.content); | ||
| // Handle tool calls if any | ||
| if (response.choices[0].message.tool_calls) { | ||
| console.log('\nTool calls:'); | ||
| for (const toolCall of response.choices[0].message.tool_calls) { | ||
| if (toolCall.type === 'function') { | ||
| console.log(`- ${toolCall.function.name}: ${toolCall.function.arguments}`); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
| /** | ||
| * Example 3: Direct usage of meta tools without AI | ||
| */ | ||
| const directMetaToolUsage = async (): Promise<void> => { | ||
| console.log('\nš ļø Example 3: Direct meta tool usage\n'); | ||
| // Initialize toolset | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch all available tools via MCP | ||
| const allTools = await toolset.fetchTools(); | ||
| console.log(`Total available tools: ${allTools.length}`); | ||
| // Get meta tools | ||
| const metaTools = await allTools.metaTools(); | ||
| // Step 1: Search for relevant tools | ||
| const filterTool = metaTools.getTool('meta_search_tools'); | ||
| if (!filterTool) throw new Error('meta_search_tools not found'); | ||
| const searchResult = await filterTool.execute({ | ||
| query: 'employee management create update list', | ||
| limit: 5, | ||
| minScore: 0.3, | ||
| }); | ||
| console.log('Found relevant tools:'); | ||
| const foundTools = searchResult.tools as Array<{ | ||
| name: string; | ||
| description: string; | ||
| score: number; | ||
| }>; | ||
| for (const tool of foundTools) { | ||
| console.log(`- ${tool.name} (score: ${tool.score.toFixed(2)}): ${tool.description}`); | ||
| } | ||
| // Step 2: Execute one of the found tools | ||
| if (foundTools.length > 0) { | ||
| const executeTool = metaTools.getTool('meta_execute_tool'); | ||
| if (!executeTool) throw new Error('meta_execute_tool not found'); | ||
| const firstTool = foundTools[0]; | ||
| console.log(`\nExecuting ${firstTool.name}...`); | ||
| try { | ||
| // Prepare parameters based on the tool's schema | ||
| let params = {} satisfies JsonObject; | ||
| if (firstTool.name === 'bamboohr_list_employees') { | ||
| params = { limit: 5 }; | ||
| } else if (firstTool.name === 'bamboohr_create_employee') { | ||
| params = { | ||
| name: 'John Doe', | ||
| email: 'john.doe@example.com', | ||
| title: 'Software Engineer', | ||
| }; | ||
| } | ||
| const result = await executeTool.execute({ | ||
| toolName: firstTool.name, | ||
| params, | ||
| }); | ||
| console.log('Execution result:', JSON.stringify(result, null, 2)); | ||
| } catch (error) { | ||
| console.error('Execution failed:', error); | ||
| } | ||
| } | ||
| }; | ||
| /** | ||
| * Example 4: Building a dynamic tool router | ||
| */ | ||
| const dynamicToolRouter = async (): Promise<void> => { | ||
| console.log('\nš Example 4: Dynamic tool router\n'); | ||
| const toolset = new StackOneToolSet({ | ||
| accountId, | ||
| baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', | ||
| }); | ||
| // Fetch tools from multiple integrations via MCP | ||
| const bamboohrTools = await toolset.fetchTools({ | ||
| actions: ['bamboohr_*'], | ||
| }); | ||
| const workdayTools = await toolset.fetchTools({ | ||
| actions: ['workday_*'], | ||
| }); | ||
| // Combine tools | ||
| const combinedTools = new Tools([...bamboohrTools.toArray(), ...workdayTools.toArray()]); | ||
| // Get meta tools for the combined set | ||
| const metaTools = await combinedTools.metaTools(); | ||
| // Create a router function that finds and executes tools based on intent | ||
| const routeAndExecute = async (intent: string, params: JsonObject = {}) => { | ||
| const filterTool = metaTools.getTool('meta_search_tools'); | ||
| const executeTool = metaTools.getTool('meta_execute_tool'); | ||
| if (!filterTool || !executeTool) throw new Error('Meta tools not found'); | ||
| // Find relevant tools | ||
| const searchResult = await filterTool.execute({ | ||
| query: intent, | ||
| limit: 1, | ||
| minScore: 0.5, | ||
| }); | ||
| const tools = searchResult.tools; | ||
| if (!Array.isArray(tools) || tools.length === 0) { | ||
| return { error: 'No relevant tools found for the given intent' }; | ||
| } | ||
| const selectedTool = tools[0] as { name: string; score: number }; | ||
| console.log(`Routing to: ${selectedTool.name} (score: ${selectedTool.score.toFixed(2)})`); | ||
| // Execute the selected tool | ||
| return await executeTool.execute({ | ||
| toolName: selectedTool.name, | ||
| params, | ||
| }); | ||
| }; | ||
| // Test the router with different intents | ||
| const intents = [ | ||
| { intent: 'I want to see all employees', params: { limit: 10 } }, | ||
| { | ||
| intent: 'Create a new job candidate', | ||
| params: { name: 'Jane Smith', email: 'jane@example.com' }, | ||
| }, | ||
| { intent: 'Find recruitment candidates', params: { status: 'active' } }, | ||
| ] as const satisfies { intent: string; params: JsonObject }[]; | ||
| for (const { intent, params } of intents) { | ||
| console.log(`\nIntent: "${intent}"`); | ||
| const result = await routeAndExecute(intent, params); | ||
| console.log('Result:', JSON.stringify(result, null, 2)); | ||
| } | ||
| }; | ||
| // Main execution | ||
| const main = async () => { | ||
| try { | ||
| // Run examples based on environment setup | ||
| if (process.env.OPENAI_API_KEY) { | ||
| await metaToolsWithAISDK(); | ||
| await metaToolsWithOpenAI(); | ||
| } else { | ||
| console.log('ā ļø OPENAI_API_KEY not found, skipping AI examples\n'); | ||
| } | ||
| // These examples work without AI | ||
| await directMetaToolUsage(); | ||
| await dynamicToolRouter(); | ||
| } catch (error) { | ||
| console.error('Error running examples:', error); | ||
| } | ||
| }; | ||
| // Run if this file is executed directly | ||
| if (import.meta.main) { | ||
| await main(); | ||
| } | ||
| export { metaToolsWithAISDK, metaToolsWithOpenAI, directMetaToolUsage, dynamicToolRouter }; |
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
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
16
-27.27%465427
-0.15%5701
-0.05%608
-0.49%