Skip to content

Commit 9475815

Browse files
authored
Merge pull request #16 from modelcontextprotocol/davidsp/init-options
Introduce Initialization options that are passed to ServerSession
2 parents 047b5d8 + cc342a0 commit 9475815

File tree

6 files changed

+86
-14
lines changed

6 files changed

+86
-14
lines changed

mcp_python/server/__init__.py

+30-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
ReadResourceResult,
3333
Resource,
3434
ResourceReference,
35+
ServerCapabilities,
3536
ServerResult,
3637
SetLevelRequest,
3738
SubscribeRequest,
@@ -40,7 +41,6 @@
4041

4142
logger = logging.getLogger(__name__)
4243

43-
4444
request_ctx: contextvars.ContextVar[RequestContext] = contextvars.ContextVar(
4545
"request_ctx"
4646
)
@@ -53,6 +53,33 @@ def __init__(self, name: str):
5353
self.notification_handlers: dict[type, Callable[..., Awaitable[None]]] = {}
5454
logger.info(f"Initializing server '{name}'")
5555

56+
def create_initialization_options(self) -> types.InitializationOptions:
57+
"""Create initialization options from this server instance."""
58+
def pkg_version(package: str) -> str:
59+
try:
60+
from importlib.metadata import version
61+
return version(package)
62+
except Exception:
63+
return "unknown"
64+
65+
return types.InitializationOptions(
66+
server_name=self.name,
67+
server_version=pkg_version("mcp_python"),
68+
capabilities=self.get_capabilities(),
69+
)
70+
71+
def get_capabilities(self) -> ServerCapabilities:
72+
"""Convert existing handlers to a ServerCapabilities object."""
73+
def get_capability(req_type: type) -> dict[str, Any] | None:
74+
return {} if req_type in self.request_handlers else None
75+
76+
return ServerCapabilities(
77+
prompts=get_capability(ListPromptsRequest),
78+
resources=get_capability(ListResourcesRequest),
79+
tools=get_capability(ListPromptsRequest),
80+
logging=get_capability(SetLevelRequest)
81+
)
82+
5683
@property
5784
def request_context(self) -> RequestContext:
5885
"""If called outside of a request context, this will raise a LookupError."""
@@ -280,9 +307,10 @@ async def run(
280307
self,
281308
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception],
282309
write_stream: MemoryObjectSendStream[JSONRPCMessage],
310+
initialization_options: types.InitializationOptions
283311
):
284312
with warnings.catch_warnings(record=True) as w:
285-
async with ServerSession(read_stream, write_stream) as session:
313+
async with ServerSession(read_stream, write_stream, initialization_options) as session:
286314
async for message in session.incoming_messages:
287315
logger.debug(f"Received message: {message}")
288316

mcp_python/server/__main__.py

+5-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
import logging
22
import sys
3-
3+
import importlib.metadata
44
import anyio
55

66
from mcp_python.server.session import ServerSession
7+
from mcp_python.server.types import InitializationOptions
78
from mcp_python.server.stdio import stdio_server
9+
from mcp_python.types import ServerCapabilities
810

911
if not sys.warnoptions:
1012
import warnings
@@ -26,8 +28,9 @@ async def receive_loop(session: ServerSession):
2628

2729

2830
async def main():
31+
version = importlib.metadata.version("mcp_python")
2932
async with stdio_server() as (read_stream, write_stream):
30-
async with ServerSession(read_stream, write_stream) as session, write_stream:
33+
async with ServerSession(read_stream, write_stream, InitializationOptions(server_name="mcp_python", server_version=version, capabilities=ServerCapabilities())) as session, write_stream:
3134
await receive_loop(session)
3235

3336

mcp_python/server/session.py

+6-8
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
BaseSession,
1111
RequestResponder,
1212
)
13+
from mcp_python.server.types import InitializationOptions
1314
from mcp_python.shared.version import SUPPORTED_PROTOCOL_VERSION
1415
from mcp_python.types import (
1516
ClientNotification,
@@ -52,9 +53,11 @@ def __init__(
5253
self,
5354
read_stream: MemoryObjectReceiveStream[JSONRPCMessage | Exception],
5455
write_stream: MemoryObjectSendStream[JSONRPCMessage],
56+
init_options: InitializationOptions
5557
) -> None:
5658
super().__init__(read_stream, write_stream, ClientRequest, ClientNotification)
5759
self._initialization_state = InitializationState.NotInitialized
60+
self._init_options = init_options
5861

5962
async def _received_request(
6063
self, responder: RequestResponder[ClientRequest, ServerResult]
@@ -66,15 +69,10 @@ async def _received_request(
6669
ServerResult(
6770
InitializeResult(
6871
protocolVersion=SUPPORTED_PROTOCOL_VERSION,
69-
capabilities=ServerCapabilities(
70-
logging=None,
71-
resources=None,
72-
tools=None,
73-
experimental=None,
74-
prompts={},
75-
),
72+
capabilities=self._init_options.capabilities,
7673
serverInfo=Implementation(
77-
name="mcp_python", version="0.1.0"
74+
name=self._init_options.server_name,
75+
version=self._init_options.server_version
7876
),
7977
)
8078
)

mcp_python/server/types.py

+8-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
from dataclasses import dataclass
66
from typing import Literal
77

8-
from mcp_python.types import Role
8+
from pydantic import BaseModel
9+
from mcp_python.types import Role, ServerCapabilities
910

1011

1112
@dataclass
@@ -25,3 +26,9 @@ class Message:
2526
class PromptResponse:
2627
messages: list[Message]
2728
desc: str | None = None
29+
30+
31+
class InitializationOptions(BaseModel):
32+
server_name: str
33+
server_version: str
34+
capabilities: ServerCapabilities

pyproject.toml

+5
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,8 @@ target-version = "py38"
3535

3636
[tool.ruff.lint.per-file-ignores]
3737
"__init__.py" = ["F401"]
38+
39+
[tool.uv]
40+
dev-dependencies = [
41+
"trio>=0.26.2",
42+
]

tests/server/test_session.py

+32-1
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22
import pytest
33

44
from mcp_python.client.session import ClientSession
5+
from mcp_python.server import Server
56
from mcp_python.server.session import ServerSession
7+
from mcp_python.server.types import InitializationOptions
68
from mcp_python.types import (
79
ClientNotification,
810
InitializedNotification,
911
JSONRPCMessage,
12+
ServerCapabilities,
1013
)
1114

1215

@@ -30,7 +33,7 @@ async def run_server():
3033
nonlocal received_initialized
3134

3235
async with ServerSession(
33-
client_to_server_receive, server_to_client_send
36+
client_to_server_receive, server_to_client_send, InitializationOptions(server_name='mcp_python', server_version='0.1.0', capabilities=ServerCapabilities())
3437
) as server_session:
3538
async for message in server_session.incoming_messages:
3639
if isinstance(message, Exception):
@@ -57,3 +60,31 @@ async def run_server():
5760
pass
5861

5962
assert received_initialized
63+
64+
65+
@pytest.mark.anyio
66+
async def test_server_capabilities():
67+
server = Server("test")
68+
69+
# Initially no capabilities
70+
caps = server.get_capabilities()
71+
assert caps.prompts is None
72+
assert caps.resources is None
73+
74+
# Add a prompts handler
75+
@server.list_prompts()
76+
async def list_prompts():
77+
return []
78+
79+
caps = server.get_capabilities()
80+
assert caps.prompts == {}
81+
assert caps.resources is None
82+
83+
# Add a resources handler
84+
@server.list_resources()
85+
async def list_resources():
86+
return []
87+
88+
caps = server.get_capabilities()
89+
assert caps.prompts == {}
90+
assert caps.resources == {}

0 commit comments

Comments
 (0)