Skip to content

Wrap your AI SDK tools with code execution capabilities - discover and execute multiple tools in a single step

Notifications You must be signed in to change notification settings

christianalfoni/ai-code-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

4 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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 / Tanstack AI 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

npm install ai-code-tools

Quick Start

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

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:

// 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:

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:

{
  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:

{
  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:

// ❌ 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

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

# 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.

About

Wrap your AI SDK tools with code execution capabilities - discover and execute multiple tools in a single step

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published