
smolagents orchestrates the agent · Naive lets it transact
smolagents and Hugging Face are trademarks of their respective owners, used here for identification and integration guidance only. No endorsement, partnership, or affiliation is implied.
ToolCallingAgent, multi-agent handoff, and first-class MCP support — point it at an MCP
server with ToolCollection.from_mcp and every tool the server exposes becomes callable.
- What smolagents ships — the agent loop (
CodeAgent,ToolCallingAgent), sandboxed code execution, model-agnostic backends (InferenceClientModel,OpenAIServerModel,LiteLLMModel, …), multi-agent orchestration, and MCP clients overstdio,streamable-http, andsse— including auth headers on remote servers. - What it doesn’t ship — a way for those agents to act as a real account: sign up for a SaaS tool, hold a funded card, or spend within a budget you control, per end-user.
How the pairing works
- smolagents turns any MCP server into a set of tools:
ToolCollection.from_mcp(...)connects, discovers the server’s tools, and hands them to your agent — no manual schema wiring. - Naive ships a hosted MCP server and mints per-user sessions — short-lived, revocable SSE endpoints whose tool list is the fused native + third-party toolset, already filtered by that user’s Account Kit.
- Each session is one URL + one bearer scoped to one user. Load it as a
ToolCollection, spread its tools into aCodeAgent, and every tool call runs as that user — gated server-side.
Tested against:
smolagents 1.26.0 (1.x — CodeAgent, ToolCollection.from_mcp,
MCPClient) with mcpadapt 0.1.20 and mcp 1.28.1, and Naive API v2 (hosted MCP
server over SSE + per-user sessions), on Python ≥ 3.10.Naive’s session URL contains /mcp/sse/…, so connect with "transport": "sse". smolagents
flipped its default transport to streamable-http in 1.21.0 and marks SSE as deprecated,
but SSE is still fully supported — set it explicitly to match Naive’s endpoint. The scoped
bearer rides in the connection headers, never in the URL. from_mcp requires
trust_remote_code=True to load MCP tools at all. There is no Python Naive SDK yet, so the
control plane (Account Kit, user, session) is shown over the REST API; you can equally
provision from the dashboard, the CLI, or the Node SDK. Pin
your versions and set the model to one you have access to.Prerequisites
- A Naive API key (
nv_sk_...) — get one from the dashboard. - A model provider key for the LLM that runs the agent (e.g.
OPENAI_API_KEY, or aHF_TOKENforInferenceClientModel). - Python ≥ 3.10.
Minimal viable integration
The shortest path to a smolagents agent that can actually transact: define a policy and provision a user (control plane, once), then at runtime mint a per-user MCP session and load it as aToolCollection.
1
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:
2
Mint a per-user MCP session
At runtime, mint a short-lived session for the user. It returns the
scoped SSE endpoint and a bearer that lives in the headers — never in the URL — and expires
(default 15 min, max 24h):
3
Build the agent — and let it transact
Load the session’s scoped SSE endpoint as a The model discovers GitHub (
ToolCollection, spread its tools into a
CodeAgent, and call run(). smolagents discovers Naive’s tools, exposes them to the model,
and runs the whole loop for you:naive_connections_connect), returns a connect link for Alice to
authorize, and attempts to issue the card (naive_cards_create) — a real card on Alice’s
account, capped by her kit. The whole loop is smolagents’; the real-world actions are Naive’s.from_mcp needs trust_remote_code=True — that’s smolagents’ gate for loading tool
definitions from a remote MCP server (without it, MCP tools won’t load at all). The session’s
tool list is already filtered by Alice’s kit, so the agent never sees a tool the policy forbids.
To narrow further, hand the agent a subset — tools=[t for t in tool_collection.tools if t.name in {"naive_connections_connect", "naive_cards_create"}] — or mint the session against a tighter
kit.Extension: human-in-the-loop spend
Because the kit setcards.requiresApproval: true, the agent cannot silently spend. This
is where the pairing is deliberately asymmetric: smolagents runs the agent, but the spend gate
lives on Naive’s servers, not in your prompt or agent config.
- In smolagents — you shape the run by narrowing which tools the agent even sees (subset
tool_collection.tools, above) and by keeping code execution sandboxed. - On the server — even if a call gets through, Naive freezes the sensitive action and
returns a pending approval (HTTP
202) instead of a live card. This holds no matter what runtime calls it.
naive_cards_create, the tool result comes back as a pending approval
rather than a live card:
pending → executed / failed / denied) and the deny endpoint.
Whatever you allow the smolagents model to write, the real enforcement is Naive’s server-side
approval gate above, which can’t be bypassed from the generated code, the tool list, or the
agent config.
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: one agent definition, every tenant
A Naive session is scoped to one user, so to serve every tenant from the same agent definition, mint a fresh session per request and open aToolCollection from it. The model,
system prompt, and tool selection stay the same — only which user’s session backs the toolset
changes, and every call is Account-Kit-gated server-side:
MCPClient directly instead of the one-shot context manager — remember to close it:
DELETE /v1/users/{user_id}/sessions/{id}.
Prefer TypeScript? Naive also plugs into TS agent stacks. On TypeScript you can provision
the control plane with the
@usenaive-sdk/server SDK (naive.accountKits.create,
naive.users.create, client.session(...)) and hand the scoped MCP session to any MCP-aware
runtime — see the Vercel AI SDK and
Mastra guides.What stays enforced
No matter how the agent is wired, 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.
- Scoped, expiring access — the agent holds a per-user session bearer, not your API key;
it expires (default 15 min) and you can
DELETE /v1/users/:user_id/sessions/:idto revoke it early.
Next steps
- MCP server — the hosted SSE server and its full tool list
- Sessions — per-user MCP sessions, TTL, and revocation
- Account Kits — author spend/capability policy
- Approvals — the human-in-the-loop lifecycle
- Pydantic AI · LlamaIndex · Agno — the same MCP-session pairing for other Python stacks
Related reading (blog)
- Naïve inside your agent framework — concept map for every integration guide
- Delegated, revocable MCP sessions — session model for Python and MCP guides
- The Governed Agent Profile — caps, approvals, and revoke on every tool call
- Building AI Agents Into Your SaaS — multi-tenant architecture this pairing assumes