modelcontextprotocol / modelcontextprotocol/python-sdk

FastMCP server with SSE transport fails to shut down on a signal

未關閉
#514 12 則留言 6 個 reaction 已指派 0 人 在 GitHub 檢視

還沒有人認領這個 Issue。

bug P3 ready for work
主要語言
Python
星號
24.3k
分支
4k
平均合併
1 天 1 小時
30 天內合併 PR
31

描述

A very simple server fails to shut down on a signal if it processed at least one request:

❯ python test-server.py --port=8085
INFO:     Started server process [216035]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8085 (Press CTRL+C to quit)
INFO:     127.0.0.1:55188 - "GET /sse HTTP/1.1" 200 OK
INFO:     127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
INFO:     127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
INFO:     127.0.0.1:55192 - "POST /messages/?session_id=e690de9733914d09aebf2b0e8c78191c HTTP/1.1" 202 Accepted
Processing request of type CallToolRequest
^CINFO:     Shutting down
INFO:     Waiting for background tasks to complete. (CTRL+C to force quit)

The expected behaviour would be:

❯ python test-server.py --port=8085
INFO:     Started server process [216006]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8085 (Press CTRL+C to quit)
^CINFO:     Shutting down
INFO:     Waiting for application shutdown.
INFO:     Application shutdown complete.
INFO:     Finished server process [216006]

Here is the code for both the server and the client.

What do I miss?

the server

import click
import sys
from pydantic import Field
from mcp.server.fastmcp import FastMCP

@click.command()
@click.option("--port", default=8085, help="Port to listen", type=int)
def main(port: int):
    mcp = FastMCP(
        "mcp-echo-tool",
        debug=True,
        log_level="INFO",
        port=port,
    )

    @mcp.tool(name="echo")
    async def echo_tool(string: str = Field(description="A string to echo back")) -> str:
        """Echoes back the input string"""
        return string

    mcp.run(transport='sse')
    return 0

if __name__ == "__main__":
    sys.exit(main())

and the client

import asyncio
import click
import sys
from mcp.client.session import ClientSession
from mcp.client.sse import sse_client

async def client(url, string):
    async with sse_client(url) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool("echo", {"string": string})
            print(result.content)

@click.command()
@click.option('--url', default='http://localhost:8085', help='MCP Server URL', type=str)
@click.argument('string', type=str)
def main(url: str, string: str):
    asyncio.run(client(url, string))

if __name__ == "__main__":
    sys.exit(main())

貢獻指南

開啟貢獻指南

從這裡開始

  1. 先讀完整個 Issue,再讀專案的貢獻指南。
  2. 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
  3. Fork 儲存庫,在一個分支上完成修改。
  4. 送出 Pull Request,並在描述裡引用這個 Issue 編號。

研究方向

使用提供的 FastMCP SSE 伺服器和用戶端重現關閉行為,先確認請求已處理完畢,再傳送訊號。接著追蹤 FastMCP SSE 伺服器的生命週期及其 Uvicorn 關閉路徑;當程序進入應用程式關閉階段,並在不需要第二個訊號的情況下結束時,即表示完成。

由索引模型根據 Issue 內容生成。

評估

技術堆疊
python
領域
api, backend
Issue 類型
缺陷
難度
4/5
預估耗時
3-5 天
活躍度
活躍
描述清晰度
基本清楚
新手友好度
48/100

把新 issue 寄到你的電子郵件信箱

精選適合新手參與的 GitHub issue 摘要。