MCPcopy
hub / github.com/modelcontextprotocol/python-sdk / stdio_client

Function stdio_client

src/mcp/client/stdio.py:114–215  ·  view source on GitHub ↗

Spawns an MCP server subprocess and connects to it over stdin/stdout. Raises: OSError: If the server process cannot be spawned. ValueError: If the spawn parameters are invalid (embedded NUL bytes).

(
    server: StdioServerParameters, errlog: TextIO = sys.stderr
)

Source from the content-addressed store, hash-verified

112
113@asynccontextmanager
114async def stdio_client(
115 server: StdioServerParameters, errlog: TextIO = sys.stderr
116) -> AsyncGenerator[TransportStreams, None]:
117 """Spawns an MCP server subprocess and connects to it over stdin/stdout.
118
119 Raises:
120 OSError: If the server process cannot be spawned.
121 ValueError: If the spawn parameters are invalid (embedded NUL bytes).
122 """
123 command = _get_executable_command(server.command)
124
125 process = await _create_platform_compatible_process(
126 command=command,
127 args=server.args,
128 env=get_default_environment() | (server.env or {}),
129 errlog=errlog,
130 cwd=server.cwd,
131 )
132
133 # The spawn succeeded; no awaits until the task group is entered, or a
134 # cancellation delivered in the gap would leak the live process.
135 read_stream_writer, read_stream = anyio.create_memory_object_stream[SessionMessage | Exception](0)
136 write_stream, write_stream_reader = anyio.create_memory_object_stream[SessionMessage](0)
137
138 shutting_down = False
139 writer_done = anyio.Event()
140
141 async def stdout_reader() -> None:
142 assert process.stdout, "Opened process is missing stdout"
143
144 stdout = TextReceiveStream(process.stdout, encoding=server.encoding, errors=server.encoding_error_handler)
145 try:
146 async with read_stream_writer:
147 try:
148 # One line at a time; no read-ahead while a delivery is blocked.
149 buffer = ""
150 async for chunk in stdout:
151 lines = (buffer + chunk).split("\n")
152 buffer = lines.pop()
153 for line in lines:
154 try:
155 await read_stream_writer.send(_parse_line(line))
156 except (anyio.ClosedResourceError, anyio.BrokenResourceError):
157 return # the session is gone; only the drain below remains
158 finally:
159 await _drain_stdout(process)
160 except anyio.ClosedResourceError:
161 pass # our own shutdown closed the stdout stream under the read
162 except (anyio.BrokenResourceError, ConnectionError):
163 # Teardown noise during shutdown, a real failure otherwise; either way
164 # the session sees clean closure when the read stream closes.
165 if not shutting_down:
166 logger.exception("Reading from the MCP server's stdout failed mid-session")
167
168 async def stdin_writer() -> None:
169 assert process.stdin, "Opened process is missing stdin"
170
171 try:

Calls 4

_get_executable_commandFunction · 0.85
get_default_environmentFunction · 0.85
shutdownFunction · 0.85