fastmcp
fastmcp copied to clipboard
ProxyClient causes tool calls to hang with repeated initialize/tools/list requests
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
initializeandtools/listMCP 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.
Are we safe to close this or do you still have any concerns here?