npm package: @stackone/ai
Description: Tools for agents to perform actions on your SaaS
Author: StackOne
Latest version: 2.6.0

# StackOne AI SDK

> A unified interface for performing actions on SaaS tools through AI-friendly APIs.

[![npm version](https://img.shields.io/npm/v/@stackone/ai?color=yellow)](https://npmjs.com/package/@stackone/ai) [![DeepWiki](https://img.shields.io/badge/DeepWiki-StackOneHQ%2Fstackone--ai--node-blue.svg?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAyCAYAAAAnWDnqAAAAAXNSR0IArs4c6QAAA05JREFUaEPtmUtyEzEQhtWTQyQLHNak2AB7ZnyXZMEjXMGeK/AIi+QuHrMnbChYY7MIh8g01fJoopFb0uhhEqqcbWTp06/uv1saEDv4O3n3dV60RfP947Mm9/SQc0ICFQgzfc4CYZoTPAswgSJCCUJUnAAoRHOAUOcATwbmVLWdGoH//PB8mnKqScAhsD0kYP3j/Yt5LPQe2KvcXmGvRHcDnpxfL2zOYJ1mFwrryWTz0advv1Ut4CJgf5uhDuDj5eUcAUoahrdY/56ebRWeraTjMt/00Sh3UDtjgHtQNHwcRGOC98BJEAEymycmYcWwOprTgcB6VZ5JK5TAJ+fXGLBm3FDAmn6oPPjR4rKCAoJCal2eAiQp2x0vxTPB3ALO2CRkwmDy5WohzBDwSEFKRwPbknEggCPB/imwrycgxX2NzoMCHhPkDwqYMr9tRcP5qNrMZHkVnOjRMWwLCcr8ohBVb1OMjxLwGCvjTikrsBOiA6fNyCrm8V1rP93iVPpwaE+gO0SsWmPiXB+jikdf6SizrT5qKasx5j8ABbHpFTx+vFXp9EnYQmLx02h1QTTrl6eDqxLnGjporxl3NL3agEvXdT0WmEost648sQOYAeJS9Q7bfUVoMGnjo4AZdUMQku50McDcMWcBPvr0SzbTAFDfvJqwLzgxwATnCgnp4wDl6Aa+Ax283gghmj+vj7feE2KBBRMW3FzOpLOADl0Isb5587h/U4gGvkt5v60Z1VLG8BhYjbzRwyQZemwAd6cCR5/XFWLYZRIMpX39AR0tjaGGiGzLVyhse5C9RKC6ai42ppWPKiBagOvaYk8lO7DajerabOZP46Lby5wKjw1HCRx7p9sVMOWGzb/vA1hwiWc6jm3MvQDTogQkiqIhJV0nBQBTU+3okKCFDy9WwferkHjtxib7t3xIUQtHxnIwtx4mpg26/HfwVNVDb4oI9RHmx5WGelRVlrtiw43zboCLaxv46AZeB3IlTkwouebTr1y2NjSpHz68WNFjHvupy3q8TFn3Hos2IAk4Ju5dCo8B3wP7VPr/FGaKiG+T+v+TQqIrOqMTL1VdWV1DdmcbO8KXBz6esmYWYKPwDL5b5FA1a0hwapHiom0r/cKaoqr+27/XcrS5UwSMbQAAAABJRU5ErkJggg==)](https://deepwiki.com/StackOneHQ/stackone-ai-node) [![Coverage](https://stackonehq.github.io/stackone-ai-node/badges.svg)](https://stackonehq.github.io/stackone-ai-node)

<!-- DeepWiki badge generated by https://deepwiki.ryoppippi.com/ -->

## StackOneToolSet

The StackOne AI SDK provides the `StackOneToolSet` class, which fetches tools dynamically from StackOne's MCP (Model Context Protocol) endpoint. This ensures you always have access to the latest tool definitions.

## Installation

```bash
# Using npm
npm install @stackone/ai zod

# Using yarn
yarn add @stackone/ai zod

# Using pnpm
pnpm add @stackone/ai zod

# Using bun
bun add @stackone/ai zod
```

> **Note:** `zod` is a peer dependency required for AI SDK integrations and internal schema validation. Version `>=3.25.0 <5` is supported.

## Usage

```typescript
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();
const employeeTool = tools.getTool('bamboohr_list_employees');
const employees = await employeeTool.execute();
```

### Authentication

Set the `STACKONE_API_KEY` environment variable:

```bash
export STACKONE_API_KEY=<your-api-key>
```

or load from a .env file using your preferred environment variable library.

### Account IDs

StackOne uses account IDs to identify different integrations. You can specify the account ID at different levels:

```typescript
import { StackOneToolSet } from '@stackone/ai';

// Single account - simplest approach
const toolset = new StackOneToolSet({ accountId: 'your-bamboohr-account' });
const tools = await toolset.fetchTools();

// Multiple accounts - returns tools from both integrations
const multiAccountToolset = new StackOneToolSet();
const allTools = await multiAccountToolset.fetchTools({
	accountIds: ['bamboohr-account-123', 'workday-account-456'],
});

// Filter to specific integration when using multiple accounts
const bamboohrOnly = await multiAccountToolset.fetchTools({
	accountIds: ['bamboohr-account-123', 'workday-account-456'],
	actions: ['bamboohr_*'], // Only BambooHR tools
});

// Set directly on a tool instance
tools.setAccountId('direct-account-id');
const currentAccountId = tools.getAccountId(); // Get the current account ID
```

## Integrations

The StackOneToolSet makes it super easy to use StackOne APIs as tools in your AI applications.

<details>
<summary><strong>With OpenAI Chat Completions API</strong></summary>

```bash
npm install @stackone/ai openai  # or: yarn/pnpm/bun add
```

```typescript
import { OpenAI } from 'openai';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();

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(),
});
```

[View full example](examples/openai-integration.ts)

</details>

<details>
<summary><strong>With OpenAI Responses API</strong></summary>

```bash
npm install @stackone/ai openai  # or: yarn/pnpm/bun add
```

```typescript
import OpenAI from 'openai';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();

const openai = new OpenAI();

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(),
});
```

[View full example](examples/openai-responses-integration.ts)

</details>

<details>
<summary><strong>With Anthropic Claude</strong></summary>

```bash
npm install @stackone/ai @anthropic-ai/sdk  # or: yarn/pnpm/bun add
```

```typescript
import Anthropic from '@anthropic-ai/sdk';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();

const anthropic = new Anthropic();

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(),
});
```

[View full example](examples/anthropic-integration.ts)

</details>

<details>
<summary><strong>With AI SDK by Vercel</strong></summary>

```bash
npm install @stackone/ai ai @ai-sdk/openai  # or: yarn/pnpm/bun add
```

```typescript
import { openai } from '@ai-sdk/openai';
import { generateText } from 'ai';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();

await generateText({
	model: openai('gpt-5.1'),
	tools: await tools.toAISDK(),
	maxSteps: 3,
});
```

[View full example](examples/ai-sdk-integration.ts)

</details>

<details>
<summary><strong>With TanStack AI</strong></summary>

```bash
npm install @stackone/ai @tanstack/ai @tanstack/ai-openai zod  # or: yarn/pnpm/bun add
```

```typescript
import { chat } from '@tanstack/ai';
import { openai } from '@tanstack/ai-openai';
import { z } from 'zod';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

const tools = await toolset.fetchTools();
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);
	},
};

const adapter = openai();
const stream = chat({
	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
}
```

[View full example](examples/tanstack-ai-integration.ts)

</details>

<details>
<summary><strong>With Claude Agent SDK</strong></summary>

```bash
npm install @stackone/ai @anthropic-ai/claude-agent-sdk zod  # or: yarn/pnpm/bun add
```

```typescript
import { query } from '@anthropic-ai/claude-agent-sdk';
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({
	accountId: 'your-account-id',
});

// Fetch tools and convert to Claude Agent SDK format
const tools = await toolset.fetchTools();
const mcpServer = await tools.toClaudeAgentSdk();

// Use with Claude Agent SDK query
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,
	},
});

for await (const message of result) {
	// Process streaming messages
}
```

[View full example](examples/claude-agent-sdk-integration.ts)

</details>

## Features

### Filtering Tools with fetchTools()

You can filter tools by account IDs, providers, and action patterns:

```typescript
// Filter by account IDs
toolset.setAccounts(['account-123', 'account-456']);
const tools = await toolset.fetchTools();
// OR
const tools = await toolset.fetchTools({
	accountIds: ['account-123', 'account-456'],
});

// Filter by providers
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'],
});

// Filter by actions with glob patterns
const tools = await toolset.fetchTools({ actions: ['*_list_employees'] });

// Combine multiple filters
const tools = await toolset.fetchTools({
	accountIds: ['account-123'],
	providers: ['hibob'],
	actions: ['*_list_*'],
});
```

This is especially useful when you want to:

- Limit tools to specific linked accounts
- Focus on specific HR/CRM/ATS providers
- Get only certain types of operations (e.g., all "list" operations)

[View full example](examples/fetch-tools.ts)

### Search Tool

Search for tools using natural language queries. Works with both semantic (cloud) and local BM25+TF-IDF search.

#### Basic Usage

```typescript
import { StackOneToolSet } from '@stackone/ai';

// Get a callable search tool
const toolset = new StackOneToolSet({ accountId: 'your-account-id' });
const searchTool = toolset.getSearchTool();

// Search for relevant tools — returns a Tools collection
const tools = await searchTool.search('manage employees', { topK: 5 });

// Execute a discovered tool directly
const listTool = tools.getTool('bamboohr_list_employees');
const result = await listTool.execute({ query: { limit: 10 } });
```

### Semantic Search

Discover tools using natural language instead of exact names. Queries like "onboard new hire" resolve to the right actions even when the tool is called `bamboohr_create_employee`.

```typescript
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({ accountId: 'your-account-id' });

// Search by intent — returns Tools collection ready for any framework
const tools = await toolset.searchTools('manage employee records', { topK: 5 });
const openAITools = tools.toOpenAI();

// Lightweight: inspect results without fetching full tool definitions
const results = await toolset.searchActionNames('time off requests', { topK: 5 });
```

#### Search Modes

Control which search backend `searchTools()` uses via the `search` option:

```typescript
// 'auto' (default) — tries semantic search first, falls back to local
const tools = await toolset.searchTools('manage employees', { search: 'auto' });

// 'semantic' — semantic API only, throws if unavailable
const tools = await toolset.searchTools('manage employees', { search: 'semantic' });

// 'local' — local BM25+TF-IDF only, no semantic API call
const tools = await toolset.searchTools('manage employees', { search: 'local' });
```

Results are automatically scoped to connectors in your linked accounts. See [Search Tools Example](examples/search-tools.ts) for `SearchTool` (`getSearchTool`) integration, AI SDK, and agent loop patterns.

### Custom Base URL

```typescript
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet({ baseUrl: 'https://api.example-dev.com' });
```

### Testing with dryRun

You can use the `dryRun` option to return the api arguments from a tool call without making the actual api call:

```typescript
import { StackOneToolSet } from '@stackone/ai';

// Initialize the toolset
const toolset = new StackOneToolSet();

const tools = await toolset.fetchTools();
const employeeTool = tools.getTool('bamboohr_list_employees');

// Use dryRun to see the request details
const dryRunResult = await employeeTool.execute({ query: { limit: 5 } }, { dryRun: true });

console.log(dryRunResult);
// {
//   url: "https://api.stackone.com/actions/rpc",
//   method: "POST",
//   headers: { ... },
//   body: "...",
//   mappedParams: { ... }
// }
```

The `dryRun` option returns an object containing:

- `url`: The full URL with query parameters
- `method`: The HTTP method
- `headers`: The request headers
- `body`: The request body
- `mappedParams`: The parameters after mapping and derivation

### Feedback Collection Tool

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.

#### How It Works

The feedback tool:

- **Requires explicit user consent** before submitting feedback
- **Collects user feedback** about their experience with StackOne tools
- **Tracks tool usage** by recording which tools were used
- **Submits to StackOne** via the `/ai/tool-feedback` endpoint
- **Uses the same API key** as other SDK operations for authentication

#### Usage

The feedback tool is automatically available when using `StackOneToolSet`:

```typescript
import { StackOneToolSet } from '@stackone/ai';

const toolset = new StackOneToolSet();
const tools = await toolset.fetchTools();

// The feedback tool is automatically included
const feedbackTool = tools.getTool('tool_feedback');

// Use with AI agents - they will ask for user consent first
const openAITools = tools.toOpenAI();
// or
const aiSdkTools = await tools.toAISDK();
```

#### Manual Usage

You can also use the feedback tool directly:

```typescript
// Get the feedback tool
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'],
});
```

#### Multiple Account Support

The feedback tool supports both single and multiple account IDs. When you provide an array of account IDs, the feedback will be sent to each account individually:

```typescript
// Single account ID (string)
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'],
});

// Multiple account IDs (array)
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'],
});
```

**Response Format**: When using multiple account IDs, the tool returns a summary of all submissions:

```typescript
{
  message: "Feedback sent to 2 account(s)",
  total_accounts: 2,
  successful: 2,
  failed: 0,
  results: [
    {
      account_id: "acc_123456",
      status: "success",
      result: { message: "Feedback successfully stored", ... }
    },
    {
      account_id: "acc_789012",
      status: "success",
      result: { message: "Feedback successfully stored", ... }
    }
  ]
}
```

#### AI Agent Integration

When AI agents use this tool, they will:

1. **Ask for user consent**: "Are you ok with sending feedback to StackOne?"
2. **Collect feedback**: Get the user's verbatim feedback
3. **Track tool usage**: Record which tools were used in the session
4. **Submit to all accounts**: Send the same feedback to each account ID provided
5. **Report results**: Show which accounts received the feedback successfully

The tool description includes clear instructions for AI agents to always ask for explicit user consent before submitting feedback.

## Development Environment

### Using Nix Flake

This project includes a Nix flake for reproducible development environments. All development tools are defined in [flake.nix](./flake.nix) and provided via Nix.

#### Installing Nix

```bash
# Install Nix with flakes enabled (if not already installed)
curl --proto '=https' --tlsv1.2 -sSf -L https://artifacts.nixos.org/experimental-installer | \
  sh -s -- install

# If flakes are not enabled, enable them with:
mkdir -p ~/.config/nix && echo "experimental-features = nix-command flakes" >> ~/.config/nix/nix.conf
```

#### Activating the Development Environment

```bash
# Automatic activation with direnv (recommended)
direnv allow

# Or manual activation
nix develop
```

The flake provides all necessary development dependencies including Node.js, pnpm, and other build tools.