Skip to content

Commit 39e169e

Browse files
Add pragma: no cover to defensive code paths in elicitation
Add coverage pragmas to defensive code paths that are difficult to test but necessary for completeness: - elicitation.py: None/NoneType annotation handling (line 48) - elicitation.py: TypeError exception in _is_string_sequence (lines 72-74) - test_elicitation.py: fallback return statements in test tools These paths handle edge cases that don't occur in normal execution but provide safety for unexpected inputs.
1 parent 1e4bfb1 commit 39e169e

File tree

2 files changed

+10
-10
lines changed

2 files changed

+10
-10
lines changed

src/mcp/server/elicitation.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _validate_elicitation_schema(schema: type[BaseModel]) -> None:
4545
for field_name, field_info in schema.model_fields.items():
4646
annotation = field_info.annotation
4747

48-
if annotation is None or annotation is types.NoneType:
48+
if annotation is None or annotation is types.NoneType: # pragma: no cover
4949
continue
5050
elif _is_primitive_field(annotation):
5151
continue
@@ -69,7 +69,7 @@ def _is_string_sequence(annotation: type) -> bool:
6969
args = get_args(annotation)
7070
# Should have single str type arg
7171
return len(args) == 1 and args[0] is str
72-
except TypeError:
72+
except TypeError: # pragma: no cover
7373
# origin is not a class, so it can't be a subclass of Sequence
7474
pass
7575
return False

tests/server/fastmcp/test_elicitation.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -230,12 +230,12 @@ async def valid_multiselect_tool(ctx: Context[ServerSession, None]) -> str:
230230
result = await ctx.elicit(message="Please provide tags", schema=ValidMultiSelectSchema)
231231
if result.action == "accept" and result.data:
232232
return f"Name: {result.data.name}, Tags: {', '.join(result.data.tags)}"
233-
return f"User {result.action}"
233+
return f"User {result.action}" # pragma: no cover
234234

235235
async def multiselect_callback(context: RequestContext[ClientSession, Any], params: ElicitRequestParams):
236236
if "Please provide tags" in params.message:
237237
return ElicitResult(action="accept", content={"name": "Test", "tags": ["tag1", "tag2"]})
238-
return ElicitResult(action="decline")
238+
return ElicitResult(action="decline") # pragma: no cover
239239

240240
await call_tool_and_assert(mcp, multiselect_callback, "valid_multiselect_tool", {}, "Name: Test, Tags: tag1, tag2")
241241

@@ -250,12 +250,12 @@ async def optional_multiselect_tool(ctx: Context[ServerSession, None]) -> str:
250250
if result.action == "accept" and result.data:
251251
tags_str = ", ".join(result.data.tags) if result.data.tags else "none"
252252
return f"Name: {result.data.name}, Tags: {tags_str}"
253-
return f"User {result.action}"
253+
return f"User {result.action}" # pragma: no cover
254254

255255
async def optional_multiselect_callback(context: RequestContext[ClientSession, Any], params: ElicitRequestParams):
256256
if "Please provide optional tags" in params.message:
257257
return ElicitResult(action="accept", content={"name": "Test", "tags": ["tag1", "tag2"]})
258-
return ElicitResult(action="decline")
258+
return ElicitResult(action="decline") # pragma: no cover
259259

260260
await call_tool_and_assert(
261261
mcp, optional_multiselect_callback, "optional_multiselect_tool", {}, "Name: Test, Tags: tag1, tag2"
@@ -342,7 +342,7 @@ async def select_favorite_color(ctx: Context[ServerSession, None]) -> str:
342342
result = await ctx.elicit(message="Select your favorite color", schema=FavoriteColorSchema)
343343
if result.action == "accept" and result.data:
344344
return f"User: {result.data.user_name}, Favorite: {result.data.favorite_color}"
345-
return f"User {result.action}"
345+
return f"User {result.action}" # pragma: no cover
346346

347347
# Test multi-select with titles using anyOf
348348
class FavoriteColorsSchema(BaseModel):
@@ -366,7 +366,7 @@ async def select_favorite_colors(ctx: Context[ServerSession, None]) -> str:
366366
result = await ctx.elicit(message="Select your favorite colors", schema=FavoriteColorsSchema)
367367
if result.action == "accept" and result.data:
368368
return f"User: {result.data.user_name}, Colors: {', '.join(result.data.favorite_colors)}"
369-
return f"User {result.action}"
369+
return f"User {result.action}" # pragma: no cover
370370

371371
# Test legacy enumNames format
372372
class LegacyColorSchema(BaseModel):
@@ -381,7 +381,7 @@ async def select_color_legacy(ctx: Context[ServerSession, None]) -> str:
381381
result = await ctx.elicit(message="Select a color (legacy format)", schema=LegacyColorSchema)
382382
if result.action == "accept" and result.data:
383383
return f"User: {result.data.user_name}, Color: {result.data.color}"
384-
return f"User {result.action}"
384+
return f"User {result.action}" # pragma: no cover
385385

386386
async def enum_callback(context: RequestContext[ClientSession, Any], params: ElicitRequestParams):
387387
if "colors" in params.message and "legacy" not in params.message:
@@ -391,7 +391,7 @@ async def enum_callback(context: RequestContext[ClientSession, Any], params: Eli
391391
return ElicitResult(action="accept", content={"user_name": "Charlie", "color": "green"})
392392
else:
393393
return ElicitResult(action="accept", content={"user_name": "Alice", "favorite_color": "blue"})
394-
return ElicitResult(action="decline")
394+
return ElicitResult(action="decline") # pragma: no cover
395395

396396
# Test single-select with titles
397397
await call_tool_and_assert(mcp, enum_callback, "select_favorite_color", {}, "User: Alice, Favorite: blue")

0 commit comments

Comments
 (0)