# Open Agent Sdk

> Build and deploy autonomous AI agents in-process using Open Agent SDK, an open-source alternative to @anthropic-ai/claude-agent-sdk that works anywhere without CLI dependencies.

- Skill: `aradotso-trending-skills/open-agent-sdk` (Agent Skill)
- Install (CLI): `npx skillmds@latest add aradotso-trending-skills/open-agent-sdk`
- Raw SKILL.md: https://api.skillmd.com/api/skills/aradotso-trending-skills/open-agent-sdk/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: aradotso (https://skillmd.com/u/aradotso-trending-skills)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/aradotso-trending-skills/open-agent-sdk

---


# Open Agent SDK

> Skill by [ara.so](https://ara.so) — Daily 2026 Skills collection.

Open Agent SDK (`@shipany/open-agent-sdk`) is a fully open-source, in-process AI agent framework for TypeScript/Node.js. It runs the complete Claude Code agent engine directly — no local CLI subprocess required — making it suitable for cloud servers, serverless functions, Docker containers, and CI/CD pipelines. It is API-compatible with `@anthropic-ai/claude-agent-sdk`.

---

## Installation

```sh
npm install @shipany/open-agent-sdk
```

Requires Node.js 18+.

---

## Authentication & Configuration

Set the Anthropic API key as an environment variable:

```sh
export ANTHROPIC_API_KEY=your-api-key
```

Or use a third-party provider (e.g. OpenRouter):

```sh
export ANTHROPIC_BASE_URL=https://openrouter.ai/api
export ANTHROPIC_API_KEY=your-openrouter-key
export ANTHROPIC_MODEL=anthropic/claude-sonnet-4-6
```

These can also be passed programmatically via `options.env` or `apiKey`/`baseURL` in `createAgent()`.

---

## Core API

### `query({ prompt, options })` — Streaming, compatible with official SDK

Returns an `AsyncGenerator<SDKMessage>`. Drop-in replacement for `@anthropic-ai/claude-agent-sdk`.

```typescript
import { query } from '@shipany/open-agent-sdk'

for await (const message of query({
  prompt: 'Find and fix the bug in auth.ts',
  options: {
    allowedTools: ['Read', 'Edit', 'Bash'],
    permissionMode: 'acceptEdits',
  },
})) {
  if (message.type === 'assistant' && message.message?.content) {
    for (const block of message.message.content) {
      if ('text' in block) process.stdout.write(block.text)
      else if ('name' in block) console.log(`\n[Tool used: ${block.name}]`)
    }
  } else if (message.type === 'result') {
    console.log(`\nDone: ${message.subtype}`)
  }
}
```

---

### `createAgent(options)` — Reusable agent with session state

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

const agent = createAgent({
  model: 'claude-sonnet-4-6',
  systemPrompt: 'You are a senior TypeScript engineer. Be concise.',
  maxTurns: 20,
})

// Blocking call
const result = await agent.prompt('Read package.json and describe the project')
console.log(result.text)
console.log(`Tokens used: ${result.usage.input_tokens + result.usage.output_tokens}`)

// Streaming call
for await (const msg of agent.query('Now add JSDoc to all exported functions')) {
  if (msg.type === 'assistant' && msg.message?.content) {
    for (const block of msg.message.content) {
      if ('text' in block) process.stdout.write(block.text)
    }
  }
}

// Session management
const history = agent.getMessages()  // full conversation history
agent.clear()                        // reset session
```

---

## Options Reference

| Option | Type | Default | Description |
|---|---|---|---|
| `model` | `string` | `claude-sonnet-4-6` | Claude model ID |
| `apiKey` | `string` | `ANTHROPIC_API_KEY` env | API key |
| `baseURL` | `string` | Anthropic API | Override for third-party providers |
| `cwd` | `string` | `process.cwd()` | Working directory for file/shell tools |
| `systemPrompt` | `string` | — | Custom system prompt prepended to agent |
| `tools` | `Tool[]` | All built-in | Override the full tool list |
| `allowedTools` | `string[]` | all | Whitelist specific tools by name |
| `permissionMode` | `string` | `bypassPermissions` | `acceptEdits`, `bypassPermissions`, `plan`, `default` |
| `maxTurns` | `number` | `100` | Maximum agentic loop iterations |
| `maxBudgetUsd` | `number` | — | Spend cap in USD |
| `mcpServers` | `object` | — | MCP server configs (stdio/SSE/HTTP) |
| `agents` | `object` | — | Named subagent definitions |
| `hooks` | `object` | — | Lifecycle hooks: `PreToolUse`, `PostToolUse`, `Stop` |
| `thinking` | `object` | — | Extended thinking config |
| `env` | `object` | — | Environment variables passed to tools |
| `resume` | `string` | — | Resume prior session by session ID |
| `canUseTool` | `function` | — | Custom permission callback `(tool, input) => boolean` |
| `includePartialMessages` | `boolean` | `false` | Emit raw streaming events |

---

## Common Patterns

### Multi-turn conversation with context

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

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

const r1 = await agent.prompt('Read src/index.ts and explain the architecture')
console.log(r1.text)

// Context from r1 is preserved automatically
const r2 = await agent.prompt('Refactor the error handling to use a Result type')
console.log(r2.text)
```

### Restrict to read-only tools

```typescript
import { query } from '@shipany/open-agent-sdk'

for await (const message of query({
  prompt: 'Review this codebase for security issues',
  options: {
    allowedTools: ['Read', 'Glob', 'Grep'],
    // No Write, Edit, or Bash — agent cannot modify files
  },
})) {
  if (message.type === 'result') console.log('Review complete')
}
```

### Custom tools

```typescript
import { createAgent, getAllBaseTools } from '@shipany/open-agent-sdk'

const dbQueryTool = {
  name: 'QueryDatabase',
  description: 'Run a read-only SQL query and return results as JSON',
  inputJSONSchema: {
    type: 'object',
    properties: {
      sql: { type: 'string', description: 'The SQL query to run' },
    },
    required: ['sql'],
  },
  get inputSchema() {
    return { safeParse: (v: unknown) => ({ success: true, data: v }) }
  },
  async prompt() { return this.description },
  async call(input: { sql: string }) {
    // Replace with your actual DB client
    const rows = [{ id: 1, name: 'Example' }]
    return { data: JSON.stringify(rows) }
  },
  userFacingName: () => 'QueryDatabase',
  isReadOnly: () => true,
  isConcurrencySafe: () => true,
  mapToolResultToToolResultBlockParam: (data: string, id: string) => ({
    type: 'tool_result' as const,
    tool_use_id: id,
    content: data,
  }),
}

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

const result = await agent.prompt('How many users signed up in the last 7 days?')
console.log(result.text)
```

### MCP server integration

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

const agent = createAgent({
  mcpServers: {
    filesystem: {
      command: 'npx',
      args: ['-y', '@modelcontextprotocol/server-filesystem', '/tmp'],
    },
    playwright: {
      command: 'npx',
      args: ['@playwright/mcp@latest'],
    },
  },
})

const result = await agent.prompt('List all .json files in /tmp')
console.log(result.text)
```

### Subagents for parallel / delegated work

```typescript
import { query } from '@shipany/open-agent-sdk'

for await (const message of query({
  prompt: 'Use the security-auditor agent to audit src/ for vulnerabilities',
  options: {
    allowedTools: ['Read', 'Glob', 'Grep', 'Agent'],
    agents: {
      'security-auditor': {
        description: 'Expert security auditor for TypeScript codebases.',
        prompt: 'Identify OWASP Top 10 vulnerabilities and suggest fixes.',
        tools: ['Read', 'Glob', 'Grep'],
      },
    },
  },
})) {
  if (message.type === 'assistant' && message.message?.content) {
    for (const block of message.message.content) {
      if ('text' in block) console.log(block.text)
    }
  }
}
```

### Custom permission callback

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

const agent = createAgent({
  canUseTool: (toolName: string, input: unknown) => {
    // Prevent deletion commands
    if (toolName === 'Bash') {
      const cmd = (input as { command?: string }).command ?? ''
      if (cmd.includes('rm ') || cmd.includes('drop table')) return false
    }
    return true
  },
})
```

### Lifecycle hooks

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

const agent = createAgent({
  hooks: {
    PreToolUse: async ({ tool, input }) => {
      console.log(`About to run tool: ${tool} with input:`, input)
    },
    PostToolUse: async ({ tool, output }) => {
      console.log(`Tool ${tool} finished`)
    },
    Stop: async ({ result }) => {
      console.log('Agent stopped. Final result:', result)
    },
  },
})
```

### Resume a previous session

```typescript
import { createAgent } from '@shipany/open-agent-sdk'

// First session
const agent1 = createAgent({ model: 'claude-sonnet-4-6' })
const r1 = await agent1.prompt('Read ARCHITECTURE.md')
const sessionId = r1.sessionId  // save this

// Later — resume where you left off
const agent2 = createAgent({
  model: 'claude-sonnet-4-6',
  resume: sessionId,
})
const r2 = await agent2.prompt('Now implement the TODO in section 3')
```

---

## Built-in Tools Reference

| Tool | Read-only | Description |
|---|---|---|
| `Read` | ✅ | Read files, images, PDFs with line numbers |
| `Glob` | ✅ | Find files by glob pattern |
| `Grep` | ✅ | Search file contents with regex (uses ripgrep) |
| `WebFetch` | ✅ | Fetch and parse web pages |
| `WebSearch` | ✅ | Web search |
| `Write` | ❌ | Create or overwrite files |
| `Edit` | ❌ | Precise string replacement in files |
| `Bash` | ❌ | Execute shell commands |
| `Agent` | — | Spawn subagents |
| `TodoWrite` | ❌ | Manage todo lists |
| `NotebookEdit` | ❌ | Edit Jupyter notebooks |
| `TaskCreate/Update/List` | — | Task management |
| `TeamCreate/Delete` | — | Agent team management |
| `EnterPlanMode/ExitPlanMode` | — | Plan approval workflow |
| `EnterWorktree/ExitWorktree` | — | Git worktree isolation |
| `ListMcpResources/ReadMcpResource` | ✅ | MCP resource access |

---

## Architecture: How It Differs from Official SDK

**Official `@anthropic-ai/claude-agent-sdk`:**
```
Your code → SDK → spawn cli.js subprocess → stdin/stdout JSON → Anthropic API
```

**Open Agent SDK:**
```
Your code → SDK → QueryEngine (in-process) → Anthropic API (direct HTTP)
```

This means:
- No CLI installation required in the deployment environment
- Works in serverless (AWS Lambda, Vercel, Cloudflare Workers with Node.js compat)
- Works in Docker with just `npm install`
- Works in CI/CD without CLI setup steps
- Programmatic access to the full agent engine

---

## Troubleshooting

**`Error: ANTHROPIC_API_KEY is not set`**
→ Export the env var or pass `apiKey` directly in `createAgent({ apiKey: process.env.MY_KEY })`.

**Agent exceeds `maxTurns` without completing**
→ Increase `maxTurns` or narrow the task. Check `message.subtype === 'max_turns'` in the result.

**Tool not found / `allowedTools` not working**
→ Tool names are case-sensitive: `'Read'`, `'Edit'`, `'Bash'`, `'Glob'`, `'Grep'`, `'WebFetch'`, etc.

**Using with OpenRouter or other providers**
→ Set `ANTHROPIC_BASE_URL` to the provider's base URL and use their model string format, e.g. `anthropic/claude-sonnet-4-6` for OpenRouter.

**Agent modifies files unexpectedly**
→ Use `allowedTools: ['Read', 'Glob', 'Grep']` to restrict to read-only tools, or set `permissionMode: 'plan'` to require approval before edits.

**MCP server fails to start**
→ Ensure the MCP server package is installed or accessible via `npx`. Check `command` and `args` match what the MCP package expects.

**TypeScript types missing**
→ The package ships its own types. Ensure `"moduleResolution": "bundler"` or `"node16"` in `tsconfig.json` and `"esModuleInterop": true`.

---

## Quick Reference

```typescript
// Minimal one-shot agent
import { createAgent } from '@shipany/open-agent-sdk'
const agent = createAgent({ model: 'claude-sonnet-4-6' })
const { text } = await agent.prompt('Summarize README.md in 3 bullet points')
console.log(text)
```

