SuperAPI AI Directory

open-agent-sdk-typescript

Agents & models

View repository

Updated 10-15 23:39

About this skill

Open Agent SDK (TypeScript)

Skill by ara.so — AI Agent Skills collection.

Open Agent SDK is a TypeScript library that runs full AI agent loops in-process without CLI dependencies or subprocesses. It supports both Anthropic and OpenAI-compatible APIs, provides 35+ built-in tools, MCP server integration, custom tool creation, skills system, subagents, and lifecycle hooks. Deploy anywhere: cloud, serverless, Docker, or CI/CD.

Installation

npm install @codeany/open-agent-sdk

Set your API key as an environment variable:

export CODEANY_API_KEY=your-api-key-here

For OpenAI-compatible models:

export CODEANY_API_TYPE=openai-completions
export CODEANY_API_KEY=sk-your-key
export CODEANY_BASE_URL=https://api.openai.com/v1
export CODEANY_MODEL=gpt-4o

For third-party Anthropic-compatible providers:

export CODEANY_BASE_URL=https://openrouter.ai/api
export CODEANY_API_KEY=sk-or-your-key
export CODEANY_MODEL=anthropic/claude-sonnet-4

Core Concepts

API Types

The SDK supports two API types:

  • anthropic-messages: Anthropic Claude models (default)
  • openai-completions: OpenAI, DeepSeek, Qwen, Mistral, or any OpenAI-compatible API

API type is auto-detected from model name. Models containing gpt-, o1, o3, deepseek, qwen, or mistral automatically use openai-completions.

Agent vs Query

  • query(): One-shot streaming query, no session persistence
  • createAgent(): Reusable agent with multi-turn conversation history

Quick Start Examples

One-Shot Streaming Query

import { query } from "@codeany/open-agent-sdk";

for await (const message of query({
  prompt: "Read package.json and tell me the project name.",
  options: {
    allowedTools: ["Read", "Glob"],
    permissionMode: "bypassPermissions",
  },
})) {
  if (message.type === "assistant") {
    for (const block of message.message.content) {
      if ("text" in block) {
        console.log(block.text);
      }
    }
  }
  
  if (message.type === "result") {
    console.log(`Cost: $${message.total_cost_usd?.toFixed(4)}`);
    console.log(`Turns: ${message.num_turns}`);
  }
}

Simple Blocking Prompt

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({ model: "claude-sonnet-4-6" });
const result = await agent.prompt("What files are in this project?");

console.log(result.text);
console.log(`Turns: ${result.num_turns}`);
console.log(`Tokens: ${result.usage.input_tokens + result.usage.output_tokens}`);

OpenAI / GPT Models

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({
  apiType: "openai-completions",
  model: "gpt-4o",
  apiKey: process.env.OPENAI_API_KEY,
  baseURL: "https://api.openai.com/v1",
});

const result = await agent.prompt("Analyze the code structure of this project");
console.log(result.text);

Multi-Turn Conversation

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({ maxTurns: 5 });

const r1 = await agent.prompt(
  'Create a file /tmp/hello.txt with "Hello World"'
);
console.log("Response 1:", r1.text);

const r2 = await agent.prompt("Read back the file you just created");
console.log("Response 2:", r2.text);

const r3 = await agent.prompt("Delete the file");
console.log("Response 3:", r3.text);

console.log(`Total messages in session: ${agent.getMessages().length}`);

// Clean up
await agent.close();

Custom Tools

Using Zod Schema (Recommended)

import { z } from "zod";
import { query, tool, createSdkMcpServer } from "@codeany/open-agent-sdk";

const getWeather = tool(
  "get_weather",
  "Get the current weather for a city",
  {
    city: z.string().describe("City name"),
    unit: z.enum(["celsius", "fahrenheit"]).optional().describe("Temperature unit"),
  },
  async ({ city, unit = "celsius" }) => {
    // Simulate API call
    const temp = unit === "celsius" ? 22 : 72;
    return {
      content: [
        {
          type: "text",
          text: `Weather in ${city}: ${temp}°${unit === "celsius" ? "C" : "F"}, sunny`,
        },
      ],
    };
  }
);

const calculator = tool(
  "calculate",
  "Perform mathematical calculations",
  {
    expression: z.string().describe("Mathematical expression to evaluate"),
  },
  async ({ expression }) => {
    try {
      const result = Function(`'use strict'; return (${expression})`)();
      return {
        content: [{ type: "text", text: `${expression} = ${result}` }],
      };
    } catch (error) {
      return {
        content: [{ type: "text", text: `Error: ${error.message}` }],
        isError: true,
      };
    }
  }
);

const server = createSdkMcpServer({
  name: "custom-tools",
  tools: [getWeather, calculator],
});

for await (const msg of query({
  prompt: "What's the weather in Tokyo? Also calculate 2**16.",
  options: { mcpServers: { "custom-tools": server } },
})) {
  if (msg.type === "assistant") {
    for (const block of msg.message.content) {
      if ("text" in block) console.log(block.text);
    }
  }
}

Low-Level Tool Definition

import { createAgent, getAllBaseTools, defineTool } from "@codeany/open-agent-sdk";

const databaseQuery = defineTool({
  name: "DatabaseQuery",
  description: "Execute SQL queries against the database",
  inputSchema: {
    type: "object",
    properties: {
      query: {
        type: "string",
        description: "SQL query to execute",
      },
      readonly: {
        type: "boolean",
        description: "Whether this is a read-only query",
      },
    },
    required: ["query"],
  },
  isReadOnly: false,
  async call(input) {
    // Validate read-only
    if (!input.readonly && /^\s*(SELECT|SHOW|DESCRIBE)/i.test(input.query)) {
      return "Error: Use readonly=true for SELECT queries";
    }
    
    // Execute query (simulated)
    return JSON.stringify({
      rows: [{ id: 1, name: "Example" }],
      rowCount: 1,
    });
  },
});

const agent = createAgent({
  tools: [...getAllBaseTools(), databaseQuery],
});

const result = await agent.prompt(
  "Query the users table and show me all records"
);
console.log(result.text);

Built-in Tools

The SDK includes 35+ built-in tools. Common ones include:

  • File operations: Read, Write, Edit, Delete, Move, Rename
  • Search: Glob, Grep, Search
  • Git: GitStatus, GitDiff, GitLog, GitCommit, GitCheckout
  • Analysis: Lint, Symbols, CodeGraph, Dependencies
  • Shell: Bash, BashSession
  • Utility: Ask, Attempt, Skill, Agent (subagents)

Restricting Tools

import { createAgent } from "@codeany/open-agent-sdk";

// Read-only agent
const readOnlyAgent = createAgent({
  allowedTools: ["Read", "Glob", "Grep", "Search"],
  permissionMode: "dontAsk",
});

// Agent without shell access
const noShellAgent = createAgent({
  disallowedTools: ["Bash", "BashSession"],
});

// Minimal tool set
const minimalAgent = createAgent({
  tools: [], // No tools at all
});

Skills

Skills are reusable prompt templates. Five built-in skills: simplify, commit, review, debug, test.

Using Built-in Skills

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent();

// The agent can invoke skills via the Skill tool
const result = await agent.prompt(
  'Use the "review" skill to review src/index.ts'
);
console.log(result.text);

Creating Custom Skills

import { registerSkill, getAllSkills, createAgent } from "@codeany/open-agent-sdk";

registerSkill({
  name: "explain",
  description: "Explain a concept in simple terms",
  userInvocable: true,
  async getPrompt(args) {
    return [
      {
        type: "text",
        text: `Explain in simple terms: ${args || "Ask what to explain."}`,
      },
    ];
  },
});

registerSkill({
  name: "optimize",
  description: "Optimize code for performance",
  userInvocable: true,
  async getPrompt(args) {
    return [
      {
        type: "text",
        text: `Analyze and optimize the following for performance:\n${args}\n\nProvide specific improvements and benchmarks.`,
      },
    ];
  },
});

console.log(`${getAllSkills().length} skills registered`);

const agent = createAgent();
const result = await agent.prompt(
  'Use the "optimize" skill on src/heavy-computation.ts'
);
console.log(result.text);

MCP Server Integration

External MCP Servers

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({
  mcpServers: {
    filesystem: {
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
    },
    postgres: {
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-postgres"],
      env: {
        DATABASE_URL: process.env.DATABASE_URL,
      },
    },
  },
});

const result = await agent.prompt("List files in /tmp and query the database");
console.log(result.text);

await agent.close(); // Important: closes MCP connections

In-Process MCP Servers

import { z } from "zod";
import { createAgent, tool, createSdkMcpServer } from "@codeany/open-agent-sdk";

const httpGet = tool(
  "http_get",
  "Make HTTP GET requests",
  {
    url: z.string().url().describe("URL to fetch"),
    headers: z.record(z.string()).optional().describe("HTTP headers"),
  },
  async ({ url, headers }) => {
    const response = await fetch(url, { headers });
    const text = await response.text();
    return {
      content: [
        {
          type: "text",
          text: `Status: ${response.status}\n\n${text}`,
        },
      ],
    };
  }
);

const mcpServer = createSdkMcpServer({
  name: "http-tools",
  tools: [httpGet],
});

const agent = createAgent({
  mcpServers: {
    http: mcpServer,
  },
});

const result = await agent.prompt("Fetch https://api.github.com/repos/codeany-ai/open-agent-sdk-typescript");
console.log(result.text);

Subagents

Subagents are specialized agents with restricted tools and custom prompts.

import { query } from "@codeany/open-agent-sdk";

for await (const msg of query({
  prompt: "Use the security-reviewer to audit src/auth.ts for vulnerabilities",
  options: {
    agents: {
      "security-reviewer": {
        description: "Expert security code reviewer",
        prompt: "You are a security expert. Analyze code for vulnerabilities, focusing on authentication, authorization, input validation, and data exposure.",
        tools: ["Read", "Glob", "Grep", "Symbols"],
      },
      "performance-optimizer": {
        description: "Performance optimization specialist",
        prompt: "You optimize code for speed and efficiency. Identify bottlenecks, suggest algorithmic improvements, and recommend caching strategies.",
        tools: ["Read", "Symbols", "Dependencies"],
      },
    },
  },
})) {
  if (msg.type === "assistant") {
    for (const block of msg.message.content) {
      if ("text" in block) console.log(block.text);
    }
  }
}

Lifecycle Hooks

20 available hook events for observability and control.

import { createAgent, createHookRegistry } from "@codeany/open-agent-sdk";

const hooks = createHookRegistry({
  PreToolUse: [
    {
      handler: async (input) => {
        console.log(`[HOOK] About to use tool: ${input.toolName}`);
        console.log(`[HOOK] Arguments:`, input.toolInput);
        
        // Block dangerous operations
        if (input.toolName === "Bash" && input.toolInput.command?.includes("rm -rf")) {
          console.warn("[HOOK] Blocking dangerous command");
          return { block: true };
        }
      },
    },
  ],
  PostToolUse: [
    {
      handler: async (input) => {
        console.log(`[HOOK] Tool ${input.toolName} completed`);
        console.log(`[HOOK] Result length:`, input.toolResult.length);
      },
    },
  ],
  PostToolUseFailure: [
    {
      handler: async (input) => {
        console.error(`[HOOK] Tool ${input.toolName} failed:`, input.error);
      },
    },
  ],
  SessionStart: [
    {
      handler: async () => {
        console.log("[HOOK] Session started");
      },
    },
  ],
  SessionEnd: [
    {
      handler: async () => {
        console.log("[HOOK] Session ended");
      },
    },
  ],
  FileChanged: [
    {
      handler: async (input) => {
        console.log(`[HOOK] File changed: ${input.path}`);
      },
    },
  ],
});

const agent = createAgent({
  hooks,
  model: "claude-sonnet-4-6",
});

const result = await agent.prompt("List files and create a new test file");
console.log(result.text);

Available Hook Events

  • Tool lifecycle: PreToolUse, PostToolUse, PostToolUseFailure
  • Session: SessionStart, SessionEnd, Stop
  • Subagents: SubagentStart, SubagentStop
  • User interaction: UserPromptSubmit, PermissionRequest, PermissionDenied
  • Tasks: TaskCreated, TaskCompleted
  • Config: ConfigChange, CwdChanged
  • File system: FileChanged
  • System: Notification, PreCompact, PostCompact, TeammateIdle

Permission Modes

Control how the agent handles permission requests:

import { createAgent } from "@codeany/open-agent-sdk";

// Bypass all permissions (default)
const autoAgent = createAgent({
  permissionMode: "bypassPermissions",
});

// Never ask, deny if permission needed
const denyAgent = createAgent({
  permissionMode: "dontAsk",
});

// Ask for permission (requires custom handler)
const askAgent = createAgent({
  permissionMode: "ask",
  // Implement permission handler via hooks
});

// Change mode mid-session
autoAgent.setPermissionMode("dontAsk");

Configuration Options

Agent Options

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({
  // API configuration
  apiType: "anthropic-messages", // or "openai-completions"
  model: "claude-sonnet-4-6",
  apiKey: process.env.CODEANY_API_KEY,
  baseURL: "https://api.anthropic.com",
  
  // Session
  sessionId: "my-session-id",
  maxTurns: 10,
  
  // Working directory
  cwd: process.cwd(),
  
  // System prompt
  systemPrompt: "You are a helpful coding assistant",
  appendSystemPrompt: "\n\nAlways explain your reasoning.",
  
  // Tools
  tools: [], // Custom tool list
  allowedTools: ["Read", "Write", "Glob"],
  disallowedTools: ["Bash"],
  
  // Permissions
  permissionMode: "bypassPermissions",
  
  // MCP servers
  mcpServers: {},
  
  // Subagents
  agents: {},
  
  // Hooks
  hooks: createHookRegistry({}),
  
  // Model parameters
  temperature: 0.7,
  maxTokens: 4096,
});

Query Options

import { query } from "@codeany/open-agent-sdk";

for await (const msg of query({
  prompt: "Your prompt here",
  options: {
    model: "gpt-4o",
    apiType: "openai-completions",
    apiKey: process.env.OPENAI_API_KEY,
    cwd: "/path/to/project",
    allowedTools: ["Read", "Glob"],
    permissionMode: "bypassPermissions",
    maxTurns: 5,
    temperature: 0.5,
  },
})) {
  // Handle messages
}

Advanced Patterns

Session Management

import { createAgent, listSessions, forkSession } from "@codeany/open-agent-sdk";

// Create agent with persistent session
const agent = createAgent({
  sessionId: "my-project-session",
});

await agent.prompt("Analyze the codebase structure");
await agent.prompt("Now suggest improvements");

// List all sessions
const sessions = await listSessions();
console.log("Saved sessions:", sessions);

// Fork a session for experimentation
const forkedId = await forkSession("my-project-session");
const forkedAgent = createAgent({ sessionId: forkedId });

await forkedAgent.prompt("Try refactoring with a different approach");

Interrupting Long-Running Queries

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent();

// Start a long-running query
const queryPromise = agent.prompt("Analyze all files in the project");

// Interrupt after 5 seconds
setTimeout(() => {
  agent.interrupt();
  console.log("Query interrupted");
}, 5000);

try {
  const result = await queryPromise;
  console.log(result.text);
} catch (error) {
  console.log("Query was interrupted");
}

Model Switching

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent({ model: "claude-sonnet-4-6" });

await agent.prompt("Quick analysis");

// Switch to more powerful model for complex task
agent.setModel("claude-opus-4");
await agent.prompt("Perform deep architectural analysis");

// Switch back to cheaper model
agent.setModel("claude-sonnet-4-6");
await agent.prompt("Summarize findings");

Streaming with Full Control

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent();

for await (const message of agent.query("Analyze this codebase")) {
  switch (message.type) {
    case "assistant":
      for (const block of message.message.content) {
        if ("text" in block) {
          process.stdout.write(block.text);
        } else if ("tool_use" in block) {
          console.log(`\n[Tool: ${block.tool_use.name}]`);
        }
      }
      break;
      
    case "tool_use":
      console.log(`\nExecuting: ${message.toolName}`);
      break;
      
    case "tool_result":
      console.log(`Tool result length: ${message.result.length} chars`);
      break;
      
    case "result":
      console.log(`\n\nCompleted in ${message.num_turns} turns`);
      console.log(`Cost: $${message.total_cost_usd?.toFixed(4)}`);
      console.log(`Tokens: ${message.usage.input_tokens + message.usage.output_tokens}`);
      break;
      
    case "error":
      console.error(`Error: ${message.error}`);
      break;
  }
}

Error Handling

import { createAgent } from "@codeany/open-agent-sdk";

const agent = createAgent();

try {
  const result = await agent.prompt("Your prompt here");
  console.log(result.text);
} catch (error) {
  if (error.message.includes("rate limit")) {
    console.error("Rate limit exceeded, retry later");
  } else if (error.message.includes("context length")) {
    console.error("Prompt too long, try simplifying");
  } else {
    console.error("Agent error:", error.message);
  }
} finally {
  await agent.close();
}

Troubleshooting

API Key Not Found

Error: API key not found

Solution: Set the CODEANY_API_KEY environment variable or pass apiKey in options:

const agent = createAgent({
  apiKey: process.env.CODEANY_API_KEY,
});

Model Not Found / Invalid API Type

Error: Model not found: gpt-4o

Solution: Explicitly set apiType for OpenAI models:

const agent = createAgent({
  apiType: "openai-completions",
  model: "gpt-4o",
  baseURL: "https://api.openai.com/v1",
});

MCP Server Connection Failed

Error: Failed to start MCP server

Solution: Ensure MCP server package is available and command is correct:

npm install -g @modelcontextprotocol/server-filesystem
const agent = createAgent({
  mcpServers: {
    filesystem: {
      command: "npx",
      args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
    },
  },
});

Permission Denied Errors

Permission denied: Cannot execute Bash

Solution: Adjust permission mode or allow specific tools:

const agent = createAgent({
  permissionMode: "bypassPermissions",
  allowedTools: ["Bash", "Read", "Write"],
});

Session Persistence Issues

Sessions are stored in .codeany/sessions/ by default. Ensure write permissions exist.

Rate Limiting

Implement retry logic with exponential backoff:

async function queryWithRetry(agent, prompt, maxRetries = 3) {
  for (let i = 0; i < maxRetries; i++) {
    try {
      return await agent.prompt(prompt);
    } catch (error) {
      if (error.message.includes("rate limit") && i < maxRetries - 1) {
        const delay = Math.pow(2, i) * 1000;
        console.log(`Rate limited, retrying in ${delay}ms...`);
        await new Promise(resolve => setTimeout(resolve, delay));
      } else {
        throw error;
      }
    }
  }
}

Web UI

The SDK includes a built-in web chat interface for testing:

npx tsx examples/web/server.ts

Then open http://localhost:8081 in your browser.

Complete Example: Code Review Agent

import { z } from "zod";
import { createAgent, tool, createSdkMcpServer, createHookRegistry } from "@codeany/open-agent-sdk";

// Custom tool for static analysis
const analyzeComplexity = tool(
  "analyze_complexity",
  "Analyze code complexity metrics",
  {
    filePath: z.string().describe("Path to file to analyze"),
  },
  async ({ filePath }) => {
    // Simplified complexity analysis
    return {
      content: [
        {
          type: "text",
          text: JSON.stringify({
            cyclomaticComplexity: 12,
            cognitiveComplexity: 8,
            linesOfCode: 245,
          }),
        },
      ],
    };
  }
);

const mcpServer = createSdkMcpServer({
  name: "code-analysis",
  tools: [analyzeComplexity],
});

// Hook to log all tool usage
const hooks = createHookRegistry({
  PreToolUse: [
    {
      handler: async (input) => {
        console.log(`[Review] Using tool: ${input.toolName}`);
      },
    },
  ],
  PostToolUse: [
    {
      handler: async (input) => {
        console.log(`[Review] Tool ${input.toolName} completed`);
      },
    },
  ],
});

const agent = createAgent({
  model: "claude-sonnet-4-6",
  systemPrompt: "You are an expert code reviewer. Focus on code quality, security, performance, and maintainability.",
  allowedTools: ["Read", "Glob", "Grep", "Symbols", "analyze_complexity"],
  mcpServers: { analysis: mcpServer },
  hooks,
  permissionMode: "bypassPermissions",
});

async function reviewCode(pattern) {
  console.log(`\n=== Starting code review for: ${pattern} ===\n`);
  
  const result = await agent.prompt(`
Review all files matching ${pattern}. For each file:
1. Analyze code quality and structure
2. Check for security vulnerabilities
3. Assess performance implications
4. Suggest improvements

Use analyze_complexity to get metrics.
Provide a summary with prioritized recommendations.
  `);
  
  console.log("\n=== Review Results ===\n");
  console.log(result.text);
  console.log(`\n=== Stats: ${result.num_turns} turns, ${result.usage.input_tokens + result.usage.output_tokens} tokens ===`);
}

reviewCode("src/**/*.ts").then(() => agent.close());