Skip to content

Documentation on mounting to existing ASGI server is missing information #1484

@lei-lei-shanda

Description

@lei-lei-shanda

Initial Checks

Description

Overview

PR #1172 is missing information.

Minimal Working Example

take a slightly modified script from here and run it with uv run --script main.py

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "mcp>=1.12",
#     "starlette",
#     "uvicorn",
# ]
# ///
"""
Basic example showing how to mount StreamableHTTP server in Starlette.

Run from the repository root:
    uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
"""

import asyncio

from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

# Create MCP server
mcp = FastMCP("My App", stateless_http=True)


@mcp.tool()
def hello() -> str:
    """A simple hello tool"""
    return "Hello from MCP!"


# Mount the StreamableHTTP server to the existing ASGI server
app = Starlette(
    routes=[
        Mount("/", app=mcp.streamable_http_app()),
    ]
)

if __name__ == "__main__":
    import asyncio
    import uvicorn

    asyncio.create_task(mcp.run(transport="streamable-http"))
    asyncio.run(uvicorn.run(app, host="0.0.0.0", port=8000))

and test it with the following bash script (bash test_mcp_server.sh)

#!/usr/bin/env bash

MCP_SERVER_PORT=8000
MCP_SERVER_HOST=0.0.0.0

# for mcp service
# first initialize it
curl -X POST "http://${MCP_SERVER_HOST}:${MCP_SERVER_PORT}/mcp" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json,text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 1,
    "method": "initialize",
    "params": {
      "protocolVersion": "2025-03-26",
      "capabilities": {
        "tools": {},
        "resources": {},
        "prompts": {}
      },
      "clientInfo": {
        "name": "test-client",
        "version": "1.0.0"
      }
    }
  }'

echo -e "\nInitialized"

curl -X POST "http://${MCP_SERVER_HOST}:${MCP_SERVER_PORT}/mcp" \
    -H "Content-Type: application/json" \
    -H "Accept: application/json,text/event-stream" \
    -d '{
    "jsonrpc": "2.0",
    "id": 2,
    "method": "tools/list",
    "params": {}
  }'

echo -e "\nTools listed"

curl -X POST http://${MCP_SERVER_HOST}:${MCP_SERVER_PORT}/mcp \
  -H "Content-Type: application/json" \
  -H "Accept: application/json,text/event-stream" \
  -d '{
    "jsonrpc": "2.0",
    "id": 3,
    "method": "tools/call",
    "params": {
      "name": "hello",
      "arguments": {}
    }
  }'

echo -e "\nTools called"

Result:

  1. with asyncio.create_task(...) removed, bash test_mcp_server.sh returns Internal Server Error. the mcp server reports
lib/python3.12/site-packages/mcp/server/streamable_http_manager.py", line 138, in handle_request
    raise RuntimeError("Task group is not initialized. Make sure to use run().")
RuntimeError: Task group is not initialized. Make sure to use run().
  1. with asycnio.create_task(...) added, bash test_mcp_server.sh returns expected result.
bash test_echo_server.sh
event: message
data: {"jsonrpc":"2.0","id":1,"result":{"protocolVersion":"2025-03-26","capabilities":{"experimental":{},"prompts":{"listChanged":false},"resources":{"subscribe":false,"listChanged":false},"tools":{"listChanged":false}},"serverInfo":{"name":"My App","version":"1.17.0"}}}


Initialized
event: message
data: {"jsonrpc":"2.0","id":2,"result":{"tools":[{"name":"hello","description":"A simple hello tool","inputSchema":{"properties":{},"title":"helloArguments","type":"object"},"outputSchema":{"properties":{"result":{"title":"Result","type":"string"}},"required":["result"],"title":"helloOutput","type":"object"}}]}}


Tools listed
event: message
data: {"jsonrpc":"2.0","id":3,"result":{"content":[{"type":"text","text":"Hello from MCP!"}],"structuredContent":{"result":"Hello from MCP!"},"isError":false}}


Tools called

Suggested Fix

I can submit a PR to update all related snippets under this folder if this is indeed an issue.

Example Code

# /// script
# requires-python = ">=3.12"
# dependencies = [
#     "mcp>=1.12",
#     "starlette",
#     "uvicorn",
# ]
# ///
"""
Basic example showing how to mount StreamableHTTP server in Starlette.

Run from the repository root:
    uvicorn examples.snippets.servers.streamable_http_basic_mounting:app --reload
"""

import asyncio

from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount

# Create MCP server
mcp = FastMCP("My App", stateless_http=True)


@mcp.tool()
def hello() -> str:
    """A simple hello tool"""
    return "Hello from MCP!"


# Mount the StreamableHTTP server to the existing ASGI server
app = Starlette(
    routes=[
        Mount("/", app=mcp.streamable_http_app()),
    ]
)

if __name__ == "__main__":
    import asyncio
    import uvicorn

    # asyncio.create_task(mcp.run(transport="streamable-http"))
    asyncio.run(uvicorn.run(app, host="0.0.0.0", port=8000))

Python & MCP Python SDK

1.17.0

Metadata

Metadata

Assignees

No one assigned

    Labels

    P0Broken core functionality, security issues, critical missing featuredocumentationImprovements or additions to documentationgood first issueGood for newcomersready for workEnough information for someone to start working on

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions