Skip to content

Commit dca448e

Browse files
committed
fix: correct Context type parameters in examples and tests
Context is Generic[LifespanContextT, RequestT], not Generic[ServerSessionT, ...]. The Context[ServerSession, None] pattern scattered across examples, tests, and docs was passing ServerSession as LifespanContextT, which is semantically wrong (the type checker only accepted it because LifespanContextT is unbounded). Both TypeVars have PEP 696 defaults (dict[str, Any] and Any respectively), so bare Context is the correct annotation when neither parameter needs to be constrained. This also removes the now-unused ServerSession imports from the affected files.
1 parent 7ba41dc commit dca448e

File tree

12 files changed

+58
-74
lines changed

12 files changed

+58
-74
lines changed

README.v2.md

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -346,13 +346,12 @@ Tools can optionally receive a Context object by including a parameter with the
346346
<!-- snippet-source examples/snippets/servers/tool_progress.py -->
347347
```python
348348
from mcp.server.mcpserver import Context, MCPServer
349-
from mcp.server.session import ServerSession
350349

351350
mcp = MCPServer(name="Progress Example")
352351

353352

354353
@mcp.tool()
355-
async def long_running_task(task_name: str, ctx: Context[ServerSession, None], steps: int = 5) -> str:
354+
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
356355
"""Execute a task with progress updates."""
357356
await ctx.info(f"Starting: {task_name}")
358357

@@ -694,13 +693,12 @@ The Context object provides the following capabilities:
694693
<!-- snippet-source examples/snippets/servers/tool_progress.py -->
695694
```python
696695
from mcp.server.mcpserver import Context, MCPServer
697-
from mcp.server.session import ServerSession
698696

699697
mcp = MCPServer(name="Progress Example")
700698

701699

702700
@mcp.tool()
703-
async def long_running_task(task_name: str, ctx: Context[ServerSession, None], steps: int = 5) -> str:
701+
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
704702
"""Execute a task with progress updates."""
705703
await ctx.info(f"Starting: {task_name}")
706704

@@ -826,7 +824,6 @@ import uuid
826824
from pydantic import BaseModel, Field
827825

828826
from mcp.server.mcpserver import Context, MCPServer
829-
from mcp.server.session import ServerSession
830827
from mcp.shared.exceptions import UrlElicitationRequiredError
831828
from mcp.types import ElicitRequestURLParams
832829

@@ -844,7 +841,7 @@ class BookingPreferences(BaseModel):
844841

845842

846843
@mcp.tool()
847-
async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerSession, None]) -> str:
844+
async def book_table(date: str, time: str, party_size: int, ctx: Context) -> str:
848845
"""Book a table with date availability check.
849846
850847
This demonstrates form mode elicitation for collecting non-sensitive user input.
@@ -868,7 +865,7 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS
868865

869866

870867
@mcp.tool()
871-
async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> str:
868+
async def secure_payment(amount: float, ctx: Context) -> str:
872869
"""Process a secure payment requiring URL confirmation.
873870
874871
This demonstrates URL mode elicitation using ctx.elicit_url() for
@@ -892,7 +889,7 @@ async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> st
892889

893890

894891
@mcp.tool()
895-
async def connect_service(service_name: str, ctx: Context[ServerSession, None]) -> str:
892+
async def connect_service(service_name: str, ctx: Context) -> str:
896893
"""Connect to a third-party service requiring OAuth authorization.
897894
898895
This demonstrates the "throw error" pattern using UrlElicitationRequiredError.
@@ -933,14 +930,13 @@ Tools can interact with LLMs through sampling (generating text):
933930
<!-- snippet-source examples/snippets/servers/sampling.py -->
934931
```python
935932
from mcp.server.mcpserver import Context, MCPServer
936-
from mcp.server.session import ServerSession
937933
from mcp.types import SamplingMessage, TextContent
938934

939935
mcp = MCPServer(name="Sampling Example")
940936

941937

942938
@mcp.tool()
943-
async def generate_poem(topic: str, ctx: Context[ServerSession, None]) -> str:
939+
async def generate_poem(topic: str, ctx: Context) -> str:
944940
"""Generate a poem using LLM sampling."""
945941
prompt = f"Write a short poem about {topic}"
946942

@@ -970,13 +966,12 @@ Tools can send logs and notifications through the context:
970966
<!-- snippet-source examples/snippets/servers/notifications.py -->
971967
```python
972968
from mcp.server.mcpserver import Context, MCPServer
973-
from mcp.server.session import ServerSession
974969

975970
mcp = MCPServer(name="Notifications Example")
976971

977972

978973
@mcp.tool()
979-
async def process_data(data: str, ctx: Context[ServerSession, None]) -> str:
974+
async def process_data(data: str, ctx: Context) -> str:
980975
"""Process data with logging."""
981976
# Different log levels
982977
await ctx.debug(f"Debug: Processing '{data}'")

examples/servers/everything-server/mcp_everything_server/server.py

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from mcp.server import ServerRequestContext
1414
from mcp.server.mcpserver import Context, MCPServer
1515
from mcp.server.mcpserver.prompts.base import UserMessage
16-
from mcp.server.session import ServerSession
1716
from mcp.server.streamable_http import EventCallback, EventMessage, EventStore
1817
from mcp.types import (
1918
AudioContent,
@@ -142,7 +141,7 @@ def test_multiple_content_types() -> list[TextContent | ImageContent | EmbeddedR
142141

143142

144143
@mcp.tool()
145-
async def test_tool_with_logging(ctx: Context[ServerSession, None]) -> str:
144+
async def test_tool_with_logging(ctx: Context) -> str:
146145
"""Tests tool that emits log messages during execution"""
147146
await ctx.info("Tool execution started")
148147
await asyncio.sleep(0.05)
@@ -155,7 +154,7 @@ async def test_tool_with_logging(ctx: Context[ServerSession, None]) -> str:
155154

156155

157156
@mcp.tool()
158-
async def test_tool_with_progress(ctx: Context[ServerSession, None]) -> str:
157+
async def test_tool_with_progress(ctx: Context) -> str:
159158
"""Tests tool that reports progress notifications"""
160159
await ctx.report_progress(progress=0, total=100, message="Completed step 0 of 100")
161160
await asyncio.sleep(0.05)
@@ -173,7 +172,7 @@ async def test_tool_with_progress(ctx: Context[ServerSession, None]) -> str:
173172

174173

175174
@mcp.tool()
176-
async def test_sampling(prompt: str, ctx: Context[ServerSession, None]) -> str:
175+
async def test_sampling(prompt: str, ctx: Context) -> str:
177176
"""Tests server-initiated sampling (LLM completion request)"""
178177
try:
179178
# Request sampling from client
@@ -198,7 +197,7 @@ class UserResponse(BaseModel):
198197

199198

200199
@mcp.tool()
201-
async def test_elicitation(message: str, ctx: Context[ServerSession, None]) -> str:
200+
async def test_elicitation(message: str, ctx: Context) -> str:
202201
"""Tests server-initiated elicitation (user input request)"""
203202
try:
204203
# Request user input from client
@@ -230,7 +229,7 @@ class SEP1034DefaultsSchema(BaseModel):
230229

231230

232231
@mcp.tool()
233-
async def test_elicitation_sep1034_defaults(ctx: Context[ServerSession, None]) -> str:
232+
async def test_elicitation_sep1034_defaults(ctx: Context) -> str:
234233
"""Tests elicitation with default values for all primitive types (SEP-1034)"""
235234
try:
236235
# Request user input with defaults for all primitive types
@@ -289,7 +288,7 @@ class EnumSchemasTestSchema(BaseModel):
289288

290289

291290
@mcp.tool()
292-
async def test_elicitation_sep1330_enums(ctx: Context[ServerSession, None]) -> str:
291+
async def test_elicitation_sep1330_enums(ctx: Context) -> str:
293292
"""Tests elicitation with enum schema variations per SEP-1330"""
294293
try:
295294
result = await ctx.elicit(
@@ -313,7 +312,7 @@ def test_error_handling() -> str:
313312

314313

315314
@mcp.tool()
316-
async def test_reconnection(ctx: Context[ServerSession, None]) -> str:
315+
async def test_reconnection(ctx: Context) -> str:
317316
"""Tests SSE polling by closing stream mid-call (SEP-1699)"""
318317
await ctx.info("Before disconnect")
319318

examples/snippets/servers/elicitation.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
from pydantic import BaseModel, Field
1111

1212
from mcp.server.mcpserver import Context, MCPServer
13-
from mcp.server.session import ServerSession
1413
from mcp.shared.exceptions import UrlElicitationRequiredError
1514
from mcp.types import ElicitRequestURLParams
1615

@@ -28,7 +27,7 @@ class BookingPreferences(BaseModel):
2827

2928

3029
@mcp.tool()
31-
async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerSession, None]) -> str:
30+
async def book_table(date: str, time: str, party_size: int, ctx: Context) -> str:
3231
"""Book a table with date availability check.
3332
3433
This demonstrates form mode elicitation for collecting non-sensitive user input.
@@ -52,7 +51,7 @@ async def book_table(date: str, time: str, party_size: int, ctx: Context[ServerS
5251

5352

5453
@mcp.tool()
55-
async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> str:
54+
async def secure_payment(amount: float, ctx: Context) -> str:
5655
"""Process a secure payment requiring URL confirmation.
5756
5857
This demonstrates URL mode elicitation using ctx.elicit_url() for
@@ -76,7 +75,7 @@ async def secure_payment(amount: float, ctx: Context[ServerSession, None]) -> st
7675

7776

7877
@mcp.tool()
79-
async def connect_service(service_name: str, ctx: Context[ServerSession, None]) -> str:
78+
async def connect_service(service_name: str, ctx: Context) -> str:
8079
"""Connect to a third-party service requiring OAuth authorization.
8180
8281
This demonstrates the "throw error" pattern using UrlElicitationRequiredError.

examples/snippets/servers/notifications.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from mcp.server.mcpserver import Context, MCPServer
2-
from mcp.server.session import ServerSession
32

43
mcp = MCPServer(name="Notifications Example")
54

65

76
@mcp.tool()
8-
async def process_data(data: str, ctx: Context[ServerSession, None]) -> str:
7+
async def process_data(data: str, ctx: Context) -> str:
98
"""Process data with logging."""
109
# Different log levels
1110
await ctx.debug(f"Debug: Processing '{data}'")

examples/snippets/servers/sampling.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
from mcp.server.mcpserver import Context, MCPServer
2-
from mcp.server.session import ServerSession
32
from mcp.types import SamplingMessage, TextContent
43

54
mcp = MCPServer(name="Sampling Example")
65

76

87
@mcp.tool()
9-
async def generate_poem(topic: str, ctx: Context[ServerSession, None]) -> str:
8+
async def generate_poem(topic: str, ctx: Context) -> str:
109
"""Generate a poem using LLM sampling."""
1110
prompt = f"Write a short poem about {topic}"
1211

examples/snippets/servers/tool_progress.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
from mcp.server.mcpserver import Context, MCPServer
2-
from mcp.server.session import ServerSession
32

43
mcp = MCPServer(name="Progress Example")
54

65

76
@mcp.tool()
8-
async def long_running_task(task_name: str, ctx: Context[ServerSession, None], steps: int = 5) -> str:
7+
async def long_running_task(task_name: str, ctx: Context, steps: int = 5) -> str:
98
"""Execute a task with progress updates."""
109
await ctx.info(f"Starting: {task_name}")
1110

tests/server/mcpserver/test_elicitation.py

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
from mcp import Client, types
99
from mcp.client.session import ClientSession, ElicitationFnT
1010
from mcp.server.mcpserver import Context, MCPServer
11-
from mcp.server.session import ServerSession
1211
from mcp.shared._context import RequestContext
1312
from mcp.types import ElicitRequestParams, ElicitResult, TextContent
1413

@@ -22,7 +21,7 @@ def create_ask_user_tool(mcp: MCPServer):
2221
"""Create a standard ask_user tool that handles all elicitation responses."""
2322

2423
@mcp.tool(description="A tool that uses elicitation")
25-
async def ask_user(prompt: str, ctx: Context[ServerSession, None]) -> str:
24+
async def ask_user(prompt: str, ctx: Context) -> str:
2625
result = await ctx.elicit(message=f"Tool wants to ask: {prompt}", schema=AnswerSchema)
2726

2827
if result.action == "accept" and result.data:
@@ -97,7 +96,7 @@ async def test_elicitation_schema_validation():
9796

9897
def create_validation_tool(name: str, schema_class: type[BaseModel]):
9998
@mcp.tool(name=name, description=f"Tool testing {name}")
100-
async def tool(ctx: Context[ServerSession, None]) -> str:
99+
async def tool(ctx: Context) -> str:
101100
try:
102101
await ctx.elicit(message="This should fail validation", schema=schema_class)
103102
return "Should not reach here" # pragma: no cover
@@ -147,7 +146,7 @@ class OptionalSchema(BaseModel):
147146
subscribe: bool | None = Field(default=False, description="Subscribe to newsletter?")
148147

149148
@mcp.tool(description="Tool with optional fields")
150-
async def optional_tool(ctx: Context[ServerSession, None]) -> str:
149+
async def optional_tool(ctx: Context) -> str:
151150
result = await ctx.elicit(message="Please provide your information", schema=OptionalSchema)
152151

153152
if result.action == "accept" and result.data:
@@ -188,7 +187,7 @@ class InvalidOptionalSchema(BaseModel):
188187
optional_list: list[int] | None = Field(default=None, description="Invalid optional list")
189188

190189
@mcp.tool(description="Tool with invalid optional field")
191-
async def invalid_optional_tool(ctx: Context[ServerSession, None]) -> str:
190+
async def invalid_optional_tool(ctx: Context) -> str:
192191
try:
193192
await ctx.elicit(message="This should fail", schema=InvalidOptionalSchema)
194193
return "Should not reach here" # pragma: no cover
@@ -214,7 +213,7 @@ class ValidMultiSelectSchema(BaseModel):
214213
tags: list[str] = Field(description="Tags")
215214

216215
@mcp.tool(description="Tool with valid list[str] field")
217-
async def valid_multiselect_tool(ctx: Context[ServerSession, None]) -> str:
216+
async def valid_multiselect_tool(ctx: Context) -> str:
218217
result = await ctx.elicit(message="Please provide tags", schema=ValidMultiSelectSchema)
219218
if result.action == "accept" and result.data:
220219
return f"Name: {result.data.name}, Tags: {', '.join(result.data.tags)}"
@@ -233,7 +232,7 @@ class OptionalMultiSelectSchema(BaseModel):
233232
tags: list[str] | None = Field(default=None, description="Optional tags")
234233

235234
@mcp.tool(description="Tool with optional list[str] field")
236-
async def optional_multiselect_tool(ctx: Context[ServerSession, None]) -> str:
235+
async def optional_multiselect_tool(ctx: Context) -> str:
237236
result = await ctx.elicit(message="Please provide optional tags", schema=OptionalMultiSelectSchema)
238237
if result.action == "accept" and result.data:
239238
tags_str = ", ".join(result.data.tags) if result.data.tags else "none"
@@ -262,7 +261,7 @@ class DefaultsSchema(BaseModel):
262261
email: str = Field(description="Email address (required)")
263262

264263
@mcp.tool(description="Tool with default values")
265-
async def defaults_tool(ctx: Context[ServerSession, None]) -> str:
264+
async def defaults_tool(ctx: Context) -> str:
266265
result = await ctx.elicit(message="Please provide your information", schema=DefaultsSchema)
267266

268267
if result.action == "accept" and result.data:
@@ -327,7 +326,7 @@ class FavoriteColorSchema(BaseModel):
327326
)
328327

329328
@mcp.tool(description="Single color selection")
330-
async def select_favorite_color(ctx: Context[ServerSession, None]) -> str:
329+
async def select_favorite_color(ctx: Context) -> str:
331330
result = await ctx.elicit(message="Select your favorite color", schema=FavoriteColorSchema)
332331
if result.action == "accept" and result.data:
333332
return f"User: {result.data.user_name}, Favorite: {result.data.favorite_color}"
@@ -351,7 +350,7 @@ class FavoriteColorsSchema(BaseModel):
351350
)
352351

353352
@mcp.tool(description="Multiple color selection")
354-
async def select_favorite_colors(ctx: Context[ServerSession, None]) -> str:
353+
async def select_favorite_colors(ctx: Context) -> str:
355354
result = await ctx.elicit(message="Select your favorite colors", schema=FavoriteColorsSchema)
356355
if result.action == "accept" and result.data:
357356
return f"User: {result.data.user_name}, Colors: {', '.join(result.data.favorite_colors)}"
@@ -366,7 +365,7 @@ class LegacyColorSchema(BaseModel):
366365
)
367366

368367
@mcp.tool(description="Legacy enum format")
369-
async def select_color_legacy(ctx: Context[ServerSession, None]) -> str:
368+
async def select_color_legacy(ctx: Context) -> str:
370369
result = await ctx.elicit(message="Select a color (legacy format)", schema=LegacyColorSchema)
371370
if result.action == "accept" and result.data:
372371
return f"User: {result.data.user_name}, Color: {result.data.color}"

0 commit comments

Comments
 (0)