Skip to content

Add ClientSession.register_tool_schema() for dynamic tool discovery patterns #3145

Open

Description

@steditt

Description

Problem

ClientSession.validate_tool_result() validates tool results against cached output schemas in self._tool_output_schemas, which is populated exclusively by list_tools(). When a tool is invoked that wasn't returned by list_tools(), two things happen:

  1. A warning is logged: "Tool {name} not listed by server, cannot validate any structured content"
  2. If the tool returns structuredContent with an outputSchema, validation is skipped entirely — a correctness gap.

This affects MCP servers that use dynamic tool discovery, where list_tools() intentionally returns only a small subset of available tools (e.g., meta-tools like a search tool) and actual tools are discovered at runtime via semantic search or catalog queries.

Use case

AWS Bedrock AgentCore Gateway is an MCP-compatible server that can host hundreds or thousands of tools. Rather than returning all tools in list_tools() (which would overwhelm LLM context windows), it exposes a built-in tool called x_amz_bedrock_agentcore_search. Clients call this tool with a natural language query, and the gateway returns matching tool definitions — including names, descriptions, input schemas, and output schemas. The client then invokes these discovered tools by name via call_tool().

At that point, the SDK doesn't recognize these tools because they never appeared in a list_tools() response. This triggers the warning and skips output validation. The only workaround today is accessing the private _tool_output_schemas dict directly.

AWS Prescriptive Guidance documents this "search function" approach as one of three canonical tool discovery strategies for MCP (alongside static definition and dynamic discovery via list_tools()).

Proposed API

A single public method on ClientSession:

def register_tool_schema(
    self, name: str, output_schema: dict[str, Any] | None = None
) -> None:
    """Register a tool's output schema for result validation.

    Use this when tools are discovered dynamically (e.g., via a catalog
    search API) and won't appear in list_tools() responses.
    """
    self._tool_output_schemas[name] = output_schema

Usage:

# Client discovers tools via semantic search (not list_tools)
search_results = await session.call_tool("x_amz_bedrock_agentcore_search", {"query": "data analysis"})

# Register discovered tools for proper validation
for tool in parse_discovered_tools(search_results):
    session.register_tool_schema(tool.name, tool.output_schema)

# Now call_tool validates structuredContent correctly, no warning logged
result = await session.call_tool("data_analysis_v2", {"input": "..."})

This is purely additive — no changes to existing behavior, no new data structures. It writes to the same dict that _absorb_tool_listing() already populates.

References

Activity

  1. changed the title [-]Add ClientSession.register_tool_schema() for dynamic tool discovery patterns[/-] [+]Add `ClientSession.register_tool_schema()` for dynamic tool discovery patterns[/+] on Jul 22, 2026
  2. JC230903 commented on Jul 22, 2026

    @JC230903

    Hi @steditt, I’d be happy to help implement this if it aligns with the SDK’s direction. Would you be open to me taking this on?

  3. HaoChiBao commented on Aug 11, 2026

    @HaoChiBao

    I'd like to implement a small public ClientSession.register_tool_schema(name, output_schema=...) (and mirror on Client if maintainers want) so dynamic discovery can populate the output-schema cache without touching _tool_output_schemas.

    I re-checked open PRs — none reference #3145 or register_tool_schema. Happy to wait for API-shape feedback / ready for work if preferred; starting a focused branch in the meantime.

    AI-assisted implementation disclosed; I will personally review and understand the full change.

  4. HaoChiBao commented on Aug 11, 2026

    @HaoChiBao

    Implementation is ready on fork branch feat/3145-register-tool-schema.

    Open compare / PR:
    https://github.com/modelcontextprotocol/python-sdk/compare/main...HaoChiBao:feat/3145-register-tool-schema?expand=1

    (gh pr create against upstream currently returns a permissions 404 for this account’s CLI token; compare URL above is the workaround. Happy to adjust API/prune semantics if maintainers want a different contract.)

    AI-assisted implementation disclosed; I reviewed the full change.

  5. added
    enhancementRequest for a new feature that's not currently supported
    P3Nice to haves, rare edge cases
    needs decisionIssue is actionable, needs maintainer decision on whether to implement
    v1Affects the v1.x maintenance line
    v2Affects the v2 line (2.x on main)
    on Aug 14, 2026
  6. AAH20 commented on Sep 28, 2026

    @AAH20

    I prepared a candidate for #3145. It adds ClientSession.register_tool_schema(name, output_schema) for catalog-discovered tools, keeps those schemas separate from tools/list cache pruning, invalidates compiled validators when a registered schema changes, and transfers schema ownership back to the server if the tool later appears in a listing. Tests cover validation, persistence across unrelated complete listings, and server-schema takeover. Validation: full tests/client suite (782 passed, 1 skipped, 1 xfailed), Ruff, and Pyright pass. I see this issue is marked needs-decision, and the SDK contribution guide says external PRs are reviewed only when a maintainer assigns the linked issue. Would a maintainer assign this issue if this API shape is desired? I will hold the PR until then.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Nice to haves, rare edge casesenhancementRequest for a new feature that's not currently supportedneeds decisionIssue is actionable, needs maintainer decision on whether to implementv1Affects the v1.x maintenance linev2Affects the v2 line (2.x on main)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions