Skip to content

Commit 7536dd9

Browse files
authored
Merge branch 'main' into main-sse-disconnect
2 parents 46f6e0a + 9e66f7c commit 7536dd9

File tree

13 files changed

+439
-14
lines changed

13 files changed

+439
-14
lines changed

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -387,6 +387,8 @@ python server.py
387387
mcp run server.py
388388
```
389389

390+
Note that `mcp run` or `mcp dev` only supports server using FastMCP and not the low-level server variant.
391+
390392
### Streamable HTTP Transport
391393

392394
> **Note**: Streamable HTTP transport is superseding SSE transport for production deployments.
@@ -694,6 +696,8 @@ if __name__ == "__main__":
694696
asyncio.run(run())
695697
```
696698

699+
Caution: The `mcp run` and `mcp dev` tool doesn't support low-level server.
700+
697701
### Writing MCP Clients
698702

699703
The SDK provides a high-level client interface for connecting to MCP servers using various [transports](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports):

src/mcp/cli/cli.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@
66
import subprocess
77
import sys
88
from pathlib import Path
9-
from typing import Annotated
9+
from typing import Annotated, Any
10+
11+
from mcp.server import FastMCP
12+
from mcp.server import Server as LowLevelServer
1013

1114
try:
1215
import typer
@@ -141,17 +144,48 @@ def _import_server(file: Path, server_object: str | None = None):
141144
module = importlib.util.module_from_spec(spec)
142145
spec.loader.exec_module(module)
143146

147+
def _check_server_object(server_object: Any, object_name: str):
148+
"""Helper function to check that the server object is supported
149+
150+
Args:
151+
server_object: The server object to check.
152+
153+
Returns:
154+
True if it's supported.
155+
"""
156+
if not isinstance(server_object, FastMCP):
157+
logger.error(
158+
f"The server object {object_name} is of type "
159+
f"{type(server_object)} (expecting {FastMCP})."
160+
)
161+
if isinstance(server_object, LowLevelServer):
162+
logger.warning(
163+
"Note that only FastMCP server is supported. Low level "
164+
"Server class is not yet supported."
165+
)
166+
return False
167+
return True
168+
144169
# If no object specified, try common server names
145170
if not server_object:
146171
# Look for the most common server object names
147172
for name in ["mcp", "server", "app"]:
148173
if hasattr(module, name):
174+
if not _check_server_object(getattr(module, name), f"{file}:{name}"):
175+
logger.error(
176+
f"Ignoring object '{file}:{name}' as it's not a valid "
177+
"server object"
178+
)
179+
continue
149180
return getattr(module, name)
150181

151182
logger.error(
152183
f"No server object found in {file}. Please either:\n"
153184
"1. Use a standard variable name (mcp, server, or app)\n"
154-
"2. Specify the object name with file:object syntax",
185+
"2. Specify the object name with file:object syntax"
186+
"3. If the server creates the FastMCP object within main() "
187+
" or another function, refactor the FastMCP object to be a "
188+
" global variable named mcp, server, or app.",
155189
extra={"file": str(file)},
156190
)
157191
sys.exit(1)
@@ -179,6 +213,9 @@ def _import_server(file: Path, server_object: str | None = None):
179213
)
180214
sys.exit(1)
181215

216+
if not _check_server_object(server, server_object):
217+
sys.exit(1)
218+
182219
return server
183220

184221

src/mcp/client/session.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,11 @@ async def send_ping(self) -> types.EmptyResult:
168168
)
169169

170170
async def send_progress_notification(
171-
self, progress_token: str | int, progress: float, total: float | None = None
171+
self,
172+
progress_token: str | int,
173+
progress: float,
174+
total: float | None = None,
175+
message: str | None = None,
172176
) -> None:
173177
"""Send a progress notification."""
174178
await self.send_notification(
@@ -179,6 +183,7 @@ async def send_progress_notification(
179183
progressToken=progress_token,
180184
progress=progress,
181185
total=total,
186+
message=message,
182187
),
183188
),
184189
)

src/mcp/client/stdio/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,8 @@ async def stdin_writer():
181181
await terminate_windows_process(process)
182182
else:
183183
process.terminate()
184+
await read_stream.aclose()
185+
await write_stream.aclose()
184186

185187

186188
def _get_executable_command(command: str) -> str:

src/mcp/server/fastmcp/server.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -952,13 +952,14 @@ def request_context(self) -> RequestContext[ServerSessionT, LifespanContextT]:
952952
return self._request_context
953953

954954
async def report_progress(
955-
self, progress: float, total: float | None = None
955+
self, progress: float, total: float | None = None, message: str | None = None
956956
) -> None:
957957
"""Report progress for the current operation.
958958
959959
Args:
960960
progress: Current progress value e.g. 24
961961
total: Optional total value e.g. 100
962+
message: Optional message e.g. Starting render...
962963
"""
963964

964965
progress_token = (
@@ -971,7 +972,10 @@ async def report_progress(
971972
return
972973

973974
await self.request_context.session.send_progress_notification(
974-
progress_token=progress_token, progress=progress, total=total
975+
progress_token=progress_token,
976+
progress=progress,
977+
total=total,
978+
message=message,
975979
)
976980

977981
async def read_resource(self, uri: str | AnyUrl) -> Iterable[ReadResourceContents]:

src/mcp/server/lowlevel/server.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ async def handle_list_resource_templates() -> list[types.ResourceTemplate]:
3737
3. Define notification handlers if needed:
3838
@server.progress_notification()
3939
async def handle_progress(
40-
progress_token: str | int, progress: float, total: float | None
40+
progress_token: str | int, progress: float, total: float | None,
41+
message: str | None
4142
) -> None:
4243
# Implementation
4344
@@ -427,13 +428,18 @@ async def handler(req: types.CallToolRequest):
427428

428429
def progress_notification(self):
429430
def decorator(
430-
func: Callable[[str | int, float, float | None], Awaitable[None]],
431+
func: Callable[
432+
[str | int, float, float | None, str | None], Awaitable[None]
433+
],
431434
):
432435
logger.debug("Registering handler for ProgressNotification")
433436

434437
async def handler(req: types.ProgressNotification):
435438
await func(
436-
req.params.progressToken, req.params.progress, req.params.total
439+
req.params.progressToken,
440+
req.params.progress,
441+
req.params.total,
442+
req.params.message,
437443
)
438444

439445
self.notification_handlers[types.ProgressNotification] = handler

src/mcp/server/session.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,7 @@ async def send_progress_notification(
282282
progress_token: str | int,
283283
progress: float,
284284
total: float | None = None,
285+
message: str | None = None,
285286
related_request_id: str | None = None,
286287
) -> None:
287288
"""Send a progress notification."""
@@ -293,6 +294,7 @@ async def send_progress_notification(
293294
progressToken=progress_token,
294295
progress=progress,
295296
total=total,
297+
message=message,
296298
),
297299
)
298300
),

src/mcp/shared/progress.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,11 @@ class ProgressContext(
4343
total: float | None
4444
current: float = field(default=0.0, init=False)
4545

46-
async def progress(self, amount: float) -> None:
46+
async def progress(self, amount: float, message: str | None = None) -> None:
4747
self.current += amount
4848

4949
await self.session.send_progress_notification(
50-
self.progress_token, self.current, total=self.total
50+
self.progress_token, self.current, total=self.total, message=message
5151
)
5252

5353

src/mcp/shared/session.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -401,7 +401,11 @@ async def _received_notification(self, notification: ReceiveNotificationT) -> No
401401
"""
402402

403403
async def send_progress_notification(
404-
self, progress_token: str | int, progress: float, total: float | None = None
404+
self,
405+
progress_token: str | int,
406+
progress: float,
407+
total: float | None = None,
408+
message: str | None = None,
405409
) -> None:
406410
"""
407411
Sends a progress notification for a request that is currently being

src/mcp/types.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -337,6 +337,11 @@ class ProgressNotificationParams(NotificationParams):
337337
total is unknown.
338338
"""
339339
total: float | None = None
340+
"""
341+
Message related to progress. This should provide relevant human readable
342+
progress information.
343+
"""
344+
message: str | None = None
340345
"""Total number of items to process (or total progress required), if known."""
341346
model_config = ConfigDict(extra="allow")
342347

0 commit comments

Comments
 (0)