Skip to content

Commit b671eaa

Browse files
committed
Return None from Context.headers when the request object has no headers
A custom request type on the request context is not required to expose headers; reading them through a blind cast raised AttributeError. Use getattr with a None fallback and drop the now-unused protocol.
1 parent 2f8b657 commit b671eaa

2 files changed

Lines changed: 11 additions & 11 deletions

File tree

src/mcp/server/mcpserver/context.py

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
from collections.abc import Iterable, Mapping
4-
from typing import TYPE_CHECKING, Any, Generic, Protocol, cast
4+
from typing import TYPE_CHECKING, Any, Generic, cast
55

66
from mcp_types import ClientCapabilities, InputResponseRequestParams, InputResponses, LoggingLevel
77
from pydantic import AnyUrl, BaseModel
@@ -22,11 +22,6 @@
2222
from mcp.server.mcpserver.server import MCPServer
2323

2424

25-
class _HasHeaders(Protocol):
26-
@property
27-
def headers(self) -> Mapping[str, str]: ...
28-
29-
3025
class Context(BaseModel, Generic[LifespanContextT, RequestT]):
3126
"""Context object providing access to MCP capabilities.
3227
@@ -226,12 +221,11 @@ def client_id(self) -> str | None:
226221
def headers(self) -> Mapping[str, str] | None:
227222
"""Request headers carried by this message, when the transport has them.
228223
229-
Populated by HTTP-based transports; `None` on stdio.
224+
Populated by HTTP-based transports; `None` on stdio or when the
225+
transport's request object carries no headers. Headers are
226+
client-supplied input - never treat one as an identity assertion.
230227
"""
231-
request = self.request_context.request
232-
if request is None:
233-
return None
234-
return cast("_HasHeaders", request).headers
228+
return cast("Mapping[str, str] | None", getattr(self.request_context.request, "headers", None))
235229

236230
@property
237231
def request_id(self) -> str:

tests/server/mcpserver/test_server.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,12 @@ def test_context_headers_is_none_without_request():
18231823
assert ctx.headers is None
18241824

18251825

1826+
def test_context_headers_is_none_when_request_carries_no_headers():
1827+
"""A transport may attach a custom request object that has no headers attribute."""
1828+
ctx = Context(request_context=_request_context(object()), mcp_server=MagicMock())
1829+
assert ctx.headers is None
1830+
1831+
18261832
async def test_read_resource_template_error():
18271833
"""Template-creation failure must surface as INTERNAL_ERROR, not INVALID_PARAMS (not-found)."""
18281834
mcp = MCPServer()

0 commit comments

Comments
 (0)