Runtype
ExploreIn production

Agent frameworks compared, and the layer none of them cover

LangGraph, Pydantic AI, Mastra, the OpenAI and Claude Agent SDKs and the Vercel AI SDK compared on loop ownership, durability and the serving layer.

Last updated 8 min read

Pick an agent framework by language and by how much of the loop you want to write: LangGraph or Pydantic AI in Python, Mastra or the Vercel AI SDK in TypeScript, the OpenAI Agents SDK or the Claude Agent SDK if you have already settled on a provider. All six stop at the same boundary. None of them serves the agent to your customers or scopes a run to a tenant.

This page compares them as code libraries, on the dimensions that actually decide the choice, and then names the layer that sits above all of them. Everything here is written for a team keeping its framework, so the last two sections are about what to add rather than what to replace.

How the six compare

Cells describe the libraries as of September 2026, checked against each project's own documentation.

FrameworkLanguageWho writes the loopControl flowState after a restartHuman pauseTracing in the boxModel coupling
LangGraphPython, TypeScriptThe runtime steps through nodes you defineA graph of nodes and edges over a typed state objectCheckpointers keyed by thread_id: in-memory built in, langgraph-checkpoint-sqlite and langgraph-checkpoint-postgres separateinterrupt() pauses a node and resumes from the checkpointLangSmith, a separate productProvider-agnostic through LangChain chat models
Pydantic AIPythonBuilt inStraight-line agent runs; explicit graphs through pydantic-graphIn-process by default; durability comes from a Temporal, DBOS, Prefect or Restate integrationTool approval patterns you write; no built-in resumeOpenTelemetry spans, read by Logfire or any OTLP backendProvider-agnostic
MastraTypeScriptBuilt in for agents; workflows are composed from stepsWorkflow steps chained with branching, plus agent loopsStorage adapters (libSQL, Postgres, MongoDB, DynamoDB, Redis and more) back memory and workflow stateWorkflow suspend and resumeOpenTelemetry tracing built inProvider-agnostic through the Vercel AI SDK
OpenAI Agents SDKPython, TypeScriptA Runner loopHandoffs between agents, guardrails that can trip a runSessions persist conversation historyGuardrail tripwires; approval is yours to buildTraces to the OpenAI dashboard, with exportersOpenAI first; other providers through LiteLLM in Python or an OpenAI-compatible client
Claude Agent SDKPython, TypeScriptThe SDK owns it end to endThe harness decides; you configure tools, hooks and permissionsResumable sessions; working state is the filesystemPermission modes and a PreToolUse hook that can deny a callHooks and logs; no trace storeClaude only
Vercel AI SDKTypeScriptYou call the function; the tool loop is bounded by stopWhenWhatever your code does between callsNone; you persist messages yourselfYours to buildOpenTelemetry, configured through the telemetry optionProvider-agnostic

What each framework does well

LangGraph

LangGraph is the only option here where control flow is a first-class artifact. Nodes read and write a typed state object, edges are conditional, and a checkpointer persists state after every superstep, which is what makes interrupt() work: the graph stops mid-run, your service returns, and a later call resumes from the saved state with a human decision folded in.

Choose it when the agent has a shape you want drawn (route on a classification, retry this branch, wait here for approval) and when a run may outlive a request. If you are weighing whether the graph model earns its cost, LangGraph alternatives covers the move, and running LangGraph multi-tenant covers keeping it.

Pydantic AI

Pydantic AI brings validation discipline to model output. You declare an output type and the library validates the model's response against it, retrying with the validation error when it fails, and a deps object typed per agent is passed into every tool through RunContext. Nothing in deps is visible to the model, which makes it the cleanest place in Python to carry caller identity.

Choose it when you are already a Pydantic shop and want typed results rather than string parsing. What a service built on it still needs is covered in Pydantic AI in production.

Mastra

Mastra packs the most into one TypeScript install: agents, workflows with suspend and resume, memory keyed by a resource and a thread, RAG, MCP client and server support, scorers for evaluation, and a dev server that exposes everything over REST. The memory keys give you a per-user handle without inventing one.

Choose it when the product is TypeScript end to end and you want evals and workflows in the repo from the first week. Mastra production gaps walks the parts that remain yours.

OpenAI Agents SDK

Three concepts carry the OpenAI Agents SDK: agents, handoffs, and guardrails. A handoff is a tool call that transfers the run to another agent, so routing between specialists is declarative. Guardrails run alongside the model and can trip a wire that halts the run before a bad input or output escapes, which is a genuinely useful primitive that most libraries here leave to middleware.

Choose it when OpenAI models are your default and you want an API small enough to read in an afternoon. OpenAI Agents SDK in production covers the operational side.

Claude Agent SDK

The Claude Agent SDK is the harness behind Claude Code, published as a library. It ships file, shell and web tools, subagents, MCP support, permission modes, and hooks that fire before and after tool calls, so a PreToolUse hook can deny a command by pattern without touching the prompt. For work that looks like an engineer's work, this is a proven loop instead of one you write.

Choose it when the agent reads a repository, runs commands and verifies results. Shipping a Claude Agent SDK agent to customers covers what changes when the work is not yours.

Vercel AI SDK

The Vercel AI SDK is the thinnest library of the six. generateText and streamText take tools defined with a schema, the multi-step tool loop is bounded explicitly by stopWhen, and the React hooks turn a streaming response into a chat UI with very little code. Provider swaps are a one-line model change.

Choose it when the interface is the product and the agent is a bounded tool loop. Vercel AI SDK on the backend covers the server-side half.

The seam

Every framework above answers the same question: given a model, some tools and a prompt, how does one run proceed? That question ends when the run ends. A second question starts where it stops, and none of the six answers it: given many callers, many accounts and several entry points, whose run was that, what was it allowed to touch, and what did it cost that account?

Four things sit in that gap. Identity is the first: each library gives you a slot to carry a tenant id into tools, and none of them checks that the id came from a verified source rather than from a request body a client controls. Entry points are the second: a framework serves whatever your process serves, so the web chat, the Slack app, the customer's API key and the nightly job are four codepaths that each have to remember the same rules.

Cost per account is the third. Token usage per call is reported by every provider SDK, but attributing a run to a tenant, summing it, and stopping a runaway turn before it finishes are things the loop has to do while it runs, not something a report can do afterward. Evidence is the fourth: an eval suite that reflects what your customers actually send needs production runs recorded with enough fidelity to be promoted into cases, which means a trace store shaped around executions rather than a log file.

These are matters of scope. A library that solved them would be a serving platform, and most teams do not want their model-call library making deployment decisions. The gap is real anyway, and it is where the work goes after the demo works. Background on what a platform layer covers is at AI agent platform.

How to close it

Start with the injection point your framework already gives you, and make it the only way caller context enters a run.

FrameworkWhere tenant context entersVisible to the model
LangGraphconfig={"configurable": {...}} on invoke, read inside the nodeNo, unless you write it into state
Pydantic AIdeps= on run, read through RunContext.depsNo
MastraA RequestContext, passed as requestContext at call time, plus the memory resourceNo
OpenAI Agents SDKA context object on Runner.run, read through RunContextWrapperNo
Claude Agent SDKThe process boundary: working directory, environment, MCP server configTool results only
Vercel AI SDKA closure captured by the tool's execute functionNo

The Pydantic AI shape generalizes to the rest:

from dataclasses import dataclass
from pydantic_ai import Agent, RunContext


@dataclass
class Caller:
    tenant_id: str
    end_user_id: str


support = Agent('openai:gpt-4o', deps_type=Caller)


@support.tool
async def list_invoices(ctx: RunContext[Caller], status: str) -> list[dict]:
    return await invoices.list(
        tenant_id=ctx.deps.tenant_id,
        end_user_id=ctx.deps.end_user_id,
        status=status,
    )

A few rules keep that pattern honest as the codebase grows. No tool signature takes a tenant or account id as a parameter, because a parameter is a value the model chooses and can be argued into changing. Every entry point builds the context object from a verified session rather than from request fields.

A test asserts the rule mechanically, since review will not catch the twentieth tool. Set the framework's own limits explicitly too, whatever they are called: stopWhen in the Vercel AI SDK, a recursion limit in LangGraph, a max-turns setting in the vendor SDKs, so a loop that stops making progress ends in seconds instead of dollars.

Instrument next, and keep the instrumentation outside the framework so it survives a framework change. Emit OpenTelemetry spans with the tenant id, end-user id, agent version and model on the root span, record token counts split by cached and uncached, and keep the full input and output of every tool call rather than a summary line. The practical steps are in instrumenting an agent built elsewhere, and the boundary checks are in the tenant isolation checklist.

Keep the loop. A framework rewrite is the most expensive way to acquire a serving layer, and the graph, the typed outputs or the harness you picked is usually the part that is working.

Where Runtype fits

Runtype is the layer above whichever of the six you keep, and the four ways in are the same for each. They stack, and porting the loop is optional.

Serve it tools first. An MCP surface publishes a product's flows, agents, records, skills and tools, so the framework you kept stays the orchestrator and calls Runtype through an MCP client for what is worth centralizing. Neither the loop nor the surface changes (setting up an MCP surface).

Register the agent second: an external agent whose endpoint speaks Runtype's unified stream (runtype-stream) or A2A lets Runtype call your loop. It then embeds in the open-source Persona widget, joins a product as a capability, answers on web chat, Slack, SMS, REST and A2A, runs on a schedule, and records tool calls and cost per run. An A2A endpoint can also be an eval suite target.

Send traces third. Any of the six can export OTLP to https://api.runtype.com/v1/otel for the Runs view, trace tree, token usage and a display-only cost estimate that your provider still bills you for. Point exactly one instrumentation at it; two double the numbers. Rebuild last: port the capability that keeps breaking into a flow once real-run evals prove parity, then gate it with runtype eval run in CI.

These layers are for your customers. A resource declares a tenancy strategy (internal, tenant-isolated or end-user-isolated) and an assurance floor (asserted or verified), a request falling short is rejected before execution, and every trace and cost figure files under its tenant and end user (end-user identity).

Where a framework is the better answer, it is the better answer: LangGraph for control flow you want to see, Pydantic AI for typed Python, Mastra for one TypeScript install, the vendor SDKs when the provider is settled, the Vercel AI SDK when the UI is the product. A ranking of the platforms that run the loop for you is at best AI agent platforms.

Frequently asked questions

Which AI agent framework should I choose?
Choose by language and by how much of the loop you want to own. Python teams that want control flow drawn explicitly pick LangGraph; Python teams that want typed outputs and injected dependencies pick Pydantic AI. TypeScript teams pick Mastra for a batteries-included framework or the Vercel AI SDK for a thin model-call library. The vendor SDKs from OpenAI and Anthropic are the shortest path if you have already settled on that provider.
Do agent frameworks handle multi-tenancy?
None of the six treats a tenant as a first-class concept. Each gives you a place to carry caller context into tools (a config object, a deps object, a closure), and each leaves it to your code to decide whether the identity on the request was verified and whether a given tool may act on that tenant's data. Isolation is a property of the code you write around the framework.
Can I switch agent frameworks later?
The loop is portable in principle and expensive in practice. Tool definitions and prompts move with modest edits, but checkpointed state, memory schemas, session storage and eval datasets are framework-shaped and rarely survive a move intact. The parts most worth keeping outside the framework are your eval cases, your traces and your tenant model.
Do I need a platform if I already use a framework?
Only when the agent leaves your own team. A framework is enough for backend automation an engineer triggers. Once customers reach the agent across accounts and surfaces, you need identity enforced before execution, per-tenant cost, approvals and evals over production traffic, and those live above the loop rather than inside it.