Skip to main content
Glama

mcp-ondemand

Your MCP servers, running only while they are used. For Claude Code and Codex. One small proxy shows your client every tool, starts a server on its first call, shares it between all your open sessions, and stops it when it goes idle.

test

Why

Claude Code starts every configured MCP server the moment a session opens, keeps all of them running until it closes, and does it again for every session. Most of those servers are never called. Each one is a full Node.js or Python process, usually behind a launcher like npx or uvx that is a process of its own.

Measured with six popular servers, started through npx as most setups do:

Server

Memory while running

Start time

playwright

259 MB

6.4 s

github

208 MB

3.9 s

filesystem

217 MB

5.3 s

memory

213 MB

5.4 s

sequential-thinking

216 MB

4.5 s

context7

237 MB

5.6 s

All six

1,350 MB

mcp-ondemand itself costs 49 MB per open session plus 66 MB once for its shared daemon. With those six servers configured:

Open sessions

Without mcp-ondemand

With it, idle

With it, two servers in use

1

1,350 MB, 6 servers

115 MB

611 MB

3

4,050 MB, 18 servers

213 MB

709 MB

5

6,750 MB, 30 servers

311 MB

807 MB

Five open sessions go from 6.8 GB to under 0.9 GB, even with two servers busy. Add servers and the gap grows: without mcp-ondemand each one costs memory in every session whether you use it or not; with it, a server you don't call costs nothing.

What you give up: the first call to a stopped server waits for it to start, a few seconds (the start times above). After that it answers at full speed until it has been idle for 10 minutes.

Numbers from Windows 11 and Node 22; on Linux and macOS the launchers are lighter, the ratio stays. Measure your own: mcp-ondemand bench for these six, mcp-ondemand bench ~/.mcp-ondemand/servers.json for your servers.

Related MCP server: mcp-orchestrator

What it does

  • Every tool, nothing running. Claude sees all your tools with their real names and schemas, answered from a cache on disk. Claude Code's per-tool permissions keep working: no meta-tools, no extra round trips.

  • Starts on first use, stops when idle. 10 minutes by default, never while a call is running.

  • Shared between sessions. Five open sessions using the same server run one copy of it. Servers that hold state, like a browser, can be kept per session instead.

  • Nothing left behind. Stopping kills the whole process tree, launchers included, and a session's own servers end with it. No more stray servers from sessions closed hours ago.

  • Complete pass-through. Prompts, resources, progress reports and cancellation reach the real server: press Esc and the call stops there, not just in Claude.

  • Optional memory limits. Restart a server that grows past its own limit, or keep all of them under one budget.

  • Easy to adopt, easy to leave. import moves your existing servers (and plugin servers) over with a backup, restore puts them back. Changes to the config reach open sessions at once.

Claude session 1 ─┐                      ┌── github       (running, shared)
Claude session 2 ─┼── mcp-ondemand ──────┼── playwright   (one per session)
Claude session 3 ─┘     daemon           └── ...          (stopped)

Install

Needs Node.js 20 or newer.

npm install -g @elis132/mcp-ondemand
mcp-ondemand import

import detects Claude Code or Codex from the active session. From a plain terminal with both installed, choose explicitly:

mcp-ondemand import --client claude
mcp-ondemand import --client codex

Claude Code uses ~/.claude.json. Codex uses $CODEX_HOME/config.toml (normally ~/.codex/config.toml), or <project>/.codex/config.toml with --project. Codex import preserves the TOML text and original server settings, disables the imported entries, and registers the proxy; restore --client codex re-enables them without overwriting unrelated settings changed since import. Native node_repl/cua_repl, disabled entries, remote servers, and entries with unsupported fields (including tool allowlists and env_vars) stay in place. Codex servers default to perSession: true and idle: 1800 to preserve project folders and isolate state; set perSession: false for servers you want to share across Codex sessions. Startup and tool timeouts are retained.

The two clients have separate server lists and daemons by default: ~/.mcp-ondemand/ for Claude Code and ~/.mcp-ondemand-codex/ for Codex. --client claude|codex works with import, restore, doctor, status, refresh and stop; MCP_ONDEMAND_CLIENT overrides detection, and MCP_ONDEMAND_HOME overrides the data folder. Both client configs are never automatically merged. Codex plugin-managed servers are left alone; --plugins is currently Claude Code only.

For Claude Code, import moves the local (stdio) MCP servers from your user-level config behind mcp-ondemand, puts one ondemand server in their place, backs up the old config, and starts each server once to build its tool cache. Remote (HTTP/SSE) servers have no local process and are left where they are.

Servers that come from Claude Code plugins (Playwright, for example) are moved separately:

mcp-ondemand import --plugins

That switches those plugins off, so they stop starting their own copy. Restart your client. claude mcp list or codex mcp list should show ondemand, and mcp-ondemand doctor --client claude (or codex) checks the setup.

Update with npm update -g @elis132/mcp-ondemand: the running daemon notices the new code and hands over by itself.

Or let your agent do it: paste SETUP_PROMPT.md into Claude Code, Codex or similar.

git clone https://github.com/elis132/mcp-ondemand.git ~/mcp-ondemand
cd ~/mcp-ondemand && npm install
node bin/mcp-ondemand.mjs import

Use node ~/mcp-ondemand/bin/mcp-ondemand.mjs wherever this README says mcp-ondemand, or run npm link once in the folder.

Use

Tools are named <server>__<tool>, so they appear as mcp__ondemand__github__create_issue in clients using MCP prefixes. Two tools of its own:

Tool

Does

ondemand_status

Which servers are running, their process ids and memory (whole process tree), calls made, the last start error

ondemand_restart

Stop a server that hangs. It starts again on its next call

Each server's own instructions are passed on to Claude. Prompts are named <server>__<prompt> like tools; resources keep their own URIs.

Commands

mcp-ondemand import [--client claude|codex] [--project <dir>] [--only a,b] [--except a,b] [--dry-run]
mcp-ondemand import --plugins [--only a,b] [--dry-run]   # servers from Claude Code plugins
mcp-ondemand refresh [name]        # start servers once to rebuild their cache
mcp-ondemand status                # what runs now, with memory (asks the daemon), or the configured servers
mcp-ondemand doctor                # check the setup and say what to fix
mcp-ondemand stop                  # stop the shared daemon and every server it runs
mcp-ondemand bench [servers.json]  # measure what your servers cost, with and without mcp-ondemand
mcp-ondemand restore [--only a,b]  # put servers back where they came from

Configure

Everything lives in the selected client's data folder (~/.mcp-ondemand/ or ~/.mcp-ondemand-codex/; set MCP_ONDEMAND_HOME to move it): servers.json, the tool cache/, config backups/, and ondemand.log.

servers.json uses the same shape as Claude Code's mcpServers, plus a few optional fields:

{
  "playwright": {
    "command": "npx",
    "args": ["-y", "@playwright/mcp@latest"],
    "idle": 1800
  },
  "github": {
    "command": "npx",
    "args": ["-y", "@modelcontextprotocol/server-github"],
    "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "..." }
  }
}

Field

Default

Meaning

idle

600

Seconds without calls before the server is stopped. 0 keeps it running for the whole session

preload

false

Start it as soon as the daemon starts instead of on the first call

perSession

false

Give each session its own copy, started in that session's project folder. For servers that keep state between calls, such as a browser

disabled

false

Keep the entry but do not offer its tools

maxMemory

none

MB. A server whose process tree uses more is stopped, never during a call, and starts fresh on its next call

For all servers together, ~/.mcp-ondemand/settings.json:

{ "memoryBudget": 2000, "idle": 600 }

Field

Meaning

memoryBudget

MB for all running servers together. Over it, the least recently used idle servers are stopped until it fits

idle

The default idle time for servers that do not set their own

Memory is checked every minute, and only when maxMemory or memoryBudget is set.

Secrets out of the file: ${NAME} and ${NAME:-fallback} in command, args, env and cwd are read from the environment when the server starts. An unset variable without a fallback fails that server with a message naming the variable.

"env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_TOKEN}" }

After adding or changing a server, run mcp-ondemand refresh <name>. A server without a cache is also started once in the background the next time a session opens.

How it works

  • Shared daemon: the first session starts a small background daemon, and every session's mcp-ondemand becomes a pipe to it (a named pipe on Windows, a unix socket elsewhere). The daemon runs the servers, so sessions share them. It exits a minute after the last session has gone and the last server has stopped. It is started through a short-lived launcher, so it is not part of any one session's process tree. Set MCP_ONDEMAND_SHARED=0 to run each session on its own instead, as in 1.1; if the daemon cannot start, a session falls back to that by itself.

  • Updates: a session running different code (a new version, a git pull, a local edit: sessions and daemon compare a fingerprint of the code) asks the running daemon to step aside. It finishes its current sessions and exits while the new one takes over. A changed servers.json or a rebuilt cache is picked up at once by open sessions, which are told only when what they see actually changed. Changed servers restart, the rest keep running.

  • Failing servers: a server that will not start is not retried on every call. It waits 5 s, then 10 s, 20 s and so on up to 5 minutes, and calls meanwhile get its last error at once. ondemand_restart retries straight away.

  • Cache: each server's tools, prompts, resources, resource templates and instructions are stored per server, tied to its command, args, env and cwd. Change any of those and the cache is rebuilt. Env values are hashed, never written to the cache.

  • Staying current: whenever a server runs, its live tool list is compared with the cache. If they differ, the cache is updated and Claude is sent notifications/tools/list_changed. A server's own list_changed does the same.

  • Calls: progress notifications from the real server are passed to the client, and a cancelled call is cancelled on the real server. Calls that arrive while a server is starting wait for the same start. A server that crashes is started fresh on the next call.

  • Stopping: the whole process tree is killed (taskkill /T on Windows, the process and its descendants elsewhere), so launchers do not leave the real server behind.

Limits

  • The first call to a stopped server is slower, by however long that server takes to start (4 to 6 seconds for the servers measured above).

  • Stopping a server loses its state. A browser automation server forgets its open page. Give such servers a longer idle, or 0, and perSession so sessions do not share one browser.

  • Shared servers run in the daemon's environment: the environment of the session that started it, with the mcp-ondemand folder as working folder. A server that depends on the session's project folder should be perSession, which starts it in that folder.

  • Only local (stdio) servers. Remote servers have no process to save.

  • Plugins: a plugin whose servers include a remote one is left alone by import --plugins, because switching the plugin off would lose that server too.

  • Resource subscriptions (resources/subscribe) are not passed through.

  • mcp-ondemand sees everything that passes between Claude and your servers, including the env you configure for them. It runs locally and makes no network requests of its own. Read the code: it is about 1,350 lines.

Compared with others

Tools Claude sees

On demand

Idle stop

Process trees

Shared across sessions

Prompts, resources

Progress, cancel

Plugins

mcp-ondemand

real tools

yes

yes

yes

yes, or per session

yes

yes

yes

mcp-snooze

real tools

yes

yes

yes (Windows untested)

no

yes

passes through

no

mcp-on-demand

none: a CLI/HTTP API Claude calls

yes, started by hand

no, stopped by hand

not documented

not documented

no

no

no

voicetreelab/lazy-mcp

2 meta-tools

yes

no

no

no

no

no

no

mcp-lazy-load, mcp-lazy-proxy

meta-tools

yes

varies

no

no

no

no

no

Meta-tool proxies were built to save context. Claude Code already loads tool schemas on demand, so here the cost that is left is processes and memory, which is what mcp-ondemand removes.

Undo

mcp-ondemand restore

puts every server back where import found it. Every change to ~/.claude.json is backed up in ~/.mcp-ondemand/backups/ first.

Develop

npm install
npm test
node bench/measure.mjs      # the measurements at the top of this README (same as mcp-ondemand bench)

The tests run the real proxy against a fake MCP server: lazy start, shared starts, idle stop, long calls, crashes, restart, cleanup when the session ends, prompts, resources, progress, cancellation, ${VAR}, two sessions sharing one daemon, perSession servers, a daemon stepping aside for another version, surviving hard disconnects and exiting when idle, backoff for failing servers, memory limits, live config changes, and import and restore of servers and plugins. CI runs them on Linux, macOS and Windows.

License

MIT

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    A
    maintenance
    Manages and dispatches multiple MCP servers from a cloud dashboard, reducing tool-context bloat by loading only the most relevant servers for a given task.
    10
    540 npm
    1
    -
  • A
    license
    A
    quality
    F
    maintenance
    Enables connecting multiple downstream MCP servers into a single gateway, with unified tool search, namespacing, and deferred loading to preserve context window.
    2
    6
    MIT
  • A
    license
    A
    quality
    B
    maintenance
    Finds MCP servers in the MCP Rating index and connects to them on demand, proxying their tools under namespaced names and notifying the client when tools change. Each server gets a short environment allowlist plus only the variables you name for it, so other API keys in your environment aren't passed on. Files aren't isolated unless you opt into Docker containers.
    13
    58 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    C
    maintenance
    Enables clients to connect once and access the union of multiple backend MCP servers' tools through a single unified interface, with namespaced tool routing, capability search, and health isolation.
    MIT