Skip to content

Latest commit

History

8 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

Open Agent SDK (Python)

PyPI versionPythonLicense: MIT

Open-source Agent SDK that runs the full agent loop in-process — no subprocess or CLI required. Deploy anywhere: cloud, serverless, Docker, CI/CD.

Also available in TypeScript: open-agent-sdk-typescript · Go: open-agent-sdk-go

Features

  • Multi-Provider — Anthropic + OpenAI-compatible APIs (DeepSeek, Qwen, vLLM, Ollama) via unified provider abstraction
  • Agent Loop — Streaming agentic loop with tool execution, multi-turn conversations, and cost tracking
  • 35 Built-in Tools — Bash, Read, Write, Edit, Glob, Grep, WebFetch, WebSearch, Agent (subagents), Skill, and more
  • Skill System — Reusable prompt templates with 5 bundled skills (commit, review, debug, simplify, test)
  • MCP Support — Connect to MCP servers via stdio, HTTP, and SSE transports
  • Permission System — Configurable tool approval with allow/deny rules and custom callbacks
  • Hook System — 20 lifecycle events for agent behavior interception
  • Session Persistence — Save/load/fork conversation sessions
  • Custom Tools — Define tools with Pydantic models or raw JSON schemas
  • Extended Thinking — Claude thinking budget configuration
  • Cost Tracking — Per-model token usage with accurate pricing (Anthropic + OpenAI + DeepSeek + Qwen)

Get started

pip install open-agent-sdk

Set your API key:

export CODEANY_API_KEY=your-api-key

Third-party providers (e.g. OpenRouter) are supported via CODEANY_BASE_URL:

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

Quick start

One-shot query (streaming)

importasynciofromopen_agent_sdkimportquery, AgentOptions, SDKMessageTypeasyncdefmain():
asyncformessageinquery(
prompt="Read pyproject.toml and tell me the project name.",
options=AgentOptions(
allowed_tools=["Read", "Glob"],
permission_mode="bypassPermissions",
),
):
ifmessage.type==SDKMessageType.ASSISTANT:
print(message.text)
asyncio.run(main())

Simple blocking prompt

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptionsasyncdefmain():
agent=create_agent(AgentOptions(model="claude-sonnet-4-5"))
result=awaitagent.prompt("What files are in this project?")
print(result.text)
print(f"Turns: {result.num_turns}, Tokens: {result.usage.input_tokens+result.usage.output_tokens}")
awaitagent.close()
asyncio.run(main())

Multi-turn conversation

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptionsasyncdefmain():
agent=create_agent(AgentOptions(max_turns=5))
r1=awaitagent.prompt('Create a file /tmp/hello.txt with "Hello World"')
print(r1.text)
r2=awaitagent.prompt("Read back the file you just created")
print(r2.text)
print(f"Session messages: {len(agent.get_messages())}")
awaitagent.close()
asyncio.run(main())

OpenAI-compatible models

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptionsasyncdefmain():
# Auto-detects openai-completions from model prefixagent=create_agent(AgentOptions(
model="gpt-4o",
api_key="sk-...",
))
print(f"API type: {agent.get_api_type()}") # openai-completionsresult=awaitagent.prompt("What is 2+2?")
print(result.text)
awaitagent.close()
# DeepSeek, Qwen, etc.agent2=create_agent(AgentOptions(
model="deepseek-chat",
api_key="sk-...",
base_url="https://api.deepseek.com/v1",
))
# Or explicit api_typeagent3=create_agent(AgentOptions(
api_type="openai-completions",
model="my-custom-model",
base_url="http://localhost:8000/v1",
))
asyncio.run(main())

Custom tools (Pydantic schema)

importasynciofrompydanticimportBaseModelfromopen_agent_sdkimportquery, create_sdk_mcp_server, AgentOptions, SDKMessageTypefromopen_agent_sdk.tool_helperimporttool, CallToolResultclassCityInput(BaseModel):
city: strasyncdefget_weather_handler(input: CityInput, ctx):
returnCallToolResult(
content=[{"type": "text", "text": f"{input.city}: 22°C, sunny"}]
)
get_weather=tool("get_weather", "Get the temperature for a city", CityInput, get_weather_handler)
server=create_sdk_mcp_server("weather", tools=[get_weather])
asyncdefmain():
asyncformsginquery(
prompt="What is the weather in Tokyo?",
options=AgentOptions(mcp_servers={"weather": server}),
):
ifmsg.type==SDKMessageType.RESULT:
print(f"Done: ${msg.total_cost:.4f}")
asyncio.run(main())

Custom tools (low-level)

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptionsfromopen_agent_sdk.tool_helperimportdefine_toolfromopen_agent_sdk.typesimportToolResult, ToolContextasyncdefcalc_handler(input: dict, ctx: ToolContext) ->ToolResult:
result=eval(input["expression"], {"__builtins__": {}})
returnToolResult(tool_use_id="", content=f"{input['expression']} = {result}")
calculator=define_tool(
name="Calculator",
description="Evaluate a math expression",
input_schema={
"properties": {"expression": {"type": "string"}},
"required": ["expression"],
},
handler=calc_handler,
read_only=True,
)
asyncdefmain():
agent=create_agent(AgentOptions(tools=[calculator]))
r=awaitagent.prompt("Calculate 2**10 * 3")
print(r.text)
awaitagent.close()
asyncio.run(main())

Skills

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptions, SDKMessageTypefromopen_agent_sdk.skillsimportregister_skill, get_all_skills, init_bundled_skills, SkillDefinitionfromopen_agent_sdk.typesimportToolContextasyncdefmain():
# 5 bundled skills are auto-initialized: commit, review, debug, simplify, testinit_bundled_skills()
print(f"Skills: {[s.nameforsinget_all_skills()]}")
# Register a custom skillasyncdefexplain_prompt(args, ctx):
return [{"type": "text", "text": f"Explain simply: {args}"}]
register_skill(SkillDefinition(
name="explain", description="Explain a concept simply",
aliases=["eli5"], user_invocable=True, get_prompt=explain_prompt,
))
# Agent can invoke skills via the Skill toolagent=create_agent(AgentOptions(max_turns=5))
result=awaitagent.prompt('Use the "explain" skill to explain git rebase')
print(result.text)
awaitagent.close()
asyncio.run(main())

MCP server integration

importasynciofromopen_agent_sdkimportcreate_agent, AgentOptions, McpStdioConfigasyncdefmain():
agent=create_agent(AgentOptions(
mcp_servers={
"filesystem": McpStdioConfig(
command="npx",
args=["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
),
},
))
result=awaitagent.prompt("List files in /tmp")
print(result.text)
awaitagent.close()
asyncio.run(main())

Subagents

importasynciofromopen_agent_sdkimportquery, AgentOptions, AgentDefinition, SDKMessageTypeasyncdefmain():
asyncformsginquery(
prompt="Use the code-reviewer agent to review src/",
options=AgentOptions(
agents={
"code-reviewer": AgentDefinition(
description="Expert code reviewer",
prompt="Analyze code quality. Focus on security and performance.",
tools=["Read", "Glob", "Grep"],
),
},
),
):
ifmsg.type==SDKMessageType.RESULT:
print("Done")
asyncio.run(main())

Permissions

importasynciofromopen_agent_sdkimportquery, AgentOptions, SDKMessageTypeasyncdefmain():
# Read-only agent — can only analyze, not modifyasyncformsginquery(
prompt="Review the code in src/ for best practices.",
options=AgentOptions(
allowed_tools=["Read", "Glob", "Grep"],
permission_mode="dontAsk",
),
):
passasyncio.run(main())

Web UI

A built-in web chat interface is included for testing:

python examples/web/server.py
# Open http://localhost:8083

API reference

Top-level functions

FunctionDescription
query(prompt, options)One-shot streaming query, returns AsyncGenerator
create_agent(options)Create a reusable agent with session persistence
tool(name, desc, model, handler)Create a tool with Pydantic schema validation
define_tool(name, ...)Low-level tool definition helper
create_sdk_mcp_server(name, tools)Bundle tools into an in-process MCP server
create_provider(api_type, ...)Create LLM provider (Anthropic or OpenAI)
get_all_base_tools()Get all 35 built-in tools
register_skill(definition)Register a custom skill
get_all_skills()List all registered skills
init_bundled_skills()Initialize 5 bundled skills
list_sessions()List persisted sessions
get_session_messages(id)Retrieve messages from a session
fork_session(id)Fork a session for branching

Agent methods

MethodDescription
await agent.query(prompt)Streaming query, returns AsyncGenerator[SDKMessage]
await agent.prompt(text)Blocking query, returns QueryResult
agent.get_messages()Get conversation history
agent.get_api_type()Get resolved API type (anthropic-messages / openai-completions)
agent.clear()Reset session
await agent.interrupt()Abort current query
await agent.set_model(model)Change model mid-session
await agent.set_permission_mode(mode)Change permission mode
await agent.close()Close MCP connections, persist session

Options (AgentOptions)

OptionTypeDefaultDescription
modelstrclaude-sonnet-4-5LLM model ID (or set CODEANY_MODEL env var)
api_typestrautoanthropic-messages or openai-completions (auto-detected from model)
api_keystrCODEANY_API_KEYAPI key
base_urlstrCustom API endpoint
cwdstros.getcwd()Working directory
system_promptstrSystem prompt override
append_system_promptstrAppend to default system prompt
toolslist[BaseTool]All built-inAdditional custom tools
allowed_toolslist[str]Tool allow-list
disallowed_toolslist[str]Tool deny-list
permission_modePermissionModebypassPermissionsdefault / acceptEdits / dontAsk / bypassPermissions / plan
can_use_toolCanUseToolFnCustom permission callback
max_turnsint10Max agentic turns
max_budget_usdfloatSpending cap
max_tokensint16000Max output tokens
thinkingThinkingConfigExtended thinking
mcp_serversdict[str, McpServerConfig]MCP server connections
agentsdict[str, AgentDefinition]Subagent definitions
hooksdict[str, list[dict]]Lifecycle hooks
resumestrResume session by ID
continue_sessionboolFalseContinue most recent session
persist_sessionboolFalsePersist session to disk
session_idstrautoExplicit session ID
json_schemadictStructured output
sandboxboolFalseFilesystem/network sandbox
envdict[str, str]Environment variables
debugboolFalseEnable debug output

Environment variables

VariableDescription
CODEANY_API_KEYAPI key (required)
CODEANY_MODELDefault model override
CODEANY_BASE_URLCustom API endpoint
CODEANY_API_TYPEanthropic-messages or openai-completions

Multi-provider support

The SDK uses a unified provider abstraction. Internally all messages use Anthropic format as the canonical representation. The provider layer handles conversion automatically:

Your Code → Agent → QueryEngine → Provider Layer → LLM API
│
┌──────────────┴──────────────┐
│ AnthropicProvider │
│ Direct pass-through │
├─────────────────────────────┤
│ OpenAIProvider │
│ Anthropic ↔ OpenAI format │
└─────────────────────────────┘

Message format conversion (OpenAI provider):

Anthropic (internal)OpenAI (wire)
system prompt string{"role": "system", "content": "..."}
tool_use content blocktool_calls[].function
tool_result content block{"role": "tool", "tool_call_id": "..."}
stop_reason: "end_turn"finish_reason: "stop"
stop_reason: "tool_use"finish_reason: "tool_calls"
stop_reason: "max_tokens"finish_reason: "length"

Auto-detection: Models starting with gpt-, deepseek-, qwen-, o1-, o3-, o4- automatically use openai-completions. Override with api_type option or CODEANY_API_TYPE env var.

Built-in tools

ToolDescription
BashExecute shell commands
ReadRead files with line numbers
WriteCreate / overwrite files
EditPrecise string replacement in files
GlobFind files by pattern
GrepSearch file contents with regex
WebFetchFetch and parse web content
WebSearchSearch the web
NotebookEditEdit Jupyter notebook cells
AgentSpawn subagents for parallel work
SkillInvoke registered skills by name
TaskCreate/List/Update/Get/Stop/OutputTask management system
TeamCreate/DeleteMulti-agent team coordination
SendMessageInter-agent messaging
EnterWorktree/ExitWorktreeGit worktree isolation
EnterPlanMode/ExitPlanModeStructured planning workflow
AskUserQuestionAsk the user for input
ToolSearchDiscover lazy-loaded tools
ListMcpResources/ReadMcpResourceMCP resource access
CronCreate/Delete/ListScheduled task management
RemoteTriggerRemote agent triggers
LSPLanguage Server Protocol (code intelligence)
ConfigDynamic configuration
TodoWriteSession todo list

Bundled skills

SkillAliasesDescription
commitciCreate git commit with well-crafted message
reviewreview-pr, crReview code changes for correctness, security, style
debuginvestigate, diagnoseSystematic debugging with structured investigation
simplifyReview changed code for reuse, quality, efficiency
testrun-testsRun tests and analyze/fix failures

Architecture

┌──────────────────────────────────────────────────────┐
│ Your Application │
│ │
│ from open_agent_sdk import create_agent │
└────────────────────────┬─────────────────────────────┘
│
┌──────────▼──────────┐
│ Agent │ Session state, tool pool,
│ query() / prompt() │ MCP connections, skills
└──────────┬──────────┘
│
┌──────────▼──────────┐
│ QueryEngine │ Agentic loop:
│ submit_message() │ API call → tools → repeat
└──────────┬──────────┘
│
┌───────────────┼───────────────┐
│ │ │
┌─────▼─────┐ ┌─────▼─────┐ ┌─────▼─────┐
│ Providers │ │ 35 Tools │ │ MCP │
│ Anthropic │ │ Bash,Read │ │ Servers │
│ OpenAI │ │ Edit,... │ │ stdio/SSE/ │
│ DeepSeek │ │ Skill,... │ │ HTTP/SDK │
└───────────┘ └───────────┘ └───────────┘

Key internals:

ComponentDescription
Provider layerAnthropic + OpenAI-compatible (DeepSeek, Qwen, vLLM, Ollama)
QueryEngineCore agentic loop with auto-compact, retry, tool orchestration
Skill system5 bundled skills (commit, review, debug, simplify, test) + custom
Auto-compactSummarizes conversation when context window fills up
Micro-compactTruncates oversized tool results
RetryExponential backoff for rate limits and transient errors
Token estimationRough token counting for budget and compaction thresholds
File cacheLRU cache for file reads
Hook system20 lifecycle events (PreToolUse, PostToolUse, SessionStart, ...)
Session storagePersist / resume / fork sessions on disk
Context injectionGit status + AGENT.md automatically injected into system prompt

Examples

#FileDescription
01examples/01_simple_query.pyStreaming query with event handling
02examples/02_multi_tool.pyMulti-tool orchestration (Glob + Bash)
03examples/03_multi_turn.pyMulti-turn session persistence
04examples/04_prompt_api.pyBlocking prompt() API
05examples/05_custom_system_prompt.pyCustom system prompt
06examples/06_mcp_server.pyMCP server integration
07examples/07_custom_tools.pyCustom tools with define_tool()
08examples/08_official_api_compat.pyquery() API pattern
09examples/09_subagents.pySubagent delegation
10examples/10_permissions.pyRead-only agent with tool restrictions
11examples/11_custom_mcp_tools.pytool() + create_sdk_mcp_server()
12examples/12_skills.pySkill system usage (register, invoke, list)
13examples/13_hooks.pyLifecycle hook configuration and execution
14examples/14_openai_compat.pyOpenAI/compatible model support (DeepSeek, etc.)
webexamples/web/Web chat UI for testing

Run any example:

python examples/01_simple_query.py

Start the web UI:

python examples/web/server.py
# Open http://localhost:8083

Project structure

open-agent-sdk-python/
├── src/open_agent_sdk/
│ ├── __init__.py # Public exports
│ ├── agent.py # Agent high-level API
│ ├── engine.py # QueryEngine agentic loop
│ ├── types.py # Core type definitions
│ ├── session.py # Session persistence
│ ├── hooks.py # Hook system (20 lifecycle events)
│ ├── tool_helper.py # Pydantic-based tool creation
│ ├── sdk_mcp_server.py # In-process MCP server factory
│ ├── providers/
│ │ ├── types.py # LLMProvider interface
│ │ ├── anthropic_provider.py # Anthropic implementation
│ │ ├── openai_provider.py # OpenAI-compatible (no SDK dependency)
│ │ └── factory.py # create_provider() factory
│ ├── skills/
│ │ ├── types.py # SkillDefinition, SkillResult
│ │ ├── registry.py # Skill registry (register, lookup, format)
│ │ └── bundled/ # 5 bundled skills (commit, review, debug, simplify, test)
│ ├── mcp/
│ │ └── client.py # MCP client (stdio/SSE/HTTP)
│ ├── tools/ # 35 built-in tools
│ │ ├── bash.py, read.py, write.py, edit.py
│ │ ├── glob_tool.py, grep.py, web_fetch.py, web_search.py
│ │ ├── agent_tool.py, skill_tool.py, send_message.py
│ │ ├── task_tools.py, team_tools.py, worktree_tools.py
│ │ ├── plan_tools.py, cron_tools.py, lsp_tool.py
│ │ └── config_tool.py, todo_tool.py, ...
│ └── utils/
│ ├── messages.py # Message creation & normalization
│ ├── tokens.py # Token estimation & cost (Anthropic + OpenAI + DeepSeek + Qwen)
│ ├── compact.py # Auto-compaction logic
│ ├── retry.py # Exponential backoff retry
│ ├── context.py # Git & project context injection
│ └── file_cache.py # LRU file state cache
├── tests/ # 265 tests
├── examples/ # 14 examples + web UI
└── pyproject.toml

Links

License

MIT

About

Open-source Agent SDK for Python. Runs the full agent loop in-process — no CLI required.

Resources

Stars

44 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages