Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SSE client connect SSE Server got Error: " Error in post_writer" and "TypeError: 'NoneType' object is not callable" #101

Closed
stevensu1977 opened this issue Dec 12, 2024 · 6 comments · Fixed by #121

Comments

@stevensu1977
Copy link

stevensu1977 commented Dec 12, 2024

Hi Guys , I want to build sse server and client from sdk example , but I'm not lucky, it can't work, SSE server and client got error :

SSE Server got starlette error like this :

.venv/lib/python3.13/site-packages/starlette/routing.py", line 74, in app
await response(scope, receive, send)
~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not callable

SSE Client ERROR: "Error in post_writer: " , anyone met have this issue ?

mcp 1.1.1
sse-starlette 2.1.3
starlette 0.41.3
uvicorn 0.32.1

sse server code :

  import sys
  import logging
  import anyio
  import click
  import httpx
  import mcp.types as types
  from mcp.server import Server
  
  
  logging.basicConfig(
      #level=logging.CRITICAL,
      level=logging.DEBUG,
      format="%(asctime)s - %(levelname)s - %(message)s",
      stream=sys.stderr,
  )
  
  async def fetch_website(
      url: str,
  ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
      headers = {
          "User-Agent": "MCP Test Server (github.com/modelcontextprotocol/python-sdk)"
      }
      async with httpx.AsyncClient(follow_redirects=True, headers=headers) as client:
          response = await client.get(url)
          response.raise_for_status()
          return [types.TextContent(type="text", text=response.text)]
  
  
  @click.command()
  @click.option("--port", default=8000, help="Port to listen on for SSE")
  @click.option(
      "--transport",
      type=click.Choice(["stdio", "sse"]),
      default="stdio",
      help="Transport type",
  )
  def main(port: int, transport: str) -> int:
      app = Server("mcp-website-fetcher")
  
      @app.call_tool()
      async def fetch_tool(
          name: str, arguments: dict
      ) -> list[types.TextContent | types.ImageContent | types.EmbeddedResource]:
          if name != "fetch":
              raise ValueError(f"Unknown tool: {name}")
          if "url" not in arguments:
              raise ValueError("Missing required argument 'url'")
          return await fetch_website(arguments["url"])
  
      @app.list_tools()
      async def list_tools() -> list[types.Tool]:
          print("list_tools")
          return [
              types.Tool(
                  name="fetch",
                  description="Fetches a website and returns its content",
                  inputSchema={
                      "type": "object",
                      "required": ["url"],
                      "properties": {
                          "url": {
                              "type": "string",
                              "description": "URL to fetch",
                          }
                      },
                  },
              )
          ]
  
      if transport == "sse":
          from mcp.server.sse import SseServerTransport
          from starlette.applications import Starlette
          from starlette.routing import Route
          from starlette.responses import Response
  
          sse = SseServerTransport("/messages")
  
          async def handle_sse(request):
              async with sse.connect_sse(
                  request.scope, request.receive, request._send
              ) as streams:
                  await app.run(
                      streams[0], streams[1], app.create_initialization_options()
                  )
  
          async def handle_messages(request):
              print(request)
              await sse.handle_post_message(request.scope, request.receive, request._send)
  
          async def handle_hello(request):
              print(request)
              return Response("Hello, World!", status_code=200)
  
          starlette_app = Starlette(
              debug=True,
              routes=[
                  Route("/sse", endpoint=handle_sse),
                  Route("/messages", endpoint=handle_messages, methods=["POST"]),
                  Route("/hello", endpoint=handle_hello, methods=["GET"]),
              ],
          )
  
          import uvicorn
          
          uvicorn.run(starlette_app, host="0.0.0.0", port=port)
      else:
          from mcp.server.stdio import stdio_server
  
          async def arun():
              async with stdio_server() as streams:
                  await app.run(
                      streams[0], streams[1], app.create_initialization_options()
                  )
  
          anyio.run(arun)
  
      return 0
  
  main()

sse client code:

  import asyncio
  import subprocess
  
  from mcp.client.session import ClientSession
  from mcp.client.sse import sse_client
  
  
  def main():
      # Start the server
      
      try:
          # Run the client logic
          asyncio.run(client_logic())
      finally:
          # Terminate the server process
          print("Server terminated.")
  
  
  async def client_logic():
      async with sse_client(url="http://127.0.0.1:8000/sse") as (read, write):
          async with ClientSession(read, write) as session:
              await session.initialize()
  
              # List available tools
              tools = await session.list_tools()
              print(tools)
  
              # Call the fetch tool
              result = await session.call_tool("fetch", {"url": "https://example.com"})
              print(result)
  
  
  if __name__ == "__main__":
      main()
@stevensu1977
Copy link
Author

Update , I changed to python 3.10.12, SSE server still have TypeError: 'NoneType' object is not callable, but client works!

(.venv) ➜  sse git:(main) ✗ python example_client.py http://localhost:8000/sse
nextCursor=None tools=[Tool(name='fetch', description='Fetches a website and returns its content', inputSchema={'type': 'object', 'required': ['url'], 'properties': {'url': {'type': 'string', 'description': 'URL to fetch'}}})]
content=[TextContent(type='text', text='<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta charset="utf-8" />\n    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1" />\n    <style type="text/css">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 2em;\n        background-color: #fdfdff;\n        border-radius: 0.5em;\n        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        div {\n            margin: 0 auto;\n            width: auto;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is for use in illustrative examples in documents. You may use this\n    domain in literature without prior coordination or asking for permission.</p>\n    <p><a href="https://www.iana.org/domains/example">More information...</a></p>\n</div>\n</body>\n</html>\n')] isError=False

1 similar comment
@stevensu1977
Copy link
Author

Update , I changed to python 3.10.12, SSE server still have TypeError: 'NoneType' object is not callable, but client works!

(.venv) ➜  sse git:(main) ✗ python example_client.py http://localhost:8000/sse
nextCursor=None tools=[Tool(name='fetch', description='Fetches a website and returns its content', inputSchema={'type': 'object', 'required': ['url'], 'properties': {'url': {'type': 'string', 'description': 'URL to fetch'}}})]
content=[TextContent(type='text', text='<!doctype html>\n<html>\n<head>\n    <title>Example Domain</title>\n\n    <meta charset="utf-8" />\n    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />\n    <meta name="viewport" content="width=device-width, initial-scale=1" />\n    <style type="text/css">\n    body {\n        background-color: #f0f0f2;\n        margin: 0;\n        padding: 0;\n        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;\n        \n    }\n    div {\n        width: 600px;\n        margin: 5em auto;\n        padding: 2em;\n        background-color: #fdfdff;\n        border-radius: 0.5em;\n        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);\n    }\n    a:link, a:visited {\n        color: #38488f;\n        text-decoration: none;\n    }\n    @media (max-width: 700px) {\n        div {\n            margin: 0 auto;\n            width: auto;\n        }\n    }\n    </style>    \n</head>\n\n<body>\n<div>\n    <h1>Example Domain</h1>\n    <p>This domain is for use in illustrative examples in documents. You may use this\n    domain in literature without prior coordination or asking for permission.</p>\n    <p><a href="https://www.iana.org/domains/example">More information...</a></p>\n</div>\n</body>\n</html>\n')] isError=False

@byt3bl33d3r
Copy link

+1 seems like SSE transport is broken in Python 3.13

@raolak
Copy link

raolak commented Dec 19, 2024

+1 .. It would be great if we can have python and javascript sse client example...

@apappascs
Copy link

Sse is broken , tried tool example:

/python3.11/site-packages/starlette/routing.py", line 74, in app
    await response(scope, receive, send)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: 'NoneType' object is not callable

likewise for fastMCP jlowin/fastmcp#69
and other reports: #83

@drorrose
Copy link

+1, seems to be broken for Python 3.12 as well, downgraded to 3.10.12 (thanks @stevensu1977) and client worked (although still getting the same error on server)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants