
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.
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:
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):
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