fastmcp icon indicating copy to clipboard operation
fastmcp copied to clipboard

ProxyClient causes tool calls to hang with repeated initialize/tools/list requests

Open zzstoatzz opened this issue 2 months ago • 1 comments

Description

When a remote MCP server is mounted via ProxyClient, calling any tool on the main server causes repeated initialize and tools/list MCP requests to the proxy and eventually hangs/times out.

Environment

  • FastMCP version: 2.12.4
  • Tested with multiple remote servers

Observations

  • ✅ Tools work fine when no proxy is mounted
  • ✅ Tools work fine when a local FastMCP instance is mounted
  • ❌ Tools hang when ProxyClient to remote server is mounted
  • During the hang, repeated initialize and tools/list MCP requests are made to the proxied server

Minimal Reproduction

import asyncio

from fastmcp import FastMCP
from fastmcp.client import Client
from fastmcp.server.proxy import ProxyClient

# Create a simple tool that should return quickly
main_mcp = FastMCP("Main Server")


@main_mcp.tool
async def quick_tool() -> dict:
    """A tool that should return immediately."""
    await asyncio.sleep(0.1)  # simulate minimal work
    return {"status": "success", "message": "This should be fast"}


# Mount a remote proxy
proxy = FastMCP.as_proxy(
    ProxyClient("https://prefect-docs-mcp.fastmcp.app/mcp"),
    name="Docs Proxy",
)
main_mcp.mount(proxy, prefix="docs")


async def test_without_proxy():
    """Test the tool without any proxy mounted."""
    test_mcp = FastMCP("Test Server")

    @test_mcp.tool
    async def quick_tool() -> dict:
        """A tool that should return immediately."""
        await asyncio.sleep(0.1)
        return {"status": "success", "message": "This should be fast"}

    async with Client(test_mcp) as client:
        print("Testing WITHOUT proxy...")
        try:
            result = await asyncio.wait_for(
                client.call_tool("quick_tool", arguments={}), timeout=3.0
            )
            print("  ✓ Completed in < 3s")
        except asyncio.TimeoutError:
            print("  ✗ TIMEOUT after 3s")


async def test_with_proxy():
    """Test the tool WITH proxy mounted."""
    async with Client(main_mcp) as client:
        print("Testing WITH proxy mounted...")
        try:
            result = await asyncio.wait_for(
                client.call_tool("quick_tool", arguments={}), timeout=3.0
            )
            print("  ✓ Completed in < 3s")
        except asyncio.TimeoutError:
            print("  ✗ TIMEOUT after 3s (proxy causing hang)")


async def test_with_local_mount():
    """Test with a local FastMCP instance mounted (for comparison)."""
    local_server = FastMCP("Local Server")

    @local_server.tool
    async def local_tool() -> str:
        return "Hello from local"

    test_mcp = FastMCP("Test Server")

    @test_mcp.tool
    async def quick_tool() -> dict:
        await asyncio.sleep(0.1)
        return {"status": "success"}

    test_mcp.mount(local_server, prefix="local")

    async with Client(test_mcp) as client:
        print("Testing with LOCAL FastMCP instance mounted...")
        try:
            result = await asyncio.wait_for(
                client.call_tool("quick_tool", arguments={}), timeout=3.0
            )
            print("  ✓ Completed in < 3s (local mount works fine)")
        except asyncio.TimeoutError:
            print("  ✗ TIMEOUT after 3s")


async def main():
    print("FastMCP ProxyClient Hang Reproduction\n" + "=" * 40)
    await test_without_proxy()
    print()
    await test_with_local_mount()
    print()
    await test_with_proxy()
    print("\nExpected: All tests complete quickly")
    print("Actual: ProxyClient test hangs with repeated initialize/tools/list calls")
    print("\nConclusion: Issue is specific to ProxyClient, not mounting in general")


if __name__ == "__main__":
    asyncio.run(main())

Expected Output

All three tests should complete in < 3s.

Actual Output

FastMCP ProxyClient Hang Reproduction
========================================
Testing WITHOUT proxy...
  ✓ Completed in < 3s

Testing with LOCAL FastMCP instance mounted...
  ✓ Completed in < 3s (local mount works fine)

Testing WITH proxy mounted...
  ✗ TIMEOUT after 3s (proxy causing hang)

Expected: All tests complete quickly
Actual: ProxyClient test hangs with repeated initialize/tools/list calls

Conclusion: Issue is specific to ProxyClient, not mounting in general

Impact

This makes ProxyClient unusable for mounting remote MCP servers, which is a critical feature for composing MCP servers (e.g., prefect-mcp-server needs to proxy docs search functionality).

Workaround

None currently - ProxyClient to remote servers is broken. Local FastMCP instance mounting works fine.

zzstoatzz avatar Oct 07 '25 19:10 zzstoatzz

Are we safe to close this or do you still have any concerns here?

strawgate avatar Oct 12 '25 01:10 strawgate