# OpenAI Agents SDK vs Claude Agent SDK

> OpenAI Agents SDK vs Claude Agent SDK is a practical comparison of abstractions, tools, approvals, observability, lock-in, and team fit.

The OpenAI Agents SDK and Claude Agent SDK both run tool-using loops, preserve conversation state, support MCP, and let a person approve risky actions. That surface similarity causes bad buying decisions. The meaningful difference is where each SDK expects the work to happen and which runtime assumptions become part of your product.

OpenAI's design starts with an application agent: instructions, typed tools, handoffs, guardrails, sessions, and a runner. Claude's design starts with the Claude Code execution harness: a model operating built-in file, shell, search, and editing tools, with hooks, permissions, project settings, skills, and subagents around it. Choose OpenAI when you want to compose a product workflow from explicit primitives. Choose Claude when the job resembles an autonomous operator working inside a real workspace. If neither description fits, use a lower-level model API and keep the loop yourself.

## The SDK names hide different products

The first comparison to make is not OpenAI versus Anthropic. It is application orchestration versus an embedded computer-work harness.

The OpenAI Agents SDK documentation calls its core set deliberately small: agents, agents as tools or handoffs, and guardrails. `Runner.run()` owns the turn loop, calls tools, sends results back to the model, and stops when the run produces final output. Sessions add memory. Tracing records generations, tool calls, guardrails, and handoffs. This is a general application framework even though its default model path uses OpenAI's Responses API.

The Claude Agent SDK overview says it gives an application the same tools, agent loop, and context management that power Claude Code. That sentence deserves more weight than most feature tables give it. The SDK is not the normal Anthropic API client with a convenience loop. It wraps an opinionated operator that already knows how to read and edit files, run commands, search, manage context, and draw on Claude Code configuration.

Anthropic's own comparison separates the Agent SDK from its Client SDK and Managed Agents. The Client SDK is for direct Messages API calls when you implement the loop. Managed Agents is a hosted product for long-running work when Anthropic manages session and sandbox infrastructure. The Agent SDK runs in your process and leaves hosting and isolation with you. Mixing those three products in one evaluation produces nonsense conclusions about deployment and lock-in.

The practical test is simple. Describe the unit of work without mentioning a model. "Route a support request, call our refund function, and return a typed decision" fits an application agent. "Inspect this repository, run tests, edit the broken files, and report what changed" fits a workspace operator. Both SDKs can be pushed into the other shape, but the amount of custom glue tells you which abstraction you are fighting.

Language support will not settle the comparison for most teams. Both projects publish Python and TypeScript libraries. The important question is which runtime will own the long-lived work. A server request may tolerate an async runner but not a child process with repository access. A worker queue may suit a workspace agent but need explicit cancellation, cleanup, and concurrency limits. Decide the process model before writing the proof of concept, because moving an agent from a request handler into an isolated worker later changes session storage, streaming, approvals, and error recovery at once.

## OpenAI gives you explicit workflow primitives

OpenAI's abstractions make control flow visible in application code. An `Agent` holds instructions, tools, optional structured output, model settings, guardrails, and possible handoffs. A `Runner` executes it. A specialist can become a tool under a manager, or a handoff can transfer the active conversation to that specialist.

That manager versus handoff distinction is useful and often blurred. With an agent-as-tool, the manager retains control, receives the specialist's result, and owns the final response. With a handoff, the specialist becomes the active agent for the rest of the turn. The consequence appears in policy placement: a manager gives you one place to apply shared output rules, while a handoff lets the specialist speak directly but moves final-output responsibility with it.

Guardrails are also specific rather than magical. The OpenAI documentation states that input guardrails run only on the first agent in a chain and output guardrails run only on the final agent. Tool guardrails wrap each custom function-tool invocation, but do not automatically cover handoffs or every hosted tool. A team that reads "guardrails supported" and assumes every boundary is protected will ship a gap. Place authorization in the tool implementation even when a model-facing guardrail also exists.

Typed tools are a strong fit for ordinary product backends. In Python, a decorated function can produce its schema from annotations and validate inputs through Pydantic. In TypeScript, Zod or JSON Schema describes the parameters. This keeps the agent layer close to the same service functions the rest of the application calls.

The SDK does not force every agent to use an OpenAI model. Python exposes model-provider integration points and beta adapters for mixed providers. The official models guide warns that feature parity does not follow the adapter: structured output, hosted search, multimodal input, and Responses-specific behavior may disappear on another backend. I agree with the warning and would go further. A model adapter reduces transport lock-in; it does not make provider semantics portable.

Use this design when you want reviewers to see a short graph of named agents and tools in normal code. It is especially clean for request-response products, service workflows, voice agents, and routing systems where each side effect already belongs behind an application API.

## Claude gives you an operator with a workspace

Claude's abstractions make a working environment the center of the run. A simple `query()` handles a one-off job. `ClaudeSDKClient` supports a bidirectional session with follow-up messages, streaming, interruption, and resume behavior. `ClaudeAgentOptions` configures tools, system prompts, permission behavior, MCP servers, hooks, subagents, budgets, and settings sources.

The built-in tool set changes the economics of the first prototype. A repository agent needs file reads, searches, edits, shell commands, and usually web access. Claude exposes that working vocabulary without making you wrap every filesystem action as a business function. The model and harness have also been designed together around tasks where the agent must inspect an unfamiliar workspace and decide which action comes next.

That convenience carries an operational bill. The SDK runs the Claude Code loop in your process, and the Python package documentation exposes errors for a missing CLI, connection failures, process failures, and malformed CLI JSON. The package can bundle a compatible CLI, but you still operate a subprocess-shaped runtime with a working directory, environment, permissions, and command execution. A container that safely runs a web API may not be an adequate sandbox for an agent that can invoke a shell.

Claude's subagents are closer to delegated workspace workers than OpenAI handoffs. You define specialists with prompts and tool access, then the main agent can assign focused tasks. Skills, slash commands, memory files, project configuration, and plugins can also enter the runtime. That is powerful for internal engineering automation because the agent can share the conventions developers already use with Claude Code.

It is also a hidden-dependency risk. If production behavior depends on a home-directory skill, an unpinned plugin, or a developer's local settings, your test fixture does not describe the deployed agent. Set `setting_sources` deliberately, package required project configuration, and start production workers with a clean home directory. Treat every loaded prompt, hook, and tool definition as code that needs versioning.

Use Claude when the job naturally asks an agent to explore and change an environment: code maintenance, incident investigation, data-file repair, migration work, or multi-step operations in a controlled workspace. For a three-function customer workflow, the same machinery can be unnecessary weight.

## Tool count matters less than tool provenance

Both SDKs support local functions and MCP, so a checklist that awards each one a point for "custom tools" and "MCP" misses the decision. You need to know where a tool executes, who validates it, who approves it, and what evidence survives afterward.

OpenAI offers several tool paths. Application function tools run in your code. Agents can call other agents as tools. The Responses-backed path can expose hosted tools, and the SDK can connect to MCP servers through local transports or let the Responses API call a publicly reachable MCP server. This range is useful, but the trust boundary changes between paths. A local function can use your in-process identity. A hosted tool sends work to provider infrastructure. A remote MCP server brings its own authentication and tenancy rules.

Claude starts with built-in operator tools, then adds custom tools and MCP. In-process custom tools can be exposed through an SDK MCP server. External MCP servers can arrive through standard transports. Skills and plugins can package instructions and capabilities around those tools. This makes Claude's ecosystem feel broad quickly because the workspace operations already exist before you write a function.

MCP improves tool portability only at the protocol boundary. A tool named `create_invoice` with a JSON schema can move between harnesses more easily than a Python decorator or a TypeScript callback. The surrounding behavior does not move automatically: OAuth storage, approval rules, retries, sampling, resource access, elicitation, logging, and error presentation still depend on the client and deployment.

Keep provider-neutral tools in a separate layer and make the SDK adapter thin. This small contract is enough to expose the part that matters:

```python
from dataclasses import dataclass
from typing import Any, Protocol

@dataclass
class ToolCall:
    call_id: str
    name: str
    arguments: dict[str, Any]
    actor_id: str
    tenant_id: str

@dataclass
class ToolResult:
    call_id: str
    content: str
    is_error: bool = False

class ToolExecutor(Protocol):
    async def execute(self, call: ToolCall) -> ToolResult: ...
```

Put authorization, idempotency, timeout policy, and audit recording behind `ToolExecutor`. Then adapt OpenAI function calls or Claude custom-tool events into `ToolCall`. Do not let either SDK invent tenant identity from conversation text. The authenticated application supplies `actor_id` and `tenant_id`, and the executor rejects mismatches before touching a side effect.

## Approval is a durable state problem

Human approval is safe only when the run can pause, survive a process restart, bind the decision to the exact action, and resume without repeating an earlier side effect. A modal with Allow and Deny buttons covers only the visible part.

OpenAI tools can declare that they need approval. A run then surfaces interruptions, and serialized `RunState` can preserve pending work for later resumption. The official human-in-the-loop guide notes that serialized state includes tool input, usage, nested agent state, trace metadata, and application context. That is operationally useful and a data-handling warning. Do not put secrets in context unless your persistence policy explicitly covers them.

Claude provides permission modes, allowed and disallowed tools, a `can_use_tool` callback for application decisions, and lifecycle hooks around tool use. Interactive clients can receive a permission request, ask a user, and continue the session. Hooks can inspect, block, or modify behavior at named events. This maps naturally to a workspace where reading a file, editing it, and running a command need different policies.

Neither mechanism replaces authorization inside the tool. Model-visible tool names are hints, not identities. Prompt instructions are not access control. A hook that checks a path string is not a filesystem sandbox. If a shell runs with broad host permissions, a policy around the nominal file-edit tool does not constrain what the shell command can reach.

A production approval record should contain at least these fields:

```json
{"run_id":"run_123","call_id":"call_456","tool":"deploy_release","args_sha256":"...","actor_id":"user_789","decision":"approved","decided_at":"2026-08-09T12:00:00Z","policy_version":"deploy-v4"}
```

Hash canonical arguments and require the same hash at execution time. Give side-effecting calls an idempotency token derived from the durable call ID. If the process dies after deployment but before the SDK records the result, the executor should return the original outcome on retry rather than deploy twice. This is the part most framework demos omit, and it is where weekend incidents begin.

## Observability must answer business questions

OpenAI has the more explicit built-in trace model for application workflows. By default, its SDK creates spans for runs, agent invocations, generations, function calls, guardrails, and handoffs. The default processor exports to OpenAI, but the Python SDK lets you add a processor or replace the processors, so sending traces elsewhere is possible. Sensitive input and output handling still needs a deliberate configuration.

Claude exposes a rich message stream and reports result metadata such as cost and usage. Hooks provide lifecycle interception. Its documentation also describes OpenTelemetry support for metrics and events. For a workspace agent, raw tool events and changed files often matter more than a pretty conversation trace, so capture the working-directory snapshot or commit, command exit codes, stderr, and artifact hashes beside model telemetry.

Do not judge either system by whether a trace viewer looks polished in a demo. Ask whether an on-call engineer can answer these questions from exported data:

- Which authenticated user caused the side effect?
- Which prompt, tool schema, policy, and model versions ran?
- What did each tool receive and return after redaction?
- Where did latency, tokens, and money accumulate?
- Can we replay the decision without repeating the write?

Create your own run envelope before calling either SDK. Generate a provider-neutral `run_id`, attach deployment and policy versions, and propagate it into traces, hook events, MCP metadata, tool calls, and application logs. Store provider IDs as secondary fields. This prevents a future migration from breaking the join between product analytics and agent telemetry.

Cost comparison also belongs here. Do not compare a single token price and declare a winner. Run the same task corpus and record successful task cost, wall time, number of tool calls, approval wait time, retries, and human cleanup minutes. An agent that costs less per token but takes twice as many wrong turns is not cheaper.

## Lock-in has five separate layers

Teams often call any provider-specific import "lock-in" while ignoring the data and operations that are much harder to move. Separate five layers before assigning risk.

1. Model lock-in covers prompts and behavior tuned to a model family. OpenAI can route through other model providers, but its own documentation warns about missing feature parity. Claude Agent SDK is built around Claude and the Claude Code harness, so model substitution is not its design goal.
2. Tool lock-in covers decorators, schemas, hosted tools, built-in shell and file behavior, MCP extensions, and error formats. MCP reduces this risk only for capabilities that fit cleanly behind MCP.
3. State lock-in covers conversation items, session identifiers, compaction behavior, pending approvals, and resumable run formats. This layer usually hurts migrations more than tool schemas.
4. Observability lock-in covers trace IDs, span formats, stored payloads, dashboards, and eval datasets. Export your own event envelope even if you keep the vendor viewer.
5. Operations lock-in covers sandboxes, CLI binaries, secrets, worker lifecycle, regional controls, and incident procedures. A framework replacement can leave all of this untouched, or force a complete rebuild.

Score each layer by exit cost, not by the existence of a proprietary type. A proprietary `Agent` constructor may take a day to replace. Reproducing years of session history, approval evidence, and eval baselines may take months. Conversely, refusing a useful built-in file tool to avoid one import can waste engineering time without reducing the expensive risks.

I argue against building a universal agent abstraction before the first production pilot. The idea is popular because an interface named `AgentProvider` looks like insurance. In practice, the interface either exposes only text and function calls, throwing away the reasons to choose either SDK, or grows branches for every provider feature. Build portability seams around tools, identity, state exports, telemetry, and evals. Let the orchestration code remain specific until a second implementation proves what is actually common.

The best lock-in test is a small exit exercise. Take five recorded tasks, replace the model-and-runner layer, and measure which artifacts you cannot reuse. Do this before signing a long internal roadmap around either harness. The result will be more honest than an architecture diagram.

## A weighted rubric forces the trade-offs into view

A selection rubric should reflect the job you are funding. Score each criterion from 1 to 5, multiply it by the weight, and require written evidence for any 4 or 5. Do not let a vendor demo count as evidence.

For a product workflow, use these weights: typed application tools and outputs 20, approval and resume durability 15, observability export 15, deployment and isolation fit 15, multi-agent control 10, model portability 10, team maintenance 10, and filesystem behavior 5. Prove the high scores with contract tests against real services. Kill a worker while an approval waits, then verify that a different worker can recover the exact pending call.

For a workspace operator, change the weights: filesystem, shell, and editing behavior 22; deployment and isolation 20; approval durability 15; multi-agent control 10; observability export 10; team maintenance 10; typed application tools 8; and model portability 5. Run isolated repository tasks, inspect every changed artifact, and try obvious sandbox escapes. A model that edits well but needs host-wide credentials should fail this category regardless of its completion score.

The numbers must sum to 100 for each job, which makes every preference consume budget. If a stakeholder raises model portability from 5 to 20, ask which other requirement loses 15 points. This exposes claims such as "everything is top priority" before they distort the pilot.

For a conventional SaaS workflow, OpenAI usually starts ahead on explicit composition, typed outputs, and trace structure. For repository and operations work, Claude usually starts ahead because its built-in workspace tools, context behavior, and project configuration remove months of harness construction. Those are starting assumptions, not final scores.

Add disqualifiers before totals. A regulated workload may reject automatic trace export until redaction and routing are proven. A multi-tenant service may reject any worker design that lacks hard workspace isolation. A company that requires model choice may reject a Claude-specific harness. A coding workflow may reject a generic function-only loop because recreating mature edit and command behavior is outside the budget.

Do not use the website-context headline numbers as a shortcut for this choice. At oleg.is, I would put the decision inside a Team & AI Audit only after mapping actual work, controls, and payroll cost; the SDK score is an input, not the promised saving.

## Test the failure path before the happy path

A two-week pilot should use the same task set, tool contracts, identities, and success checks for both SDKs. Pick 20 to 30 representative tasks if you have them, but prefer ten real tasks over a hundred synthetic prompts. Include work that requires clarification, an unavailable dependency, a denied action, stale state, and a tool that returns malformed data.

Instrument both implementations with the neutral run envelope. Pin prompts, model versions, tool schemas, permission policy, and environment image. Give each system the same time and dollar budget. If one SDK includes a mature built-in capability, use it; forcing both into the lowest common denominator defeats the comparison. Keep the outcome checks common instead.

Run at least these failure injections:

- Terminate the worker after a side effect but before the tool result returns.
- Change a tool schema while a session waits for approval.
- Deny a high-risk call and see whether the agent finds an unsafe alternate route.
- Remove network access or an MCP dependency midway through the task.
- Feed a tool result containing instructions that conflict with the system policy.

Reviewers should label each task successful, safely failed, or falsely successful. The third category matters most: the agent says it completed the job, but the repository, ticket, deployment, or customer record disagrees. Track human correction time separately from model cost.

At the end, ask another engineer to operate each pilot using only the runbook. They should rotate a secret, inspect a failed run, resume an approval, change a tool policy, and roll back the deployment. An impressive completion rate with an opaque operating model is not production readiness.

The pilot should leave reusable assets even if you reject both SDKs: a task corpus, tool contracts, authorization rules, approval records, an event envelope, and acceptance checks. Those assets make the next framework evaluation faster and reduce dependence on whichever model won this round.

## The selection rule is operationally boring

Choose the OpenAI Agents SDK when your agent belongs inside an application, most tools are already service functions, typed outputs matter, and you want explicit manager or handoff semantics. It is also the better starting point when built-in workflow tracing and the option to experiment with other model providers carry real weight, provided you test every non-OpenAI path you plan to use.

Choose the Claude Agent SDK when the agent's job is to operate in a workspace, especially a codebase, and you want Claude Code's file, shell, edit, context, hook, permission, skill, plugin, and subagent behavior through a library. Accept that this choice ties more of the experience to Claude and makes sandbox design part of your application architecture.

Choose neither when the task is a short model call, when your team must own every loop transition, or when a thin direct API integration already meets the acceptance test. Frameworks create state and policy surfaces that you then have to operate. Adding one without a need is negative progress.

Whichever SDK wins, keep four things outside it: authenticated identity, side-effect execution, durable approval evidence, and provider-neutral evaluation fixtures. Those boundaries let you use the strongest parts of a harness without asking it to become your security model or your system of record.

Then schedule an exit drill after the first stable release. Replace one agent path, export one session, replay one approval, and send one trace to a different backend. If that exercise is cheap, your lock-in is controlled. If it fails, you have found the next engineering task while the system is still small enough to change.
