Mount the server's streamable HTTP app on the in-process bridge and yield an httpx client. Yields the httpx client (rooted at the in-process origin) and the live session manager. Tests use this in two ways: for raw-httpx assertions (status codes, headers, SSE bytes) the test speaks HTTP
(
server: Server | MCPServer,
*,
stateless_http: bool = False,
json_response: bool = False,
event_store: EventStore | None = None,
retry_interval: int | None = None,
transport_security: TransportSecuritySettings | None = NO_DNS_REBINDING_PROTECTION,
on_request: Callable[[httpx.Request], Awaitable[None]] | None = None,
on_response: Callable[[httpx.Response], Awaitable[None]] | None = None,
headers: dict[str, str] | None = None,
auth: AuthSettings | None = None,
token_verifier: TokenVerifier | None = None,
auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None,
)
| 169 | |
| 170 | @asynccontextmanager |
| 171 | async def mounted_app( |
| 172 | server: Server | MCPServer, |
| 173 | *, |
| 174 | stateless_http: bool = False, |
| 175 | json_response: bool = False, |
| 176 | event_store: EventStore | None = None, |
| 177 | retry_interval: int | None = None, |
| 178 | transport_security: TransportSecuritySettings | None = NO_DNS_REBINDING_PROTECTION, |
| 179 | on_request: Callable[[httpx.Request], Awaitable[None]] | None = None, |
| 180 | on_response: Callable[[httpx.Response], Awaitable[None]] | None = None, |
| 181 | headers: dict[str, str] | None = None, |
| 182 | auth: AuthSettings | None = None, |
| 183 | token_verifier: TokenVerifier | None = None, |
| 184 | auth_server_provider: OAuthAuthorizationServerProvider[Any, Any, Any] | None = None, |
| 185 | ) -> AsyncIterator[tuple[httpx.AsyncClient, StreamableHTTPSessionManager]]: |
| 186 | """Mount the server's streamable HTTP app on the in-process bridge and yield an httpx client. |
| 187 | |
| 188 | Yields the httpx client (rooted at the in-process origin) and the live session manager. Tests |
| 189 | use this in two ways: for raw-httpx assertions (status codes, headers, SSE bytes) the test |
| 190 | speaks HTTP through the yielded client directly; for client-driven assertions the test wraps |
| 191 | that client in `client_via_http(http)`, which lets several `Client`s share the one mounted |
| 192 | session manager. `on_request` observes every outgoing HTTP request before it leaves the |
| 193 | yielded client; `on_response` observes every HTTP response as its headers arrive (response |
| 194 | bodies of SSE streams are not yet read at that point). |
| 195 | |
| 196 | DNS-rebinding protection is disabled by default; pass explicit settings (or `None` for the |
| 197 | localhost auto-enable behaviour) to test the protection itself. |
| 198 | """ |
| 199 | lowlevel = server._lowlevel_server if isinstance(server, MCPServer) else server |
| 200 | app = lowlevel.streamable_http_app( |
| 201 | stateless_http=stateless_http, |
| 202 | json_response=json_response, |
| 203 | event_store=event_store, |
| 204 | retry_interval=retry_interval, |
| 205 | transport_security=transport_security, |
| 206 | auth=auth, |
| 207 | token_verifier=token_verifier, |
| 208 | auth_server_provider=auth_server_provider, |
| 209 | ) |
| 210 | event_hooks: dict[str, list[Callable[..., Awaitable[None]]]] = {} |
| 211 | if on_request is not None: |
| 212 | event_hooks["request"] = [on_request] |
| 213 | if on_response is not None: |
| 214 | event_hooks["response"] = [on_response] |
| 215 | async with ( |
| 216 | server.session_manager.run(), |
| 217 | httpx.AsyncClient( |
| 218 | transport=StreamingASGITransport(app), base_url=BASE_URL, event_hooks=event_hooks, headers=headers |
| 219 | ) as http_client, |
| 220 | ): |
| 221 | yield http_client, server.session_manager |
| 222 | |
| 223 | |
| 224 | @asynccontextmanager |