From 5508697b13ae063ace613a2b8b5d78e25c1d76b1 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Wed, 6 Nov 2024 11:05:20 +0000 Subject: [PATCH 1/2] Inherit environment variables deemed safe by default --- mcp_python/client/stdio.py | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/mcp_python/client/stdio.py b/mcp_python/client/stdio.py index f9404d3e..51c4df8b 100644 --- a/mcp_python/client/stdio.py +++ b/mcp_python/client/stdio.py @@ -1,3 +1,4 @@ +import os import sys from contextlib import asynccontextmanager @@ -10,6 +11,34 @@ from mcp_python.types import JSONRPCMessage +# Environment variables to inherit by default +DEFAULT_INHERITED_ENV_VARS = ( + ["APPDATA", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "PATH", + "PROCESSOR_ARCHITECTURE", "SYSTEMDRIVE", "SYSTEMROOT", "TEMP", + "USERNAME", "USERPROFILE"] + if sys.platform == "win32" + else ["HOME", "LOGNAME", "PATH", "SHELL", "TERM", "USER"] +) + + +def get_default_environment() -> dict[str, str]: + """Returns a default environment object including only environment variables deemed safe to inherit.""" + env: dict[str, str] = {} + + for key in DEFAULT_INHERITED_ENV_VARS: + value = os.environ.get(key) + if value is None: + continue + + if value.startswith("()"): + # Skip functions, which are a security risk + continue + + env[key] = value + + return env + + class StdioServerParameters(BaseModel): command: str """The executable to run to start the server.""" @@ -17,11 +46,11 @@ class StdioServerParameters(BaseModel): args: list[str] = Field(default_factory=list) """Command line arguments to pass to the executable.""" - env: dict[str, str] = Field(default_factory=dict) + env: dict[str, str] | None = None """ The environment to use when spawning the process. - The environment is NOT inherited from the parent process by default. + If not specified, the result of get_default_environment() will be used. """ @@ -41,7 +70,9 @@ async def stdio_client(server: StdioServerParameters): write_stream, write_stream_reader = anyio.create_memory_object_stream(0) process = await anyio.open_process( - [server.command, *server.args], env=server.env, stderr=sys.stderr + [server.command, *server.args], + env=server.env if server.env is not None else get_default_environment(), + stderr=sys.stderr ) async def stdout_reader(): From df901f698dde47a90c56a84c5a122de7b351b570 Mon Sep 17 00:00:00 2001 From: Justin Spahr-Summers Date: Wed, 6 Nov 2024 11:08:22 +0000 Subject: [PATCH 2/2] Manually fix `ruff` issue --- mcp_python/client/stdio.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mcp_python/client/stdio.py b/mcp_python/client/stdio.py index 51c4df8b..f69578e8 100644 --- a/mcp_python/client/stdio.py +++ b/mcp_python/client/stdio.py @@ -10,7 +10,6 @@ from mcp_python.types import JSONRPCMessage - # Environment variables to inherit by default DEFAULT_INHERITED_ENV_VARS = ( ["APPDATA", "HOMEDRIVE", "HOMEPATH", "LOCALAPPDATA", "PATH", @@ -22,7 +21,10 @@ def get_default_environment() -> dict[str, str]: - """Returns a default environment object including only environment variables deemed safe to inherit.""" + """ + Returns a default environment object including only environment variables deemed + safe to inherit. + """ env: dict[str, str] = {} for key in DEFAULT_INHERITED_ENV_VARS: