Introduction

The original plan was to run VSCode Server or Zed Remote Server on the compute side (EPYC + Blackwell) and use the Mac as a thin client. But with an M1 Mac sitting idle, I ended up placing editors on the Mac side instead. Currently Zed handles remote editing with extensions and LSP disabled, while VSCode Remote SSH is used when MCP is needed.

During this configuration shift, I discovered that MCP handling over remote SSH is fundamentally different between VSCode and Zed. This note records why MCP failed in Zed and worked in VSCode Remote SSH, analyzed from the perspective of execution ownership: which side resolves the command, which PATH gets used, and which side owns the process lifecycle.

Background and Goal

Original Plan

Run an editor server on the compute side and make the Mac a thin terminal.

  • VSCode Server or Zed Remote Server on the compute machine
  • MCP servers also on compute, using local runtime directly
  • Mac as nothing more than a terminal and browser

Actual Setup

Keeping the M1 Mac idle felt wasteful, so editors now run on the Mac.

  • Zed: remote editing only. Extensions and LSP disabled — used as a pure editor
  • VSCode Remote SSH: used when MCP is needed
  • Compute server handles inference and MCP server execution

With this setup, the goal was to run MCP servers using the runtime already on the compute server. I wanted to use remote uvx, remote python, and containerized runtimes directly, not mediate through the local side.

The requirements were:

  • Use the remote machine’s PATH and binaries as-is
  • Clean up processes automatically when the session ends
  • Avoid growing a pile of always-on helper processes
  • Keep compatibility with one-shot execution through podman run --rm

The Core Issue: Where the Command Runs

The central issue turned out to be the execution model behind the MCP command entry. Two clients can expose similar configuration surfaces while making very different assumptions about where commands run.

That difference also lines up with the related note on VSCode Server setup, where the VSCode Server environment is already treated as a controlled single-connection workspace with self-contained runtimes. Once that assumption exists, keeping MCP processes on the remote side is the natural extension.

Why Zed Failed

The primary problem was that Zed resolved MCP server commands from the local side, effectively the Mac. Even when I placed .zed/settings.json inside the remote project, that did not make Zed use the remote uvx or python. The command still originated locally.

That meant the mismatch happened before the MCP server even had a chance to run. I wanted remote PATH resolution, but the process entry point already belonged to the local machine. From that point on, every workaround was fighting the client model itself.

Zed also treats the MCP entry as a stdio-based local starting point, which makes it an awkward fit for naturally attaching remote MCP servers. Proxy ideas such as mcp-remote were possible in theory, but without SSE support they added operational complexity without fixing the basic mismatch.

Concrete Failures Observed in Zed

The concrete failures were consistent:

  • Remote-installed uvx and serena were never referenced.
  • Starting serena through local uvx could not use the compute server PATH.
  • Port forwarding did not materially help because the startup model was still local.
  • No listening MCP process appeared on the remote side during use.

At that point the issue was clearly architectural, not a single bad setting. Continuing to patch around it would only add more exceptions and more fragile glue.

Why VSCode Remote SSH Worked

VSCode Remote SSH worked because the extension host itself runs on the remote machine. As a result, mcp.servers command and args follow the compute server environment directly. That is exactly what I wanted from the beginning.

Once MCP starts as a remote subprocess, the whole model simplifies. If the server uses Python or uvx, it uses the environment that is already installed on the compute server. When the VSCode session ends, the subprocess is cleaned up automatically, so I do not need a separate process-reaper story.

This also fits very well with podman run --rm. I can launch MCP servers only when needed, keep the execution isolated, and leave no long-lived container behind after the session ends.

Observed Behavior (verified with ss -ltnp)

I confirmed the behavior with ss -ltnp. When MCP was launched from VSCode Remote, a listener such as users:(("python",pid=xxxx)) appeared on the compute server and disappeared when the VSCode connection closed. That matched the expected model exactly: MCP existed as a remote subprocess owned by the session.

During the Zed attempts, the equivalent remote-side listener never appeared. That observation lined up with the earlier conclusion that the startup path was local, not remote. Once the observed process behavior matched the architectural explanation, there was no reason to keep forcing the Zed path.

Final Design Direction

The final direction was to standardize MCP startup through /opt/containers/runtime/mcps/run.sh. That wrapper can call podman run --rm, which gives me dependency isolation, controlled volume scoping, and clean teardown in one place.

When VSCode calls that wrapper through MCP command, the result is exactly what I want: the process starts as a Remote SSH subprocess, ends automatically with the session, and leaves no container behind because of --rm.

I may still move a few frequently used MCP servers to SSE and quadlet later. For now, subprocess mode is enough, and it keeps the design simpler. Persistent services should only be introduced where startup cost or reuse justifies them.

Conclusion

The conclusion is straightforward. Zed was a poor fit for MCP in this use case because its MCP model is effectively local-first, while I needed direct access to the remote runtime. VSCode Remote SSH worked because MCP commands ran as remote subprocesses and therefore matched the compute server environment naturally.

That led to a practical division: VSCode Remote SSH for MCP-dependent work, Zed (ext/LSP off) for pure editing. MCP operations use VSCode + Remote SSH + subprocess execution as the default, with isolation and portability handled by /opt/containers/runtime/mcps/run.sh plus podman run --rm.

The setup drifted from the original “editor server on compute” plan, but the pragmatic choice of keeping the M1 Mac active resulted in a clean split: editors on Mac, MCP execution on compute. Once the execution owner is correct, the rest of the design becomes much simpler.

Next Steps

The next useful step is to formalize the actual run.sh interface and the corresponding mcp.servers configuration shape in VSCode. The current note is enough as a decision record, but not yet a reusable operations template.

I also want a clear rule for when to move a server from subprocess mode to persistent SSE mode. A threshold based on startup cost, call frequency, and shared cache requirements would keep that transition deliberate instead of ad hoc.