HTTP Request Access in Hyperia Servers

Added in v2.2.11 – seamlessly reach Starlette Request objects from inside tools, resources, prompts, or any helper buried five calls deep.

When a Hyperia server is exposed via Streamable HTTP (or legacy SSE), each MCP request rides an HTTP envelope. Sometimes you need headers, cookies, query params, or the client’s IP to tailor behaviour—rate limiting, per‑tenant routing, feature flags, etc.

Hyperia exposes a dependency helper so you never have to pass Request objects by hand.


1 get_http_request() Helper

from hyperia.server.dependencies import get_http_request   # starlette.req proxy
from starlette.requests import Request

@mcp.tool()
async def whois():
    req: Request = get_http_request()              # 👍 safe anywhere in call‑stack
    return {
        "ua": req.headers.get("user-agent", "?"),
        "ip": req.client.host if req.client else "?",
        "path": req.url.path,
    }

Key Points

  • Works only in an active HTTP context. If you call it from STDIO / in‑memory runs you’ll get RuntimeError("No active HTTP request").

  • Returns the Starlette Request (FastAPI is Starlette under the hood).

  • Zero coupling—keep function signatures clean; no phantom request params exposed to LLMs.


2 Typical Use‑Cases

2.1 Auth Header Introspection

@mcp.tool()
async def auth_info():
    req = get_http_request()
    auth = req.headers.get("authorization", "")
    scheme, _, token = auth.partition(" ")
    return {"scheme": scheme.title(), "token_present": bool(token)}

2.2 Tenant Resolution via Custom Header

async def get_tenant_id() -> str:
    req = get_http_request()
    return req.headers.get("X-Tenant", "public")

@mcp.resource("data://tenant/{key}")
async def per_tenant_data(key: str) -> dict:
    tenant = await get_tenant_id()
    return db.fetch(tenant, key)   # pseudo code

2.3 Query Param Parsing

@mcp.tool()
async def search_default(page_size: int = 20):
    req = get_http_request()
    size = int(req.query_params.get("page_size", page_size))
    return backend.search(limit=size)

3 Advanced Patterns

3.1 Store Request on Context for Down‑Stream Helpers

from hyperia import Context

@mcp.tool()
async def analyse(ctx: Context):
    req = get_http_request()
    ctx.session_state["request_id"] = req.headers.get("X-Request-ID", ctx.request_id)
    ...

3.2 Custom Middleware Injection

Need CORS, tracing, or IP‑whitelisting?

from starlette.middleware.base import BaseHTTPMiddleware

class TraceMiddleware(BaseHTTPMiddleware):
    async def dispatch(self, request, call_next):
        request.state.trace_id = request.headers.get("X-Trace") or uuid4().hex
        response = await call_next(request)
        response.headers["X-Trace"] = request.state.trace_id
        return response

mcp = Hyperia("API", http_middleware=[TraceMiddleware])

Inside any tool you can then get_http_request() and read request.state.trace_id.

3.3 Ensuring Availability for Mounted Servers

When you mount servers under a Starlette/FastAPI parent, the parent’s middleware executes first. get_http_request() still works because the dependency walks the ASGI scope chain.


4 Security & Edge‑Cases

Scenario
Behaviour

Called from STDIO / in‑memory transport

Raises RuntimeError

Behind reverse proxy

You may need X‑Forwarded‑For logic to get client IP; implement Starlette’s TrustedHostMiddleware or similar

Large file uploads

Use await req.body() or req.stream() inside the tool—mind memory limits

HTTP/2 push trailers

Available via req.headers.raw but uncommon


5 Unit‑Testing Helpers

from starlette.testclient import TestClient as StarClient
from hyperia import Hyperia

srv = Hyperia("T")
@srv.tool() async def echo_ip():
    return get_http_request().client.host

client = StarClient(srv.http_app())
assert client.post("/mcp/tools/call", json={...}).json()["text"] == "127.0.0.1"

6 Migration Notes (≤ v2.2.10)

Pre‑2.2.11 you had to call ctx.get_http_request() from within a tool. That helper is deprecated; switch to the standalone get_http_request() function for clarity and for use in non‑tool helpers.


7 Recap Checklist

  1. Import once: from hyperia.server.dependencies import get_http_request.

  2. Call it inside HTTP‑backed requests only.

  3. Treat returned object as Starlette Request.

  4. Use for headers, cookies, query‑params, file uploads, client IP, etc.

  5. For STDIO/in‑memory modes, guard with if transport.is_http or let RuntimeError propagate.

Last updated