From 2d55eabb2f223d929cd999b6e3bcd7756672c17c Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Mon, 21 Oct 2024 14:47:44 +0100 Subject: [PATCH 1/4] Update types for protocol version 2024-10-07 --- mcp_python/types.py | 70 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/mcp_python/types.py b/mcp_python/types.py index 983964a05..78464ec8b 100644 --- a/mcp_python/types.py +++ b/mcp_python/types.py @@ -21,8 +21,10 @@ not separate types in the schema. """ +LATEST_PROTOCOL_VERSION = "2024-10-07"; ProgressToken = str | int +Cursor = str class RequestParams(BaseModel): @@ -64,6 +66,14 @@ class Request(BaseModel, Generic[RequestParamsT, MethodT]): model_config = ConfigDict(extra="allow") +class PaginatedRequest(Request[RequestParamsT, MethodT]): + cursor: Cursor | None = None + """ + An opaque token representing the current pagination position. + If provided, the server should return results starting after this cursor. + """ + + class Notification(BaseModel, Generic[NotificationParamsT, MethodT]): """Base class for JSON-RPC notifications.""" @@ -83,6 +93,14 @@ class Result(BaseModel): """ +class PaginatedResult(Result): + nextCursor: Cursor | None = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + + RequestId = str | int @@ -115,6 +133,7 @@ class JSONRPCResponse(BaseModel): INVALID_REQUEST = -32600 METHOD_NOT_FOUND = -32601 INVALID_PARAMS = -32602 +INTERNAL_ERROR = -32603 class ErrorData(BaseModel): @@ -191,7 +210,7 @@ class ServerCapabilities(BaseModel): class InitializeRequestParams(RequestParams): """Parameters for the initialize request.""" - protocolVersion: Literal[1] + protocolVersion: str | int """The latest version of the Model Context Protocol that the client supports.""" capabilities: ClientCapabilities clientInfo: Implementation @@ -211,7 +230,7 @@ class InitializeRequest(Request): class InitializeResult(Result): """After receiving an initialize request from the client, the server sends this.""" - protocolVersion: Literal[1] + protocolVersion: str | int """The version of the Model Context Protocol that the server wants to use.""" capabilities: ServerCapabilities serverInfo: Implementation @@ -265,7 +284,7 @@ class ProgressNotification(Notification): params: ProgressNotificationParams -class ListResourcesRequest(Request): +class ListResourcesRequest(PaginatedRequest): """Sent from the client to request a list of resources the server has.""" method: Literal["resources/list"] @@ -277,6 +296,10 @@ class Resource(BaseModel): uri: AnyUrl """The URI of this resource.""" + name: str + """A human-readable name for this resource.""" + description: str | None = None + """A description of what this resource represents.""" mimeType: str | None = None """The MIME type of this resource, if known.""" model_config = ConfigDict(extra="allow") @@ -290,7 +313,7 @@ class ResourceTemplate(BaseModel): A URI template (according to RFC 6570) that can be used to construct resource URIs. """ - name: str | None = None + name: str """A human-readable name for the type of resource this template refers to.""" description: str | None = None """A human-readable description of what this template is for.""" @@ -302,11 +325,23 @@ class ResourceTemplate(BaseModel): model_config = ConfigDict(extra="allow") -class ListResourcesResult(Result): +class ListResourcesResult(PaginatedResult): """The server's response to a resources/list request from the client.""" - resourceTemplates: list[ResourceTemplate] | None = None - resources: list[Resource] | None = None + resources: list[Resource] + + +class ListResourceTemplatesRequest(PaginatedRequest): + """Sent from the client to request a list of resource templates the server has.""" + + method: Literal["resources/templates/list"] + params: RequestParams | None = None + + +class ListResourceTemplatesResult(PaginatedResult): + """The server's response to a resources/templates/list request from the client.""" + + resourceTemplates: list[ResourceTemplate] class ReadResourceRequestParams(RequestParams): @@ -430,7 +465,7 @@ class ResourceUpdatedNotification(Notification): params: ResourceUpdatedNotificationParams -class ListPromptsRequest(Request): +class ListPromptsRequest(PaginatedRequest): """Sent from the client to request a list of prompts and prompt templates.""" method: Literal["prompts/list"] @@ -461,7 +496,7 @@ class Prompt(BaseModel): model_config = ConfigDict(extra="allow") -class ListPromptsResult(Result): +class ListPromptsResult(PaginatedResult): """The server's response to a prompts/list request from the client.""" prompts: list[Prompt] @@ -526,7 +561,17 @@ class GetPromptResult(Result): messages: list[SamplingMessage] -class ListToolsRequest(Request): +class PromptListChangedNotification(Notification): + """ + An optional notification from the server to the client, informing it that the list + of prompts it offers has changed. + """ + + method: Literal["notifications/prompts/list_changed"] + params: NotificationParams | None = None + + +class ListToolsRequest(PaginatedRequest): """Sent from the client to request a list of tools the server has.""" method: Literal["tools/list"] @@ -545,7 +590,7 @@ class Tool(BaseModel): model_config = ConfigDict(extra="allow") -class ListToolsResult(Result): +class ListToolsResult(PaginatedResult): """The server's response to a tools/list request from the client.""" tools: list[Tool] @@ -742,6 +787,7 @@ class ClientRequest( | GetPromptRequest | ListPromptsRequest | ListResourcesRequest + | ListResourceTemplatesRequest | ReadResourceRequest | SubscribeRequest | UnsubscribeRequest @@ -771,6 +817,7 @@ class ServerNotification( | ResourceUpdatedNotification | ResourceListChangedNotification | ToolListChangedNotification + | PromptListChangedNotification ] ): pass @@ -784,6 +831,7 @@ class ServerResult( | GetPromptResult | ListPromptsResult | ListResourcesResult + | ListResourceTemplatesResult | ReadResourceResult | CallToolResult | ListToolsResult From eb1024c654d787fcca81acefd161d3daadbb6b0a Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Mon, 21 Oct 2024 14:50:44 +0100 Subject: [PATCH 2/4] Update protocol version handling --- mcp_python/client/session.py | 7 ++++--- mcp_python/server/session.py | 4 ++-- mcp_python/shared/version.py | 4 +++- tests/client/test_session.py | 5 +++-- tests/test_types.py | 6 +++--- 5 files changed, 15 insertions(+), 11 deletions(-) diff --git a/mcp_python/client/session.py b/mcp_python/client/session.py index 769e945a8..266e741c9 100644 --- a/mcp_python/client/session.py +++ b/mcp_python/client/session.py @@ -2,8 +2,9 @@ from pydantic import AnyUrl from mcp_python.shared.session import BaseSession -from mcp_python.shared.version import SUPPORTED_PROTOCOL_VERSION +from mcp_python.shared.version import SUPPORTED_PROTOCOL_VERSIONS from mcp_python.types import ( + LATEST_PROTOCOL_VERSION, CallToolResult, ClientCapabilities, ClientNotification, @@ -49,7 +50,7 @@ async def initialize(self) -> InitializeResult: InitializeRequest( method="initialize", params=InitializeRequestParams( - protocolVersion=SUPPORTED_PROTOCOL_VERSION, + protocolVersion=LATEST_PROTOCOL_VERSION, capabilities=ClientCapabilities( sampling=None, experimental=None ), @@ -60,7 +61,7 @@ async def initialize(self) -> InitializeResult: InitializeResult, ) - if result.protocolVersion != SUPPORTED_PROTOCOL_VERSION: + if result.protocolVersion not in SUPPORTED_PROTOCOL_VERSIONS: raise RuntimeError( "Unsupported protocol version from the server: " f"{result.protocolVersion}" diff --git a/mcp_python/server/session.py b/mcp_python/server/session.py index 375e557db..be8a4df81 100644 --- a/mcp_python/server/session.py +++ b/mcp_python/server/session.py @@ -11,8 +11,8 @@ BaseSession, RequestResponder, ) -from mcp_python.shared.version import SUPPORTED_PROTOCOL_VERSION from mcp_python.types import ( + LATEST_PROTOCOL_VERSION, ClientNotification, ClientRequest, CreateMessageResult, @@ -67,7 +67,7 @@ async def _received_request( await responder.respond( ServerResult( InitializeResult( - protocolVersion=SUPPORTED_PROTOCOL_VERSION, + protocolVersion=LATEST_PROTOCOL_VERSION, capabilities=self._init_options.capabilities, serverInfo=Implementation( name=self._init_options.server_name, diff --git a/mcp_python/shared/version.py b/mcp_python/shared/version.py index bc8db2050..61d1fbeda 100644 --- a/mcp_python/shared/version.py +++ b/mcp_python/shared/version.py @@ -1 +1,3 @@ -SUPPORTED_PROTOCOL_VERSION = 1 +from mcp_python.types import LATEST_PROTOCOL_VERSION + +SUPPORTED_PROTOCOL_VERSIONS = [1, LATEST_PROTOCOL_VERSION] diff --git a/tests/client/test_session.py b/tests/client/test_session.py index cb7f03840..f71a4cbab 100644 --- a/tests/client/test_session.py +++ b/tests/client/test_session.py @@ -3,6 +3,7 @@ from mcp_python.client.session import ClientSession from mcp_python.types import ( + LATEST_PROTOCOL_VERSION, ClientNotification, ClientRequest, Implementation, @@ -41,7 +42,7 @@ async def mock_server(): result = ServerResult( InitializeResult( - protocolVersion=1, + protocolVersion=LATEST_PROTOCOL_VERSION, capabilities=ServerCapabilities( logging=None, resources=None, @@ -88,7 +89,7 @@ async def listen_session(): # Assert the result assert isinstance(result, InitializeResult) - assert result.protocolVersion == 1 + assert result.protocolVersion == LATEST_PROTOCOL_VERSION assert isinstance(result.capabilities, ServerCapabilities) assert result.serverInfo == Implementation(name="mock-server", version="0.1.0") diff --git a/tests/test_types.py b/tests/test_types.py index decd1c81d..d9bc46a6b 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,4 +1,4 @@ -from mcp_python.types import ClientRequest, JSONRPCMessage, JSONRPCRequest +from mcp_python.types import LATEST_PROTOCOL_VERSION, ClientRequest, JSONRPCMessage, JSONRPCRequest def test_jsonrpc_request(): @@ -7,7 +7,7 @@ def test_jsonrpc_request(): "id": 1, "method": "initialize", "params": { - "protocolVersion": 1, + "protocolVersion": LATEST_PROTOCOL_VERSION, "capabilities": {"batch": None, "sampling": None}, "clientInfo": {"name": "mcp_python", "version": "0.1.0"}, }, @@ -21,4 +21,4 @@ def test_jsonrpc_request(): assert request.root.id == 1 assert request.root.method == "initialize" assert request.root.params is not None - assert request.root.params["protocolVersion"] == 1 + assert request.root.params["protocolVersion"] == LATEST_PROTOCOL_VERSION From ffff3d9450f6c850405cb07238662dd04a03b531 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Mon, 21 Oct 2024 14:54:47 +0100 Subject: [PATCH 3/4] ruff --fix --- mcp_python/types.py | 2 +- tests/test_types.py | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/mcp_python/types.py b/mcp_python/types.py index 78464ec8b..012122ed7 100644 --- a/mcp_python/types.py +++ b/mcp_python/types.py @@ -21,7 +21,7 @@ not separate types in the schema. """ -LATEST_PROTOCOL_VERSION = "2024-10-07"; +LATEST_PROTOCOL_VERSION = "2024-10-07" ProgressToken = str | int Cursor = str diff --git a/tests/test_types.py b/tests/test_types.py index d9bc46a6b..e2c0ac279 100644 --- a/tests/test_types.py +++ b/tests/test_types.py @@ -1,4 +1,9 @@ -from mcp_python.types import LATEST_PROTOCOL_VERSION, ClientRequest, JSONRPCMessage, JSONRPCRequest +from mcp_python.types import ( + LATEST_PROTOCOL_VERSION, + ClientRequest, + JSONRPCMessage, + JSONRPCRequest, +) def test_jsonrpc_request(): From 9eb239b3fb1819edc860016a6e2a1dba85008a49 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Mon, 21 Oct 2024 14:55:27 +0100 Subject: [PATCH 4/4] Remove resourceTemplates from list_resources --- mcp_python/server/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mcp_python/server/__init__.py b/mcp_python/server/__init__.py index c6b73cb25..38600a6ce 100644 --- a/mcp_python/server/__init__.py +++ b/mcp_python/server/__init__.py @@ -162,7 +162,7 @@ def decorator(func: Callable[[], Awaitable[list[Resource]]]): async def handler(_: Any): resources = await func() return ServerResult( - ListResourcesResult(resources=resources, resourceTemplates=None) + ListResourcesResult(resources=resources) ) self.request_handlers[ListResourcesRequest] = handler