diff --git a/src/mcp/server/lowlevel/server.py b/src/mcp/server/lowlevel/server.py index e29c021b7..6657ac66d 100644 --- a/src/mcp/server/lowlevel/server.py +++ b/src/mcp/server/lowlevel/server.py @@ -139,6 +139,8 @@ def __init__( self, name: str, version: str | None = None, + title: str | None = None, + description: str | None = None, instructions: str | None = None, website_url: str | None = None, icons: list[types.Icon] | None = None, @@ -149,6 +151,8 @@ def __init__( ): self.name = name self.version = version + self.title = title + self.description = description self.instructions = instructions self.website_url = website_url self.icons = icons @@ -186,6 +190,8 @@ def pkg_version(package: str) -> str: experimental_capabilities or {}, ), instructions=self.instructions, + server_title=self.title, + server_description=self.description, website_url=self.website_url, icons=self.icons, ) diff --git a/src/mcp/server/models.py b/src/mcp/server/models.py index ddf716cb9..5ccecd936 100644 --- a/src/mcp/server/models.py +++ b/src/mcp/server/models.py @@ -16,5 +16,7 @@ class InitializationOptions(BaseModel): server_version: str capabilities: ServerCapabilities instructions: str | None = None + server_title: str | None = None + server_description: str | None = None website_url: str | None = None icons: list[Icon] | None = None diff --git a/src/mcp/server/session.py b/src/mcp/server/session.py index 8f0baa3e9..ec2f56cce 100644 --- a/src/mcp/server/session.py +++ b/src/mcp/server/session.py @@ -176,7 +176,9 @@ async def _received_request(self, responder: RequestResponder[types.ClientReques capabilities=self._init_options.capabilities, serverInfo=types.Implementation( name=self._init_options.server_name, + title=self._init_options.server_title, version=self._init_options.server_version, + description=self._init_options.server_description, websiteUrl=self._init_options.website_url, icons=self._init_options.icons, ), diff --git a/src/mcp/types.py b/src/mcp/types.py index 654c00660..74b008243 100644 --- a/src/mcp/types.py +++ b/src/mcp/types.py @@ -265,6 +265,15 @@ class Implementation(BaseMetadata): version: str + description: str | None = None + """ + An optional human-readable description of what this implementation does. + + This can be used by clients or servers to provide context about their purpose + and capabilities. For example, a server might describe the types of resources + or tools it provides, while a client might describe its intended use case. + """ + websiteUrl: str | None = None """An optional URL of the website for this implementation.""" diff --git a/tests/server/test_session.py b/tests/server/test_session.py index 34f9c6e28..f47a55885 100644 --- a/tests/server/test_session.py +++ b/tests/server/test_session.py @@ -83,6 +83,62 @@ async def run_server(): assert received_initialized +@pytest.mark.anyio +async def test_server_session_initialize_with_title_and_description(): + """Test that server_title and server_description are passed through to serverInfo.""" + server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](1) + client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](1) + + async def message_handler( + message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception, + ) -> None: + if isinstance(message, Exception): + raise message + + async def run_server(): + async with ServerSession( + client_to_server_receive, + server_to_client_send, + InitializationOptions( + server_name="test-server", + server_version="1.0.0", + server_title="Test Server Title", + server_description="A description of what this server does.", + capabilities=ServerCapabilities(), + ), + ) as server_session: + async for message in server_session.incoming_messages: # pragma: no branch + if isinstance(message, Exception): # pragma: no cover + raise message + + if isinstance(message, ClientNotification) and isinstance( + message.root, InitializedNotification + ): # pragma: no branch + return + + result: types.InitializeResult | None = None + try: + async with ( + ClientSession( + server_to_client_receive, + client_to_server_send, + message_handler=message_handler, + ) as client_session, + anyio.create_task_group() as tg, + ): + tg.start_soon(run_server) + + result = await client_session.initialize() + except anyio.ClosedResourceError: # pragma: no cover + pass + + assert result is not None + assert result.serverInfo.name == "test-server" + assert result.serverInfo.title == "Test Server Title" + assert result.serverInfo.version == "1.0.0" + assert result.serverInfo.description == "A description of what this server does." + + @pytest.mark.anyio async def test_server_capabilities(): server = Server("test")