MCP Tool Execution Hangs Indefinitely In Stdio Mode When Calling External Python Scripts
Introduction
The MCP (Message Command Protocol) tool is a powerful framework for building and managing distributed systems. However, when using the FastMCP server in stdio transport mode, any tool that attempts to execute an external Python script will hang indefinitely until timeout, without returning any result or error. This behavior is not observed when using the SSE (Server-Sent Events) transport mode. In this article, we will delve into the details of this issue and explore possible solutions.
Describe the Bug
When using FastMCP in stdio transport mode, any tool that attempts to execute an external Python script will hang indefinitely until timeout, without returning any result or error. The same code works perfectly when using SSE transport mode. This behavior is a significant issue, as it prevents users from executing external scripts and obtaining results in a timely manner.
To Reproduce
To reproduce this behavior, follow these steps:
Step 1: Create a Minimal External Script
Create a minimal external script (minimal_script.py) that simply prints output and exits:
import sys
def main():
print("Successfully Call!")
return 0
if __name__ == "__main__":
sys.exit(main())
Step 2: Create an MCP Server with a Tool
Create an MCP server with a tool that calls this external script:
import os
import sys
import asyncio
import subprocess
from typing import Dict, Any
from mcp.server.fastmcp import FastMCP
# Initialize MCP
mcp = FastMCP("bug_demo")
@mcp.tool()
async def call_external_script() -> Dict[str, Any]:
"""Call external script and return result"""
# Get script path
current_dir = os.path.dirname(os.path.abspath(__file__))
script_path = os.path.join(current_dir, "minimal_script.py")
# Build command
cmd = [sys.executable, script_path]
try:
# Execute external command
process = await asyncio.create_subprocess_exec(
*cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE
)
# Wait for process to complete and get output
stdout, stderr = await process.communicate()
return {
"success": process.returncode == 0,
"stdout": stdout.decode().strip(),
"stderr": stderr.decode().strip(),
"return_code": process.returncode
}
except Exception as e:
return {
"success": False,
"error": str(e)
}
if __name__ == "__main__":
mcp.run(transport="stdio")
# mcp.run(transport="sse")
Step 3: Run the Server in Stdio Mode
Run the server in stdio mode.
Step 4: Call the Tool
Call the tool (Using MCP Inspector) that executes the external script.
Step 5: Observe the Behavior
Observe that the tool call hangs indefinitely with no response.
Expected Behavior
The tool should execute the external Python script, capture its output, and return the results promptly, regardless of the transport mode used (stdio or SSEScreenshots
When using stdio, the tool will hang indefinitely until timeout.
However, correct results can be obtained by using SSE mode.
Desktop (please complete the following information):
- OS: Windows 11 24H2 26100.3775
- Python 3.10.0
- mcp 1.7.1
Additional Context
- The issue persists regardless of whether using asyncio.create_subprocess_exec() or subprocess.run().
- I tried printing something in an external py script and found that when calling the tool in STDIO mode, it would be unresponsive, but after timeout, the external py script would be executed and something would be printed out. This is like the running of a py script being blocked until the tool call times out before it can run.
Possible Solutions
Based on the analysis, the following possible solutions can be explored:
- Use SSE Transport Mode: As observed, using SSE transport mode resolves the issue. This suggests that the problem lies in the stdio transport mode implementation.
- Modify the Tool Code: The tool code can be modified to use a different approach for executing external scripts, such as using the
subprocess
module or a third-party library. - Update the MCP Server: The MCP server can be updated to fix the issue in the stdio transport mode implementation.
- Use a Different Python Version: The issue might be specific to the Python version being used. Trying a different version might resolve the problem.
Conclusion
Q: What is the MCP tool, and what is its purpose?
A: The MCP (Message Command Protocol) tool is a powerful framework for building and managing distributed systems. Its purpose is to provide a flexible and scalable way to communicate between different components of a system.
Q: What is the issue with the MCP tool in stdio mode?
A: When using the FastMCP server in stdio transport mode, any tool that attempts to execute an external Python script will hang indefinitely until timeout, without returning any result or error.
Q: What are the steps to reproduce the issue?
A: To reproduce the issue, follow these steps:
- Create a minimal external script (minimal_script.py) that simply prints output and exits.
- Create an MCP server with a tool that calls this external script.
- Run the server in stdio mode.
- Call the tool (Using MCP Inspector) that executes the external script.
- Observe that the tool call hangs indefinitely with no response.
Q: What is the expected behavior?
A: The tool should execute the external Python script, capture its output, and return the results promptly, regardless of the transport mode used (stdio or SSE).
Q: What are the possible solutions to the issue?
A: Based on the analysis, the following possible solutions can be explored:
- Use SSE Transport Mode: As observed, using SSE transport mode resolves the issue. This suggests that the problem lies in the stdio transport mode implementation.
- Modify the Tool Code: The tool code can be modified to use a different approach for executing external scripts, such as using the
subprocess
module or a third-party library. - Update the MCP Server: The MCP server can be updated to fix the issue in the stdio transport mode implementation.
- Use a Different Python Version: The issue might be specific to the Python version being used. Trying a different version might resolve the problem.
Q: What are the system requirements for reproducing the issue?
A: The system requirements for reproducing the issue are:
- OS: Windows 11 24H2 26100.3775
- Python 3.10.0
- mcp 1.7.1
Q: What are the additional context and observations?
A: The issue persists regardless of whether using asyncio.create_subprocess_exec() or subprocess.run(). Additionally, printing something in an external py script and found that when calling the tool in STDIO mode, it would be unresponsive, but after timeout, the external py script would be executed and something would be printed out. This is like the running of a py script being blocked until the tool call times out before it can run.
Q: What is the next step to resolve the issue?
A: The next step to resolve the issue is to further investigate and test the possible solutions mentioned above. This includes using SSE transport mode, modifying the tool code, updating the MCP server, or using a different Python version.