Reduce MCP Server Memory Use Across Coding-Agent Sessions

2026-09-24 - 9 min read
Daniel Young
Daniel Young
Founder, DRYCodeWorks

Running 25 coding-agent sessions on a 64 GB workstation exposed a scaling problem—not a RAM shortage. Here’s how we centralized MCP and model-serving processes without changing the developer workflow.

One day, we had 25 coding-agent sessions open on a 64 GB development workstation, and the machine was under real memory pressure. The machine was not too small for any one session. The problem was how much runtime each session brought with it.

Several coding-agent sessions connecting to shared local services.

Most of the repeated Model Context Protocol (MCP) process load came from one large client's database integrations: multiple environments, multiple regions, and a separate server configuration for each target. Opening more sessions multiplied that server footprint. We changed where those processes lived, not how developers used their tools.

Here, memory means physical random-access memory (RAM) used by running processes. It does not mean an agent's conversation context, persistent memory, or vector embeddings.

The multiplier is sessions × servers

With the stdio (standard input/output) transport, the MCP client launches the server as a subprocess. That makes the process lifetime follow the client session. Open another session with the same MCP configuration, and it can launch another copy. The MCP transport specification documents that subprocess relationship.

A useful first estimate is:

rough process estimatetext
per-session MCPs × open sessions = MCP server instances

MCP server instances × average process-tree footprint = rough RAM cost

This is a model, not a precise memory formula. One configured server may start a shell wrapper and a runtime process; another may keep a database client or cache resident. Operating-system memory accounting also makes the sum of process resident set size (RSS) a poor estimate of unique physical memory.

The shape of the workload matters. A single session with a few lightweight tools is different from 25 sessions each inheriting a broad set of regional and environment-specific database connections. The 64 GB number did not tell us how much duplicated service state the harness had started.

The workstation was adequate for each workload in isolation. The pressure came from repeating the workload across sessions.

Diagnose pressure, not just “free RAM”

Before changing the architecture, record a baseline under the workload that actually causes the slowdown. On macOS, use Activity Monitor’s Memory tab and watch the Memory Pressure graph, swap, and the process list. Apple’s Activity Monitor guide explains the indicators.

Group processes by their parent session and configured MCP target. Count the process trees, not only command lines containing mcp: a Node process, Python server, or wrapper may not have mcp in its name. Record the open-session count and which client, environment, and region each server serves.

Repeat the same workload after the change. Compare system memory pressure and swap as well as process counts. Do not turn summed RSS into a claim about exact RAM saved, and do not compare an idle machine with a busy one. We did not establish a clean before-and-after GiB figure, so this guide makes no numeric savings claim.

Move shared MCP process ownership to the host

The key change was to run eligible MCP servers behind one machine-local proxy. The harnesses connect to named HTTP endpoints on loopback; the long-lived proxy owns the configured backend processes. Instead of each coding-agent session launching its own server set, sessions reuse the host-level service.

The mcp-proxy project bridges local stdio servers to HTTP and supports named servers. In our setup, the service binds to 127.0.0.1:18450, and the harness configuration points each named server at its proxy route:

Install the proxy once and run it under a host service manager. The command below binds the listener to loopback and loads the named backends from a JSON file:

start the shared proxybash
uv tool install mcp-proxy

mcp-proxy \
--host 127.0.0.1 \
--port 18450 \
--pass-environment \
--named-server-config ./mcp-proxy.json

The named-server file maps each environment and region to its own process. This example assumes mcp-server-postgres is installed and that the host service manager provides the database URLs in its environment:

mcp-proxy.jsonjson
{
"mcpServers": {
  "orders-staging": {
    "command": "sh",
    "args": ["-c", "exec mcp-server-postgres \"$ORDERS_STAGING_DATABASE_URL\""]
  },
  "orders-production-us-west": {
    "command": "sh",
    "args": ["-c", "exec mcp-server-postgres \"$ORDERS_PRODUCTION_US_WEST_DATABASE_URL\""]
  }
}
}

--pass-environment passes the proxy process environment to each backend. Load the DSNs from a protected source, keep unrelated credentials out of that service environment, and never put connection strings in the checked-in JSON.

For Claude Code, the client-side entry points at the named HTTP route:

Claude Code .mcp.jsonjson
{
"mcpServers": {
  "orders-staging": {
    "type": "http",
    "url": "http://127.0.0.1:18450/servers/orders-staging/mcp"
  },
  "orders-production-us-west": {
    "type": "http",
    "url": "http://127.0.0.1:18450/servers/orders-production-us-west/mcp"
  }
}
}

Codex CLI can point at the same URL using its remote MCP configuration:

~/.codex/config.tomltoml
[mcp_servers.orders-staging]
url = "http://127.0.0.1:18450/servers/orders-staging/mcp"

[mcp_servers.orders-production-us-west]
url = "http://127.0.0.1:18450/servers/orders-production-us-west/mcp"

See the Codex MCP configuration guide for the client-side URL format.

The process definitions live with the proxy, not in every session's launch configuration. The proxy is supervised as a host service, and the backend configuration retains distinct names for the environment and region. This is not a request to merge several databases into one endpoint; it is a way to let the same configured server process serve the sessions that need that exact target.

The existing developer workflow stays intact: open a session, use the same named tools, and select the same environment-specific target. The routing beneath the harness changes from launching a child process to connecting to a local HTTP endpoint.

This strategy is not tied to one harness. We have implemented it for Claude Code and Codex, too. Any harness that can connect to an HTTP MCP endpoint can use the same pattern; only its client-side configuration changes.

For Claude Code model traffic, we also use claude-code-proxy, an Anthropic-compatible API backed by Codex. It is a different layer from mcp-proxy: one routes model requests, the other owns MCP server processes.

If you distribute harness configuration through Claude Code plugins, our private plugin marketplace guide covers the packaging and rollout side.

BEFORE: per-session processesAFTER: shared host servicesThe same configured server set repeatsSession 1MCP server setregional + environment copiesSession 2MCP server setanother process groupSession NMCP server setanother process groupProcess count grows with sessions × serversSessions connect over loopback HTTPSession 1Session 2Session N127.0.0.1 MCP proxynamed HTTP endpointsowns shared server processesOne configured backend per named targetKeep environment and region identity explicitModel serving is a separate shared pathAgent sessionsmodel requestsMTPLX gatewaystarts one model child on demandMTPLX modelone shared child process

Keep the local service local

A loopback listener is not an authentication boundary between processes on the same machine. Any local process that can reach the port may be able to call the exposed tools. Do not bind a single-user development proxy to 0.0.0.0 or a tailnet interface unless you have added and reviewed an authentication and authorization layer.

Keep credentials out of the checked-in server config. Load them from a protected source when the service starts, and keep separate named entries for distinct database roles, regions, and environments. In particular, do not make a read-only production endpoint indistinguishable from a development endpoint just because both are behind one proxy.

A shared proxy expands the impact of a bad configuration: one incorrect route or credential can affect every session using that named endpoint. Keep environment identity visible in the endpoint name, and verify the destination before wiring a production connection into the shared service.

Put the coding-agent model behind a shared gateway

We routed Oh My Pi (OMP), an open-source terminal coding-agent harness, through a shared local MTPLX gateway. Its provider configuration points to an OpenAI-compatible endpoint; the gateway owns the MTPLX process and starts one model child when a request arrives. It unloads that child after the configured idle period instead of leaving a separate model process attached to each session.

The relevant OMP configuration is intentionally small:

OMP models.ymlyaml
providers:
mtplx:
  baseUrl: http://127.0.0.1:18441/v1
  apiKey: LOCAL_AGENT_GATEWAY_TOKEN
  authHeader: true
  api: openai-completions

The shared gateway listens on 127.0.0.1:18441; MTPLX runs as its child on 127.0.0.1:18442. In this setup, the gateway starts the child on the first chat completion request and unloads it after 300 seconds without queued or active work. OMP configuration and model serving are separate responsibilities: OMP keeps session state, while one host-level model runtime handles requests. See the OMP model configuration guide and MTPLX project for the serving pieces.

One terminology detail matters: the repository routes OMP inference through MTPLX. Mnemopi's actual embedding/vectorization API is configured separately, through the local retrieval service. Do not describe the MTPLX endpoint as the vectorization service.

Centralize runtime ownership; keep session state and target identity explicit.

Do not share every server blindly

A shared process is safe only if the server's configuration and state can safely serve multiple sessions. A fixed, read-only database target may be a good candidate. A server that holds a browser session, mutable per-client state, or a conversation-specific in-process cache may not be.

Use these checks before moving a server behind a shared endpoint:

  • Same target: Every caller of a named endpoint is supposed to use the same environment, region, permissions, and data scope.
  • Compatible state: One session cannot observe or mutate another session's private in-process state.
  • Local trust boundary: The host, local processes, and service account are trusted to reach the tools exposed by the proxy.
  • Concurrency: The backend and its connection limits can handle calls from several sessions at once.
  • Failure scope: If the proxy or backend exits, every session using that named server loses access until it recovers.

If any of those conditions do not hold, keep that server per-session or split it into separate named services with the correct state and identity boundaries. Process sharing is an optimization, not a reason to weaken isolation.

Apply the pattern to your harness

Start with the duplicated workload, not with a proxy install command. The goal is to centralize only the process state that sessions can safely reuse.

  1. Inventory: Count open sessions and the MCP process trees each session starts. Record the server's environment, region, role, and whether it keeps mutable state.
  2. Choose candidates: Select backends with stable configuration and safe concurrent use. Keep per-session or stateful tools out of the shared pool.
  3. Start a loopback service: Put the backend definitions under a host-level supervisor. Bind to 127.0.0.1, keep secrets in a protected runtime source, and use stable names for each environment-specific target.
  4. Redirect the harness: Replace stdio launch entries for the selected servers with their named HTTP endpoints. Leave tool names and the workflow developers invoke unchanged.
  5. Centralize model serving separately: Point OMP at the shared inference gateway; give it an explicit lifecycle so the model loads when needed and can unload when idle.
  6. Exercise the real workload: Verify representative tools against each intended target, then repeat the original session mix and compare memory pressure, swap, process counts, and latency.

The useful result is not a promise that a 64 GB workstation can never run short of memory. It is a better scaling curve for workloads that were needlessly multiplying identical server processes: adding another session no longer has to add another copy of every shared MCP backend or local model runtime.

As you add clients, regions, and environments, make process ownership part of the harness design. Ask which work belongs to a session and which work belongs to the machine. That question preserves the isolation you need without making every open terminal tab pay the startup and memory cost of the same infrastructure.