mirror of
https://github.com/PrefectHQ/fastmcp.git
synced 2026-08-20 12:34:17 +02:00
92 lines
4.2 KiB
Text
92 lines
4.2 KiB
Text
---
|
|
title: asgi_transport
|
|
sidebarTitle: asgi_transport
|
|
---
|
|
|
|
# `fastmcp.utilities.asgi_transport`
|
|
|
|
|
|
An in-process, full-duplex HTTP transport for driving ASGI applications from httpx.
|
|
|
|
Ported from the MCP Python SDK's test suite (`tests/interaction/transports/_bridge.py`,
|
|
MIT licensed).
|
|
|
|
`httpx2.ASGITransport` runs the application to completion and only then hands the buffered
|
|
response to the caller, so a server that streams its response — as the streamable HTTP
|
|
transport's SSE responses do — can never converse with the client mid-request: a
|
|
server-initiated request nested inside a still-open call deadlocks.
|
|
`StreamingASGITransport` removes that limitation by running the application as a background
|
|
task and forwarding every `http.response.body` chunk to the client the moment it is sent.
|
|
Everything happens on the one event loop: no sockets, no threads, no sleeps.
|
|
|
|
The behavioural contract:
|
|
|
|
- The request body is buffered before the application is invoked (MCP requests are small
|
|
JSON documents); the response streams chunk by chunk.
|
|
- Closing the response — or the whole client — delivers `http.disconnect` to the
|
|
application, exactly as a real server sees when its peer goes away.
|
|
- An exception the application raises before sending `http.response.start` fails the
|
|
originating request with that same exception. After the response has started, a failure
|
|
is visible to the client only through the response itself (status code, truncated body) —
|
|
the same signal a real server over a real socket would give.
|
|
|
|
The transport owns an anyio task group for the application tasks; it is opened and closed by
|
|
`httpx2.AsyncClient`'s own context manager, so the client must be used as a context manager.
|
|
Closing the transport cancels every running application task by default; set
|
|
`cancel_on_close=False` to wait for the application's own disconnect handling instead, which
|
|
is what the legacy SSE transport relies on for resource cleanup.
|
|
|
|
|
|
## Functions
|
|
|
|
### `run_asgi_lifespan` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/utilities/asgi_transport.py#L224" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
|
|
|
```python
|
|
run_asgi_lifespan(app: ASGIApp) -> AsyncIterator[None]
|
|
```
|
|
|
|
|
|
Run an ASGI application's lifespan, driving the protocol as a real server does.
|
|
|
|
The application's lifespan runs inside a dedicated task for the whole duration of
|
|
the context. This matters because a lifespan typically owns cancel scopes and task
|
|
groups — anyio requires those to be exited by the task that entered them, which
|
|
rules out entering the lifespan on one task and leaving it on another (as a pytest
|
|
fixture's setup and teardown phases may do).
|
|
|
|
**Args:**
|
|
- `app`: The ASGI application whose lifespan should run.
|
|
|
|
**Raises:**
|
|
- `RuntimeError`: If the application reports `lifespan.startup.failed`, or reports
|
|
`lifespan.shutdown.failed` (or crashes during shutdown) while the context
|
|
body itself completed successfully. A failure inside the body takes
|
|
precedence and propagates unchanged.
|
|
|
|
|
|
## Classes
|
|
|
|
### `StreamingASGITransport` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/utilities/asgi_transport.py#L71" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
|
|
|
|
|
Drive an ASGI application in-process, streaming each response as it is produced.
|
|
|
|
This is an `httpx2` transport, so it plugs into anything that accepts an
|
|
`httpx2.AsyncClient` — including FastMCP's client transports via their
|
|
`httpx_client_factory` argument.
|
|
|
|
**Args:**
|
|
- `app`: The ASGI application to drive (e.g. `FastMCP.http_app()`).
|
|
- `cancel_on_close`: When True (the default), closing the transport cancels every
|
|
application task still running, so harness teardown can never hang. Set to
|
|
False to wait for the application's own disconnect handling to complete
|
|
instead, which the legacy SSE server transport relies on for cleanup.
|
|
|
|
|
|
**Methods:**
|
|
|
|
#### `handle_async_request` <sup><a href="https://github.com/PrefectHQ/fastmcp/blob/main/fastmcp_slim/fastmcp/utilities/asgi_transport.py#L128" target="_blank"><Icon icon="github" style="width: 14px; height: 14px;" /></a></sup>
|
|
|
|
```python
|
|
handle_async_request(self, request: httpx2.Request) -> httpx2.Response
|
|
```
|