npm package: @pluslabs/ai-sdk-remote-tools
Description: A library for creating remote tools for the Vercel AI SDK
Author: Magic, Inc
Latest version: 0.0.3

# AI SDK Remote Tools

A library for creating and consuming remote tools for the [Vercel AI SDK](https://sdk.vercel.ai/docs). This package allows you to expose AI tools over HTTP and consume them from any client, enabling distributed AI tool architectures.

## Features

- **Remote Tool Execution** - Execute AI SDK tools over HTTP
- **Type-Safe** - Full TypeScript support with Zod schema validation
- **Caching** - Built-in caching for improved performance
- **Authentication** - Optional API key authentication
- **Tool Filtering** - Filter tools by inclusion or exclusion lists
- **Next.js Integration** - First-class support for Next.js App Router
- **Function-Based Tools** - Support for parameterized tool definitions

## Installation

```bash
npm install @pluslabs/ai-sdk-remote-tools
```

## Quick Start

### Server Setup (Next.js App Router)

Create an API route to expose your tools:

```typescript
// app/api/tools/route.ts
import { tool } from 'ai';
import { generateAPIHandlers } from '@pluslabs/ai-sdk-remote-tools/next';
import { z } from 'zod';

const tools = {
    add: tool({
        description: 'Add two numbers together',
        parameters: z.object({
            a: z.number().describe('First number'),
            b: z.number().describe('Second number')
        }),
        execute: async ({ a, b }) => {
            return { result: a + b };
        }
    }),

    getWeather: tool({
        description: 'Get weather for a location',
        parameters: z.object({
            location: z.string().describe('City name'),
            units: z.enum(['celsius', 'fahrenheit']).optional()
        }),
        execute: async ({ location, units = 'celsius' }) => {
            // Your weather API logic here
            return {
                location,
                temperature: 22,
                condition: 'sunny'
            };
        }
    })
};

export const { GET, POST } = generateAPIHandlers({
    tools,
    apiKey: process.env.TOOLS_API_KEY // HIGHLY RECOMMENDED - protects your tools from unauthorized access
});
```

### Client Setup

Use the remote tools in your AI application (server-side only):

```typescript
import { createRemoteTools } from '@pluslabs/ai-sdk-remote-tools';
import { generateText } from 'ai';
import { openai } from '@ai-sdk/openai';

// Create remote tools client
const remoteTools = createRemoteTools({
    toolsUrl: 'http://localhost:3000/api/tools',
    apiKey: process.env.TOOLS_API_KEY // Match the API key from your server
});

// Fetch and use tools
const tools = await remoteTools();

const result = await generateText({
    model: openai('gpt-4'),
    prompt: 'What is 42 + 58?',
    tools
});

console.log(result.text);
```

## API Reference

### Server API

#### `generateAPIHandlers(options)`

Creates GET and POST handlers for exposing tools via HTTP.

**Parameters:**

- `tools` (required): Record of AI SDK tools or tool factories
- `argsSchemas` (optional): Record of Zod schemas for validating tool arguments
- `apiKey` (optional but highly recommended): API key for authentication. **Without this, your tools are publicly accessible to anyone who knows the endpoint URL.**

**Returns:**

An object with `GET` and `POST` handler functions compatible with Next.js App Router.

**Example:**

```typescript
import { generateAPIHandlers } from '@pluslabs/ai-sdk-remote-tools/next';
import { tool } from 'ai';
import { z } from 'zod';

const tools = {
    multiply: tool({
        description: 'Multiply two numbers',
        parameters: z.object({
            a: z.number(),
            b: z.number()
        }),
        execute: async ({ a, b }) => ({ result: a * b })
    })
};

export const { GET, POST } = generateAPIHandlers({
    tools,
    apiKey: 'secret-key-123'
});
```

#### `toolsToJSONSchema(tools, argsSchemas?)`

Converts AI SDK tools to JSON Schema format for serialization.

**Parameters:**

- `tools` (required): Record of AI SDK tools or tool factories
- `argsSchemas` (optional): Record of Zod schemas for tool arguments

**Returns:**

Array of tool definitions in JSON Schema format.

**Example:**

```typescript
import { toolsToJSONSchema } from '@pluslabs/ai-sdk-remote-tools';
import { tool } from 'ai';
import { z } from 'zod';

const tools = {
    greet: tool({
        description: 'Greet someone',
        parameters: z.object({
            name: z.string()
        }),
        execute: async ({ name }) => `Hello, ${name}!`
    })
};

const schemas = toolsToJSONSchema(tools);
console.log(schemas);
// [
//   {
//     name: 'greet',
//     description: 'Greet someone',
//     inputSchema: { type: 'object', properties: { name: { type: 'string' } }, ... },
//   }
// ]
```

### Client API

#### `createRemoteTools(options)`

Creates a remote tools client for fetching and executing tools from a server.

**Parameters:**

- `toolsUrl` (required): URL of the tools endpoint (must be server-to-server, not client-accessible)
- `apiKey` (optional but highly recommended): API key for authentication. Must match the server's `apiKey`.

**Returns:**

A function that fetches tools from the remote endpoint.

**Example:**

```typescript
import { createRemoteTools } from '@pluslabs/ai-sdk-remote-tools';

const remoteTools = createRemoteTools({
    toolsUrl: 'https://api.example.com/tools',
    apiKey: 'your-api-key'
});

// Fetch all tools
const tools = await remoteTools({});

// Fetch specific tools only
const limitedTools = await remoteTools({
    only: ['add', 'multiply']
});

// Exclude specific tools
const filteredTools = await remoteTools({
    exclude: ['dangerousTool']
});
```

#### Remote Tools Function Options

The function returned by `createRemoteTools` accepts the following options:

- `only` (optional): Array of tool names to include
- `exclude` (optional): Array of tool names to exclude
- `[toolName]` (optional): Additional arguments to pass to function-based tools

## Advanced Usage

### Function-Based Tools

Create tools that accept configuration parameters:

```typescript
// Server
import { generateAPIHandlers } from '@pluslabs/ai-sdk-remote-tools/next';
import { tool } from 'ai';
import { z } from 'zod';

const tools = {
    calculator: (args: { operation: string }) =>
        tool({
            description: `Calculator for ${args.operation}`,
            parameters: z.object({
                a: z.number(),
                b: z.number()
            }),
            execute: async ({ a, b }) => {
                const operations = {
                    add: (a: number, b: number) => a + b,
                    subtract: (a: number, b: number) => a - b,
                    multiply: (a: number, b: number) => a * b
                };
                return { result: operations[args.operation](a, b) };
            }
        })
};

const argsSchemas = {
    calculator: z.object({
        operation: z.enum(['add', 'subtract', 'multiply'])
    })
};

export const { GET, POST } = generateAPIHandlers({
    tools,
    argsSchemas
});
```

```typescript
// Client
const remoteTools = createRemoteTools({
    toolsUrl: 'http://localhost:3000/api/tools'
});

const tools = await remoteTools({
    calculator: { operation: 'multiply' }
});
```

### Caching

The client automatically caches tool definitions for 1 minute to reduce network requests. Caching is based on the hash of the options passed to the remote tools function.

```typescript
const remoteTools = createRemoteTools({ toolsUrl });

// First call fetches from server
const tools1 = await remoteTools();

// Second call uses cache (within 1 minute)
const tools2 = await remoteTools();

// Different options bypass cache
const tools3 = await remoteTools({ only: ['add'] });
```

### Authentication

**Always protect your tools with API key authentication in production.**

Without an API key, your tools endpoint is publicly accessible. Anyone who discovers the URL can:

- List all available tools and their schemas
- Execute any tool with arbitrary parameters
- Potentially access sensitive data or perform unauthorized operations

```typescript
// Server - ALWAYS set an API key in production
export const { GET, POST } = generateAPIHandlers({
    tools,
    apiKey: process.env.TOOLS_API_KEY // Required for security
});

// Client - Must match server API key
const remoteTools = createRemoteTools({
    toolsUrl: 'http://localhost:3000/api/tools',
    apiKey: process.env.TOOLS_API_KEY // Required if server has apiKey
});
```

The API key is sent via the `x-api-key` header and must match exactly between client and server.

### Error Handling

The client throws errors for server-side issues:

```typescript
try {
    const tools = await remoteTools();
} catch (error) {
    if (error.message.includes('AI SDK Remote Tools Error')) {
        console.error('Server error:', error.message);
    } else {
        console.error('Network error:', error);
    }
}
```

## License

MIT

## Author

Cristian Bote