# Real MCP server dependency experiment This is a local, controlled reproduction using the official PyPI package `mcp-server-git==2026.7.10`. It adds no project dependencies and makes no external submissions. See [RESULTS.md](RESULTS.md) for observed outcomes and [PLAN.md](PLAN.md) for the selection and checks recorded before installation. ## Run the complete experiment Prerequisites: Python with `venv`/`ensurepip`, Git, uv, and access to `pypi.org` and `files.pythonhosted.org`. The recorded run used Python 3.14.3 and uv 0.11.14 on macOS arm64. Run from this directory: ```sh RUN_ROOT=$(mktemp -d /private/tmp/asn-real-mcp-XXXXXX) EVIDENCE_ROOT=$(mktemp -d ./rerun-results-XXXXXX) python3 -B reproduce.py --scenario all --run-root "$RUN_ROOT" --output-dir "$EVIDENCE_ROOT" ``` The script creates four separate environments: | Directory under run root | Purpose | | --- | --- | | `broken` | Fixed server 2026.7.10 + MCP 2.0.0, retained unchanged | | `natural` | Fixed server 2026.7.10; SDK selected naturally today | | `pip-repaired` | Separate MCP 2.0.0 failure, then the pip `mcp<2` repair | | `uv-tools/mcp-server-git` | Separate uv tool MCP 2.0.0 failure, then `--force --with mcp<2` | The deliberately incompatible SDK is installed through the normal package resolver, within the server's declared dependency range. No server source is patched. Server wheel hashes are checked against official PyPI metadata, and each installed `mcp_server_git/server.py` is directly compared by SHA-256 to that file in the official wheel. This is not a file-by-file hash audit of every installed package. `--scenario broken|natural|pip|uv` runs a single branch. Use a fresh run root and output directory for every invocation. The script refuses any nonempty output directory before starting subprocesses or downloads, and refuses to reuse an existing venv. Omitting `--output-dir` writes to `/evidence`, never this directory's archived `results/`. A dated `/private/tmp/asn-journey-2026-10-13*` root is also accepted for the parent experiment's separately authorized controller. The overwrite protection can be checked without downloading or installing anything: ```sh python3 -B -m unittest -v test_output_guard.py ``` ## Commands under test The controller supplies a temporary cache, ignores pip configuration with `--isolated`, sets the public PyPI index explicitly, and records actual argv in `results/commands.json`. The essential pip operations are: ```sh python -m pip install 'mcp-server-git==2026.7.10' 'mcp==2.0.0' python -m pip install 'mcp-server-git==2026.7.10' 'mcp<2' python -m pip check ``` The uv branch sets `UV_TOOL_DIR`, `UV_TOOL_BIN_DIR`, `UV_CACHE_DIR`, and `UV_PYTHON_INSTALL_DIR` below the temporary run root, disables config loading and Python downloads, and runs: ```sh uv tool install --python --with 'mcp==2.0.0' 'mcp-server-git==2026.7.10' uv tool install --python --force --with 'mcp<2' 'mcp-server-git==2026.7.10' uv pip check --python ``` `mcp<2` intentionally tests a real resolver constraint, so its selected release can change later. All recorded environments have exact `*-requirements.txt` snapshots. For the captured complete dependency set, install `results/broken-requirements.txt` or `results/pip-after-requirements.txt` into a **fresh temporary venv**, using its own Python and a temporary pip cache. PyPI download hashes are preserved in the corresponding `*-distributions.json` files. The controlled failure explicitly pins MCP 2.0.0 independently of today's natural resolution. ## Protocol check and independent diagnostic fixture `probe.py` uses only the Python standard library. It awaits `initialize` before sending `notifications/initialized`, then awaits `tools/list`, then actually calls `git_status` on the temporary empty repository. The check requires `isError != true` and expected status text. EOF, protocol errors, invalid stdout, and timeouts fail the check. It closes stdin and records process exit status after verification. ```sh python3 -B probe.py --repo "$RUN_ROOT/empty-repo" --run-root "$RUN_ROOT" \ --output ./protocol.json -- \ "$RUN_ROOT/pip-repaired/bin/python" -m mcp_server_git \ --repository "$RUN_ROOT/empty-repo" ``` Use the uv-installed executable at `$RUN_ROOT/uv-bin/mcp-server-git` to check the actual uv launcher. The complete experiment checks both the tool environment's Python module and this console entrypoint. For an independent diagnostic task, the controller can build only `--scenario broken`, copy `probe.py` under a neutral name, and give the agent the `broken/bin/python` launch command and temporary repository path. Keep this README, the repair script, dependency construction commands, and controller results outside that agent's context. The fixture's normal verification command expects success; the controller alone may pass `--expect list-tools-error` to assert the known initial failure. Building a fixture and demonstrating a repair here does not itself establish an independent agent's behavior. The server process gets a small explicit environment with no credential variables. Git global/system configuration is disabled. `git_status` reads only the new empty temporary repository. All caches, binaries, and downloaded wheels are temporary; no server touches the project repository. ## Evidence files - `results/provenance.json`: official package metadata, wheel hashes, decorator locations. - `results/host.json`: Python, uv, Git, OS and architecture. - `results/commands.json`: installation/check commands, exit codes and bounded sanitized stdout/stderr. - `results/*-environment.json`, `*-requirements.txt`: actual installed versions and source hash check. - `results/*-distributions.json`: pip's distribution URLs and SHA-256 values. - `results/*-protocol.json`: initialization, tool names/schema, real tool-call result or startup traceback. - `results/summary.json`, `validation.json`: postprocessed compact outcome table and consistency checks for the archived run only. The reproduction script emits fresh per-scenario evidence; it does not regenerate these two archive summaries or copy their passed status into a rerun. Recorded temporary paths use `$RUN_ROOT`; user-home prefixes are redacted. These are deliberately selected command and protocol results, not conversation transcripts or hidden reasoning.