| name | open-agent-sdk-typescript |
| description | Build AI agents with in-process agent loops using Anthropic or OpenAI APIs, custom tools, MCP servers, and multi-turn conversations |
| triggers | ["create an AI agent with open-agent-sdk","set up agent with custom tools and MCP","build a conversational agent loop","use open-agent-sdk with OpenAI or Anthropic","implement agent skills and subagents","add lifecycle hooks to my agent","create streaming agent queries","integrate MCP servers with my agent"] |
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}`);
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" }) => {
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 }) => {
{
result = ()();
{
: [{ : , : }],
};
} (error) {
{
: [{ : , : }],
: ,
};
}
}
);
server = ({
: ,
: [getWeather, calculator],
});
( msg ({
: ,
: { : { : server } },
})) {
(msg. === ) {
( block msg..) {
( block) .(block.);
}
}
}
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) {
if (!input.readonly && /^\s*(SELECT|SHOW|DESCRIBE)/i.test(input.query)) {
return "Error: Use readonly=true for SELECT queries";
}
return JSON.stringify({
rows: [{ id: 1, name: "Example" }],
rowCount: 1,
});
},
});
agent = ({
: [...(), databaseQuery],
});
result = agent.(
);
.(result.);
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";
const readOnlyAgent = createAgent({
allowedTools: ["Read", "Glob", "Grep", "Search"],
permissionMode: "dontAsk",
});
const noShellAgent = createAgent({
disallowedTools: ["Bash", "BashSession"],
});
const minimalAgent = createAgent({
tools: [],
});
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();
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.(result.);
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();
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();
.(result.);
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);
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.(, input.);
},
},
],
: [
{
: () => {
.();
},
},
],
: [
{
: () => {
.();
},
},
],
: [
{
: (input) => {
.();
},
},
],
});
agent = ({
hooks,
: ,
});
result = agent.();
.(result.);
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";
const autoAgent = createAgent({
permissionMode: "bypassPermissions",
});
const denyAgent = createAgent({
permissionMode: "dontAsk",
});
const askAgent = createAgent({
permissionMode: "ask",
});
autoAgent.setPermissionMode("dontAsk");
Configuration Options
Agent Options
import { createAgent } from "@codeany/open-agent-sdk";
const agent = createAgent({
apiType: "anthropic-messages",
model: "claude-sonnet-4-6",
apiKey: process.env.CODEANY_API_KEY,
baseURL: "https://api.anthropic.com",
sessionId: "my-session-id",
maxTurns: 10,
cwd: process.cwd(),
systemPrompt: "You are a helpful coding assistant",
appendSystemPrompt: "\n\nAlways explain your reasoning.",
tools: [],
allowedTools: ["Read", "Write", "Glob"],
disallowedTools: ["Bash"],
permissionMode: "bypassPermissions",
mcpServers: {},
agents: {},
hooks: createHookRegistry({}),
: ,
: ,
});
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,
},
})) {
}
Advanced Patterns
Session Management
import { createAgent, listSessions, forkSession } from "@codeany/open-agent-sdk";
const agent = createAgent({
sessionId: "my-project-session",
});
await agent.prompt("Analyze the codebase structure");
await agent.prompt("Now suggest improvements");
const sessions = await listSessions();
console.log("Saved sessions:", sessions);
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();
const queryPromise = agent.prompt("Analyze all files in the project");
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");
agent.setModel("claude-opus-4");
await agent.prompt("Perform deep architectural analysis");
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 :
.();
.();
.();
;
:
.();
;
}
}
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";
const analyzeComplexity = tool(
"analyze_complexity",
"Analyze code complexity metrics",
{
filePath: z.string().describe("Path to file to analyze"),
},
async ({ filePath }) => {
return {
content: [
{
type: "text",
text: JSON.stringify({
cyclomaticComplexity: 12,
cognitiveComplexity: 8,
linesOfCode: 245,
}),
},
],
};
}
);
const mcpServer = createSdkMcpServer({
name: "code-analysis",
tools: [analyzeComplexity],
});
const hooks = createHookRegistry({
PreToolUse: [
{
handler: async (input) => {
console.log(`[Review] Using tool: ${input.toolName}`);
},
},
],
: [
{
: (input) => {
.();
},
},
],
});
agent = ({
: ,
: ,
: [, , , , ],
: { : mcpServer },
hooks,
: ,
});
() {
.();
result = agent.();
.();
.(result.);
.();
}
().( agent.());