fix(tools): gate MCP resource and prompt registration behind enabledTools

The enabledTools allowlist was only enforced for MCP tools returned by
session.list_tools(). Resources and prompts from session.list_resources()
and session.list_prompts() were registered unconditionally, allowing a
deny-all or restrictive enabledTools config to leak resource and prompt
capabilities to the model.

Now resources and prompts are only registered when allow_all_tools is
true (default ["*"] wildcard). Any explicit tool restriction — including
enabledTools: [] (deny-all) or a list of specific tool names — also
blocks resource and prompt registration from that server.

Fixes #4435
This commit is contained in:
michaelxer
2026-06-25 16:10:37 +08:00
committed by Xubin Ren
parent f60b3c7920
commit 246ea8ef61
2 changed files with 114 additions and 21 deletions
+41 -21
View File
@@ -797,31 +797,51 @@ async def connect_mcp_servers(
", ".join(available_wrapped_names) or "(none)",
)
try:
resources_result = await session.list_resources()
for resource in resources_result.resources:
wrapper = MCPResourceWrapper(
session, name, resource, resource_timeout=cfg.tool_timeout
)
registry.register(wrapper)
registered_count += 1
# Only register resources and prompts when no tool restriction is
# active. enabledTools is a per-*tool* allowlist; resources and
# prompts have no equivalent name filter, so they must be skipped
# whenever the operator specified a tool subset. An empty list
# (deny-all) or a list of specific tool names both indicate that
# the operator intended to restrict capabilities — registering
# unrestricted resource/prompt wrappers would violate that intent.
# The default ["*"] (allow-all) means no restriction was intended.
register_extras = allow_all_tools
if register_extras:
try:
resources_result = await session.list_resources()
for resource in resources_result.resources:
wrapper = MCPResourceWrapper(
session, name, resource, resource_timeout=cfg.tool_timeout
)
registry.register(wrapper)
registered_count += 1
logger.debug(
"MCP: registered resource '{}' from server '{}'",
wrapper.name,
name,
)
except Exception as e:
logger.debug(
"MCP: registered resource '{}' from server '{}'", wrapper.name, name
"MCP server '{}': resources not supported or failed: {}", name, e
)
except Exception as e:
logger.debug("MCP server '{}': resources not supported or failed: {}", name, e)
try:
prompts_result = await session.list_prompts()
for prompt in prompts_result.prompts:
wrapper = MCPPromptWrapper(
session, name, prompt, prompt_timeout=cfg.tool_timeout
try:
prompts_result = await session.list_prompts()
for prompt in prompts_result.prompts:
wrapper = MCPPromptWrapper(
session, name, prompt, prompt_timeout=cfg.tool_timeout
)
registry.register(wrapper)
registered_count += 1
logger.debug(
"MCP: registered prompt '{}' from server '{}'",
wrapper.name,
name,
)
except Exception as e:
logger.debug(
"MCP server '{}': prompts not supported or failed: {}", name, e
)
registry.register(wrapper)
registered_count += 1
logger.debug("MCP: registered prompt '{}' from server '{}'", wrapper.name, name)
except Exception as e:
logger.debug("MCP server '{}': prompts not supported or failed: {}", name, e)
logger.info(
"MCP server '{}': connected, {} capabilities registered", name, registered_count