Skip to content

Commit da31a3a

Browse files
authored
Merge branch 'main' into main
2 parents 382d37d + 07ae8c0 commit da31a3a

21 files changed

+502
-129
lines changed

src/mcp/client/session.py

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ async def initialize(self) -> types.InitializeResult:
151151
result = await self.send_request(
152152
types.ClientRequest(
153153
types.InitializeRequest(
154-
method="initialize",
155154
params=types.InitializeRequestParams(
156155
protocolVersion=types.LATEST_PROTOCOL_VERSION,
157156
capabilities=types.ClientCapabilities(
@@ -170,20 +169,14 @@ async def initialize(self) -> types.InitializeResult:
170169
if result.protocolVersion not in SUPPORTED_PROTOCOL_VERSIONS:
171170
raise RuntimeError(f"Unsupported protocol version from the server: {result.protocolVersion}")
172171

173-
await self.send_notification(
174-
types.ClientNotification(types.InitializedNotification(method="notifications/initialized"))
175-
)
172+
await self.send_notification(types.ClientNotification(types.InitializedNotification()))
176173

177174
return result
178175

179176
async def send_ping(self) -> types.EmptyResult:
180177
"""Send a ping request."""
181178
return await self.send_request(
182-
types.ClientRequest(
183-
types.PingRequest(
184-
method="ping",
185-
)
186-
),
179+
types.ClientRequest(types.PingRequest()),
187180
types.EmptyResult,
188181
)
189182

@@ -198,7 +191,6 @@ async def send_progress_notification(
198191
await self.send_notification(
199192
types.ClientNotification(
200193
types.ProgressNotification(
201-
method="notifications/progress",
202194
params=types.ProgressNotificationParams(
203195
progressToken=progress_token,
204196
progress=progress,
@@ -214,7 +206,6 @@ async def set_logging_level(self, level: types.LoggingLevel) -> types.EmptyResul
214206
return await self.send_request(
215207
types.ClientRequest(
216208
types.SetLevelRequest(
217-
method="logging/setLevel",
218209
params=types.SetLevelRequestParams(level=level),
219210
)
220211
),
@@ -226,7 +217,6 @@ async def list_resources(self, cursor: str | None = None) -> types.ListResources
226217
return await self.send_request(
227218
types.ClientRequest(
228219
types.ListResourcesRequest(
229-
method="resources/list",
230220
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
231221
)
232222
),
@@ -238,7 +228,6 @@ async def list_resource_templates(self, cursor: str | None = None) -> types.List
238228
return await self.send_request(
239229
types.ClientRequest(
240230
types.ListResourceTemplatesRequest(
241-
method="resources/templates/list",
242231
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
243232
)
244233
),
@@ -250,7 +239,6 @@ async def read_resource(self, uri: AnyUrl) -> types.ReadResourceResult:
250239
return await self.send_request(
251240
types.ClientRequest(
252241
types.ReadResourceRequest(
253-
method="resources/read",
254242
params=types.ReadResourceRequestParams(uri=uri),
255243
)
256244
),
@@ -262,7 +250,6 @@ async def subscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
262250
return await self.send_request(
263251
types.ClientRequest(
264252
types.SubscribeRequest(
265-
method="resources/subscribe",
266253
params=types.SubscribeRequestParams(uri=uri),
267254
)
268255
),
@@ -274,7 +261,6 @@ async def unsubscribe_resource(self, uri: AnyUrl) -> types.EmptyResult:
274261
return await self.send_request(
275262
types.ClientRequest(
276263
types.UnsubscribeRequest(
277-
method="resources/unsubscribe",
278264
params=types.UnsubscribeRequestParams(uri=uri),
279265
)
280266
),
@@ -293,7 +279,6 @@ async def call_tool(
293279
result = await self.send_request(
294280
types.ClientRequest(
295281
types.CallToolRequest(
296-
method="tools/call",
297282
params=types.CallToolRequestParams(
298283
name=name,
299284
arguments=arguments,
@@ -337,7 +322,6 @@ async def list_prompts(self, cursor: str | None = None) -> types.ListPromptsResu
337322
return await self.send_request(
338323
types.ClientRequest(
339324
types.ListPromptsRequest(
340-
method="prompts/list",
341325
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
342326
)
343327
),
@@ -349,7 +333,6 @@ async def get_prompt(self, name: str, arguments: dict[str, str] | None = None) -
349333
return await self.send_request(
350334
types.ClientRequest(
351335
types.GetPromptRequest(
352-
method="prompts/get",
353336
params=types.GetPromptRequestParams(name=name, arguments=arguments),
354337
)
355338
),
@@ -370,7 +353,6 @@ async def complete(
370353
return await self.send_request(
371354
types.ClientRequest(
372355
types.CompleteRequest(
373-
method="completion/complete",
374356
params=types.CompleteRequestParams(
375357
ref=ref,
376358
argument=types.CompletionArgument(**argument),
@@ -386,7 +368,6 @@ async def list_tools(self, cursor: str | None = None) -> types.ListToolsResult:
386368
result = await self.send_request(
387369
types.ClientRequest(
388370
types.ListToolsRequest(
389-
method="tools/list",
390371
params=types.PaginatedRequestParams(cursor=cursor) if cursor is not None else None,
391372
)
392373
),
@@ -402,13 +383,7 @@ async def list_tools(self, cursor: str | None = None) -> types.ListToolsResult:
402383

403384
async def send_roots_list_changed(self) -> None:
404385
"""Send a roots/list_changed notification."""
405-
await self.send_notification(
406-
types.ClientNotification(
407-
types.RootsListChangedNotification(
408-
method="notifications/roots/list_changed",
409-
)
410-
)
411-
)
386+
await self.send_notification(types.ClientNotification(types.RootsListChangedNotification()))
412387

413388
async def _received_request(self, responder: RequestResponder[types.ServerRequest, types.ClientResult]) -> None:
414389
ctx = RequestContext[ClientSession, Any](

src/mcp/client/stdio/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"HOMEPATH",
3333
"LOCALAPPDATA",
3434
"PATH",
35+
"PATHEXT",
3536
"PROCESSOR_ARCHITECTURE",
3637
"SYSTEMDRIVE",
3738
"SYSTEMROOT",

src/mcp/client/streamable_http.py

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -281,17 +281,19 @@ async def _handle_post_request(self, ctx: RequestContext) -> None:
281281
if is_initialization:
282282
self._maybe_extract_session_id_from_response(response)
283283

284-
content_type = response.headers.get(CONTENT_TYPE, "").lower()
285-
286-
if content_type.startswith(JSON):
287-
await self._handle_json_response(response, ctx.read_stream_writer, is_initialization)
288-
elif content_type.startswith(SSE):
289-
await self._handle_sse_response(response, ctx, is_initialization)
290-
else:
291-
await self._handle_unexpected_content_type(
292-
content_type,
293-
ctx.read_stream_writer,
294-
)
284+
# Per https://modelcontextprotocol.io/specification/2025-06-18/basic#notifications:
285+
# The server MUST NOT send a response to notifications.
286+
if isinstance(message.root, JSONRPCRequest):
287+
content_type = response.headers.get(CONTENT_TYPE, "").lower()
288+
if content_type.startswith(JSON):
289+
await self._handle_json_response(response, ctx.read_stream_writer, is_initialization)
290+
elif content_type.startswith(SSE):
291+
await self._handle_sse_response(response, ctx, is_initialization)
292+
else:
293+
await self._handle_unexpected_content_type(
294+
content_type,
295+
ctx.read_stream_writer,
296+
)
295297

296298
async def _handle_json_response(
297299
self,

src/mcp/server/auth/provider.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ async def revoke_token(
300300

301301
def construct_redirect_uri(redirect_uri_base: str, **params: str | None) -> str:
302302
parsed_uri = urlparse(redirect_uri_base)
303-
query_params = [(k, v) for k, vs in parse_qs(parsed_uri.query) for v in vs]
303+
query_params = [(k, v) for k, vs in parse_qs(parsed_uri.query).items() for v in vs]
304304
for k, v in params.items():
305305
if v is not None:
306306
query_params.append((k, v))

src/mcp/server/fastmcp/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from importlib.metadata import version
44

55
from .server import Context, FastMCP
6-
from .utilities.types import Image
6+
from .utilities.types import Audio, Image
77

88
__version__ = version("mcp")
9-
__all__ = ["FastMCP", "Context", "Image"]
9+
__all__ = ["FastMCP", "Context", "Image", "Audio"]

src/mcp/server/fastmcp/utilities/func_metadata.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
from mcp.server.fastmcp.exceptions import InvalidSignature
2323
from mcp.server.fastmcp.utilities.logging import get_logger
24-
from mcp.server.fastmcp.utilities.types import Image
24+
from mcp.server.fastmcp.utilities.types import Audio, Image
2525
from mcp.types import ContentBlock, TextContent
2626

2727
logger = get_logger(__name__)
@@ -506,6 +506,9 @@ def _convert_to_content(
506506
if isinstance(result, Image):
507507
return [result.to_image_content()]
508508

509+
if isinstance(result, Audio):
510+
return [result.to_audio_content()]
511+
509512
if isinstance(result, list | tuple):
510513
return list(
511514
chain.from_iterable(

src/mcp/server/fastmcp/utilities/types.py

Lines changed: 48 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import base64
44
from pathlib import Path
55

6-
from mcp.types import ImageContent
6+
from mcp.types import AudioContent, ImageContent
77

88

99
class Image:
@@ -52,3 +52,50 @@ def to_image_content(self) -> ImageContent:
5252
raise ValueError("No image data available")
5353

5454
return ImageContent(type="image", data=data, mimeType=self._mime_type)
55+
56+
57+
class Audio:
58+
"""Helper class for returning audio from tools."""
59+
60+
def __init__(
61+
self,
62+
path: str | Path | None = None,
63+
data: bytes | None = None,
64+
format: str | None = None,
65+
):
66+
if not bool(path) ^ bool(data):
67+
raise ValueError("Either path or data can be provided")
68+
69+
self.path = Path(path) if path else None
70+
self.data = data
71+
self._format = format
72+
self._mime_type = self._get_mime_type()
73+
74+
def _get_mime_type(self) -> str:
75+
"""Get MIME type from format or guess from file extension."""
76+
if self._format:
77+
return f"audio/{self._format.lower()}"
78+
79+
if self.path:
80+
suffix = self.path.suffix.lower()
81+
return {
82+
".wav": "audio/wav",
83+
".mp3": "audio/mpeg",
84+
".ogg": "audio/ogg",
85+
".flac": "audio/flac",
86+
".aac": "audio/aac",
87+
".m4a": "audio/mp4",
88+
}.get(suffix, "application/octet-stream")
89+
return "audio/wav" # default for raw binary data
90+
91+
def to_audio_content(self) -> AudioContent:
92+
"""Convert to MCP AudioContent."""
93+
if self.path:
94+
with open(self.path, "rb") as f:
95+
data = base64.b64encode(f.read()).decode()
96+
elif self.data is not None:
97+
data = base64.b64encode(self.data).decode()
98+
else:
99+
raise ValueError("No audio data available")
100+
101+
return AudioContent(type="audio", data=data, mimeType=self._mime_type)

src/mcp/server/session.py

Lines changed: 5 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,6 @@ async def send_log_message(
186186
await self.send_notification(
187187
types.ServerNotification(
188188
types.LoggingMessageNotification(
189-
method="notifications/message",
190189
params=types.LoggingMessageNotificationParams(
191190
level=level,
192191
data=data,
@@ -202,7 +201,6 @@ async def send_resource_updated(self, uri: AnyUrl) -> None:
202201
await self.send_notification(
203202
types.ServerNotification(
204203
types.ResourceUpdatedNotification(
205-
method="notifications/resources/updated",
206204
params=types.ResourceUpdatedNotificationParams(uri=uri),
207205
)
208206
)
@@ -225,7 +223,6 @@ async def create_message(
225223
return await self.send_request(
226224
request=types.ServerRequest(
227225
types.CreateMessageRequest(
228-
method="sampling/createMessage",
229226
params=types.CreateMessageRequestParams(
230227
messages=messages,
231228
systemPrompt=system_prompt,
@@ -247,11 +244,7 @@ async def create_message(
247244
async def list_roots(self) -> types.ListRootsResult:
248245
"""Send a roots/list request."""
249246
return await self.send_request(
250-
types.ServerRequest(
251-
types.ListRootsRequest(
252-
method="roots/list",
253-
)
254-
),
247+
types.ServerRequest(types.ListRootsRequest()),
255248
types.ListRootsResult,
256249
)
257250

@@ -273,7 +266,6 @@ async def elicit(
273266
return await self.send_request(
274267
types.ServerRequest(
275268
types.ElicitRequest(
276-
method="elicitation/create",
277269
params=types.ElicitRequestParams(
278270
message=message,
279271
requestedSchema=requestedSchema,
@@ -287,11 +279,7 @@ async def elicit(
287279
async def send_ping(self) -> types.EmptyResult:
288280
"""Send a ping request."""
289281
return await self.send_request(
290-
types.ServerRequest(
291-
types.PingRequest(
292-
method="ping",
293-
)
294-
),
282+
types.ServerRequest(types.PingRequest()),
295283
types.EmptyResult,
296284
)
297285

@@ -307,7 +295,6 @@ async def send_progress_notification(
307295
await self.send_notification(
308296
types.ServerNotification(
309297
types.ProgressNotification(
310-
method="notifications/progress",
311298
params=types.ProgressNotificationParams(
312299
progressToken=progress_token,
313300
progress=progress,
@@ -321,33 +308,15 @@ async def send_progress_notification(
321308

322309
async def send_resource_list_changed(self) -> None:
323310
"""Send a resource list changed notification."""
324-
await self.send_notification(
325-
types.ServerNotification(
326-
types.ResourceListChangedNotification(
327-
method="notifications/resources/list_changed",
328-
)
329-
)
330-
)
311+
await self.send_notification(types.ServerNotification(types.ResourceListChangedNotification()))
331312

332313
async def send_tool_list_changed(self) -> None:
333314
"""Send a tool list changed notification."""
334-
await self.send_notification(
335-
types.ServerNotification(
336-
types.ToolListChangedNotification(
337-
method="notifications/tools/list_changed",
338-
)
339-
)
340-
)
315+
await self.send_notification(types.ServerNotification(types.ToolListChangedNotification()))
341316

342317
async def send_prompt_list_changed(self) -> None:
343318
"""Send a prompt list changed notification."""
344-
await self.send_notification(
345-
types.ServerNotification(
346-
types.PromptListChangedNotification(
347-
method="notifications/prompts/list_changed",
348-
)
349-
)
350-
)
319+
await self.send_notification(types.ServerNotification(types.PromptListChangedNotification()))
351320

352321
async def _handle_incoming(self, req: ServerRequestResponder) -> None:
353322
await self._incoming_message_stream_writer.send(req)

0 commit comments

Comments
 (0)