Skip to content

Commit 6f95028

Browse files
committed
Review nits: Annotated recursion looks at the type only; drop Iterable from content origins; prompt() docstring
- Annotated[X, meta...]: only X is a type, so recurse into it alone (and cover the nested-Annotated shape with a test). - Iterable[...] values are typically generators, which _convert_to_content does not unroll, so the annotation no longer counts as content; Sequence stays because its runtime value is a list or tuple. - @mcp.prompt() docstring lists the bare content forms render() now accepts.
1 parent 2251112 commit 6f95028

3 files changed

Lines changed: 13 additions & 5 deletions

File tree

src/mcp/server/mcpserver/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -918,8 +918,8 @@ def prompt(
918918
) -> Callable[[_CallableT], _CallableT]:
919919
"""Decorator to register a prompt.
920920
921-
The function returns the prompt messages (a string, `Message`, dict,
922-
or a sequence of these), or an `InputRequiredResult` to request
921+
The function returns the prompt messages (a string, content block, `Image`/`Audio`,
922+
`Message`, dict, or a sequence of these), or an `InputRequiredResult` to request
923923
client input first (the 2026-07-28 multi-round-trip flow — read
924924
`ctx.input_responses` on the retry).
925925

src/mcp/server/mcpserver/utilities/func_metadata.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import functools
22
import inspect
33
import json
4-
from collections.abc import Awaitable, Callable, Iterable, Sequence
4+
from collections.abc import Awaitable, Callable, Sequence
55
from itertools import chain
66
from types import GenericAlias
77
from typing import Annotated, Any, Union, cast, get_args, get_origin, get_type_hints
@@ -34,7 +34,8 @@ def _is_input_required_type(obj: Any) -> bool:
3434

3535

3636
_CONTENT_TYPES = (*get_args(ContentBlock), Image, Audio)
37-
_CONTENT_SEQUENCE_ORIGINS = (list, tuple, Sequence, Iterable)
37+
# `_convert_to_content` unrolls list/tuple values; a `Sequence[...]` annotation is one of those at runtime.
38+
_CONTENT_SEQUENCE_ORIGINS = (list, tuple, Sequence)
3839

3940

4041
def _returns_content(annotation: Any) -> bool:
@@ -44,7 +45,9 @@ def _returns_content(annotation: Any) -> bool:
4445
origin = get_origin(annotation)
4546
if origin is None:
4647
return isinstance(annotation, type) and issubclass(annotation, _CONTENT_TYPES)
47-
if origin is Annotated or is_union_origin(origin) or origin in _CONTENT_SEQUENCE_ORIGINS:
48+
if origin is Annotated:
49+
return _returns_content(get_args(annotation)[0])
50+
if is_union_origin(origin) or origin in _CONTENT_SEQUENCE_ORIGINS:
4851
return any(_returns_content(arg) for arg in get_args(annotation))
4952
return False
5053

tests/server/mcpserver/test_func_metadata.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -871,6 +871,10 @@ def _returns_audio_clips() -> tuple[Audio, ...]:
871871
raise NotImplementedError
872872

873873

874+
def _returns_described_blocks() -> list[Annotated[TextContent, Field(description="one line each")]]:
875+
raise NotImplementedError
876+
877+
874878
def _returns_call_tool_result_annotated_with_blocks() -> Annotated[CallToolResult, list[TextContent]]:
875879
raise NotImplementedError
876880

@@ -882,6 +886,7 @@ def _returns_call_tool_result_annotated_with_blocks() -> Annotated[CallToolResul
882886
_returns_blocks,
883887
_returns_strings_and_images,
884888
_returns_audio_clips,
889+
_returns_described_blocks,
885890
_returns_call_tool_result_annotated_with_blocks,
886891
],
887892
)

0 commit comments

Comments
 (0)