Skip to content

Commit 3863f20

Browse files
authored
Server initialize response update to last spec (add title, description) (#1634)
1 parent f397783 commit 3863f20

File tree

7 files changed

+73
-1
lines changed

7 files changed

+73
-1
lines changed

src/mcp/server/fastmcp/server.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,9 +147,12 @@ class FastMCP(Generic[LifespanResultT]):
147147
def __init__( # noqa: PLR0913
148148
self,
149149
name: str | None = None,
150+
title: str | None = None,
151+
description: str | None = None,
150152
instructions: str | None = None,
151153
website_url: str | None = None,
152154
icons: list[Icon] | None = None,
155+
version: str | None = None,
153156
auth_server_provider: (OAuthAuthorizationServerProvider[Any, Any, Any] | None) = None,
154157
token_verifier: TokenVerifier | None = None,
155158
event_store: EventStore | None = None,
@@ -204,9 +207,12 @@ def __init__( # noqa: PLR0913
204207

205208
self._mcp_server = MCPServer(
206209
name=name or "FastMCP",
210+
title=title,
211+
description=description,
207212
instructions=instructions,
208213
website_url=website_url,
209214
icons=icons,
215+
version=version,
210216
# TODO(Marcelo): It seems there's a type mismatch between the lifespan type from an FastMCP and Server.
211217
# We need to create a Lifespan type that is a generic on the server type, like Starlette does.
212218
lifespan=(lifespan_wrapper(self, self.settings.lifespan) if self.settings.lifespan else default_lifespan), # type: ignore
@@ -245,6 +251,14 @@ def __init__( # noqa: PLR0913
245251
def name(self) -> str:
246252
return self._mcp_server.name
247253

254+
@property
255+
def title(self) -> str | None:
256+
return self._mcp_server.title
257+
258+
@property
259+
def description(self) -> str | None:
260+
return self._mcp_server.description
261+
248262
@property
249263
def instructions(self) -> str | None:
250264
return self._mcp_server.instructions
@@ -257,6 +271,10 @@ def website_url(self) -> str | None:
257271
def icons(self) -> list[Icon] | None:
258272
return self._mcp_server.icons
259273

274+
@property
275+
def version(self) -> str | None:
276+
return self._mcp_server.version
277+
260278
@property
261279
def session_manager(self) -> StreamableHTTPSessionManager:
262280
"""Get the StreamableHTTP session manager.

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
@@ -181,6 +185,8 @@ def pkg_version(package: str) -> str:
181185
return InitializationOptions(
182186
server_name=self.name,
183187
server_version=self.version if self.version else pkg_version("mcp"),
188+
title=self.title,
189+
description=self.description,
184190
capabilities=self.get_capabilities(
185191
notification_options or NotificationOptions(),
186192
experimental_capabilities or {},

src/mcp/server/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
class InitializationOptions(BaseModel):
1515
server_name: str
1616
server_version: str
17+
title: str | None = None
18+
description: str | None = None
1719
capabilities: ServerCapabilities
1820
instructions: str | None = None
1921
website_url: str | None = None

src/mcp/server/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ async def _received_request(self, responder: RequestResponder[types.ClientReques
178178
capabilities=self._init_options.capabilities,
179179
serverInfo=types.Implementation(
180180
name=self._init_options.server_name,
181+
title=self._init_options.title,
182+
description=self._init_options.description,
181183
version=self._init_options.server_version,
182184
websiteUrl=self._init_options.website_url,
183185
icons=self._init_options.icons,

src/mcp/types.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@ class Implementation(BaseMetadata):
249249

250250
version: str
251251

252+
title: str | None = None
253+
"""An optional human-readable title for this implementation."""
254+
255+
description: str | None = None
256+
"""An optional human-readable description of what this implementation does."""
257+
252258
websiteUrl: str | None = None
253259
"""An optional URL of the website for this implementation."""
254260

tests/server/fastmcp/test_server.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
BlobResourceContents,
2121
ContentBlock,
2222
EmbeddedResource,
23+
Icon,
2324
ImageContent,
2425
TextContent,
2526
TextResourceContents,
@@ -29,9 +30,23 @@
2930
class TestServer:
3031
@pytest.mark.anyio
3132
async def test_create_server(self):
32-
mcp = FastMCP(instructions="Server instructions")
33+
mcp = FastMCP(
34+
title="FastMCP Server",
35+
description="Server description",
36+
instructions="Server instructions",
37+
website_url="https://example.com/mcp_server",
38+
version="1.0",
39+
icons=[Icon(src="https://example.com/icon.png", mimeType="image/png", sizes=["48x48", "96x96"])],
40+
)
3341
assert mcp.name == "FastMCP"
42+
assert mcp.title == "FastMCP Server"
43+
assert mcp.description == "Server description"
3444
assert mcp.instructions == "Server instructions"
45+
assert mcp.website_url == "https://example.com/mcp_server"
46+
assert mcp.version == "1.0"
47+
assert isinstance(mcp.icons, list)
48+
assert len(mcp.icons) == 1
49+
assert mcp.icons[0].src == "https://example.com/icon.png"
3550

3651
@pytest.mark.anyio
3752
async def test_normalize_path(self):

tests/server/fastmcp/test_title.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,29 @@
1010
from mcp.types import Prompt, Resource, ResourceTemplate, Tool, ToolAnnotations
1111

1212

13+
@pytest.mark.anyio
14+
async def test_server_name_title_description_version():
15+
"""Test that server title and description are set and retrievable correctly."""
16+
mcp = FastMCP(
17+
name="TestServer",
18+
title="Test Server Title",
19+
description="This is a test server description.",
20+
version="1.0",
21+
)
22+
23+
assert mcp.title == "Test Server Title"
24+
assert mcp.description == "This is a test server description."
25+
assert mcp.version == "1.0"
26+
27+
# Start server and connect client
28+
async with create_connected_server_and_client_session(mcp._mcp_server) as client:
29+
init_result = await client.initialize()
30+
assert init_result.serverInfo.name == "TestServer"
31+
assert init_result.serverInfo.title == "Test Server Title"
32+
assert init_result.serverInfo.description == "This is a test server description."
33+
assert init_result.serverInfo.version == "1.0"
34+
35+
1336
@pytest.mark.anyio
1437
async def test_tool_title_precedence():
1538
"""Test that tool title precedence works correctly: title > annotations.title > name."""

0 commit comments

Comments
 (0)