|
1 | 1 | from __future__ import annotations |
2 | 2 |
|
3 | 3 | from collections.abc import Iterable, Mapping |
4 | | -from typing import TYPE_CHECKING, Any, Generic, cast |
| 4 | +from typing import TYPE_CHECKING, Any, Generic, Literal, cast, overload |
5 | 5 |
|
6 | | -from mcp_types import ClientCapabilities, InputResponseRequestParams, InputResponses, LoggingLevel |
| 6 | +from mcp_types import ClientCapabilities, InputRequiredResult, InputResponseRequestParams, InputResponses, LoggingLevel |
7 | 7 | from pydantic import AnyUrl, BaseModel |
8 | 8 | from typing_extensions import deprecated |
9 | 9 |
|
@@ -99,21 +99,49 @@ async def report_progress(self, progress: float, total: float | None = None, mes |
99 | 99 | """ |
100 | 100 | await self.request_context.session.report_progress(progress, total, message) |
101 | 101 |
|
102 | | - async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]: |
| 102 | + @overload |
| 103 | + async def read_resource( |
| 104 | + self, uri: str | AnyUrl, *, allow_input_required: Literal[False] = False |
| 105 | + ) -> Iterable[ReadResourceContents]: ... |
| 106 | + |
| 107 | + @overload |
| 108 | + async def read_resource( |
| 109 | + self, uri: str | AnyUrl, *, allow_input_required: bool |
| 110 | + ) -> Iterable[ReadResourceContents] | InputRequiredResult: ... |
| 111 | + |
| 112 | + async def read_resource( |
| 113 | + self, uri: str | AnyUrl, *, allow_input_required: bool = False |
| 114 | + ) -> Iterable[ReadResourceContents] | InputRequiredResult: |
103 | 115 | """Read a resource by URI. |
104 | 116 |
|
105 | 117 | Args: |
106 | 118 | uri: Resource URI to read |
| 119 | + allow_input_required: When `False` (default), an |
| 120 | + `InputRequiredResult` returned by a resource template function |
| 121 | + (the 2026-07-28 multi-round-trip flow) raises instead of being |
| 122 | + returned. Pass `True` to receive it — a handler may forward it |
| 123 | + as its own result; the retry's answers then arrive on |
| 124 | + `ctx.input_responses`. |
107 | 125 |
|
108 | 126 | Returns: |
109 | | - The resource content as either text or bytes |
| 127 | + The resource content as either text or bytes, or — only with |
| 128 | + `allow_input_required=True` — the `InputRequiredResult` the |
| 129 | + resource template function returned. |
110 | 130 |
|
111 | 131 | Raises: |
112 | 132 | ResourceNotFoundError: If no resource or template matches the URI. |
113 | 133 | ResourceError: If template creation or resource reading fails. |
| 134 | + RuntimeError: If the resource returned an `InputRequiredResult` |
| 135 | + and `allow_input_required` is `False`. |
114 | 136 | """ |
115 | 137 | assert self._mcp_server is not None, "Context is not available outside of a request" |
116 | | - return await self._mcp_server.read_resource(uri, self) |
| 138 | + result = await self._mcp_server.read_resource(uri, self) |
| 139 | + if isinstance(result, InputRequiredResult) and not allow_input_required: |
| 140 | + raise RuntimeError( |
| 141 | + "Resource returned InputRequiredResult; pass allow_input_required=True to " |
| 142 | + "receive it and forward it as this handler's result." |
| 143 | + ) |
| 144 | + return result |
117 | 145 |
|
118 | 146 | async def elicit( |
119 | 147 | self, |
|
0 commit comments