Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
Dear developpers,
I am developing with MCP SDK Python on Windows. I noticed that the resource cleanup process of my local process defined inside the lifespan
is not executed at all. To investigate the issue, I tested with the minimal code below and found that the code after the yield
statement is never executed, regardless of whether an error occurs or not.
I haven’t been able to test this on platforms other than Windows or with the SSE transport.
"""agent_runner.py
A minimum code to use MCP server.
Please run this file.
"""
import os
import sys
import asyncio
import logging
from dotenv import load_dotenv
from openai import AsyncOpenAI
from agents import Agent, Runner, OpenAIChatCompletionsModel
from agents.mcp import MCPServerStdio, MCPServerStdioParams
load_dotenv() # loading OPENAI_API_KEY
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("agent_runner")
# disable library loggers
logging.getLogger('mcp').setLevel(logging.ERROR)
logging.getLogger('httpx').setLevel(logging.ERROR)
async def main():
# Launch MCP server
params = MCPServerStdioParams(
command=sys.executable,
args=[os.path.join(os.path.dirname(__file__), "srv_stdio.py")],
env={},
encoding="utf-8"
)
async with MCPServerStdio(params=params, name="MCP tool") as mcp_server:
logger.info("4. MCP stdio server running")
# Create agent
agent = Agent(
name="AgentWithMCP",
instructions="Answer with using tools.",
model=OpenAIChatCompletionsModel(model="gpt-4", openai_client=AsyncOpenAI()),
mcp_servers=[mcp_server]
)
prompt = "Please reverse the word 'hello'."
logger.info(f" PROMPT: {prompt}")
result = await Runner.run(agent, input=prompt)
logger.info(f" FINAL OUTPUT: {result.final_output}")
if __name__ == "__main__":
logger.info("1. Starting program...")
asyncio.run(main())
logger.info("8. Program terminated.")
"""srv_stdio.py
A minimum code to show ignoring after "yield" in lifetime.
Please put this file on the same folder as "agent_runner.py".
"""
from __future__ import annotations as _annotations
import os
import sys
import logging
from collections.abc import AsyncIterator
from contextlib import asynccontextmanager
from mcp.server.fastmcp import FastMCP
from mcp.server.lowlevel.server import Server, LifespanResultT, RequestT
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("stdio server")
# disable library loggers
logging.getLogger('mcp').setLevel(logging.ERROR)
logging.getLogger('httpx').setLevel(logging.ERROR)
if sys.platform == "win32" and os.environ.get('PYTHONIOENCODING') is None:
sys.stdin.reconfigure(encoding="utf-8")
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
@asynccontextmanager
async def lifespan(server: Server[LifespanResultT, RequestT]) -> AsyncIterator[object]:
logger.info('3. Launching Server...')
try:
yield {}
finally:
logger.info('6. Terminating Server '
'(Want to release my resources here).') # not shown
mcp = FastMCP(
"EchoMCPServer",
lifespan=lifespan,
)
@mcp.tool()
def echo(msg: str) -> str:
"""Make the passed string reversed."""
logger.info(f"5. [echo] called with msg='{msg}'")
return msg[::-1]
if __name__ == "__main__":
logger.info("2. Starting MCP server...")
mcp.run()
logger.info("7. MCP server terminated.") # not shown
The outputs missing "6. Terminating Server ~" and "7. MCP server terminated.".
INFO:agent_runner:1. Starting program...
INFO:stdio server:2. Starting MCP server...
INFO:stdio server:3. Launching Server...
INFO:agent_runner:4. MCP stdio server running
INFO:agent_runner: PROMPT: Please reverse the word 'hello'.
INFO:stdio server:5. [echo] called with msg='olleh'
INFO:agent_runner: FINAL OUTPUT: The reverse of 'hello' is 'olleh'.
INFO:agent_runner:8. Program terminated.
I suspected that this issue might be due to the process being forcibly terminated when exiting the with
block in MCPServerStdio
. Upon investigation, I found that modifying the following section allows the sample code to correctly display "6. Terminating Server ~" and "7. MCP server terminated.", and the cleanup process is executed as intended.
# mcp/client/stdio/win32.py
102 - process.terminate()
102 + os.kill(process.pid, signal.CTRL_C_EVENT)
This change seems to resolve the issue on my end, but do you think it could be helpful for improving the MCP SDK overall?
Example Code
Python & MCP Python SDK
Python 3.10
mcp 1.9.4
openai-agents 0.0.17