Skip to content

The cleanup procedure after "yield" in lifespan is unreachable on Windows #1027

Description

@ScatterTemple

Initial Checks

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.pyA minimum code to use MCP server.Please run this file."""importosimportsysimportasyncioimportloggingfromdotenvimportload_dotenvfromopenaiimportAsyncOpenAIfromagentsimportAgent, Runner, OpenAIChatCompletionsModelfromagents.mcpimportMCPServerStdio, MCPServerStdioParamsload_dotenv() # loading OPENAI_API_KEYlogging.basicConfig(level=logging.INFO)
logger=logging.getLogger("agent_runner")
# disable library loggerslogging.getLogger('mcp').setLevel(logging.ERROR)
logging.getLogger('httpx').setLevel(logging.ERROR)
asyncdefmain():
# Launch MCP serverparams=MCPServerStdioParams(
command=sys.executable,
args=[os.path.join(os.path.dirname(__file__), "srv_stdio.py")],
env={},
encoding="utf-8"
)
asyncwithMCPServerStdio(params=params, name="MCP tool") asmcp_server:
logger.info("4. MCP stdio server running")
# Create agentagent=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=awaitRunner.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.pyA minimum code to show ignoring after "yield" in lifetime.Please put this file on the same folder as "agent_runner.py"."""from __future__ importannotationsas_annotationsimportosimportsysimportloggingfromcollections.abcimportAsyncIteratorfromcontextlibimportasynccontextmanagerfrommcp.server.fastmcpimportFastMCPfrommcp.server.lowlevel.serverimportServer, LifespanResultT, RequestTlogging.basicConfig(level=logging.INFO)
logger=logging.getLogger("stdio server")
# disable library loggerslogging.getLogger('mcp').setLevel(logging.ERROR)
logging.getLogger('httpx').setLevel(logging.ERROR)
ifsys.platform=="win32"andos.environ.get('PYTHONIOENCODING') isNone:
sys.stdin.reconfigure(encoding="utf-8")
sys.stdout.reconfigure(encoding="utf-8")
sys.stderr.reconfigure(encoding="utf-8")
@asynccontextmanagerasyncdeflifespan(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 shownmcp=FastMCP(
"EchoMCPServer",
lifespan=lifespan,
)
@mcp.tool()defecho(msg: str) ->str:
"""Make the passed string reversed."""logger.info(f"5. [echo] called with msg='{msg}'")
returnmsg[::-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.py102-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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions