Skip to content

Commit 8e000d4

Browse files
authored
Merge branch 'main' into main
2 parents d1de7f4 + de89457 commit 8e000d4

File tree

12 files changed

+486
-190
lines changed

12 files changed

+486
-190
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1840,7 +1840,7 @@ import asyncio
18401840

18411841
from mcp.client.session import ClientSession
18421842
from mcp.client.stdio import StdioServerParameters, stdio_client
1843-
from mcp.types import Resource
1843+
from mcp.types import PaginatedRequestParams, Resource
18441844

18451845

18461846
async def list_all_resources() -> None:
@@ -1857,7 +1857,7 @@ async def list_all_resources() -> None:
18571857

18581858
while True:
18591859
# Fetch a page of resources
1860-
result = await session.list_resources(cursor=cursor)
1860+
result = await session.list_resources(params=PaginatedRequestParams(cursor=cursor))
18611861
all_resources.extend(result.resources)
18621862

18631863
print(f"Fetched {len(result.resources)} resources")

examples/servers/simple-auth/mcp_simple_auth/server.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,6 @@ class ResourceServerSettings(BaseSettings):
4545
# RFC 8707 resource validation
4646
oauth_strict: bool = False
4747

48-
# TODO(Marcelo): Is this even needed? I didn't have time to check.
49-
def __init__(self, **data: Any):
50-
"""Initialize settings with values from environment variables."""
51-
super().__init__(**data)
52-
5348

5449
def create_resource_server(settings: ResourceServerSettings) -> FastMCP:
5550
"""

examples/snippets/clients/pagination_client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
from mcp.client.session import ClientSession
88
from mcp.client.stdio import StdioServerParameters, stdio_client
9-
from mcp.types import Resource
9+
from mcp.types import PaginatedRequestParams, Resource
1010

1111

1212
async def list_all_resources() -> None:
@@ -23,7 +23,7 @@ async def list_all_resources() -> None:
2323

2424
while True:
2525
# Fetch a page of resources
26-
result = await session.list_resources(cursor=cursor)
26+
result = await session.list_resources(params=PaginatedRequestParams(cursor=cursor))
2727
all_resources.extend(result.resources)
2828

2929
print(f"Fetched {len(result.resources)} resources")

src/mcp/client/session.py

Lines changed: 136 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import logging
22
from datetime import timedelta
3-
from typing import Any, Protocol
3+
from typing import Any, Protocol, overload
44

55
import anyio.lowlevel
66
from anyio.streams.memory import MemoryObjectReceiveStream, MemoryObjectSendStream
77
from jsonschema import SchemaError, ValidationError, validate
88
from pydantic import AnyUrl, TypeAdapter
9+
from typing_extensions import deprecated
910

1011
import mcp.types as types
1112
from mcp.shared.context import RequestContext
@@ -212,25 +213,79 @@ async def set_logging_level(self, level: types.LoggingLevel) -> types.EmptyResul
212213
types.EmptyResult,
213214
)
214215

215-
async def list_resources(self, cursor: str | None = None) -> types.ListResourcesResult:
216-
"""Send a resources/list request."""
216+
@overload
217+
@deprecated("Use list_resources(params=PaginatedRequestParams(...)) instead")
218+
async def list_resources(self, cursor: str | None) -> types.ListResourcesResult: ...
219+
220+
@overload
221+
async def list_resources(self, *, params: types.PaginatedRequestParams | None) -> types.ListResourcesResult: ...
222+
223+
@overload
224+
async def list_resources(self) -> types.ListResourcesResult: ...
225+
226+
async def list_resources(
227+
self,
228+
cursor: str | None = None,
229+
*,
230+
params: types.PaginatedRequestParams | None = None,
231+
) -> types.ListResourcesResult:
232+
"""Send a resources/list request.
233+
234+
Args:
235+
cursor: Simple cursor string for pagination (deprecated, use params instead)
236+
params: Full pagination parameters including cursor and any future fields
237+
"""
238+
if params is not None and cursor is not None:
239+
raise ValueError("Cannot specify both cursor and params")
240+
241+
if params is not None:
242+
request_params = params
243+
elif cursor is not None:
244+
request_params = types.PaginatedRequestParams(cursor=cursor)
245+
else:
246+
request_params = None
247+
217248
return await self.send_request(
218-
types.ClientRequest(
219-
types.ListResourcesRequest(
220-
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
221-
)
222-
),
249+
types.ClientRequest(types.ListResourcesRequest(params=request_params)),
223250
types.ListResourcesResult,
224251
)
225252

226-
async def list_resource_templates(self, cursor: str | None = None) -> types.ListResourceTemplatesResult:
227-
"""Send a resources/templates/list request."""
253+
@overload
254+
@deprecated("Use list_resource_templates(params=PaginatedRequestParams(...)) instead")
255+
async def list_resource_templates(self, cursor: str | None) -> types.ListResourceTemplatesResult: ...
256+
257+
@overload
258+
async def list_resource_templates(
259+
self, *, params: types.PaginatedRequestParams | None
260+
) -> types.ListResourceTemplatesResult: ...
261+
262+
@overload
263+
async def list_resource_templates(self) -> types.ListResourceTemplatesResult: ...
264+
265+
async def list_resource_templates(
266+
self,
267+
cursor: str | None = None,
268+
*,
269+
params: types.PaginatedRequestParams | None = None,
270+
) -> types.ListResourceTemplatesResult:
271+
"""Send a resources/templates/list request.
272+
273+
Args:
274+
cursor: Simple cursor string for pagination (deprecated, use params instead)
275+
params: Full pagination parameters including cursor and any future fields
276+
"""
277+
if params is not None and cursor is not None:
278+
raise ValueError("Cannot specify both cursor and params")
279+
280+
if params is not None:
281+
request_params = params
282+
elif cursor is not None:
283+
request_params = types.PaginatedRequestParams(cursor=cursor)
284+
else:
285+
request_params = None
286+
228287
return await self.send_request(
229-
types.ClientRequest(
230-
types.ListResourceTemplatesRequest(
231-
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
232-
)
233-
),
288+
types.ClientRequest(types.ListResourceTemplatesRequest(params=request_params)),
234289
types.ListResourceTemplatesResult,
235290
)
236291

@@ -317,14 +372,40 @@ async def _validate_tool_result(self, name: str, result: types.CallToolResult) -
317372
except SchemaError as e:
318373
raise RuntimeError(f"Invalid schema for tool {name}: {e}")
319374

320-
async def list_prompts(self, cursor: str | None = None) -> types.ListPromptsResult:
321-
"""Send a prompts/list request."""
375+
@overload
376+
@deprecated("Use list_prompts(params=PaginatedRequestParams(...)) instead")
377+
async def list_prompts(self, cursor: str | None) -> types.ListPromptsResult: ...
378+
379+
@overload
380+
async def list_prompts(self, *, params: types.PaginatedRequestParams | None) -> types.ListPromptsResult: ...
381+
382+
@overload
383+
async def list_prompts(self) -> types.ListPromptsResult: ...
384+
385+
async def list_prompts(
386+
self,
387+
cursor: str | None = None,
388+
*,
389+
params: types.PaginatedRequestParams | None = None,
390+
) -> types.ListPromptsResult:
391+
"""Send a prompts/list request.
392+
393+
Args:
394+
cursor: Simple cursor string for pagination (deprecated, use params instead)
395+
params: Full pagination parameters including cursor and any future fields
396+
"""
397+
if params is not None and cursor is not None:
398+
raise ValueError("Cannot specify both cursor and params")
399+
400+
if params is not None:
401+
request_params = params
402+
elif cursor is not None:
403+
request_params = types.PaginatedRequestParams(cursor=cursor)
404+
else:
405+
request_params = None
406+
322407
return await self.send_request(
323-
types.ClientRequest(
324-
types.ListPromptsRequest(
325-
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
326-
)
327-
),
408+
types.ClientRequest(types.ListPromptsRequest(params=request_params)),
328409
types.ListPromptsResult,
329410
)
330411

@@ -363,14 +444,40 @@ async def complete(
363444
types.CompleteResult,
364445
)
365446

366-
async def list_tools(self, cursor: str | None = None) -> types.ListToolsResult:
367-
"""Send a tools/list request."""
447+
@overload
448+
@deprecated("Use list_tools(params=PaginatedRequestParams(...)) instead")
449+
async def list_tools(self, cursor: str | None) -> types.ListToolsResult: ...
450+
451+
@overload
452+
async def list_tools(self, *, params: types.PaginatedRequestParams | None) -> types.ListToolsResult: ...
453+
454+
@overload
455+
async def list_tools(self) -> types.ListToolsResult: ...
456+
457+
async def list_tools(
458+
self,
459+
cursor: str | None = None,
460+
*,
461+
params: types.PaginatedRequestParams | None = None,
462+
) -> types.ListToolsResult:
463+
"""Send a tools/list request.
464+
465+
Args:
466+
cursor: Simple cursor string for pagination (deprecated, use params instead)
467+
params: Full pagination parameters including cursor and any future fields
468+
"""
469+
if params is not None and cursor is not None:
470+
raise ValueError("Cannot specify both cursor and params")
471+
472+
if params is not None:
473+
request_params = params
474+
elif cursor is not None:
475+
request_params = types.PaginatedRequestParams(cursor=cursor)
476+
else:
477+
request_params = None
478+
368479
result = await self.send_request(
369-
types.ClientRequest(
370-
types.ListToolsRequest(
371-
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
372-
)
373-
),
480+
types.ClientRequest(types.ListToolsRequest(params=request_params)),
374481
types.ListToolsResult,
375482
)
376483

src/mcp/server/fastmcp/resources/base.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
field_validator,
1414
)
1515

16-
from mcp.types import Icon
16+
from mcp.types import Annotations, Icon
1717

1818

1919
class Resource(BaseModel, abc.ABC):
@@ -31,6 +31,7 @@ class Resource(BaseModel, abc.ABC):
3131
pattern=r"^[a-zA-Z0-9]+/[a-zA-Z0-9\-+.]+$",
3232
)
3333
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for this resource")
34+
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource")
3435

3536
@field_validator("name", mode="before")
3637
@classmethod

src/mcp/server/fastmcp/resources/resource_manager.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
from mcp.server.fastmcp.resources.base import Resource
1111
from mcp.server.fastmcp.resources.templates import ResourceTemplate
1212
from mcp.server.fastmcp.utilities.logging import get_logger
13-
from mcp.types import Icon
13+
from mcp.types import Annotations, Icon
1414

1515
if TYPE_CHECKING:
1616
from mcp.server.fastmcp.server import Context
@@ -63,6 +63,7 @@ def add_template(
6363
description: str | None = None,
6464
mime_type: str | None = None,
6565
icons: list[Icon] | None = None,
66+
annotations: Annotations | None = None,
6667
) -> ResourceTemplate:
6768
"""Add a template from a function."""
6869
template = ResourceTemplate.from_function(
@@ -73,6 +74,7 @@ def add_template(
7374
description=description,
7475
mime_type=mime_type,
7576
icons=icons,
77+
annotations=annotations,
7678
)
7779
self._templates[template.uri_template] = template
7880
return template

src/mcp/server/fastmcp/resources/templates.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
from mcp.server.fastmcp.utilities.context_injection import find_context_parameter, inject_context
1414
from mcp.server.fastmcp.utilities.convertors import CONVERTOR_TYPES, Convertor
1515
from mcp.server.fastmcp.utilities.func_metadata import func_metadata
16-
from mcp.types import Icon
16+
from mcp.types import Annotations, Icon
1717

1818
if TYPE_CHECKING:
1919
from mcp.server.fastmcp.server import Context
@@ -30,6 +30,7 @@ class ResourceTemplate(BaseModel):
3030
description: str | None = Field(description="Description of what the resource does")
3131
mime_type: str = Field(default="text/plain", description="MIME type of the resource content")
3232
icons: list[Icon] | None = Field(default=None, description="Optional list of icons for the resource template")
33+
annotations: Annotations | None = Field(default=None, description="Optional annotations for the resource template")
3334
fn: Callable[..., Any] = Field(exclude=True)
3435
parameters: dict[str, Any] = Field(description="JSON schema for function parameters")
3536
context_kwarg: str | None = Field(None, description="Name of the kwarg that should receive context")
@@ -46,6 +47,7 @@ def from_function(
4647
description: str | None = None,
4748
mime_type: str | None = None,
4849
icons: list[Icon] | None = None,
50+
annotations: Annotations | None = None,
4951
context_kwarg: str | None = None,
5052
) -> ResourceTemplate:
5153
"""Create a template from a function."""
@@ -74,6 +76,7 @@ def from_function(
7476
description=description or fn.__doc__ or "",
7577
mime_type=mime_type or "text/plain",
7678
icons=icons,
79+
annotations=annotations,
7780
fn=fn,
7881
parameters=parameters,
7982
context_kwarg=context_kwarg,
@@ -151,6 +154,7 @@ async def create_resource(
151154
description=self.description,
152155
mime_type=self.mime_type,
153156
icons=self.icons,
157+
annotations=self.annotations,
154158
fn=lambda: result, # Capture result in closure
155159
)
156160
except Exception as e:

src/mcp/server/fastmcp/resources/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from pydantic import AnyUrl, Field, ValidationInfo, validate_call
1515

1616
from mcp.server.fastmcp.resources.base import Resource
17-
from mcp.types import Icon
17+
from mcp.types import Annotations, Icon
1818

1919

2020
class TextResource(Resource):
@@ -82,6 +82,7 @@ def from_function(
8282
description: str | None = None,
8383
mime_type: str | None = None,
8484
icons: list[Icon] | None = None,
85+
annotations: Annotations | None = None,
8586
) -> "FunctionResource":
8687
"""Create a FunctionResource from a function."""
8788
func_name = name or fn.__name__
@@ -99,6 +100,7 @@ def from_function(
99100
mime_type=mime_type or "text/plain",
100101
fn=fn,
101102
icons=icons,
103+
annotations=annotations,
102104
)
103105

104106

0 commit comments

Comments
 (0)