File tree Expand file tree Collapse file tree 4 files changed +80
-3
lines changed Expand file tree Collapse file tree 4 files changed +80
-3
lines changed Original file line number Diff line number Diff line change @@ -269,11 +269,31 @@ async def advanced_tool(ctx: Context) -> str:
269269 return f " Server: { server_name} "
270270```
271271
272+ For web applications, you can access the underlying HTTP request:
273+
274+ ``` python
275+ @mcp.tool ()
276+ async def handle_web_request (ctx : Context) -> dict :
277+ """ Access HTTP request information from the Starlette request."""
278+ request = ctx.get_starlette_request()
279+
280+ # Access HTTP headers, query parameters, etc.
281+ user_agent = request.headers.get(" user-agent" , " Unknown" )
282+ client_ip = request.client.host if request.client else " Unknown"
283+
284+ return {
285+ " user_agent" : user_agent,
286+ " client_ip" : client_ip,
287+ " path" : request.url.path,
288+ }
289+ ```
290+
272291** Advanced Properties:**
273292
274293- ** ` ctx.fastmcp -> FastMCP ` ** : Access the server instance the context belongs to
275294- ** ` ctx.session ` ** : Access the raw ` mcp.server.session.ServerSession ` object
276295- ** ` ctx.request_context ` ** : Access the raw ` mcp.shared.context.RequestContext ` object
296+ - ** ` ctx.get_starlette_request() -> Request ` ** : Access the active Starlette request object (when running with a web server)
277297
278298<Warning >
279299Direct use of ` session ` or ` request_context ` requires understanding the low-level MCP Python SDK and may be less stable than using the methods provided directly on the ` Context ` object.
Original file line number Diff line number Diff line change 1313 SamplingMessage ,
1414 TextContent ,
1515)
16- from pydantic import BaseModel
16+ from pydantic import BaseModel , ConfigDict
1717from pydantic .networks import AnyUrl
18+ from starlette .requests import Request
1819
1920from fastmcp .server .server import FastMCP
21+ from fastmcp .utilities .http import get_current_starlette_request
2022from fastmcp .utilities .logging import get_logger
2123
2224logger = get_logger (__name__ )
@@ -59,6 +61,8 @@ def my_tool(x: int, ctx: Context) -> str:
5961 _request_context : RequestContext [ServerSessionT , LifespanContextT ] | None
6062 _fastmcp : FastMCP | None
6163
64+ model_config = ConfigDict (arbitrary_types_allowed = True )
65+
6266 def __init__ (
6367 self ,
6468 * ,
@@ -222,3 +226,10 @@ async def sample(
222226 )
223227
224228 return result .content
229+
230+ def get_starlette_request (self ) -> Request :
231+ """Get the active starlette request."""
232+ request = get_current_starlette_request ()
233+ if request is None :
234+ raise ValueError ("Request is not available outside a Starlette request" )
235+ return request
Original file line number Diff line number Diff line change 5959from fastmcp .tools import ToolManager
6060from fastmcp .tools .tool import Tool
6161from fastmcp .utilities .decorators import DecoratedFunction
62+ from fastmcp .utilities .http import RequestMiddleware
6263from fastmcp .utilities .logging import configure_logging , get_logger
6364
6465if TYPE_CHECKING :
@@ -822,10 +823,11 @@ async def run_sse_async(
822823 log_level : str | None = None ,
823824 ) -> None :
824825 """Run the server using SSE transport."""
825- starlette_app = self .sse_app ()
826+ app = self .sse_app ()
827+ app = RequestMiddleware (app )
826828
827829 config = uvicorn .Config (
828- starlette_app ,
830+ app ,
829831 host = host or self .settings .host ,
830832 port = port or self .settings .port ,
831833 log_level = log_level or self .settings .log_level .lower (),
Original file line number Diff line number Diff line change 1+ from __future__ import annotations
2+
3+ from contextlib import (
4+ asynccontextmanager ,
5+ )
6+ from contextvars import ContextVar
7+
8+ from starlette .requests import Request
9+
10+ from fastmcp .utilities .logging import get_logger
11+
12+ logger = get_logger (__name__ )
13+
14+
15+ _current_starlette_request : ContextVar [Request | None ] = ContextVar (
16+ "starlette_request" ,
17+ default = None ,
18+ )
19+
20+
21+ @asynccontextmanager
22+ async def starlette_request_context (request : Request ):
23+ token = _current_starlette_request .set (request )
24+ try :
25+ yield
26+ finally :
27+ _current_starlette_request .reset (token )
28+
29+
30+ def get_current_starlette_request () -> Request | None :
31+ return _current_starlette_request .get ()
32+
33+
34+ class RequestMiddleware :
35+ """
36+ Middleware that stores each request in a ContextVar
37+ """
38+
39+ def __init__ (self , app ):
40+ self .app = app
41+
42+ async def __call__ (self , scope , receive , send ):
43+ async with starlette_request_context (Request (scope )):
44+ await self .app (scope , receive , send )
You can’t perform that action at this time.
0 commit comments