Skip to content

Commit 5587c02

Browse files
Merge pull request #28 from modelcontextprotocol/justin/spec-updates
Update to spec version 2024-11-05
2 parents 48beb52 + c7d8f11 commit 5587c02

File tree

8 files changed

+381
-40
lines changed

8 files changed

+381
-40
lines changed

mcp_python/client/session.py

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@
1212
ClientNotification,
1313
ClientRequest,
1414
ClientResult,
15+
CompleteResult,
1516
EmptyResult,
17+
GetPromptResult,
1618
Implementation,
1719
InitializedNotification,
1820
InitializeResult,
1921
JSONRPCMessage,
22+
ListPromptsResult,
2023
ListResourcesResult,
24+
ListToolsResult,
2125
LoggingLevel,
26+
PromptReference,
2227
ReadResourceResult,
28+
ResourceReference,
2329
ServerNotification,
2430
ServerRequest,
2531
)
@@ -61,7 +67,14 @@ async def initialize(self) -> InitializeResult:
6167
params=InitializeRequestParams(
6268
protocolVersion=LATEST_PROTOCOL_VERSION,
6369
capabilities=ClientCapabilities(
64-
sampling=None, experimental=None
70+
sampling=None,
71+
experimental=None,
72+
roots={
73+
# TODO: Should this be based on whether we
74+
# _will_ send notifications, or only whether
75+
# they're supported?
76+
"listChanged": True
77+
},
6578
),
6679
clientInfo=Implementation(name="mcp_python", version="0.1.0"),
6780
),
@@ -220,3 +233,80 @@ async def call_tool(
220233
),
221234
CallToolResult,
222235
)
236+
237+
async def list_prompts(self) -> ListPromptsResult:
238+
"""Send a prompts/list request."""
239+
from mcp_python.types import ListPromptsRequest
240+
241+
return await self.send_request(
242+
ClientRequest(
243+
ListPromptsRequest(
244+
method="prompts/list",
245+
)
246+
),
247+
ListPromptsResult,
248+
)
249+
250+
async def get_prompt(
251+
self, name: str, arguments: dict[str, str] | None = None
252+
) -> GetPromptResult:
253+
"""Send a prompts/get request."""
254+
from mcp_python.types import GetPromptRequest, GetPromptRequestParams
255+
256+
return await self.send_request(
257+
ClientRequest(
258+
GetPromptRequest(
259+
method="prompts/get",
260+
params=GetPromptRequestParams(name=name, arguments=arguments),
261+
)
262+
),
263+
GetPromptResult,
264+
)
265+
266+
async def complete(
267+
self, ref: ResourceReference | PromptReference, argument: dict
268+
) -> CompleteResult:
269+
"""Send a completion/complete request."""
270+
from mcp_python.types import (
271+
CompleteRequest,
272+
CompleteRequestParams,
273+
CompletionArgument,
274+
)
275+
276+
return await self.send_request(
277+
ClientRequest(
278+
CompleteRequest(
279+
method="completion/complete",
280+
params=CompleteRequestParams(
281+
ref=ref,
282+
argument=CompletionArgument(**argument),
283+
),
284+
)
285+
),
286+
CompleteResult,
287+
)
288+
289+
async def list_tools(self) -> ListToolsResult:
290+
"""Send a tools/list request."""
291+
from mcp_python.types import ListToolsRequest
292+
293+
return await self.send_request(
294+
ClientRequest(
295+
ListToolsRequest(
296+
method="tools/list",
297+
)
298+
),
299+
ListToolsResult,
300+
)
301+
302+
async def send_roots_list_changed(self) -> None:
303+
"""Send a roots/list_changed notification."""
304+
from mcp_python.types import RootsListChangedNotification
305+
306+
await self.send_notification(
307+
ClientNotification(
308+
RootsListChangedNotification(
309+
method="notifications/roots/list_changed",
310+
)
311+
)
312+
)

mcp_python/server/__init__.py

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
ClientNotification,
1919
ClientRequest,
2020
CompleteRequest,
21+
EmbeddedResource,
2122
EmptyResult,
2223
ErrorData,
2324
JSONRPCMessage,
@@ -31,6 +32,7 @@
3132
PingRequest,
3233
ProgressNotification,
3334
Prompt,
35+
PromptMessage,
3436
PromptReference,
3537
ReadResourceRequest,
3638
ReadResourceResult,
@@ -40,6 +42,7 @@
4042
ServerResult,
4143
SetLevelRequest,
4244
SubscribeRequest,
45+
TextContent,
4346
Tool,
4447
UnsubscribeRequest,
4548
)
@@ -117,8 +120,6 @@ def get_prompt(self):
117120
GetPromptRequest,
118121
GetPromptResult,
119122
ImageContent,
120-
SamplingMessage,
121-
TextContent,
122123
)
123124
from mcp_python.types import (
124125
Role as Role,
@@ -133,7 +134,7 @@ def decorator(
133134

134135
async def handler(req: GetPromptRequest):
135136
prompt_get = await func(req.params.name, req.params.arguments)
136-
messages: list[SamplingMessage] = []
137+
messages: list[PromptMessage] = []
137138
for message in prompt_get.messages:
138139
match message.content:
139140
case str() as text_content:
@@ -144,15 +145,17 @@ async def handler(req: GetPromptRequest):
144145
data=img_content.data,
145146
mimeType=img_content.mime_type,
146147
)
148+
case types.EmbeddedResource() as resource:
149+
content = EmbeddedResource(
150+
type="resource", resource=resource.resource
151+
)
147152
case _:
148153
raise ValueError(
149154
f"Unexpected content type: {type(message.content)}"
150155
)
151156

152-
sampling_message = SamplingMessage(
153-
role=message.role, content=content
154-
)
155-
messages.append(sampling_message)
157+
prompt_message = PromptMessage(role=message.role, content=content)
158+
messages.append(prompt_message)
156159

157160
return ServerResult(
158161
GetPromptResult(description=prompt_get.desc, messages=messages)
@@ -169,9 +172,7 @@ def decorator(func: Callable[[], Awaitable[list[Resource]]]):
169172

170173
async def handler(_: Any):
171174
resources = await func()
172-
return ServerResult(
173-
ListResourcesResult(resources=resources)
174-
)
175+
return ServerResult(ListResourcesResult(resources=resources))
175176

176177
self.request_handlers[ListResourcesRequest] = handler
177178
return func
@@ -216,7 +217,6 @@ async def handler(req: ReadResourceRequest):
216217

217218
return decorator
218219

219-
220220
def set_logging_level(self):
221221
from mcp_python.types import EmptyResult
222222

@@ -276,14 +276,51 @@ async def handler(_: Any):
276276
return decorator
277277

278278
def call_tool(self):
279-
from mcp_python.types import CallToolResult
279+
from mcp_python.types import (
280+
CallToolResult,
281+
EmbeddedResource,
282+
ImageContent,
283+
TextContent,
284+
)
280285

281-
def decorator(func: Callable[..., Awaitable[Any]]):
286+
def decorator(
287+
func: Callable[
288+
..., Awaitable[list[str | types.ImageContent | types.EmbeddedResource]]
289+
],
290+
):
282291
logger.debug("Registering handler for CallToolRequest")
283292

284293
async def handler(req: CallToolRequest):
285-
result = await func(req.params.name, (req.params.arguments or {}))
286-
return ServerResult(CallToolResult(toolResult=result))
294+
try:
295+
results = await func(req.params.name, (req.params.arguments or {}))
296+
content = []
297+
for result in results:
298+
match result:
299+
case str() as text:
300+
content.append(TextContent(type="text", text=text))
301+
case types.ImageContent() as img:
302+
content.append(
303+
ImageContent(
304+
type="image",
305+
data=img.data,
306+
mimeType=img.mime_type,
307+
)
308+
)
309+
case types.EmbeddedResource() as resource:
310+
content.append(
311+
EmbeddedResource(
312+
type="resource", resource=resource.resource
313+
)
314+
)
315+
316+
return ServerResult(CallToolResult(content=content, isError=False))
317+
except Exception as e:
318+
return ServerResult(
319+
CallToolResult(
320+
content=[TextContent(type="text", text=str(e))],
321+
isError=True,
322+
)
323+
)
287324

288325
self.request_handlers[CallToolRequest] = handler
289326
return func

mcp_python/server/session.py

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,16 @@
2323
InitializeRequest,
2424
InitializeResult,
2525
JSONRPCMessage,
26+
ListRootsResult,
2627
LoggingLevel,
28+
ModelPreferences,
29+
PromptListChangedNotification,
30+
ResourceListChangedNotification,
2731
SamplingMessage,
2832
ServerNotification,
2933
ServerRequest,
3034
ServerResult,
35+
ToolListChangedNotification,
3136
)
3237

3338

@@ -132,7 +137,7 @@ async def send_resource_updated(self, uri: AnyUrl) -> None:
132137
)
133138
)
134139

135-
async def request_create_message(
140+
async def create_message(
136141
self,
137142
messages: list[SamplingMessage],
138143
*,
@@ -142,6 +147,7 @@ async def request_create_message(
142147
temperature: float | None = None,
143148
stop_sequences: list[str] | None = None,
144149
metadata: dict[str, Any] | None = None,
150+
model_preferences: ModelPreferences | None = None,
145151
) -> CreateMessageResult:
146152
"""Send a sampling/create_message request."""
147153
from mcp_python.types import (
@@ -161,12 +167,26 @@ async def request_create_message(
161167
maxTokens=max_tokens,
162168
stopSequences=stop_sequences,
163169
metadata=metadata,
170+
modelPreferences=model_preferences,
164171
),
165172
)
166173
),
167174
CreateMessageResult,
168175
)
169176

177+
async def list_roots(self) -> ListRootsResult:
178+
"""Send a roots/list request."""
179+
from mcp_python.types import ListRootsRequest
180+
181+
return await self.send_request(
182+
ServerRequest(
183+
ListRootsRequest(
184+
method="roots/list",
185+
)
186+
),
187+
ListRootsResult,
188+
)
189+
170190
async def send_ping(self) -> EmptyResult:
171191
"""Send a ping request."""
172192
from mcp_python.types import PingRequest
@@ -198,3 +218,33 @@ async def send_progress_notification(
198218
)
199219
)
200220
)
221+
222+
async def send_resource_list_changed(self) -> None:
223+
"""Send a resource list changed notification."""
224+
await self.send_notification(
225+
ServerNotification(
226+
ResourceListChangedNotification(
227+
method="notifications/resources/list_changed",
228+
)
229+
)
230+
)
231+
232+
async def send_tool_list_changed(self) -> None:
233+
"""Send a tool list changed notification."""
234+
await self.send_notification(
235+
ServerNotification(
236+
ToolListChangedNotification(
237+
method="notifications/tools/list_changed",
238+
)
239+
)
240+
)
241+
242+
async def send_prompt_list_changed(self) -> None:
243+
"""Send a prompt list changed notification."""
244+
await self.send_notification(
245+
ServerNotification(
246+
PromptListChangedNotification(
247+
method="notifications/prompts/list_changed",
248+
)
249+
)
250+
)

mcp_python/server/types.py

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
"""
2-
This module provides simpler types to use with the server for managing prompts.
2+
This module provides simpler types to use with the server for managing prompts
3+
and tools.
34
"""
45

56
from dataclasses import dataclass
67
from typing import Literal
78

89
from pydantic import BaseModel
910

10-
from mcp_python.types import Role, ServerCapabilities
11+
from mcp_python.types import (
12+
BlobResourceContents,
13+
Role,
14+
ServerCapabilities,
15+
TextResourceContents,
16+
)
1117

1218

1319
@dataclass
@@ -17,10 +23,15 @@ class ImageContent:
1723
mime_type: str
1824

1925

26+
@dataclass
27+
class EmbeddedResource:
28+
resource: TextResourceContents | BlobResourceContents
29+
30+
2031
@dataclass
2132
class Message:
2233
role: Role
23-
content: str | ImageContent
34+
content: str | ImageContent | EmbeddedResource
2435

2536

2637
@dataclass

0 commit comments

Comments
 (0)