From 0dbbd4905772778eabf9c1e4f66f245ecd2cf95a Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:33:03 +0000 Subject: [PATCH 1/7] build(deps): add @anthropic-ai/sdk as optional peer dependency Add @anthropic-ai/sdk ^0.52.0 to the peer dependencies catalog and package.json to enable Anthropic Claude integration. The dependency is marked as optional so users only need to install it when they intend to use the toAnthropic() conversion methods. --- package.json | 4 ++++ pnpm-lock.yaml | 18 ++++++++++++++++++ pnpm-workspace.yaml | 1 + 3 files changed, 23 insertions(+) diff --git a/package.json b/package.json index cfbd9c1..418c3bf 100644 --- a/package.json +++ b/package.json @@ -65,10 +65,14 @@ "vitest": "catalog:dev" }, "peerDependencies": { + "@anthropic-ai/sdk": "catalog:peer", "ai": "catalog:peer", "openai": "catalog:peer" }, "peerDependenciesMeta": { + "@anthropic-ai/sdk": { + "optional": true + }, "ai": { "optional": true }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9065a53..91ffd94 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -73,6 +73,9 @@ catalogs: specifier: ^4.1.13 version: 4.1.13 peer: + '@anthropic-ai/sdk': + specifier: ^0.52.0 + version: 0.52.0 ai: specifier: ^5.0.108 version: 5.0.108 @@ -96,6 +99,9 @@ catalogs: importers: .: dependencies: + '@anthropic-ai/sdk': + specifier: catalog:peer + version: 0.52.0 '@modelcontextprotocol/sdk': specifier: catalog:prod version: 1.24.3(zod@4.1.13) @@ -188,6 +194,9 @@ importers: '@ai-sdk/openai': specifier: catalog:dev version: 2.0.80(zod@4.1.13) + '@anthropic-ai/sdk': + specifier: catalog:peer + version: 0.52.0 '@clack/prompts': specifier: catalog:dev version: 0.11.0 @@ -242,6 +251,13 @@ packages: } engines: { node: '>=18' } + '@anthropic-ai/sdk@0.52.0': + resolution: + { + integrity: sha512-d4c+fg+xy9e46c8+YnrrgIQR45CZlAi7PwdzIfDXDM6ACxEZli1/fxhURsq30ZpMZy6LvSkr41jGq5aF5TD7rQ==, + } + hasBin: true + '@babel/generator@7.28.5': resolution: { @@ -3761,6 +3777,8 @@ snapshots: dependencies: json-schema: 0.4.0 + '@anthropic-ai/sdk@0.52.0': {} + '@babel/generator@7.28.5': dependencies: '@babel/parser': 7.28.5 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index c4f9f75..9a05ea5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -29,6 +29,7 @@ catalogs: vitest: ^4.0.15 zod: ^4.1.13 peer: + '@anthropic-ai/sdk': ^0.52.0 ai: ^5.0.108 openai: ^6.2.0 prod: From 7ced47da3b75404c059969a8aafe34fae10cca19 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:33:29 +0000 Subject: [PATCH 2/7] feat(tool): add toAnthropic() method for Anthropic Claude integration Implement toAnthropic() conversion methods on both BaseTool and Tools classes to enable seamless integration with the Anthropic Claude SDK. The method converts StackOne tools to the Anthropic Tool format with: - name: tool identifier - description: tool description for Claude - input_schema: JSON Schema with type, properties, and required fields Uses the official Tool type from @anthropic-ai/sdk/resources for type safety and API compatibility. Relates to #207 --- src/tool.ts | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/tool.ts b/src/tool.ts index 5ecdd97..55fb9aa 100644 --- a/src/tool.ts +++ b/src/tool.ts @@ -1,3 +1,4 @@ +import type { Tool as AnthropicTool } from '@anthropic-ai/sdk/resources'; import * as orama from '@orama/orama'; import type { ChatCompletionFunctionTool } from 'openai/resources/chat/completions'; import { DEFAULT_HYBRID_ALPHA } from './consts'; @@ -181,6 +182,22 @@ export class BaseTool { }; } + /** + * Convert the tool to Anthropic format + * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use + */ + toAnthropic(): AnthropicTool { + return { + name: this.name, + description: this.description, + input_schema: { + type: 'object', + properties: this.parameters.properties, + required: this.parameters.required, + }, + }; + } + /** * Convert the tool to AI SDK format */ @@ -358,6 +375,14 @@ export class Tools implements Iterable { return this.tools.map((tool) => tool.toOpenAI()); } + /** + * Convert all tools to Anthropic format + * @see https://docs.anthropic.com/en/docs/build-with-claude/tool-use + */ + toAnthropic(): AnthropicTool[] { + return this.tools.map((tool) => tool.toAnthropic()); + } + /** * Convert all tools to AI SDK format */ From 4681f9ed4ef76f5289113f8b2c4905983485cdd9 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:33:41 +0000 Subject: [PATCH 3/7] test(tool): add tests for toAnthropic() conversion methods Add comprehensive tests for both BaseTool.toAnthropic() and Tools.toAnthropic() methods to verify correct conversion to Anthropic's Tool format. Tests verify: - Tool name and description are correctly mapped - input_schema.type is set to 'object' - Properties are correctly transferred - Array conversion for Tools collection works properly Relates to #207 --- src/tool.test.ts | 72 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/tool.test.ts b/src/tool.test.ts index 14e9cde..f71b3fb 100644 --- a/src/tool.test.ts +++ b/src/tool.test.ts @@ -72,6 +72,21 @@ describe('StackOneTool', () => { ).toBe('string'); }); + it('should convert to Anthropic tool format', () => { + const tool = createMockTool(); + const anthropicFormat = tool.toAnthropic(); + + expect(anthropicFormat.name).toBe('test_tool'); + expect(anthropicFormat.description).toBe('Test tool'); + expect(anthropicFormat.input_schema.type).toBe('object'); + const properties = anthropicFormat.input_schema.properties as Record< + string, + { type: string } + >; + expect(properties.id).toBeDefined(); + expect(properties.id.type).toBe('string'); + }); + it('should convert to AI SDK tool format', async () => { const tool = createMockTool(); @@ -317,6 +332,63 @@ describe('Tools', () => { expect(openAITools[1].function.name).toBe('tool2'); }); + it('should convert all tools to Anthropic format', () => { + const tool1 = new BaseTool( + 'tool1', + 'Tool 1', + { + type: 'object', + properties: { id: { type: 'string' } }, + }, + { + kind: 'http', + method: 'GET', + url: 'https://api.example.com/test/{id}', + bodyType: 'json', + params: [ + { + name: 'id', + location: ParameterLocation.PATH, + type: 'string', + }, + ], + }, + ); + + const tool2 = new BaseTool( + 'tool2', + 'Tool 2', + { + type: 'object', + properties: { name: { type: 'string' } }, + }, + { + kind: 'http', + method: 'POST', + url: 'https://api.example.com/test', + bodyType: 'json', + params: [ + { + name: 'name', + location: ParameterLocation.BODY, + type: 'string', + }, + ], + }, + ); + + const tools = new Tools([tool1, tool2]); + const anthropicTools = tools.toAnthropic(); + + expect(anthropicTools).toBeInstanceOf(Array); + expect(anthropicTools.length).toBe(2); + expect(anthropicTools[0].name).toBe('tool1'); + expect(anthropicTools[0].description).toBe('Tool 1'); + expect(anthropicTools[0].input_schema.type).toBe('object'); + expect(anthropicTools[1].name).toBe('tool2'); + expect(anthropicTools[1].description).toBe('Tool 2'); + }); + it('should convert all tools to AI SDK tools', async () => { const tool1 = createMockTool(); const tool2 = new StackOneTool( From b4ea0c5518345eee1152b483d56afb4cbb88e60b Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:33:56 +0000 Subject: [PATCH 4/7] feat(example): add Anthropic Claude integration example Add anthropic-integration.ts demonstrating how to use StackOne tools with Anthropic's Claude API. The example shows: - Initialising StackOneToolSet with account configuration - Fetching HRIS tools via MCP - Converting tools to Anthropic format using toAnthropic() - Creating messages with tool calls using claude-haiku-4-5-20241022 - Verifying tool_use blocks in the response Also adds @anthropic-ai/sdk as a dev dependency in the examples package.json. Closes #207 --- examples/anthropic-integration.ts | 63 +++++++++++++++++++++++++++++++ examples/package.json | 1 + 2 files changed, 64 insertions(+) create mode 100644 examples/anthropic-integration.ts diff --git a/examples/anthropic-integration.ts b/examples/anthropic-integration.ts new file mode 100644 index 0000000..9a26bf6 --- /dev/null +++ b/examples/anthropic-integration.ts @@ -0,0 +1,63 @@ +/** + * This example shows how to use StackOne tools with Anthropic Claude. + */ + +import assert from 'node:assert'; +import process from 'node:process'; +import Anthropic from '@anthropic-ai/sdk'; +import { StackOneToolSet } from '@stackone/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-hris-account-id'; + +const anthropicIntegration = async (): Promise => { + // Initialise StackOne + const toolset = new StackOneToolSet({ + accountId, + baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', + }); + + // Fetch HRIS tools via MCP + const tools = await toolset.fetchTools({ + actions: ['hris_get_*'], + }); + const anthropicTools = tools.toAnthropic(); + + // Initialise Anthropic client + const anthropic = new Anthropic(); + + // Create a message with tool calls + const response = await anthropic.messages.create({ + model: 'claude-haiku-4-5-20241022', + max_tokens: 1024, + system: 'You are a helpful assistant that can access HRIS information.', + messages: [ + { + role: 'user', + content: 'What is the employee with id: c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA phone number?', + }, + ], + tools: anthropicTools, + }); + + // Verify the response contains tool use + assert(response.content.length > 0, 'Expected at least one content block in the response'); + + const toolUseBlock = response.content.find((block) => block.type === 'tool_use'); + assert(toolUseBlock !== undefined, 'Expected a tool_use block in the response'); + assert(toolUseBlock.type === 'tool_use', 'Expected block to be tool_use type'); + assert(toolUseBlock.name === 'hris_get_employee', 'Expected tool call to be hris_get_employee'); + + // Verify the input contains the expected fields + const input = toolUseBlock.input as Record; + assert(input.id === 'c28xIQaWQ6MzM5MzczMDA2NzMzMzkwNzIwNA', 'Expected id to match the query'); +}; + +// Run the example +await anthropicIntegration(); diff --git a/examples/package.json b/examples/package.json index 17919d4..5ea8664 100644 --- a/examples/package.json +++ b/examples/package.json @@ -12,6 +12,7 @@ }, "devDependencies": { "@ai-sdk/openai": "catalog:dev", + "@anthropic-ai/sdk": "catalog:peer", "@clack/prompts": "catalog:dev", "@types/node": "catalog:dev", "ai": "catalog:peer", From a00e8a37773a131e00e889c2e8eed6cc2e705a8c Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 10 Dec 2025 17:35:58 +0000 Subject: [PATCH 5/7] chore(test): format tool.test.ts --- src/tool.test.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/tool.test.ts b/src/tool.test.ts index f71b3fb..40b90ad 100644 --- a/src/tool.test.ts +++ b/src/tool.test.ts @@ -79,10 +79,7 @@ describe('StackOneTool', () => { expect(anthropicFormat.name).toBe('test_tool'); expect(anthropicFormat.description).toBe('Test tool'); expect(anthropicFormat.input_schema.type).toBe('object'); - const properties = anthropicFormat.input_schema.properties as Record< - string, - { type: string } - >; + const properties = anthropicFormat.input_schema.properties as Record; expect(properties.id).toBeDefined(); expect(properties.id.type).toBe('string'); }); From 666f2468fec3c1b2988b67a59c10ddbfee2444c4 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 11 Dec 2025 15:51:01 +0000 Subject: [PATCH 6/7] Apply suggestion from @glebedel --- examples/anthropic-integration.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/anthropic-integration.ts b/examples/anthropic-integration.ts index 9a26bf6..25ab304 100644 --- a/examples/anthropic-integration.ts +++ b/examples/anthropic-integration.ts @@ -23,9 +23,9 @@ const anthropicIntegration = async (): Promise => { baseUrl: process.env.STACKONE_BASE_URL ?? 'https://api.stackone.com', }); - // Fetch HRIS tools via MCP + // Filter for any relevant tools const tools = await toolset.fetchTools({ - actions: ['hris_get_*'], + actions: ['*_list_*', '*_search_*'], }); const anthropicTools = tools.toAnthropic(); From bfc0a6807768f90172b2e66a1c67c1a5d8ae7b03 Mon Sep 17 00:00:00 2001 From: Guillaume Date: Thu, 11 Dec 2025 15:51:58 +0000 Subject: [PATCH 7/7] Apply suggestion from @glebedel --- examples/anthropic-integration.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/anthropic-integration.ts b/examples/anthropic-integration.ts index 25ab304..f92dc30 100644 --- a/examples/anthropic-integration.ts +++ b/examples/anthropic-integration.ts @@ -36,7 +36,7 @@ const anthropicIntegration = async (): Promise => { const response = await anthropic.messages.create({ model: 'claude-haiku-4-5-20241022', max_tokens: 1024, - system: 'You are a helpful assistant that can access HRIS information.', + system: 'You are a helpful assistant that can access tools.', messages: [ { role: 'user',