Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions src/mcp/server/lowlevel/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ def __init__(
self,
name: str,
version: str | None = None,
title: str | None = None,
description: str | None = None,
Comment on lines +142 to +143
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to bottom to avoid positional kwarg errors

instructions: str | None = None,
website_url: str | None = None,
icons: list[types.Icon] | None = None,
Expand All @@ -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
Expand Down Expand Up @@ -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,
)
Expand Down
2 changes: 2 additions & 0 deletions src/mcp/server/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions src/mcp/server/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
),
Expand Down
9 changes: 9 additions & 0 deletions src/mcp/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""

Expand Down
56 changes: 56 additions & 0 deletions tests/server/test_session.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove pragma, instead just fail if there's an exception

raise message

if isinstance(message, ClientNotification) and isinstance(
message.root, InitializedNotification
): # pragma: no branch
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove all pragmas if possible, use asserts with failures instead

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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

remove exception catching and either accept it through asserts or fail

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")
Expand Down
Loading