# AI Code Tools

Supercharge your AI SDK tools with code execution capabilities. Discover and execute multiple tools in a single step through JavaScript code generation.

## What is this?

AI Code Tools wraps your existing Vercel AI SDK tools and adds two powerful meta-tools:

1. **`discover_tools`** - Let the LLM search through available tools by name and schema
2. **`execute_tools`** - Let the LLM write JavaScript code to orchestrate multiple tool calls in one step

This allows LLMs to be more efficient by composing multiple operations together instead of making sequential tool calls with round-trips.

## Installation

```bash
npm install ai-code-tools
```

## Quick Start

Simply wrap your existing AI SDK tools with `createCodeTools()`:

```typescript
import { createCodeTools } from 'ai-code-tools';
import { generateText } from 'ai';
import { anthropic } from '@ai-sdk/anthropic';
import { z } from 'zod';

// Your existing tools
const myTools = {
  getCurrentWeather: {
    description: 'Get the current weather in a location',
    inputSchema: z.object({
      location: z.string().describe('The city name'),
    }),
    execute: async ({ location }) => {
      // Your weather API call
      return { temperature: 72, condition: 'sunny' };
    },
  },
  sendEmail: {
    description: 'Send an email',
    inputSchema: z.object({
      to: z.string().describe('Email address'),
      subject: z.string().describe('Email subject'),
      body: z.string().describe('Email body'),
    }),
    execute: async ({ to, subject, body }) => {
      // Your email sending logic
      return { sent: true };
    },
  },
};

// Wrap your tools - that's it!
const tools = createCodeTools(myTools);

// Use with AI SDK as normal
const result = await generateText({
  model: anthropic('claude-sonnet-4-5-20250929'),
  prompt: 'Get the weather in San Francisco and email it to user@example.com',
  tools,
});
```

## How It Works

Instead of the LLM making multiple sequential tool calls:

```
1. Call getCurrentWeather({ location: "San Francisco" })
2. Wait for response...
3. Call sendEmail({ to: "user@example.com", subject: "Weather", body: "72°F, sunny" })
4. Wait for response...
```

The LLM can now discover available tools and execute them together:

```javascript
// Generated by the LLM via execute_tools
const weather = await tools.getCurrentWeather({ location: "San Francisco" });
const emailResult = await tools.sendEmail({
  to: "user@example.com",
  subject: "Weather Update",
  body: `The weather in San Francisco is ${weather.temperature}°F and ${weather.condition}`
});
return emailResult;
```

All in a single step!

## API

### `createCodeTools(tools)`

Takes your tools object and returns a new tools object with two additional meta-tools:

```typescript
type Tool = {
  description?: string;
  inputSchema: z.ZodType<any>;
  execute: (params: any) => Promise<any> | any;
};

function createCodeTools(tools: Record<string, Tool>): Record<string, Tool>;
```

### Generated Meta-Tools

#### `discover_tools`

Allows the LLM to search for available tools by name and schema:

```typescript
{
  description: "Discover available tools by searching tool names and schemas with a query pattern",
  inputSchema: z.object({
    query: z.string().describe("The search query to find relevant tools")
  })
}
```

#### `execute_tools`

Allows the LLM to execute JavaScript code with access to your tools:

```typescript
{
  description: "Execute JavaScript code with access to tools via the 'tools' object. Return the result you want access to. IMPORTANT: Only use this tool to call the available tools and return their results.",
  inputSchema: z.object({
    code: z.string().describe("The JavaScript code to execute")
  })
}
```

## Security

Security is a top priority. The generated JavaScript code goes through **AST-based validation** using Acorn before execution.

### What's Protected

The code validation blocks all potentially dangerous operations:

- **Module System**: `require()`, `import`, dynamic imports
- **Code Execution**: `eval()`, `Function` constructor, `setTimeout`, `setInterval`, `setImmediate`
- **Global Access**: `process`, `global`, `globalThis`, `module`, `exports`
- **File System**: `__dirname`, `__filename`
- **Constructor Escapes**: Nested constructor chains like `{}.constructor.constructor`

### How It Works

Before executing any code, it's parsed into an Abstract Syntax Tree (AST) and validated:

```typescript
// ❌ This will be blocked before execution
const code = `
  const fs = require('fs');
  return fs.readFileSync('/etc/passwd');
`;
// Error: "Code validation failed: Forbidden identifier: require"

// ✅ This is allowed
const code = `
  const weather = await tools.getCurrentWeather({ location: "NYC" });
  return weather.temperature;
`;
// Returns: 72
```

### Why It's Safe

1. **Instruction-Level Protection**: The tool description explicitly tells the LLM not to attempt system access
2. **AST Validation**: All code is parsed and validated before execution - dangerous patterns are caught statically
3. **Sandboxed Context**: Code only has access to:
   - Your provided tools
   - Safe JavaScript globals (Math, JSON, Array, etc.)
   - No access to Node.js APIs or system resources

### Test Coverage

The library includes comprehensive security tests covering:
- Module system attacks (require, import)
- Code execution attacks (eval, Function constructor)
- Global object access (process, global, globalThis)
- Timer-based attacks (setTimeout, setInterval)
- Sneaky bypass attempts (constructor chains, indirect access)

All 19 security tests pass. See `src/execute_tools.test.ts` for details.

## Benefits

### For the LLM
- **Efficiency**: Compose multiple operations in one step instead of sequential round-trips
- **Discovery**: Search through available tools before using them
- **Flexibility**: Use standard JavaScript to orchestrate tool calls

### For Developers
- **Zero Configuration**: Just wrap your existing tools
- **No Changes Needed**: Your tool definitions stay exactly the same
- **Type Safe**: Full TypeScript support with Zod schemas
- **Secure by Default**: AST-based validation protects against code injection

## Use Cases

- **Multi-step workflows**: Get data, transform it, and send it somewhere
- **Conditional logic**: Execute tools based on results of previous calls
- **Data aggregation**: Combine results from multiple tools
- **Complex orchestration**: Let the LLM compose tools creatively

## Example: Complex Workflow

```typescript
const tools = createCodeTools({
  searchProducts: {
    description: 'Search for products',
    inputSchema: z.object({
      query: z.string(),
    }),
    execute: async ({ query }) => {
      return [
        { id: 1, name: 'Laptop', price: 999 },
        { id: 2, name: 'Mouse', price: 29 },
      ];
    },
  },
  getProductDetails: {
    description: 'Get detailed product information',
    inputSchema: z.object({
      productId: z.number(),
    }),
    execute: async ({ productId }) => {
      return { id: productId, stock: 10, rating: 4.5 };
    },
  },
  addToCart: {
    description: 'Add product to shopping cart',
    inputSchema: z.object({
      productId: z.number(),
      quantity: z.number(),
    }),
    execute: async ({ productId, quantity }) => {
      return { success: true, cartTotal: 999 };
    },
  },
});

const result = await generateText({
  model: anthropic('claude-sonnet-4-5-20250929'),
  prompt: 'Search for laptops, find the one with the best rating, and add it to my cart',
  tools,
});

// The LLM can execute_tools with code like:
// const products = await tools.searchProducts({ query: "laptops" });
// const details = await Promise.all(
//   products.map(p => tools.getProductDetails({ productId: p.id }))
// );
// const best = details.sort((a, b) => b.rating - a.rating)[0];
// return await tools.addToCart({ productId: best.id, quantity: 1 });
```

## Development

```bash
# Install dependencies
npm install

# Build
npm run build

# Run tests
npm test

# Run unit tests with Vitest
npm run test:unit
```

## License

MIT

## Contributing

Contributions are welcome! Please ensure all security tests pass before submitting PRs.
