MCP Python SDK의 stdio 연결을 직접 테스트하던 중, 부모 프로세스에 설정한 더미 토큰이 자식 프로세스에서는 사라지는 것을 발견했습니다. 같은 Python으로 일반적인 subprocess를 사용하여 실행할 때는 값이 보입니다.
MCP 서버로 전달되는 환경 변수는 이름을 정해 명시해야 합니다. 저는 이 방식을 구현하고 싶습니다. 부모의 환경 전체를 전달하면 사용하지 않는 비밀 정보까지 모두 넘겨주게 되기 때문입니다.
인증 대상과의 통신이나 LLM 호출은 사용하지 않고, 로컬 자식 프로세스로 환경 변수가 도착하는지 여부만을 분리하여 테스트했습니다.
macOS, CPython 3.12.13, mcp==1.27.1로 실행했습니다. 의존성은 anyio==4.13.0, pydantic==2.13.4입니다. 대상은 mcp.client.stdio.stdio_client입니다.
부모 프로세스에 더미 변수 2개를 설정합니다. 하나는 필수적인 설정인 MCP_DEMO_TOKEN이고, 다른 하나는 불필요한 설정인 MCP_DEMO_OTHER입니다. 실제 API 키는 사용하지 않았습니다.
env_probe.py에 저장하고, 위 환경에서 python env_probe.py를 실행합니다.
import asyncio
import json
import os
...
자식 프로세스는 환경 변수의 유무를 JSON-RPC 형식으로 한 줄 반환합니다. 이 코드는 프로세스 시작과 읽기 검증용이며, MCP 초기화나 툴 호출은 생략했습니다. 토큰 값은 출력하지 않습니다.
직접 테스트한 결과는 다음과 같습니다:
subprocess token=True other=True path=True
SDK default token=False other=False path=True
SDK empty token=False other=False path=True
...
일반적인 subprocess를 사용할 때는 두 값 모두 전달되었습니다. 하지만 SDK에서 env를 생략하면 둘 다 전달되지 않습니다.
처음에 의아했던 것은 그 다음의 path=True였습니다.
SDK의 env에 빈 딕셔너리({})를 전달했음에도 불구하고, PATH는 남아있었습니다.
설치된 1.27.1 버전에서 mcp/client/stdio/__init__.py 파일을 읽어보았습니다. 같은 버전의 소스 코드에서는 stdio_client()가 get_default_environment() 딕셔너리에 server.env를 추가하여 자식 프로세스를 시작하고 있었습니다.
이 macOS 환경에서 기본적으로 상속 대상에 포함되는 이름은 HOME, LOGNAME, PATH, SHELL, TERM, USER입니다. 부모에 값이 있는 변수만 가져오고, 값이 ()로 시작하는 것은 제외합니다. Windows용 목록은 별도로 존재합니다.
MCP_DEMO_TOKEN은 이 목록에 없기 때문에, 부모에서 설정했다고 해서 자동으로 가져와지지 않습니다. env={}를 지정해도 기본 딕셔너리에 아무것도 추가되지 않는다는 의미입니다. 이번 세 가지 경우를 나열하면 다음과 같습니다.
| SDK에 전달하는 env | 자식에게 전달되는 딕셔너리 생성 방식 |
|---|
| 생략 또는 None | SDK가 선택한 기본 변수 |
| ... | |
SDK의 env로 기본 변수를 일괄 삭제하는 지정은 할 수 없습니다. 표준 라이브러리에 직접 전달하는 env와 혼동하기 쉽습니다. | |
코드에서 사용된 sys.executable는 같은 Python을 사용하기 위해 고정했습니다. 단순히 python이라는 이름만으로는 어떤 가상 환경을 보고 있었는지 확인할手間이 늘어납니다.
SDK all의 경우 불필요한 변수까지 전달되었습니다. dict(os.environ)로 연결 오류가 해결되더라도, 전달 범위는 좁혀지지 않습니다.
저는 시작 설정에 필요한 변수 이름을 나열하는 방식을 사용하겠습니다. 이것이 바로 SDK explicit입니다. 실제 이름으로도 부모에서는 os.environ[이름]으로 읽습니다. 설정이 빠지면 자식 프로세스를 시작하기 전에 멈춥니다. get()을 사용하여 None을 전달하면 다른 오류가 발생할 수 있으므로, 설정 누락을 추적하기 더 쉽습니다.
이 제한만으로는 자식 프로세스를 격리할 수 없습니다. 같은 OS 사용자 권한으로 움직이며, 읽을 수 있는 파일에서 다른 정보를 얻어낼 수 있습니다. other=False는 이 시작 시점에 더미 변수를 환경을 통해 전달하지 않았다는 것만을 의미합니다. 외부 서버의 파일 권한이나 실행 환경 분리는 별도로 결정해야 합니다.
확인한 것은 로컬 시작까지입니다. 각 AI 앱의 설정 처리나 HTTP 연결, 실제 서비스의 인증은 대상이 아닙니다.
MCP 서버에서 설정을 찾을 수 없을 때는, 부모 셸(parent shell)에서 보이는지 여부 외에도 SDK가 자식 프로세스에 전달하는 이름을 확인해야 한다. 필요한 변수를 하나씩 명시함으로써, 이번 예제에서는 토큰은 전달하면서도 불필요한 변수는 보내지 않을 수 있었다. 구동 확인을 위해서는 값을 출력하지 않는 작은 프로브(probe)를 사용할 수 있다. 연결을 수정하는 변경 사항과 서버로 전달할 정보를 늘리는 변경 사항을 한 줄에서 검토할 수 있도록 하고 싶다.