If you’ve tried to ship an AI agent inside a Flutter app, you’ve hit the same wall. The major agent SDKs — Anthropic’s Claude Agent SDK, OpenAI’s Agents SDK, Mastra, LangChain — are all built for a server. They assume Python or Node. They spawn MCP subprocesses. They expect to live in a process you can kill -9.

None of that ports cleanly to an iOS app sandbox. Or, more bluntly: it doesn’t port at all. Apple’s app review will reject a binary that ships a Node interpreter inside your bundle. You can’t exec in an iOS sandbox. Even on Android, where you technically can, you’re carrying a runtime that has nothing to do with what your app is for.

agentlib closes that gap. It is the only open-source agent SDK that runs natively inside a Flutter app — pure Dart, no subprocess, no exec, with native bridges to Apple Foundation Models and Gemini Nano. Same API shape as the Claude Agent SDK, but the code ships in your app binary, not on a server it talks to.

What “mobile-native” actually means

A lot of frameworks claim to “support” mobile. They usually mean “you can call our HTTP API from a mobile app”. That’s fine — you can call any HTTP API from a mobile app — but it’s not the same thing as an agent SDK on the device. An agent SDK on the device:

  1. Doesn’t need a server. The orchestrator, the tool dispatcher, the snapshot timeline, the hooks — all of it runs in your app process.
  2. Calls on-device models when it can. Apple Intelligence’s foundation model, Gemini Nano on Pixel, llama.cpp via Fllama, Gemma 3 via MediaPipe — all without a network round trip.
  3. Survives the OS. Backgrounding, suspension, push wake-up, low battery — every one is a lifecycle hook you can wire into.
  4. Plays nice with reviewers. No subprocess, no shell escape, no exec of arbitrary code. The sandbox stays sealed.
  5. Lets the UI react in real time. Streaming events go straight into a Dart Stream<RunEvent>, which slots into StreamBuilder, Riverpod, BLoC, or whatever state management the team already uses.

agentlib does all five.

What the alternatives look like

It helps to be specific about what “use the Claude SDK in a Flutter app” actually involves.

Option A: Spawn a Node process on the device. You bundle a Node binary, stream the Claude SDK’s stdout back to Flutter via platform channels, and pray Apple doesn’t reject the app. Some teams do this. They also spend most of their engineering time on the integration rather than the product.

Option B: Wrap the SDK behind your own HTTP server. You run the Claude Agent SDK on a backend, expose a thin HTTP wrapper, and have the app call it. This works, but: every request needs a network round-trip, no offline use, you carry server cost, and you’ve added a whole new service to operate.

Option C: Re-implement the agent loop in Dart. Some teams do this from scratch — call the model API, parse tool calls, dispatch tools, manage messages. That’s exactly what agentlib is, except agentlib already did the hard parts: streaming parsers for Anthropic / OpenAI / Google, on-device provider implementations, snapshots, hooks, subagents, skills, the Sh mini-shell. Tens of thousands of lines of agent loop you don’t have to write.

agentlib is option D: ship the SDK in your app, in the language you already write.

What’s in agentlib that’s worth knowing about

If you’ve used the Claude Agent SDK, the shape will feel familiar. agentlib explicitly mirrors the primitives that Anthropic and OpenAI converged on, plus a few mobile-only additions.

AgentSpec + Runner. A value-class descriptor and a streaming event-driven loop. Compose AgentSpecs; pass them to Runner.runStreamed(...); consume Stream<RunEvent>. Events include TextDelta, ThinkingDelta, ToolCalled, ToolResult, SnapshotCreated, Finished.

Subagents. Sync, parallel, or background. Each gets its own context window so deep research doesn’t pollute the parent. Dispatch via the Task tool or the task CLI builtin.

Skills. Markdown bundles at /workspace/.skills/<name>/SKILL.md. The orchestrator sees only name + description until invocation — then the body loads. A 30-skill catalog costs ~600 tokens in the orchestrator, not 12 K.

Snapshots. snapshot(), revert(), fork(). Content-addressed (SHA-256). SQLite-backed in production. Auto before highImpact: true tool calls. Users can undo. You can fork to try a different model mid-run.

21 hooks. PreToolUse, PostToolUse, PermissionRequestHook, OnSuspend, OnResume, OnLowBattery, OnNetworkChange, OnHandoff, UserPromptSubmit, StopHook… everything you’d want to observe is observable.

Vfs + Sh. A virtual filesystem (/workspace, /scratch, /system, /apps, /bin, /memory, /snapshots) plus a sandboxed Unix mini-shell that parses pipes, redirects, $VAR, $(cmd). No real exec, no &, no eval.

ModelRoute. Capability-aware fallback. ModelRoute.preferOnDevice([FoundationModelsProvider(), GeminiNanoProvider(), FllamaProvider(...)], fallback: AnthropicProvider(...)) walks the list and picks the first provider that can serve the request.

MCP. HTTP and WebSocket transports. MCP tools surface as /bin/mcp.<server>.<tool> CLIs in Sh pipelines.

A first agent

import 'package:agentlib/agentlib.dart';

final agent = AgentSpec(
  name: 'helper',
  instructions: 'You are a helpful mobile assistant.',
  model: ModelRoute.preferOnDevice(
    onDevice: [FoundationModelsProvider(), GeminiNanoProvider()],
    fallback: AnthropicProvider(apiKey: '<KEY>'),
  ),
);

final run = Runner.runStreamed(
  agent.toRunConfig(vfs: Vfs(), threadId: 'main'),
  input: 'Why is agentlib mobile-native?',
);
await for (final e in run.events) {
  if (e is TextDelta) stdout.write(e.delta);
}

That’s the full agent. On an iPhone 15 Pro running iOS 19, this never leaves the device — Apple Foundation Models serves the request. On a Pixel 8, Gemini Nano serves it. On a 2019 iPhone XR with iOS 16, the cloud fallback kicks in. Your code is identical.

Why not just embed MLC LLM or gpustack directly?

You can. agentlib is not trying to be a model runtime — it’s an agent SDK on top of model runtimes. The runtimes it wraps (FoundationModels, AICore, MediaPipe LlmInference, Fllama / llama.cpp) are the right primitives for inference. agentlib adds the loop: tool calling, hooks, snapshots, subagents, the streaming event surface, MCP transports.

If you want the loop in Dart but a different runtime, implement the ModelProvider interface — it’s two methods — and plug it in.

Bottom line

If your agent runs on a server, use the Claude Agent SDK or Mastra. If your agent runs on the device, use agentlib. A surprising number of products do both — Claude SDK on the backend, agentlib on the device — and we built MCP transports in agentlib precisely so they can interoperate.

Install is one pubspec line. The hello-agent example is 79 lines. The five quickstart Flutter apps in the example/quickstarts folder cover the common cases — hello agent, multi-tool, on-device, voice, mobile automation.

Read the primitives in detail next, or jump to /install and ship something.