Skip to main content
Microsoft Semantic Kernel is an open-source SDK for building AI agents in Python, .NET, and Java. You give a kernel a chat service and a set of plugins (native functions, OpenAPI specs, or MCP servers); Semantic Kernel runs the automatic function-calling loop — and, with ChatCompletionAgent, wraps that into a first-class agent with instructions and memory.
  • What Semantic Kernel ships — a Kernel, native/OpenAPI/MCP plugins, automatic function calling (FunctionChoiceBehavior.Auto), ChatCompletionAgent, structured outputs, filters/telemetry, and first-class MCP client support via MCPSsePlugin / MCPStreamableHttpPlugin / MCPStdioPlugin.
  • 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.
That’s the half Naive adds. You keep Semantic Kernel’s orchestration; Naive gives each agent a tenant identity, a virtual card, 1,000+ third-party connections, and an Account Kit that bounds exactly what the agent can do — enforced server-side, with human approval on the sensitive actions.

How the pairing works

  • Semantic Kernel turns any MCP server into a plugin: point an MCPSsePlugin at the server URL, add_plugin it to the kernel or agent, and each MCP tool becomes a kernel_function the model can call — 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. Build an MCPSsePlugin from it, hand it to a ChatCompletionAgent, and every tool call runs as that user — gated server-side.
Tested against: semantic-kernel 1.43.x with the [mcp] extra (semantic_kernel.connectors.mcp.MCPSsePlugin with headers, ChatCompletionAgent, OpenAIChatCompletion, OpenAIChatPromptExecutionSettings, FunctionChoiceBehavior.Auto), the mcp Python package 1.x (SSE transport), and Naive API v2 (hosted MCP server over SSE + per-user sessions), on Python ≥ 3.10.Naive’s session URL contains /mcp/sse/…, so pair it with Semantic Kernel’s MCPSsePlugin (not MCPStreamableHttpPlugin). The scoped bearer rides in the plugin’s headers argument, never the URL. MCP support isn’t in the base install — add the mcp extra (semantic-kernel[mcp]). There is no language-specific Naive SDK for Python or .NET yet — provision the control plane over the REST API, the dashboard, the CLI, or the Node SDK (@usenaive-sdk/server). Pin your versions and set the model to one you have access to. The MCPSsePlugin connectors are marked experimental upstream.

Prerequisites

  • A Naive API key (nv_sk_...) — get one from the dashboard.
  • An OPENAI_API_KEY for the model that runs the agent (or swap in any other Semantic Kernel chat service, e.g. AzureChatCompletion).
  • Python ≥ 3.10.

Minimal viable integration

The shortest path to a Semantic Kernel 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 hand it to a ChatCompletionAgent.
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

Point Semantic Kernel’s MCPSsePlugin at the session’s scoped SSE endpoint, add_plugin it to a ChatCompletionAgent, and call get_response. Semantic Kernel discovers Naive’s tools, exposes them as kernel functions, and runs the automatic function-calling loop for you (call tool → feed result back → continue). The plugin is an async context manager — keep it open for the duration of the run:
The model discovers GitHub (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 agent loop is Semantic Kernel’s; the real-world actions are Naive’s.
That’s the moat in ~40 lines: the same Semantic Kernel agent that would otherwise just describe spending money now issues a policy-bounded card on a specific user’s account.
The session’s tool list is already filtered by Alice’s kit — the agent never sees a tool the policy forbids. Each MCP tool is registered as a kernel function under the plugin name (e.g. Naive-naive_cards_create). To narrow further per agent, scope the model with FunctionChoiceBehavior.Auto(filters={"included_functions": ["Naive-naive_connections_connect"]}), or load a subset with MCPSsePlugin(..., load_tools=True) and a dedicated session whose kit is tighter.

Extension: human-in-the-loop spend

Because the kit set cards.requiresApproval: true, the agent cannot silently spend. When the model calls the card-issuing tool, Naive freezes the request and the tool result comes back as a pending approval (HTTP 202) instead of a live card:
The immediate 202 response uses action; approval records from the approvals API use action_type. The agent relays the pending status instead of claiming success. Your app then resolves it out of band — and on approval, Naive replays the frozen action server-side:
See Approvals for the full lifecycle (pending → executed / failed / denied) and the deny endpoint.
Want a UX gate inside the run too? Add a Semantic Kernel auto-function-invocation filter that inspects each function call and pauses for confirmation. That shapes the conversation; the real enforcement is Naive’s server-side approval gate above, which applies regardless of prompt or kernel configuration.
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 build the MCPSsePlugin from it. The instructions, model, and settings stay identical — only which user’s session backs the plugin changes, and every call is Account-Kit-gated server-side:
Nothing about the agent widens what a user may do: the toolset is the intersection of the session and that user’s Account Kit, enforced on Naive’s servers. Revoke a session early with DELETE /v1/users/{user_id}/sessions/{id}.
Because it’s Semantic Kernel, you can also pin a typed result: set settings.response_format = MySchema (a Pydantic model) so the run returns a validated object — e.g. { github_connect_url, card_status } — instead of free text, while the transacting still happens through Naive’s gated tools.

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 kernel 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/:id to revoke it early.

Next steps