22
33import httpx
44import pytest
5+ from mcp_types import (
6+ CallToolRequestParams ,
7+ CallToolResult ,
8+ ListToolsResult ,
9+ PaginatedRequestParams ,
10+ TextContent ,
11+ Tool ,
12+ )
513from starlette .applications import Starlette
614from starlette .middleware import Middleware
715from starlette .middleware .authentication import AuthenticationMiddleware
1422from mcp .server .auth .middleware .bearer_auth import BearerAuthBackend
1523from mcp .server .auth .provider import AccessToken
1624from mcp .server .streamable_http_manager import StreamableHTTPSessionManager
17- from mcp .server .transport_security import TransportSecuritySettings
18- from mcp .types import (
19- CallToolRequestParams ,
20- CallToolResult ,
21- ListToolsResult ,
22- PaginatedRequestParams ,
23- TextContent ,
24- Tool ,
25- )
2625
2726
2827class _EchoTokenVerifier :
@@ -43,14 +42,34 @@ async def _handle_list_tools(ctx: ServerRequestContext, params: PaginatedRequest
4342
4443
4544class _MutableBearerAuth (httpx .Auth ):
46- def __init__ (self , token : str ) -> None :
45+ def __init__ (self , token : str | None ) -> None :
4746 self .token = token
4847
4948 def auth_flow (self , request : httpx .Request ):
50- request .headers ["Authorization" ] = f"Bearer { self .token } "
49+ if self .token is not None :
50+ request .headers ["Authorization" ] = f"Bearer { self .token } "
5151 yield request
5252
5353
54+ async def _call_whoami (asgi_app : Starlette , host : str , token : str | None ) -> str :
55+ auth = _MutableBearerAuth (token )
56+ async with (
57+ httpx .ASGITransport (asgi_app ) as transport ,
58+ httpx .AsyncClient (
59+ transport = transport ,
60+ base_url = f"http://{ host } " ,
61+ auth = auth ,
62+ timeout = httpx .Timeout (30 , read = 30 ),
63+ follow_redirects = True ,
64+ ) as http_client ,
65+ ):
66+ transport_ctx = streamable_http_client (f"http://{ host } /mcp" , http_client = http_client )
67+ async with Client (transport_ctx ) as client : # pragma: no branch
68+ result = await client .call_tool ("whoami" , {})
69+ assert isinstance (result .content [0 ], TextContent )
70+ return result .content [0 ].text
71+
72+
5473@pytest .mark .anyio
5574async def test_get_access_token_reflects_current_request_in_stateful_session () -> None :
5675 host = "testserver"
@@ -61,11 +80,7 @@ async def test_get_access_token_reflects_current_request_in_stateful_session() -
6180 on_list_tools = _handle_list_tools ,
6281 )
6382
64- security = TransportSecuritySettings (
65- allowed_hosts = [host , f"{ host } :*" ],
66- allowed_origins = [f"http://{ host } :*" ],
67- )
68- session_manager = StreamableHTTPSessionManager (app = server , security_settings = security , stateless = False )
83+ session_manager = StreamableHTTPSessionManager (app = server , stateless = False )
6984
7085 asgi_app = Starlette (
7186 routes = [Mount ("/mcp" , app = session_manager .handle_request )],
@@ -76,25 +91,7 @@ async def test_get_access_token_reflects_current_request_in_stateful_session() -
7691 lifespan = lambda app : session_manager .run (),
7792 )
7893
79- auth = _MutableBearerAuth ("token-A" )
8094 async with asgi_app .router .lifespan_context (asgi_app ):
81- async with (
82- httpx .ASGITransport (asgi_app ) as transport ,
83- httpx .AsyncClient (
84- transport = transport ,
85- base_url = f"http://{ host } " ,
86- auth = auth ,
87- timeout = httpx .Timeout (30 , read = 30 ),
88- follow_redirects = True ,
89- ) as http_client ,
90- ):
91- transport_ctx = streamable_http_client (f"http://{ host } /mcp" , http_client = http_client )
92- async with Client (transport_ctx ) as client :
93- r1 = await client .call_tool ("whoami" , {})
94- assert isinstance (r1 .content [0 ], TextContent )
95- assert r1 .content [0 ].text == "token-A"
96-
97- auth .token = "token-B"
98- r2 = await client .call_tool ("whoami" , {})
99- assert isinstance (r2 .content [0 ], TextContent )
100- assert r2 .content [0 ].text == "token-B"
95+ assert await _call_whoami (asgi_app , host , "token-A" ) == "token-A"
96+ assert await _call_whoami (asgi_app , host , "token-B" ) == "token-B"
97+ assert await _call_whoami (asgi_app , host , None ) == "<none>"
0 commit comments