Skip to content

Commit e41fa5c

Browse files
committed
feat: support setting title and description for server
Add support for the `title` and `description` fields in the `Implementation` type, allowing servers to provide human-readable metadata during initialization. Changes: - Add `description` field to `types.Implementation` - Add `server_title` and `server_description` to `InitializationOptions` - Wire up title/description through `Server` constructor and session - Add test for title and description passthrough Github-Issue:#1783
1 parent a3a4b8d commit e41fa5c

File tree

5 files changed

+75
-0
lines changed

5 files changed

+75
-0
lines changed

src/mcp/server/lowlevel/server.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ def __init__(
139139
self,
140140
name: str,
141141
version: str | None = None,
142+
title: str | None = None,
143+
description: str | None = None,
142144
instructions: str | None = None,
143145
website_url: str | None = None,
144146
icons: list[types.Icon] | None = None,
@@ -149,6 +151,8 @@ def __init__(
149151
):
150152
self.name = name
151153
self.version = version
154+
self.title = title
155+
self.description = description
152156
self.instructions = instructions
153157
self.website_url = website_url
154158
self.icons = icons
@@ -186,6 +190,8 @@ def pkg_version(package: str) -> str:
186190
experimental_capabilities or {},
187191
),
188192
instructions=self.instructions,
193+
server_title=self.title,
194+
server_description=self.description,
189195
website_url=self.website_url,
190196
icons=self.icons,
191197
)

src/mcp/server/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,5 +16,7 @@ class InitializationOptions(BaseModel):
1616
server_version: str
1717
capabilities: ServerCapabilities
1818
instructions: str | None = None
19+
server_title: str | None = None
20+
server_description: str | None = None
1921
website_url: str | None = None
2022
icons: list[Icon] | None = None

src/mcp/server/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,9 @@ async def _received_request(self, responder: RequestResponder[types.ClientReques
176176
capabilities=self._init_options.capabilities,
177177
serverInfo=types.Implementation(
178178
name=self._init_options.server_name,
179+
title=self._init_options.server_title,
179180
version=self._init_options.server_version,
181+
description=self._init_options.server_description,
180182
websiteUrl=self._init_options.website_url,
181183
icons=self._init_options.icons,
182184
),

src/mcp/types.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,15 @@ class Implementation(BaseMetadata):
265265

266266
version: str
267267

268+
description: str | None = None
269+
"""
270+
An optional human-readable description of what this implementation does.
271+
272+
This can be used by clients or servers to provide context about their purpose
273+
and capabilities. For example, a server might describe the types of resources
274+
or tools it provides, while a client might describe its intended use case.
275+
"""
276+
268277
websiteUrl: str | None = None
269278
"""An optional URL of the website for this implementation."""
270279

tests/server/test_session.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,62 @@ async def run_server():
8383
assert received_initialized
8484

8585

86+
@pytest.mark.anyio
87+
async def test_server_session_initialize_with_title_and_description():
88+
"""Test that server_title and server_description are passed through to serverInfo."""
89+
server_to_client_send, server_to_client_receive = anyio.create_memory_object_stream[SessionMessage](1)
90+
client_to_server_send, client_to_server_receive = anyio.create_memory_object_stream[SessionMessage](1)
91+
92+
async def message_handler(
93+
message: RequestResponder[types.ServerRequest, types.ClientResult] | types.ServerNotification | Exception,
94+
) -> None:
95+
if isinstance(message, Exception):
96+
raise message
97+
98+
async def run_server():
99+
async with ServerSession(
100+
client_to_server_receive,
101+
server_to_client_send,
102+
InitializationOptions(
103+
server_name="test-server",
104+
server_version="1.0.0",
105+
server_title="Test Server Title",
106+
server_description="A description of what this server does.",
107+
capabilities=ServerCapabilities(),
108+
),
109+
) as server_session:
110+
async for message in server_session.incoming_messages: # pragma: no branch
111+
if isinstance(message, Exception): # pragma: no cover
112+
raise message
113+
114+
if isinstance(message, ClientNotification) and isinstance(
115+
message.root, InitializedNotification
116+
): # pragma: no branch
117+
return
118+
119+
result: types.InitializeResult | None = None
120+
try:
121+
async with (
122+
ClientSession(
123+
server_to_client_receive,
124+
client_to_server_send,
125+
message_handler=message_handler,
126+
) as client_session,
127+
anyio.create_task_group() as tg,
128+
):
129+
tg.start_soon(run_server)
130+
131+
result = await client_session.initialize()
132+
except anyio.ClosedResourceError: # pragma: no cover
133+
pass
134+
135+
assert result is not None
136+
assert result.serverInfo.name == "test-server"
137+
assert result.serverInfo.title == "Test Server Title"
138+
assert result.serverInfo.version == "1.0.0"
139+
assert result.serverInfo.description == "A description of what this server does."
140+
141+
86142
@pytest.mark.anyio
87143
async def test_server_capabilities():
88144
server = Server("test")

0 commit comments

Comments
 (0)