Agents SDK blocked_tool_names: hide Sume paid tools from a model

create_static_tool_filter takes allowed and blocked tool names. Use it so an agent on Sume's hosted MCP can read, but never sees a paid generate tool.

5 min readSume
All posts

Filter at the server object. In the OpenAI Agents SDK for Python, MCPServerStreamableHttp accepts a tool_filter, and create_static_tool_filter builds one from allowed_tool_names and blocked_tool_names (read 2026-10-05). A model that never sees generate_video cannot call it, whatever the prompt says.

Allow list first, block list second

The SDK page describes two filter styles: a static filter with allow and block lists, and a dynamic callable that receives a ToolFilterContext and returns a boolean per tool (read 2026-10-05). For Sume, an allow list is safer. Sume's tool inventory changes, and a block list only hides names you remembered to write down.

Sume's docs split the hosted tools into read tools and paid tools. Discovery and reads, such as tools_list, balance_get, catalog_list, jobs_status and jobs_result, spend nothing. Paid tools include generate_image, generate_video, music_create and tts_create.

Tool filter options for a Sume server, read 2026-10-05
ApproachHowRisk
Allow listallowed_tool_names=[read tools]New Sume tools stay hidden until you add them
Block listblocked_tool_names=[paid tools]A newly added paid tool is visible by default
Dynamic filterCallable on ToolFilterContextMore code to test
OAuth mcp:read sessionServer hides write and paid toolsNot a filter, but a second layer

A read-only Sume agent

This agent can discover tools, check the balance and read jobs. The key comes from the environment, and the script exits if it is missing. It needs the openai-agents package and network access.

import asyncio, os
from agents import Agent, Runner
from agents.mcp import MCPServerStreamableHttp, create_static_tool_filter

READ_ONLY = ["tools_list", "mcp_health", "balance_get",
             "catalog_list", "jobs_status", "jobs_result"]

async def main() -> None:
    key = os.environ["SUME_API_KEY"]
    async with MCPServerStreamableHttp(
        name="sume",
        params={"url": "https://mcp.sume.com/mcp",
                "headers": {"Authorization": f"Bearer {key}"}},
        tool_filter=create_static_tool_filter(
            allowed_tool_names=READ_ONLY),
    ) as server:
        agent = Agent(name="Auditor", mcp_servers=[server],
                      instructions="Report the balance. Do not generate.")
        result = await Runner.run(agent, "What is my balance?")
        print(result.final_output)

asyncio.run(main())

Why a filter and not just a prompt

An instruction such as do not generate anything is advice. A filter is enforcement. If injected text in a scraped page tells the model to start a render, the tool is not in its list, so the call cannot be formed.

Keep a second layer. Sume paid calls still need idempotency_key, and max_spend_usd is enforced only when you provide it, so give agents that may spend an explicit cap.

Approvals still apply to what remains

Filtering decides what the model can see. Approval decides what it may run. The SDK page describes require_approval as always, never, a boolean, or a per-tool mapping, and the two features combine: expose a small set of tools, then require approval on any that change data (read 2026-10-05).

If you later add a paid tool to the allow list, add it to the approval mapping in the same change, so there is no window in which it is visible and unapproved.

Test the filter

Check the list the model sees, not the one you intended.

  • List the server's tools after the filter and assert no paid name appears.
  • Run the agent against a read-only OAuth session as well, to confirm both layers agree.
  • Re-review the allow list whenever Sume adds a tool to its inventory.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume