Pydantic AI MCP server: give an agent Sume's hosted tools
Connect a Pydantic AI agent to Sume's hosted MCP server with MCPToolset and an API-key header, filter the tools, and hold paid calls for approval.

To give a Pydantic AI agent Sume's tools, create an MCPToolset for https://mcp.sume.com/mcp with your Sume API key in an Authorization: Bearer header, keep only the tools the agent needs with .filtered(), hold paid calls with .approval_required(), and register the toolset with Agent(toolsets=[...]).
Pydantic AI's side comes from its MCP client guide, MCPToolset reference, and Toolsets pages; Sume's side comes from OAuth and API keys, MCP tools and gates, and Jobs and results, all read on 2026-09-27. Sume has no Pydantic AI package or plugin: this is Pydantic AI's own MCP client talking to Sume's remote server. Sume's basics page says hosted MCP still works but is not part of the primary path today.
How do I connect a Pydantic AI agent to Sume's MCP server?
Install pydantic-ai-slim[mcp]. MCPToolset wraps the FastMCP client, and a URL string connects over Streamable HTTP, which Pydantic AI calls the recommended way to reach a remote MCP server; only a path ending in /sse switches to SSE. Register the toolset through the agent's toolsets argument. Pydantic AI recommends its MCP capability for most uses and MCPToolset when you manage the client yourself or need options the capability doesn't expose; this post uses MCPToolset so it can be wrapped with .filtered() and .approval_required(). The example shows the agent five Sume tools and pauses generate_video for approval unless the call is a dry run:
import os
from pydantic_ai import Agent, DeferredToolRequests
from pydantic_ai.mcp import MCPToolset
ALLOWED = {"tools_schema", "balance_get", "generate_video", "jobs_wait", "jobs_result"}
sume = (
MCPToolset(
"https://mcp.sume.com/mcp",
headers={"Authorization": f"Bearer {os.environ['SUME_API_KEY']}"},
)
.filtered(lambda ctx, tool_def: tool_def.name in ALLOWED)
.approval_required(
lambda ctx, tool_def, args: tool_def.name == "generate_video" and not args.get("dry_run")
)
)
agent = Agent(
"openai:gpt-5.2",
instructions="Preview generate_video with dry_run=true first. Wait with jobs_wait; never resubmit.",
toolsets=[sume],
output_type=[str, DeferredToolRequests],
)Should the Sume key go in auth or headers?
Either works. For HTTP transports, MCPToolset takes auth: a bearer-token string, an httpx2.Auth, or 'oauth' for FastMCP's OAuth flow. Pydantic AI's docs say static headers like API keys can go in headers instead. Sume's MCP docs accept the key as Authorization: Bearer or as x-api-key, so send one of the two.
- Sume keeps API-key remote MCP as the path for automation that does not speak OAuth. An API-key session sees write and paid tools, which is why the example filters.
headersand a customhttp_clientare mutually exclusive.- A shared
MCPToolsetconnects as a single identity. If each of your users has their own Sume key, build the toolset per run with@agent.toolset(per_run_step=False). - Read the key from the environment. Sume's docs say not to paste API keys into chat and to rotate a key that appears in logs or chat history.
Which Sume tools should the agent see?
filtered() decides ahead of each step of the run which tools are available, from your function's answer for each tool definition; approval_required() then decides per call whether to pause. Sume's live tool ids use underscores, and the Sume MCP tools list groups every hosted tool by whether it reads, writes, or spends. The example's two functions work out like this:
| Tool | Sume group | `filtered()` | `approval_required()` |
|---|---|---|---|
tools_schema | Discovery | Kept | No |
balance_get | Account and catalog | Kept | No |
generate_video | Paid | Kept | Yes, unless dry_run |
jobs_wait, jobs_result | Jobs, read | Kept | No |
jobs_cancel | Jobs, write | Dropped | Not reached |
How do paid Sume calls wait for a person?
The function you pass to approval_required() receives the run context, the tool definition, and the validated arguments. When it returns true, the run ends with DeferredToolRequests, whose approvals list the pending calls; that is why the agent's output_type includes it. Resume with the run's message_history and DeferredToolResults(approvals={tool_call_id: True}). A False sends "The tool call was denied." back to the model.
The gate fits Sume's playbook for inspecting a tool before paying: call the paid tool with dry_run=true, confirm the estimate, balance, and queue behavior, then submit with a fresh idempotency_key, which every write and paid tool requires. max_spend_usd caps a call only when you pass it. Safe automation for AI agents that call paid APIs covers the gates.
Will a long jobs_wait call time out?
Not with the defaults. Pydantic AI's timeouts page says read_timeout bounds a single MCP request, 300 seconds by default, and that the agent's tool_timeout doesn't apply to tools from an MCP server; init_timeout covers the initial connection and handshake. Sume's jobs_wait holds one call for at most 55 seconds, or 50 when timeout_seconds is omitted, so a wait fits.
- On
wait_slice_expired, calljobs_waitagain with the same ids; never resubmit the paid create. - A
524,522,523, or525onjobs_waitis a transport failure, never a job outcome. - When the server reports a tool error, Pydantic AI by default sends it back to the model as a retry prompt (
tool_error_behavior='retry'). Set'failed'to record it as a failed tool result and let the model decide what to do next. - MCP tool call timeouts on long-running video jobs covers batch waits and reading a whole wave of results.
Sources
Related posts
More in Integrations
- Python webhook HMAC verification in FastAPI and Django
Verify a Sume webhook in Python: HMAC-SHA256 over timestamp.raw_body, split the signature header on commas, compare in constant time, answer fast.
- Shopify product video AI API with products/create webhooks
Answer Shopify's products/create webhook within five seconds, run a Sume Format from a queue, then upload the MP4 to Shopify with a staged upload.
- Slack bot to generate video: a slash command with Sume's API
Ack Slack's slash command within 3000 ms, submit POST /v1/videos with a callback_url, then post the URL to response_url when Sume's webhook lands.
- Cline MCP remote server: add Sume's hosted MCP
Add Sume's hosted MCP server to Cline as a remote server with type streamableHttp and an API-key header, and keep paid tools out of autoApprove.
Written by Sume