Skip to content

Commit c8af6a1

Browse files
committed
Revert "Merge branch 'main' into fix/windows_stdio_subprocess"
This reverts commit d3e0975, reversing changes made to 1c6c6fb.
1 parent 5db1b10 commit c8af6a1

File tree

3 files changed

+40
-112
lines changed

3 files changed

+40
-112
lines changed

README.md

Lines changed: 20 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -315,42 +315,27 @@ async def long_task(files: list[str], ctx: Context) -> str:
315315
Authentication can be used by servers that want to expose tools accessing protected resources.
316316

317317
`mcp.server.auth` implements an OAuth 2.0 server interface, which servers can use by
318-
providing an implementation of the `OAuthAuthorizationServerProvider` protocol.
318+
providing an implementation of the `OAuthServerProvider` protocol.
319319

320-
```python
321-
from mcp import FastMCP
322-
from mcp.server.auth.provider import OAuthAuthorizationServerProvider
323-
from mcp.server.auth.settings import (
324-
AuthSettings,
325-
ClientRegistrationOptions,
326-
RevocationOptions,
327-
)
328-
329-
330-
class MyOAuthServerProvider(OAuthAuthorizationServerProvider):
331-
# See an example on how to implement at `examples/servers/simple-auth`
332-
...
333-
334-
335-
mcp = FastMCP(
336-
"My App",
337-
auth_server_provider=MyOAuthServerProvider(),
338-
auth=AuthSettings(
339-
issuer_url="https://myapp.com",
340-
revocation_options=RevocationOptions(
341-
enabled=True,
342-
),
343-
client_registration_options=ClientRegistrationOptions(
344-
enabled=True,
345-
valid_scopes=["myscope", "myotherscope"],
346-
default_scopes=["myscope"],
320+
```
321+
mcp = FastMCP("My App",
322+
auth_server_provider=MyOAuthServerProvider(),
323+
auth=AuthSettings(
324+
issuer_url="https://myapp.com",
325+
revocation_options=RevocationOptions(
326+
enabled=True,
327+
),
328+
client_registration_options=ClientRegistrationOptions(
329+
enabled=True,
330+
valid_scopes=["myscope", "myotherscope"],
331+
default_scopes=["myscope"],
332+
),
333+
required_scopes=["myscope"],
347334
),
348-
required_scopes=["myscope"],
349-
),
350335
)
351336
```
352337

353-
See [OAuthAuthorizationServerProvider](src/mcp/server/auth/provider.py) for more details.
338+
See [OAuthServerProvider](src/mcp/server/auth/provider.py) for more details.
354339

355340
## Running Your Server
356341

@@ -477,12 +462,15 @@ For low level server with Streamable HTTP implementations, see:
477462
- Stateful server: [`examples/servers/simple-streamablehttp/`](examples/servers/simple-streamablehttp/)
478463
- Stateless server: [`examples/servers/simple-streamablehttp-stateless/`](examples/servers/simple-streamablehttp-stateless/)
479464

465+
466+
480467
The streamable HTTP transport supports:
481468
- Stateful and stateless operation modes
482469
- Resumability with event stores
483-
- JSON or SSE response formats
470+
- JSON or SSE response formats
484471
- Better scalability for multi-node deployments
485472

473+
486474
### Mounting to an Existing ASGI Server
487475

488476
> **Note**: SSE transport is being superseded by [Streamable HTTP transport](https://modelcontextprotocol.io/specification/2025-03-26/basic/transports#streamable-http).

src/mcp/client/stdio/__init__.py

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -108,28 +108,20 @@ async def stdio_client(server: StdioServerParameters, errlog: TextIO = sys.stder
108108
read_stream_writer, read_stream = anyio.create_memory_object_stream(0)
109109
write_stream, write_stream_reader = anyio.create_memory_object_stream(0)
110110

111-
try:
112-
command = _get_executable_command(server.command)
113-
114-
# Open process with stderr piped for capture
115-
process = await _create_platform_compatible_process(
116-
command=command,
117-
args=server.args,
118-
env=(
119-
{**get_default_environment(), **server.env}
120-
if server.env is not None
121-
else get_default_environment()
122-
),
123-
errlog=errlog,
124-
cwd=server.cwd,
125-
)
126-
except OSError:
127-
# Clean up streams if process creation fails
128-
await read_stream.aclose()
129-
await write_stream.aclose()
130-
await read_stream_writer.aclose()
131-
await write_stream_reader.aclose()
132-
raise
111+
command = _get_executable_command(server.command)
112+
113+
# Open process with stderr piped for capture
114+
process = await _create_platform_compatible_process(
115+
command=command,
116+
args=server.args,
117+
env=(
118+
{**get_default_environment(), **server.env}
119+
if server.env is not None
120+
else get_default_environment()
121+
),
122+
errlog=errlog,
123+
cwd=server.cwd,
124+
)
133125

134126
async def stdout_reader():
135127
assert process.stdout, "Opened process is missing stdout"
@@ -185,18 +177,12 @@ async def stdin_writer():
185177
yield read_stream, write_stream
186178
finally:
187179
# Clean up process to prevent any dangling orphaned processes
188-
try:
189-
if sys.platform == "win32":
190-
await terminate_windows_process(process)
191-
else:
192-
process.terminate()
193-
except ProcessLookupError:
194-
# Process already exited, which is fine
195-
pass
180+
if sys.platform == "win32":
181+
await terminate_windows_process(process)
182+
else:
183+
process.terminate()
196184
await read_stream.aclose()
197185
await write_stream.aclose()
198-
await read_stream_writer.aclose()
199-
await write_stream_reader.aclose()
200186

201187

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

tests/client/test_stdio.py

Lines changed: 2 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,11 @@
22

33
import pytest
44

5-
from mcp.client.session import ClientSession
6-
from mcp.client.stdio import (
7-
StdioServerParameters,
8-
stdio_client,
9-
)
10-
from mcp.shared.exceptions import McpError
5+
from mcp.client.stdio import StdioServerParameters, stdio_client
116
from mcp.shared.message import SessionMessage
12-
from mcp.types import CONNECTION_CLOSED, JSONRPCMessage, JSONRPCRequest, JSONRPCResponse
7+
from mcp.types import JSONRPCMessage, JSONRPCRequest, JSONRPCResponse
138

149
tee: str = shutil.which("tee") # type: ignore
15-
python: str = shutil.which("python") # type: ignore
1610

1711

1812
@pytest.mark.anyio
@@ -56,43 +50,3 @@ async def test_stdio_client():
5650
assert read_messages[1] == JSONRPCMessage(
5751
root=JSONRPCResponse(jsonrpc="2.0", id=2, result={})
5852
)
59-
60-
61-
@pytest.mark.anyio
62-
async def test_stdio_client_bad_path():
63-
"""Check that the connection doesn't hang if process errors."""
64-
server_params = StdioServerParameters(
65-
command="python", args=["-c", "non-existent-file.py"]
66-
)
67-
async with stdio_client(server_params) as (read_stream, write_stream):
68-
async with ClientSession(read_stream, write_stream) as session:
69-
# The session should raise an error when the connection closes
70-
with pytest.raises(McpError) as exc_info:
71-
await session.initialize()
72-
73-
# Check that we got a connection closed error
74-
assert exc_info.value.error.code == CONNECTION_CLOSED
75-
assert "Connection closed" in exc_info.value.error.message
76-
77-
78-
@pytest.mark.anyio
79-
async def test_stdio_client_nonexistent_command():
80-
"""Test that stdio_client raises an error for non-existent commands."""
81-
# Create a server with a non-existent command
82-
server_params = StdioServerParameters(
83-
command="/path/to/nonexistent/command",
84-
args=["--help"],
85-
)
86-
87-
# Should raise an error when trying to start the process
88-
with pytest.raises(Exception) as exc_info:
89-
async with stdio_client(server_params) as (_, _):
90-
pass
91-
92-
# The error should indicate the command was not found
93-
error_message = str(exc_info.value)
94-
assert (
95-
"nonexistent" in error_message
96-
or "not found" in error_message.lower()
97-
or "cannot find the file" in error_message.lower() # Windows error message
98-
)

0 commit comments

Comments
 (0)