Skip to content

Feature request: public tool-enumeration API #3967

@dreamrec

Description

@dreamrec

Summary

I'm building an MCP server (LivePilot — 325 tools across 45 domains, production system for Ableton Live 12) and I need to iterate every registered tool at module-import time in order to post-process the generated JSON Schemas.

There's currently no stable public way to do this, so I'm reaching into private internals and the paths have changed across FastMCP versions. I'd love a small public surface to depend on instead.

Why I need to walk the registry

Some MCP hosts serialize integer/number tool arguments as JSON strings ("5" instead of 5). To keep those tools working, I walk every tool's schema and mutate numeric properties into anyOf: [integer, string] with a coercion hint. ~60 of my tools accept numeric parameters, so without this post-processing they error out on those hosts.

The patch is applied at import time (before any event loop is running), so await mcp.list_tools() isn't a natural fit — it'd require spinning an event loop just to read registrations.

What the workaround looks like today

After several private-internal renames, my current probe chain is:

def _get_all_tools():
    """Get all registered tools — defends against FastMCP internal drift."""
    probes = [
        # FastMCP 0.x
        ("_tool_manager._tools", lambda: list(mcp._tool_manager._tools.values())),
        # FastMCP 3.0–3.2
        ("_local_provider._components",
         lambda: list(mcp._local_provider._components.values())),
        # FastMCP 3.3+ speculative (anticipated rename)
        ("_local_provider._tools",
         lambda: list(mcp._local_provider._tools.values())),
        # Future public API — this FR
        ("list_tools", lambda: list(mcp.list_tools())),
    ]
    for label, fn in probes:
        try:
            tools = fn()
        except (AttributeError, TypeError):
            continue
        if tools:
            return tools
    # Loud stderr diagnostic + return []
    ...

I pin fastmcp>=3.0.0,<3.3.0 specifically because of this fragility. (LivePilot source)

What I'd love

Any one of these would work — a tiny, stable public surface:

# Option A — property
for tool in mcp.tools:
    ...

# Option B — sync accessor
for tool in mcp.list_tools_sync():
    ...

The sync-iteration flavor is the important bit for the import-time use case. The internals can stay private; just expose the registry.

Scope

~10 lines of upstream code (a property on FastMCP that returns the current tools).

Happy to send a PR if a maintainer can confirm this is a direction you're open to. Thank you for FastMCP — it's the backbone of the whole project.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementImprovement to existing functionality. For issues and smaller PR improvements.serverRelated to FastMCP server implementation or server-side functionality.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions