- The model layer — one
generateText/streamTextinterface across OpenAI, Anthropic, Google, and dozens of other providers. - The tool-calling loop — define
tool()s with a schema and anexecute, setstopWhen, and the SDK runs multi-step tool calls until the job is done. - Streaming + UI —
streamText,useChat, and structured output for full-stack apps.
How the pairing works
- Naive’s SDK exposes a small, drop-in toolset via
agentTools()— a discover-then-run meta-toolset (search apps/primitives, then run them) instead of thousands of schemas. - Each Naive tool ships an Anthropic-style
input_schema(plain JSON Schema). The AI SDK’stool()accepts a raw JSON schema via thejsonSchema()helper — so the adapter is a few lines, with no manual Zod rewrites. - Every call stays gated by the user’s Account Kit on Naive’s servers; sensitive actions
resolve to a
pending_approvalpayload.
Tested against:
@usenaive-sdk/server 0.12.x (Naive API v2), ai 7.0.x
(generateText, tool, jsonSchema, stopWhen, stepCountIs), @ai-sdk/openai
4.0.x, and — for the MCP extension — @ai-sdk/mcp 2.0.x (createMCPClient, SSE
transport), on Node ≥ 20.Version assumptions: AI SDK v5+ renamed a tool’s parameters → inputSchema and
replaced maxSteps with stopWhen: stepCountIs(n). In v7 the MCP client moved out of
the core ai package into @ai-sdk/mcp (createMCPClient; the old
experimental_createMCPClient import from ai is gone). Naive’s hosted MCP server speaks
SSE. Pin your versions and set the model to a provider you have access to.Prerequisites
- A Naive API key (
nv_sk_...) — get one from the dashboard. - An
OPENAI_API_KEY(or any other AI SDK provider key) for the model that runs the agent. - Node ≥ 20.
Minimal viable integration
The shortest path to an AI SDK agent that can actually transact: define a policy, provision a user, adapt Naive’s tools, and run the loop.Define the policy, then provision a user
An Account Kit is the spend/capability policy. Here a tenant
user gets a card (capped at $500, approval required), the vault, and an allowlist of apps.
Everything the agent does is bounded by this kit — server-side. These are one-time
control-plane calls:
Adapt Naive's tools into AI SDK tools
client.agentTools() returns tools as JSON-schema definitions plus a handle(name, input)
dispatcher. Map each Naive tool’s input_schema through jsonSchema() and route the
executor through handle. The result is a plain ToolSet keyed by tool name:naive_search_apps, naive_connect_app,
naive_run_capability, naive_search_primitives, naive_run_primitive, …) — a handful of
tools that reach every app and primitive the kit allows, instead of thousands of schemas.jsonSchema() passes the schema through for the model; the SDK doesn’t deep-validate
optional fields the discover-then-run tools use. That’s fine — Naive re-validates every call
server-side against the Account Kit, so the model can’t smuggle out-of-policy arguments
past the agent.Run the loop — and let it transact
Hand the adapted tools to
generateText and set a stopWhen so the multi-step loop
terminates. The SDK calls the model, runs the tool, feeds the result back, and repeats:- The model discovers GitHub (
naive_connect_app), returns a connect link for Alice to authorize, and attempts to issue the card (naive_run_primitive→cards.create). - The card is a real card on Alice’s account, capped by her kit.
- The whole agent loop is the AI SDK’s; the real-world actions are Naive’s.
generateText loop that would otherwise just
describe spending money now issues a policy-bounded card on a specific user’s account.
Extension: human-in-the-loop spend (two gates)
Because the kit setcards.requiresApproval: true, the agent cannot silently spend. You
get two complementary layers — pair them for defense in depth:
- On the server (the real boundary) — Naive freezes the action and returns a pending
approval (HTTP
202) instead of a live card. This holds no matter what runtime calls it, and is enforced server-side regardless of prompt or agent configuration. - In the loop (optional early interrupt) — AI SDK v7 supports tool approval at the
generateText/streamTextlevel via thetoolApprovaloption, which pauses before a flagged tool runs so you can surface it in your UI. Treat this as a UX convenience; the enforcement that matters is Naive’s server-side gate.
202 / isPendingApproval payload uses action; approval records from
approvals.list() or approvals.get() use action_type.
Your app then resolves it out of band — and on approval, Naive replays the frozen action
server-side:
isPendingApproval(res), or
poll a single approval to completion with client.approvals.wait(approvalId). See
Approvals for the full lifecycle (pending → executed / failed / denied).
Approvals are only enforced for agent (API-key / MCP) calls on real tenant users. A human
acting in your dashboard, and agent calls on the operator’s own default user, bypass the
gate — so end-user agents stay governed while your own automation isn’t slowed down.
Alternative: hand a scoped MCP session to the agent
If the agent runs somewhere you don’t fully trust (an edge runtime, a third-party host), don’t ship it your API key. Mint a short-lived, per-user MCP session and point an MCP client at its scoped SSE endpoint — the bearer lives only in the request headers and expires. The AI SDK auto-discovers Naive’s tools as MCP tools:- Same Account Kit, same approval gates — just delivered as a remote MCP server instead of in-process tools.
- The session is scoped to one user and expires (default 15 min, max 24h); revoke early with
client.sessions.revoke(session.id).
What stays enforced
No matter which path you choose, the policy is enforced where it matters — on Naive’s servers, not in your prompt or your agent config:- Identity — every action runs as a specific tenant user, fully isolated from your other users.
- Capability bounds — the Account Kit decides which
primitives and which apps the agent can touch (
allowlist/blocklist/ per-tool). - Scoped spend — virtual cards are capped per card and per user; the model can’t raise its own limit.
- Human-in-the-loop — sensitive actions (cards, domains, KYC, formation, connecting an app) freeze as approvals until a human says yes.
Next steps
- SDK overview — the full Naive client surface
- Agent tools — what
agentTools()exposes and howhandle()works - Account Kits — author spend/capability policy
- Approvals — the human-in-the-loop lifecycle
- Sessions — per-user MCP sessions for untrusted runtimes