mcp-pacemaker 2.1.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -2
- package/CONTRIBUTING.md +10 -3
- package/README.md +98 -760
- package/autostart/windows/legacy-access.ps1 +359 -0
- package/bin/account-upgrade-controller.mjs +1 -1
- package/bin/account-upgrade-worker.mjs +7 -0
- package/bin/cli-main.mjs +61 -5
- package/bin/dependency-lock.json +642 -0
- package/bin/legacy-access.mjs +73 -0
- package/bin/legacy-installation.mjs +75 -19
- package/bin/legacy-task.mjs +24 -0
- package/bin/legacy-upgrade.mjs +66 -10
- package/bin/managed-upgrade.mjs +102 -12
- package/bin/mcp-bridge.mjs +38 -20
- package/bin/prewarm.mjs +28 -6
- package/bin/service-control.mjs +10 -5
- package/bin/upgrade-capability.json +2 -1
- package/bin/upgrade-dependencies.mjs +67 -6
- package/bin/upgrade-package.mjs +2 -0
- package/bin/windows-legacy/LegacyProcessBroker.build.json +3 -3
- package/bin/windows-legacy/LegacyProcessBroker.exe +0 -0
- package/bin/windows-legacy/src/LegacyNative.cs +60 -11
- package/bin/windows-legacy/src/LegacyProcessBroker.cs +317 -197
- package/bin/windows-legacy-process.mjs +12 -5
- package/docs/account-upgrades.md +17 -4
- package/docs/compatibility.md +39 -0
- package/docs/configuration.md +146 -0
- package/docs/dashboards.md +125 -0
- package/docs/execution-context.md +11 -5
- package/docs/http-api.md +47 -0
- package/docs/install.md +52 -0
- package/docs/operations.md +99 -0
- package/docs/upgrades.md +324 -0
- package/package.json +10 -3
- package/tools/windows-legacy/README.md +118 -92
- package/tools/windows-legacy/inventory.mjs +8 -6
- package/ui/dist/third-party-manifest.json +1 -1
package/README.md
CHANGED
|
@@ -1,46 +1,57 @@
|
|
|
1
1
|
# mcp-pacemaker
|
|
2
2
|
|
|
3
|
-
**
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
**Keep your MCP servers running outside the editor or CLI.** One long-lived local bridge runs your MCP servers and every editor and agent CLI connects to it, so a CLI exiting no longer kills them.
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install -g mcp-pacemaker # Node 22 or 24, run as a normal user
|
|
9
|
+
mcp-pacemaker init # imports your servers, asks before rewiring any host
|
|
10
|
+
```
|
|
7
11
|
|
|
8
12
|
[Install from npm](https://www.npmjs.com/package/mcp-pacemaker) ·
|
|
9
13
|
[Report a problem](https://github.com/girishkvs/mcp-pacemaker/issues) ·
|
|
10
|
-
[Release notes](https://github.com/girishkvs/mcp-pacemaker/releases)
|
|
14
|
+
[Release notes](https://github.com/girishkvs/mcp-pacemaker/releases) ·
|
|
15
|
+
[Demo and design write-up](https://girishkonda.com/projects/mcp-pacemaker/)
|
|
11
16
|
|
|
12
|
-
> This README describes **2.1.
|
|
13
|
-
> a source branch or GitHub tag is not proof of publication.
|
|
17
|
+
> This README describes **2.1.1**.
|
|
14
18
|
> See the [release changes](CHANGELOG.md).
|
|
15
19
|
|
|
20
|
+
## What it does
|
|
21
|
+
|
|
16
22
|
MCP hosts — editors (VS Code, Cursor, Claude Desktop) **and** CLIs (Claude Code, Copilot CLI,
|
|
17
23
|
Codex, Gemini) — run stdio MCP servers as child processes. When the host restarts, crashes, or
|
|
18
|
-
exits (every one-shot CLI run), **those servers die**.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
exits (every one-shot CLI run), **those servers die**.
|
|
25
|
+
|
|
26
|
+
- **Keeps servers running.** `mcp-pacemaker` runs your servers as children of a small,
|
|
27
|
+
long-lived bridge and exposes each over the MCP HTTP+SSE / Streamable HTTP transport.
|
|
28
|
+
- **Shares one bridge.** Multiple hosts use one bridge, with an isolated child per session by
|
|
29
|
+
default. Opt into pre-warming or reuse one initialized child for compatible stateless tools
|
|
30
|
+
sessions with [shared mode](docs/configuration.md#shared-mode). Wire hosts to one bridge, or
|
|
31
|
+
give each its own.
|
|
32
|
+
- **Survives reboots and token expiry.** A cross-platform supervisor + OS auto-start keep the
|
|
33
|
+
bridge itself alive across reboots and sleep/wake, and a pluggable auth layer injects a fresh
|
|
34
|
+
token per request for HTTP servers.
|
|
35
|
+
- **Reconnects returning clients.** After a restart, a returning client can reuse a recorded
|
|
36
|
+
session ID to re-establish its handshake against a fresh server. This does not restore
|
|
37
|
+
arbitrary tool state or preserve TCP connections. Clients that stop retrying after a dropped
|
|
38
|
+
connection may still need a reload; socket handoff is separate planned work.
|
|
39
|
+
- **Shows what is running.** `status`, `top` and a web dashboard show whether each server is up
|
|
40
|
+
and which clients use it. See [dashboards](docs/dashboards.md).
|
|
30
41
|
|
|
31
42
|
## Quick start
|
|
32
43
|
|
|
33
|
-
Install **mcp-pacemaker 2.1.
|
|
44
|
+
Install **mcp-pacemaker 2.1.1** from npm. Use a maintained **Node.js 22 or 24**
|
|
34
45
|
release; Windows also needs PowerShell 7 and Windows Script Host.
|
|
35
|
-
See [installation requirements](
|
|
46
|
+
See [installation requirements](docs/install.md) before setup.
|
|
36
47
|
|
|
37
48
|
```bash
|
|
38
|
-
npm view mcp-pacemaker@2.1.
|
|
39
|
-
npm install -g mcp-pacemaker@2.1.
|
|
49
|
+
npm view mcp-pacemaker@2.1.1 version
|
|
50
|
+
npm install -g mcp-pacemaker@2.1.1
|
|
40
51
|
mcp-pacemaker --version
|
|
41
52
|
```
|
|
42
53
|
|
|
43
|
-
The version check prints `2.1.
|
|
54
|
+
The version check prints `2.1.1` without running setup.
|
|
44
55
|
|
|
45
56
|
> **Run it as an ordinary user.** From 2.1.0 the bridge, supervisor and default upgrades
|
|
46
57
|
> refuse a privileged caller *at startup* — a non-administrator terminal on Windows and a
|
|
@@ -49,7 +60,7 @@ The version check prints `2.1.0` without running setup.
|
|
|
49
60
|
> normal user instead. See the [user-runtime contract](docs/execution-context.md).
|
|
50
61
|
|
|
51
62
|
Before setup, have a readable JSON host configuration containing server definitions, or
|
|
52
|
-
prepare [`~/.mcp-pacemaker/servers.json`](#serversjson). Supported import sources are
|
|
63
|
+
prepare [`~/.mcp-pacemaker/servers.json`](docs/configuration.md#serversjson). Supported import sources are
|
|
53
64
|
`vscode`, `cursor`, `claude` (Claude Desktop), `copilot-cli` and `gemini`.
|
|
54
65
|
Codex and Claude Code are wiring destinations, not import sources.
|
|
55
66
|
|
|
@@ -109,6 +120,19 @@ them in.
|
|
|
109
120
|
|
|
110
121
|
</details>
|
|
111
122
|
|
|
123
|
+
## Everyday commands
|
|
124
|
+
|
|
125
|
+
| Command | What it does |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `init [--from source] [--client a,b] [--yes]` | Interactive setup: import one source, wire selected hosts, auto-start, launch |
|
|
128
|
+
| `status` | Bridges, wired hosts, servers, and any server currently failing |
|
|
129
|
+
| `top` / `dashboard` | Live terminal UI / web dashboard |
|
|
130
|
+
| `doctor` | Diagnose config, bridge reachability, host wiring |
|
|
131
|
+
| `uninstall` | Stop managed bridges, remove auto-start, restore available host-file `.bak` copies; native entries are not restored |
|
|
132
|
+
|
|
133
|
+
The [command reference](docs/operations.md#command-reference) lists every command, including
|
|
134
|
+
`reload`, `logs`, `prewarm`, `upgrade` and `--instance`.
|
|
135
|
+
|
|
112
136
|
## Which process-sharing mode?
|
|
113
137
|
|
|
114
138
|
Sharing a bridge does not automatically share a server process.
|
|
@@ -121,20 +145,7 @@ Sharing a bridge does not automatically share a server process.
|
|
|
121
145
|
|
|
122
146
|
Shared clients must send identical initialization parameters, including `clientInfo`, and
|
|
123
147
|
empty client capabilities. It is not automatic cross-editor sharing. See
|
|
124
|
-
[shared-mode limits](#shared-mode) before enabling it.
|
|
125
|
-
|
|
126
|
-
## Choosing a transport tool
|
|
127
|
-
|
|
128
|
-
Documentation checked September 27, 2026; this is a use-case guide, not a feature benchmark.
|
|
129
|
-
|
|
130
|
-
| Tool | Consider it when |
|
|
131
|
-
|---|---|
|
|
132
|
-
| [Supergateway](https://github.com/supercorp-ai/supergateway) | You want command-line conversion between stdio and HTTP/SSE/WebSocket transports |
|
|
133
|
-
| [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) | You want a Python-based adapter between local stdio clients/servers and remote MCP transports |
|
|
134
|
-
| **mcp-pacemaker** | You want workstation process supervision, host wiring, auto-start, lifecycle diagnostics and explicit isolation/pooling/sharing policies |
|
|
135
|
-
|
|
136
|
-
These projects have overlapping capabilities. Reconnecting to a fresh server is not recovery
|
|
137
|
-
of arbitrary application state, and a transport bridge is not a sandbox for untrusted tools.
|
|
148
|
+
[shared-mode limits](docs/configuration.md#shared-mode) before enabling it.
|
|
138
149
|
|
|
139
150
|
## Supported hosts
|
|
140
151
|
|
|
@@ -156,766 +167,93 @@ loopback.
|
|
|
156
167
|
**Topologies:** wire every host to one shared bridge (default), or give each host its own bridge
|
|
157
168
|
(`install --client X --port N`) — or any mix. `status`/`doctor` report all bridges + wired hosts.
|
|
158
169
|
|
|
159
|
-
##
|
|
160
|
-
|
|
161
|
-
Use a maintained **Node.js 22 or 24** release. Node 20 remains a compatibility target but is
|
|
162
|
-
end-of-life, not a recommended secure runtime. The bridge has no npm runtime dependencies;
|
|
163
|
-
the setup CLI and dashboard have their own dependencies.
|
|
164
|
-
|
|
165
|
-
**Run as an ordinary user.** From 2.1.0 the bridge, supervisor and default upgrades verify the
|
|
166
|
-
caller before any runtime work and refuse elevated or root callers at startup with
|
|
167
|
-
`User-runtime execution refused: …`. On Linux and macOS that includes `root`, which is the
|
|
168
|
-
default user in most container images, so run the bridge as a normal account there. The
|
|
169
|
-
[user-runtime contract](docs/execution-context.md) describes what is checked and why; explicit
|
|
170
|
-
Windows account administration has a separate
|
|
171
|
-
[controller/worker path](docs/account-upgrades.md).
|
|
172
|
-
|
|
173
|
-
Install the released package from [npm](https://www.npmjs.com/package/mcp-pacemaker).
|
|
174
|
-
Its repository link should point to
|
|
175
|
-
[`girishkvs/mcp-pacemaker`](https://github.com/girishkvs/mcp-pacemaker).
|
|
176
|
-
Use an exact version for a repeatable installation:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
npm view mcp-pacemaker@2.1.0 version
|
|
180
|
-
npm install -g mcp-pacemaker@2.1.0
|
|
181
|
-
mcp-pacemaker --version
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
The current line uses the `latest` npm channel; the maintenance line uses `legacy` when that
|
|
185
|
-
channel has been published. Check the actual registry values before choosing a channel:
|
|
186
|
-
|
|
187
|
-
```bash
|
|
188
|
-
npm view mcp-pacemaker dist-tags --json
|
|
189
|
-
```
|
|
190
|
-
|
|
191
|
-
GitHub tags and npm publication are separate: a tag does not establish that the same
|
|
192
|
-
version or channel is available on npm, or that its package bytes are identical.
|
|
193
|
-
`@1` and `@2` are version ranges, not the maintained channels. They can select a version
|
|
194
|
-
withdrawn from a channel. `upgrade --self` resolves the installed major's channel and prints
|
|
195
|
-
an exact-version command; it never installs or downgrades anything.
|
|
196
|
-
|
|
197
|
-
Use a durable installation directory for a long-running service. Do not register autostart
|
|
198
|
-
from an `npx` cache: that cache may be removed later. Bare CLI invocation can enter the setup
|
|
199
|
-
wizard; use `--version` for a read-only installation check.
|
|
200
|
-
|
|
201
|
-
Windows setup also needs PowerShell 7 (`pwsh`) and Windows Script Host for its launcher.
|
|
202
|
-
Automatic configuration edits need .NET Framework 4.6.2 or newer. The bundled helper is
|
|
203
|
-
unsigned; npm provenance is not Authenticode signing or an application-control allowlist.
|
|
204
|
-
Windows ARM64 is not validated.
|
|
205
|
-
|
|
206
|
-
### Version compatibility
|
|
170
|
+
## Choosing a transport tool
|
|
207
171
|
|
|
208
|
-
|
|
209
|
-
[legacy documentation](https://github.com/girishkvs/mcp-pacemaker/blob/v1.3.1/README.md)
|
|
210
|
-
and [support policy](SECURITY.md#supported-versions). Those documents do not establish
|
|
211
|
-
that a legacy version is available on npm.
|
|
172
|
+
Documentation checked September 27, 2026; this is a use-case guide, not a feature benchmark.
|
|
212
173
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
or interrupted transactions with 2.x. See the [real-version gates](CONTRIBUTING.md#real-version-compatibility-gates)
|
|
219
|
-
for exact pairs; only the exact executed pairs are covered.
|
|
174
|
+
| Tool | Consider it when |
|
|
175
|
+
|---|---|
|
|
176
|
+
| [Supergateway](https://github.com/supercorp-ai/supergateway) | You want command-line conversion between stdio and HTTP/SSE/WebSocket transports |
|
|
177
|
+
| [mcp-proxy](https://github.com/sparfenyuk/mcp-proxy) | You want a Python-based adapter between local stdio clients/servers and remote MCP transports |
|
|
178
|
+
| **mcp-pacemaker** | You want workstation process supervision, host wiring, auto-start, lifecycle diagnostics and explicit isolation/pooling/sharing policies |
|
|
220
179
|
|
|
221
|
-
|
|
180
|
+
These projects have overlapping capabilities. Reconnecting to a fresh server is not recovery
|
|
181
|
+
of arbitrary application state, and a transport bridge is not a sandbox for untrusted tools.
|
|
222
182
|
|
|
223
|
-
|
|
224
|
-
|---|---|
|
|
225
|
-
| `init [--from source] [--client a,b] [--yes]` | Interactive setup: import one source, wire selected hosts, auto-start, launch |
|
|
226
|
-
| `status` | Bridges, wired hosts, servers, and any server currently failing |
|
|
227
|
-
| `reload` | Re-read `servers.json` into the running bridge without restarting it |
|
|
228
|
-
| `doctor` | Diagnose config, bridge reachability, host wiring |
|
|
229
|
-
| `prewarm [--port N] [--json]` | One view of stdio candidates, pools, shared-child state and measured process starts |
|
|
230
|
-
| `prewarm --enable <server> [--count N]` | Explicitly enable warm slots; `--disable <server>` turns them off |
|
|
231
|
-
| `logs [--follow] [--since 2h] [--server name] [--grep text]` | Read and filter durable logs, including retained rotation |
|
|
232
|
-
| `top` / `dashboard` | Live terminal UI / web dashboard |
|
|
233
|
-
| `plan --client <host>` | Dry run — show exactly what `install` would change |
|
|
234
|
-
| `import --from <source>` | Build `~/.mcp-pacemaker/servers.json` from one supported JSON host config |
|
|
235
|
-
| `install --client <host> [--port N]` | Wire one host (its own `--port` gives it its own bridge) |
|
|
236
|
-
| `emit --client <host>` | Print the config entries without writing anything |
|
|
237
|
-
| `start` / `stop` | Start or stop bridges (`--port` for one, else all) |
|
|
238
|
-
| `upgrade --to <version> [--plan] [--yes]` | Stage and replace one managed installation, including its CLI, preserving port/config/host wiring |
|
|
239
|
-
| `upgrade --to <version> --sxs [--port N]` | Keep the old instance and start an isolated new one; omitted port is OS-assigned |
|
|
240
|
-
| `upgrade --recover` | Resume or roll back an interrupted managed replacement from its durable journal |
|
|
241
|
-
| `[--instance <directory>]` | Select one installation when discovery finds several; valid before or after the command |
|
|
242
|
-
| `upgrade [--self]` | Legacy host repair; `--self` still prints same-major installation guidance only |
|
|
243
|
-
| `update-check [--json]` | Compare the installed version with its maintained npm channel |
|
|
244
|
-
| `uninstall` | Stop managed bridges, remove auto-start, restore available host-file `.bak` copies; native entries are not restored |
|
|
183
|
+
## Upgrades
|
|
245
184
|
|
|
246
185
|
> Package installation alone does not upgrade a running backend. Use explicit `upgrade --to`
|
|
247
186
|
> for managed replacement. Bare `upgrade` retains the older host-repair behavior.
|
|
248
187
|
|
|
249
|
-
|
|
188
|
+
Managed upgrades need **2.1.0 or newer** and must run as an ordinary user. Start with
|
|
189
|
+
`mcp-pacemaker upgrade --to <exact-version> --plan`, which changes nothing. `--sxs` keeps the
|
|
190
|
+
old instance and starts an isolated new one. Do not overwrite a package directory while its
|
|
191
|
+
supervisor or bridge is running. An upgrade may end open sessions and calls in progress;
|
|
192
|
+
clients reconnect, and no call is retried. It never changes `servers.json` or your client setup.
|
|
250
193
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
194
|
+
The [one-time Windows 1.3 restart migration](docs/upgrades.md#one-time-windows-13-restart-migration)
|
|
195
|
+
restarts **Pacemaker, not Windows**. It stops the 1.3 bridge and the processes it started, so
|
|
196
|
+
open sessions end. Confirm it at the prompt or with `--yes`. Run it from a separate durable
|
|
197
|
+
package; do not overwrite the running 1.3 package first. If 1.3 was set up from an
|
|
198
|
+
administrator terminal, run `mcp-pacemaker upgrade --fix-legacy-access` once from an
|
|
199
|
+
administrator terminal first; see [that section](docs/upgrades.md#13-installs-set-up-from-an-administrator-terminal).
|
|
256
200
|
|
|
257
|
-
|
|
258
|
-
Its complete path must fit within 103 UTF-8 bytes. `install` and `init` check this before
|
|
259
|
-
changing configuration or autostart; use a shorter configuration location rather than
|
|
260
|
-
relying on an implicit socket-location fallback.
|
|
201
|
+
See [upgrades and rollback](docs/upgrades.md) for recovery, rollback and side-by-side instances.
|
|
261
202
|
|
|
262
|
-
|
|
263
|
-
(`/<name>/mcp`). Shared mode requires Streamable HTTP.
|
|
203
|
+
## Uninstall
|
|
264
204
|
|
|
265
205
|
`install` backs up an existing JSON/TOML host file to `.bak`; repeating it replaces that backup.
|
|
266
206
|
`uninstall` restores available host-file backups, leaves files without backups as-is, and
|
|
267
207
|
keeps `servers.json`. Native Claude Code registrations are not backed up or automatically
|
|
268
208
|
restored; record their original definitions separately and restore them through Claude Code.
|
|
269
209
|
|
|
270
|
-
### Replacing or rolling back a running installation
|
|
271
|
-
|
|
272
|
-
These managed-upgrade commands require **2.1.0 or newer**.
|
|
273
|
-
Version 2.0.2 does not include them.
|
|
274
|
-
|
|
275
|
-
Run the bridge, supervisor, default upgrades and recovery as an **ordinary user**:
|
|
276
|
-
a normal, non-administrator terminal on Windows and a non-root session on Linux/macOS.
|
|
277
|
-
The [user-runtime contract](docs/execution-context.md) rejects privileged or
|
|
278
|
-
unverified callers before runtime work, so a replacement does not inherit different
|
|
279
|
-
authority. Read-only plans remain available. Explicit Windows account administration has a separate
|
|
280
|
-
[controller/worker path and qualification requirements](docs/account-upgrades.md).
|
|
281
|
-
|
|
282
|
-
For a **stable managed instance created by this implementation**:
|
|
283
|
-
|
|
284
210
|
```bash
|
|
285
|
-
mcp-pacemaker
|
|
286
|
-
mcp-pacemaker upgrade --to <exact-version>
|
|
287
|
-
# Noninteractive confirmation of the displayed local plan:
|
|
288
|
-
mcp-pacemaker upgrade --to <exact-version> --yes
|
|
289
|
-
|
|
290
|
-
# Keep the existing service and host wiring:
|
|
291
|
-
mcp-pacemaker upgrade --to <exact-version> --sxs
|
|
292
|
-
mcp-pacemaker upgrade --to <exact-version> --sxs --port 9001
|
|
293
|
-
```
|
|
294
|
-
|
|
295
|
-
`--to` requires canonical stable SemVer, not `latest`, a range, or a Git/file spec.
|
|
296
|
-
Selecting another major is explicit and printed in the plan; it is not a compatibility
|
|
297
|
-
claim. The target must ship the managed admission/readiness protocol. Older targets
|
|
298
|
-
without that protocol are rejected before interruption, including for automatic SxS ports.
|
|
299
|
-
|
|
300
|
-
Normal upgrades preserve the selected port: `--port` is valid **only with `--sxs`**.
|
|
301
|
-
Without an SxS port, the actual new loopback listener binds port 0 and stays open;
|
|
302
|
-
the OS-selected port is displayed and saved for later starts. The read-only plan says
|
|
303
|
-
“OS-assigned,” not a guessed port. An explicitly requested busy port fails without adoption.
|
|
304
|
-
SxS gets separate config, nonce, logs and session files. It preserves the source working
|
|
305
|
-
directory for relative server commands and does not rewire any host or enable pooling.
|
|
306
|
-
|
|
307
|
-
When discovery finds multiple instances, use the exact directory printed by setup/upgrade:
|
|
308
|
-
|
|
309
|
-
```bash
|
|
310
|
-
mcp-pacemaker upgrade --instance <directory> --to <exact-version> --plan
|
|
311
|
-
mcp-pacemaker start --instance <directory>
|
|
312
|
-
mcp-pacemaker stop --instance <directory>
|
|
313
|
-
```
|
|
314
|
-
|
|
315
|
-
`--instance <directory>` and `--instance=<directory>` work before or after the
|
|
316
|
-
command. Selection is normalized once, including paths with spaces; conflicting
|
|
317
|
-
selectors or port/config overrides are rejected before an action. Text after `--`
|
|
318
|
-
is not interpreted as a selector. An invalid explicit selection never falls back
|
|
319
|
-
to the default instance.
|
|
320
|
-
CLI port options use canonical decimal integers from 1 to 65535; scientific notation, hex,
|
|
321
|
-
signs, surrounding whitespace and zero-padded spellings are rejected before requests.
|
|
322
|
-
|
|
323
|
-
Selected `start`, `stop`, `reload`, `prewarm`, `status`, `doctor`, `top`, `logs`,
|
|
324
|
-
`dashboard`, version/help, and upgrade commands use the same instance context.
|
|
325
|
-
The TUI's recycle key uses that instance's port **and nonce directory**; logs use
|
|
326
|
-
its config directory. Bare `mcp-pacemaker --instance <directory>` shows status
|
|
327
|
-
even when SxS has no host-wiring state. Host setup/removal commands (`init`,
|
|
328
|
-
`import`, `install`, `uninstall`) are refused with `--instance`; selection alone
|
|
329
|
-
does not authorize rewriting host configuration or changing autostart.
|
|
330
|
-
Generated prewarm Cancel/Undo commands retain the selected instance and config,
|
|
331
|
-
so they resolve the same CLI version and nonce directory. Windows commands use
|
|
332
|
-
an encoded PowerShell launcher with an explicit native argument list to preserve
|
|
333
|
-
spaces, quotes and shell characters; unmanaged `--config` commands remain supported.
|
|
334
|
-
|
|
335
|
-
The stable per-instance launcher selects checked version directories. Upgrades never
|
|
336
|
-
overwrite a running package or rewrite per-version autostart tasks. The previous selection
|
|
337
|
-
and package are retained. The existing command entry point dispatches to the committed
|
|
338
|
-
version's CLI and its own dependencies: ordinary `--version`, `status`, `update-check`
|
|
339
|
-
and the next `upgrade` use that version. An unfinished activation keeps the previous
|
|
340
|
-
CLI available for recovery; rollback restores that CLI selection. SxS does not change
|
|
341
|
-
the default CLI. Use `--instance <directory>` to select its CLI explicitly.
|
|
342
|
-
|
|
343
|
-
Target dependencies are resolved into a private lock and restored with npm's archive
|
|
344
|
-
integrity checks before interruption. Both the lock and installed dependency files enter
|
|
345
|
-
the checked version inventory. Target CLI startup and version are checked before quiescing.
|
|
346
|
-
The target archive and resolved CLI dependencies require SHA-512 integrity metadata.
|
|
347
|
-
SHA-1-only or missing integrity information is rejected before interruption; the
|
|
348
|
-
upgrader does not downgrade its checks to accommodate a registry.
|
|
349
|
-
Acquisition uses the configured npm registry, or explicit
|
|
350
|
-
`--registry`, with no registry fallback or global npm changes. Existing npm permissions,
|
|
351
|
-
TLS, release-age policy and URL-prefix mapping remain in effect; lifecycle scripts are
|
|
352
|
-
disabled for acquisition. For npm 12 mirrors, use your approved command-local
|
|
353
|
-
`replace-registry-host` full-URL-prefix mapping back through that registry—not a
|
|
354
|
-
hard-coded mirror URL or public-registry bypass.
|
|
355
|
-
|
|
356
|
-
Download, archive integrity, package/engine/native-binary checks and an owned packaged
|
|
357
|
-
startup check finish before interruption. Admission then closes and waits up to eight
|
|
358
|
-
seconds for safe work to drain. Open MCP sessions and unresolved config transactions
|
|
359
|
-
currently cause a refusal; close/settle them and retry. No uncertain tool call or failed
|
|
360
|
-
configuration commit is replayed. This is a **short restart**, not socket handoff or
|
|
361
|
-
zero downtime. Clients and dashboard tabs may need a reload.
|
|
362
|
-
|
|
363
|
-
The supervisor persists admission hold **before** forwarding quiesce. A lost reply,
|
|
364
|
-
failed drain, bridge crash or replacement child does not release it. Only explicit
|
|
365
|
-
resume/recovery or a verified activation opens admission again. Upgrade stop is tied to
|
|
366
|
-
the drained generation; any replacement generation must remain held.
|
|
367
|
-
|
|
368
|
-
The activation journal blocks unattended starts after an interrupted transition.
|
|
369
|
-
Selection and launch intent are separate durable transitions. A selected target that
|
|
370
|
-
was provably never launched can recover using the exact previous stopped-generation
|
|
371
|
-
receipt. After launch intent, an empty port alone is not shutdown proof.
|
|
372
|
-
Recovery parses bounded instance/journal identities before checking executable
|
|
373
|
-
bytes. It fully verifies and runs the retained recovery CLI, so a damaged or
|
|
374
|
-
missing target file cannot prevent a safe pre-launch rollback. A damaged previous
|
|
375
|
-
package, stale identity or unknown journal still causes a refusal.
|
|
376
|
-
|
|
377
|
-
| Journal state | Recovery boundary |
|
|
378
|
-
|---|---|
|
|
379
|
-
| Prepared / quiescing | Explicitly resume the matching previous service |
|
|
380
|
-
| Stopping | Reconcile the exact drained generation |
|
|
381
|
-
| Stopped / selecting / selected | Require the previous completed-stop receipt; the target has no launch intent |
|
|
382
|
-
| Launching | Require a new held generation and verified shutdown; no empty-port inference |
|
|
383
|
-
| Rollback-selecting / rollback-selected | Require the durable rollback stop proof before selecting/restarting previous code |
|
|
384
|
-
| Rollback-launching | Require proof for the newly launched previous generation; otherwise refuse |
|
|
385
|
-
| Admitting / admitting-rollback | Admission may have occurred; no automatic rollback |
|
|
386
|
-
| Committed / rolled-back / aborted | No interrupted activation remains |
|
|
387
|
-
|
|
388
|
-
Automatic rollback is restricted to startup failure before target admission, with
|
|
389
|
-
unchanged configuration bytes, identity and security and verified shutdown. If admission
|
|
390
|
-
may have occurred, recovery refuses rather than overwriting user state.
|
|
391
|
-
|
|
392
|
-
```bash
|
|
393
|
-
mcp-pacemaker upgrade --recover --instance <directory> --yes
|
|
394
|
-
```
|
|
395
|
-
|
|
396
|
-
A crash can leave `upgrade.lock`. Recovery does not steal it based on a reusable PID.
|
|
397
|
-
Verify the original upgrader has exited, preserve the journal, and remove only that
|
|
398
|
-
instance's lock before recovery. Keep packages, configuration and transaction files.
|
|
399
|
-
Journal writes are file-flushed and atomically renamed; Windows directory-entry
|
|
400
|
-
durability across power loss is not claimed.
|
|
401
|
-
|
|
402
|
-
### One-time Windows 1.3 restart migration
|
|
403
|
-
|
|
404
|
-
The normal `upgrade --to <exact-version>` confirmation also supports the exact known
|
|
405
|
-
**1.3.0 Windows supervisor/launcher and per-user task shape**, with one port in the
|
|
406
|
-
existing install state. Invoke an upgrade-capable CLI from a separate durable package;
|
|
407
|
-
do not overwrite the running 1.3 package first. `--plan` remains read-only.
|
|
408
|
-
|
|
409
|
-
This transition restarts **Pacemaker, not Windows**. Pause callers before confirming.
|
|
410
|
-
The interactive confirmation states that clients will disconnect and 1.3 cannot drain
|
|
411
|
-
or certify every in-flight HTTP outcome. Zero reported sessions or child PIDs does not
|
|
412
|
-
prove that HTTP calls finished. Ordinary `--yes` and unattended invocation refuse this
|
|
413
|
-
transition; there is no separate maintenance flag and no automatic call replay.
|
|
414
|
-
|
|
415
|
-
Target package, CLI dependencies, native identities and packaged startup are verified
|
|
416
|
-
before downtime. A separate broker retains handles for the selected supervisor, bridge
|
|
417
|
-
and a bounded set of provably owned descendants. Only the verified selected task is
|
|
418
|
-
held. Stop uses those handles and verifies their exit before starting the held target
|
|
419
|
-
on the same port. It does not use a name/PID kill or assume task disable stopped the
|
|
420
|
-
running supervisor. Configuration, host wiring and port remain unchanged; the existing
|
|
421
|
-
CLI entry dispatches the newly selected managed CLI.
|
|
422
|
-
|
|
423
|
-
Preparation stays outside managed discovery until the instance identity and recovery
|
|
424
|
-
journal are durable; the prepared directory is then published together. A pre-journal
|
|
425
|
-
I/O failure leaves the old installation discoverable. Failed preparation is retained,
|
|
426
|
-
not guessed safe to delete. A terminal rolled-back/aborted candidate is excluded only
|
|
427
|
-
after its bounded journal and legacy-source binding validate, so damaged target files
|
|
428
|
-
do not prevent retrying the still-running legacy installation.
|
|
429
|
-
|
|
430
|
-
The task's **resource owner and full security descriptor** are separate from its run-as
|
|
431
|
-
account. Planning and every task operation require owner/group/DACL/SACL/integrity
|
|
432
|
-
inspection through Task Scheduler. If the required security read is denied or unsupported,
|
|
433
|
-
migration refuses before touching the task; it does not enable privileges or elevate.
|
|
434
|
-
Existing-task updates preserve security rather than reassigning its owner: they clear
|
|
435
|
-
only the temporary definition's embedded descriptor, pass no replacement security
|
|
436
|
-
descriptor, disable automatic principal-ACE edits, and verify the complete raw resource
|
|
437
|
-
descriptor again afterward. Protected backups retain the original raw descriptor and XML.
|
|
438
|
-
Restoration requires exact equality of all non-XML registration/security fields and
|
|
439
|
-
Task Scheduler-normalized XML, ignoring only the exact embedded
|
|
440
|
-
`Task/RegistrationInfo/SecurityDescriptor` representation. Raw revision preconditions,
|
|
441
|
-
backups and the actual returned XML/hash are not rewritten; other metadata or XML drift refuses.
|
|
442
|
-
Concurrent or uncertain security changes require inspection, not permission repair.
|
|
443
|
-
The resource owner may be the runtime user or canonical builtin Administrators;
|
|
444
|
-
that does not change the run-as account. Default scope remains **current user**,
|
|
445
|
-
but Windows execution and recovery require a freshly verified ordinary caller
|
|
446
|
-
before staging or mutation; an elevated or unverified caller refuses rather than
|
|
447
|
-
launching a runtime with different authority. Read-only planning is unchanged.
|
|
448
|
-
Explicit original-account administration uses a task-only
|
|
449
|
-
trusted controller and an original-user worker; see
|
|
450
|
-
[account upgrade scope and qualification](docs/account-upgrades.md).
|
|
451
|
-
`--all-users` is explicitly unsupported.
|
|
452
|
-
|
|
453
|
-
The legacy receipt reports `legacyRootStopVerified`, `observedDescendantsStopped` and
|
|
454
|
-
`treeCompleteness: "unproven"` separately. This is **not** the modern Job Object
|
|
455
|
-
complete-descendant proof. Historical or uncaptured orphan processes cannot be fully
|
|
456
|
-
attributed; they are left untouched and the limitation is reported without claiming an
|
|
457
|
-
orphan count. The new managed runtime contains future children from birth.
|
|
458
|
-
|
|
459
|
-
Unknown package/task shapes, changed owner/generation/configuration, a failed captured
|
|
460
|
-
process stop, or a foreign listener prevent activation. A foreign listener is never
|
|
461
|
-
stopped. Original code and protected configuration/task backups are retained. A verified
|
|
462
|
-
pre-admission target failure can restore the original CLI and scoped task and verify a
|
|
463
|
-
fresh legacy backend; uncertain launch, registration or admission outcomes require
|
|
464
|
-
explicit recovery. No configuration is deleted or replayed.
|
|
465
|
-
|
|
466
|
-
| Legacy journal phase | Recovery boundary |
|
|
467
|
-
|---|---|
|
|
468
|
-
| `legacy-prepared` / `legacy-held` | Verify the original generations and unchanged state; restore only the recorded task hold |
|
|
469
|
-
| `legacy-holding` | Task write outcome may be unknown; inspect before recovery |
|
|
470
|
-
| `legacy-stopping` | Read-only captured-generation absence check; an incomplete observed set refuses recovery |
|
|
471
|
-
| `legacy-stopped` | Revalidate partial stop and free port before safe old-version rollback |
|
|
472
|
-
| `legacy-launching` / `legacy-wiring` | Require modern held-target shutdown proof; a closed port alone is insufficient |
|
|
473
|
-
| `legacy-admitting` | Traffic may have entered; no automatic rollback |
|
|
474
|
-
| Legacy rollback intent phases | An interrupted task/CLI/launch outcome is reported as uncertain, not retried blindly |
|
|
475
|
-
| `legacy-rolled-back` / `legacy-aborted` | Original installation remains selected; the staged managed candidate stays dormant |
|
|
476
|
-
|
|
477
|
-
A killed migration can also leave the profile's `legacy-upgrade/upgrade.lock`.
|
|
478
|
-
Verify that exact upgrader has exited before removing its lock; never steal it by PID
|
|
479
|
-
guessing. Preserve the journal and backup files.
|
|
480
|
-
|
|
481
|
-
This adapter does not support direct unregistered bridges, other legacy versions,
|
|
482
|
-
multiple legacy ports, Linux/macOS registrations, or legacy-source SxS. Targets must
|
|
483
|
-
declare `legacyRestartProtocol: 1`; older published targets without it refuse before
|
|
484
|
-
legacy downtime.
|
|
485
|
-
|
|
486
|
-
Managed SxS keeps its own verified package copy as well as separate runtime state.
|
|
487
|
-
If a crash happens during port-0 startup before the port is durably recorded, recovery
|
|
488
|
-
reports an unknown launch outcome rather than marking it safely aborted. Preserve
|
|
489
|
-
that instance and its journal until readiness or shutdown can be verified.
|
|
490
|
-
|
|
491
|
-
For an older installation requiring a separately verified manual migration:
|
|
492
|
-
|
|
493
|
-
Do not overwrite a package directory while its supervisor or bridge is running. Workers and
|
|
494
|
-
native helpers can load later, and dashboard assets are read from disk: replacement can mix
|
|
495
|
-
old and new code even when the original process still reports its old version.
|
|
496
|
-
|
|
497
|
-
Identify the selected port, installation directory, configuration directory and OS autostart
|
|
498
|
-
entry first. Settle or cancel 2.x batches, recover interrupted transactions with 2.x, and keep
|
|
499
|
-
a protected configuration backup. Disable the selected autostart entry and stop its supervisor
|
|
500
|
-
and bridge before replacing files; prove they cannot restart during replacement. Keep the
|
|
501
|
-
known-good installation until the new backend's version and instance identity are confirmed.
|
|
502
|
-
Re-register the intended autostart path, refresh dashboard tabs, and confirm host wiring.
|
|
503
|
-
|
|
504
|
-
One global npm prefix holds one package version and its shared command names. Installing the
|
|
505
|
-
other major there replaces it. Separate ports do not isolate credentials, nonces, logs or
|
|
506
|
-
session files: manual side-by-side operation needs separate durable code roots and separate
|
|
507
|
-
configuration directories, plus separate host/HOME state for CLI-managed setup.
|
|
508
|
-
`init/import --config` selects a source host configuration, not an isolated runtime profile.
|
|
509
|
-
There is no package `--profile` or `MCP_HOME` switch; managed SxS uses explicit instance
|
|
510
|
-
directories instead. Do not use an unscoped stop/uninstall
|
|
511
|
-
operation when another instance must remain running.
|
|
512
|
-
|
|
513
|
-
## Dashboards
|
|
514
|
-
|
|
515
|
-
Two views of the same live state. Neither is required — the bridge runs headless — but both are
|
|
516
|
-
useful for answering "is this server actually up, and who is using it?"
|
|
517
|
-
|
|
518
|
-
### Web dashboard
|
|
519
|
-
|
|
520
|
-
`mcp-pacemaker dashboard`, or open `http://127.0.0.1:<port>/ui`. It streams over SSE, so it
|
|
521
|
-
updates on its own without polling or a refresh.
|
|
522
|
-
|
|
523
|
-

|
|
524
|
-
|
|
525
|
-
Per server it shows the transport (`stdio` / `http`), the sharing policy (`pool` when pre-warming),
|
|
526
|
-
live sessions against the cap, request count, the actual OS pids, the warm-pool count, how long
|
|
527
|
-
ago it was last used, and **which clients are connected** — above, one server is shared by
|
|
528
|
-
VS Code and Claude Code while others are held by Copilot, Cursor and Codex. `Recycle` restarts
|
|
529
|
-
that server's processes; it is disabled for servers with no session to restart. The live log at
|
|
530
|
-
the bottom is the bridge's own log, filterable.
|
|
531
|
-
|
|
532
|
-
### Pre-warming controls
|
|
533
|
-
|
|
534
|
-
The **Pre-Warming** tab puts every stdio candidate, current pool and shared server in one table.
|
|
535
|
-
It shows cold-start p50/p95, observed peak sessions, process starts, warm/target counts,
|
|
536
|
-
shared-child state and pending work, and an action.
|
|
537
|
-
HTTP proxies are excluded because the bridge does not launch their processes.
|
|
538
|
-
|
|
539
|
-
Choose **Enable pre-warming (N)** on a slow-server card or in the table. The button shows
|
|
540
|
-
the exact warm count and the extra resident-process cost before you click. Nothing enables
|
|
541
|
-
it automatically. **Disable pre-warming** removes idle warm children without killing active
|
|
542
|
-
sessions after the change is applied.
|
|
543
|
-
|
|
544
|
-
Changes are batched in a **complete pending copy** of `servers.json`. Every accepted change
|
|
545
|
-
resets a five-second countdown. When editing stops, reload validates the pending copy, retains
|
|
546
|
-
the original as `servers.previous.json`, flips the pending file into place, and activates the
|
|
547
|
-
new settings. The dashboard shows **Pending** or **Applying** until that succeeds; active
|
|
548
|
-
mode and warm counts are not changed early. **Reload now** flushes the same batch immediately.
|
|
549
|
-
|
|
550
|
-
**Cancel batch** discards a pending batch without changing active settings. **Undo batch**
|
|
551
|
-
stages the full previous configuration for another five-second batch, only if no intervening
|
|
552
|
-
active-file edit has occurred. These actions affect the entire batch; the dashboard names all
|
|
553
|
-
affected servers. An unrelated pending batch must be cancelled or applied before Undo.
|
|
554
|
-
|
|
555
|
-
The same controls are available without a browser:
|
|
556
|
-
|
|
557
|
-
```sh
|
|
558
|
-
mcp-pacemaker prewarm --port 8791
|
|
559
|
-
mcp-pacemaker prewarm --port 8791 --enable filesystem --count 2
|
|
560
|
-
mcp-pacemaker prewarm --port 8791 --disable filesystem
|
|
561
|
-
# Changes are queued; the printed command cancels a pending batch or undoes it after application.
|
|
562
|
-
```
|
|
563
|
-
|
|
564
|
-
Use `--config <path>` when the bridge's config and admin nonce are outside the default
|
|
565
|
-
directory. `--json` returns the measured snapshot. The CLI and UI use the same server
|
|
566
|
-
eligibility and recommendation data; a missing latency sample is not reported as a zero-cost
|
|
567
|
-
startup. The CLI returns after staging, so consecutive commands can join the same batch.
|
|
568
|
-
`prewarm` shows pending settings separately from active values. Config remains strict JSON,
|
|
569
|
-
not JSONC; the pending copy preserves unrelated JSON and requires a matching active revision.
|
|
570
|
-
Conflicts are reported rather than overwriting someone else's edit.
|
|
571
|
-
|
|
572
|
-
On Windows, saves run with the normal bridge account and use **directory-inherited auditing**.
|
|
573
|
-
The dashboard gives a non-blocking notice: custom per-file audit rules may not carry forward
|
|
574
|
-
to the new active copy. The retained original keeps its original metadata. No audit-read
|
|
575
|
-
privilege, UAC prompt, or permanently elevated bridge is required.
|
|
576
|
-
|
|
577
|
-
This does not permit broader access to configuration data. Candidates must have appropriate
|
|
578
|
-
access restrictions before any configuration bytes are written, and supported integrity
|
|
579
|
-
protections must be retained. Actual source write denial or unsupported protection remains
|
|
580
|
-
an error. Configuration operations run in a serialized worker so file inspection does not
|
|
581
|
-
block MCP traffic.
|
|
582
|
-
|
|
583
|
-
Windows security inspection uses the bundled, own-source .NET Framework helper rather than
|
|
584
|
-
starting PowerShell or compiling code on each write. Automatic edits require .NET Framework
|
|
585
|
-
4.6.2 or later; modern Windows includes it. The helper's source, build instructions and
|
|
586
|
-
integrity metadata are [included in the package](tools/windows-security-helper/README.md).
|
|
587
|
-
Windows ARM64 execution is not yet validated.
|
|
588
|
-
|
|
589
|
-
Staging requests and file commits each have a bounded execution budget; the acknowledged
|
|
590
|
-
five-second batching delay does not keep the original HTTP request open. A commit already in
|
|
591
|
-
progress can finish after its deadline. The result distinguishes a pre-commit failure from
|
|
592
|
-
a committed or unknown outcome; it never automatically retries or claims a timed-out write
|
|
593
|
-
was rolled back.
|
|
594
|
-
|
|
595
|
-
The two file moves are not an atomic exchange. Reload coordinates them with validated recovery
|
|
596
|
-
state and non-replacing destination placement. Existing MCP traffic continues using the
|
|
597
|
-
previous in-memory configuration during the flip; an external reader can briefly find the
|
|
598
|
-
active path absent. Recovery must not overwrite a file created by an external editor.
|
|
599
|
-
Reread the settings after an uncertain result.
|
|
600
|
-
|
|
601
|
-
### Upgrading from 1.3.0
|
|
602
|
-
|
|
603
|
-
Version 2.0.0 changes the configuration-save protocol; it does not require a new `servers.json`
|
|
604
|
-
format. Update the bridge and CLI together, then reload any open dashboard pages. A 1.3.0
|
|
605
|
-
client with a current nonce gets HTTP 409 before staging, with an instruction to update the
|
|
606
|
-
client. An already-open tab retains the old nonce after a bridge restart and gets HTTP 401;
|
|
607
|
-
refresh it to load the new dashboard and admin session.
|
|
608
|
-
|
|
609
|
-
Custom API clients must send `x-mcp-pooling-batch: 1`. Treat HTTP 202 as acceptance, not
|
|
610
|
-
completion, and follow the returned batch ID in `prewarm.batches` until it is applied,
|
|
611
|
-
cancelled or failed. CLI scripts must likewise wait for application before relying on new
|
|
612
|
-
settings. Undo now restores the complete batch and may affect several servers.
|
|
613
|
-
Updated clients still understand the immediate HTTP 200 response from a 1.3.0 bridge.
|
|
614
|
-
|
|
615
|
-
Before downgrading, cancel or apply pending changes with 2.0 and resolve any interrupted
|
|
616
|
-
transaction using 2.0 recovery. Stop that bridge before starting 1.3.0 with the active config.
|
|
617
|
-
Do not use a 1.3.0 binary to recover 2.0 transaction files or delete those files to bypass a
|
|
618
|
-
recovery error. Keep a protected backup of the configuration before changing versions.
|
|
619
|
-
If the writer worker exits, further automatic edits are refused until the bridge restarts;
|
|
620
|
-
existing MCP sessions continue.
|
|
621
|
-
|
|
622
|
-
### Health
|
|
623
|
-
|
|
624
|
-
The `Health` tab runs the same checks as `mcp-pacemaker doctor`, so you can see them without a
|
|
625
|
-
terminal.
|
|
626
|
-
|
|
627
|
-

|
|
628
|
-
|
|
629
|
-
It checks the Node version, that the config parses, every server definition (including a stdio
|
|
630
|
-
server whose relative path will not resolve from the directory it would run in — a failure that
|
|
631
|
-
is otherwise silent until a client first calls it, and a server named after one of the bridge's
|
|
632
|
-
own routes, which is unreachable), any server currently failing, and that the admin nonce file
|
|
633
|
-
exists.
|
|
634
|
-
|
|
635
|
-
### Terminal dashboard
|
|
636
|
-
|
|
637
|
-
`mcp-pacemaker top` is the same data as an Ink TUI, for when a browser is inconvenient:
|
|
638
|
-
|
|
639
|
-
```
|
|
640
|
-
🫀 mcp-pacemaker top ● :8850 · up 176s · v1.1.0
|
|
641
|
-
SERVER TYPE HEALTH SESS WARM REQ PID TOKEN ERROR / CLIENTS
|
|
642
|
-
filesystem stdio ok 2 1/1 8 186276,12 - ⇄ Visual Studio Code,Claude Code
|
|
643
|
-
github stdio ok 1 - 2 146436 - ⇄ GitHub Copilot
|
|
644
|
-
sqlite stdio ok 1 - 2 102108 - ⇄ Cursor
|
|
645
|
-
slow-tool stdio ok 1 - 2 103776 - ⇄ Codex CLI
|
|
646
|
-
search-api http FAIL×3 0 - 4 - 42m upstream rejected the crede
|
|
647
|
-
docs-api http ? 0 - 0 - -
|
|
648
|
-
↑↓ select · r recycle · q quit
|
|
649
|
-
```
|
|
650
|
-
|
|
651
|
-
`HEALTH` is `ok` when the last request to that server succeeded, `FAIL×n` after n consecutive
|
|
652
|
-
failures, and `?` when nothing has called it yet — deliberately not `ok`, since no evidence is not
|
|
653
|
-
the same as working. `WARM` shows `warm/minWarm` for pooled servers and `-` for the rest, `TOKEN`
|
|
654
|
-
the time left on a cached credential, and the last column either the connected clients or the last
|
|
655
|
-
error. `↑↓` selects a row and `r` recycles it.
|
|
656
|
-
|
|
657
|
-
## Reading durable logs
|
|
658
|
-
|
|
659
|
-
```sh
|
|
660
|
-
mcp-pacemaker logs --since 2h --server filesystem
|
|
661
|
-
mcp-pacemaker logs --follow --grep timeout
|
|
662
|
-
mcp-pacemaker logs --config /path/to/servers.json --since 2026-09-07T12:00:00Z
|
|
663
|
-
```
|
|
664
|
-
|
|
665
|
-
`logs` reads `bridge.log.1` then `bridge.log` beside the selected config, including when
|
|
666
|
-
the bridge is stopped. `--since` accepts durations in seconds/minutes/hours/days or an ISO
|
|
667
|
-
timestamp with timezone. `--server` matches the exact log-header server name; `--grep` is
|
|
668
|
-
a case-insensitive literal match, not a regular expression. Matching multiline records retain
|
|
669
|
-
their header and context.
|
|
670
|
-
|
|
671
|
-
`--follow` handles complete new lines, split UTF-8 writes, truncation and rollover until
|
|
672
|
-
interrupted. It warns if rotation has already discarded an unread generation. Only retained
|
|
673
|
-
history is available; it cannot recover logs the bridge has deleted. Individual lines over
|
|
674
|
-
1 MiB fail explicitly rather than being silently truncated.
|
|
675
|
-
|
|
676
|
-
## Editing servers.json while it runs
|
|
677
|
-
|
|
678
|
-
Save the file and the bridge picks it up. Servers whose definition did not change are left
|
|
679
|
-
completely alone — same processes, same live sessions — so adding one server does not cost you the
|
|
680
|
-
fourteen that were already working:
|
|
681
|
-
|
|
682
|
-
```bash
|
|
683
|
-
$ mcp-pacemaker reload # or just save the file; the bridge watches it
|
|
684
|
-
✓ reloaded /Users/you/.mcp-pacemaker/servers.json on :8850
|
|
685
|
-
· added: postgres
|
|
686
|
-
· changed: github
|
|
687
|
-
· 2 session(s) restarted; untouched servers kept theirs
|
|
688
|
-
```
|
|
689
|
-
|
|
690
|
-
A file that does not parse, or a server with neither `command` nor `url`, is **rejected whole** —
|
|
691
|
-
the bridge logs why and keeps serving the last good config, so a half-written save cannot take your
|
|
692
|
-
servers down. Set `MCP_CONFIG_WATCH=0` to require an explicit `reload` instead of watching.
|
|
693
|
-
|
|
694
|
-
## Server health
|
|
695
|
-
|
|
696
|
-
Every server carries a health verdict in `/api/status`, `doctor`, `top` and the dashboard:
|
|
697
|
-
|
|
698
|
-
| State | Meaning |
|
|
699
|
-
|---|---|
|
|
700
|
-
| `ok` | The last request to this server succeeded |
|
|
701
|
-
| `failing` | Recent requests are failing, with a count of how many in a row |
|
|
702
|
-
| `unknown` | Nothing has called it yet |
|
|
703
|
-
|
|
704
|
-
This is passive and free — it is derived from traffic the bridge is already proxying. A JSON-RPC
|
|
705
|
-
error *from* the server counts as healthy (it answered); a 401/403, a 5xx, a spawn failure or a
|
|
706
|
-
non-zero exit counts as failing, and the state clears the moment a request succeeds again.
|
|
707
|
-
|
|
708
|
-
A server nobody calls stays `unknown` forever, which is honest but not much help if its credential
|
|
709
|
-
quietly expires overnight. `MCP_HEALTH_INTERVAL_MS` adds a periodic probe for HTTP servers so they
|
|
710
|
-
get a verdict without waiting for a client:
|
|
711
|
-
|
|
712
|
-
```bash
|
|
713
|
-
MCP_HEALTH_INTERVAL_MS=300000 mcp-pacemaker start # probe every 5 minutes
|
|
211
|
+
mcp-pacemaker uninstall # use the CLI from the matching installed version
|
|
714
212
|
```
|
|
715
213
|
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
## HTTP API
|
|
722
|
-
|
|
723
|
-
Everything the dashboards show is plain HTTP on the same loopback port, so you can script it.
|
|
724
|
-
Read endpoints need no auth; administrative writes require a nonce.
|
|
214
|
+
This stops managed bridges, removes auto-start entries and restores available host-file
|
|
215
|
+
backups. It leaves files without backups and native Claude Code registrations unchanged.
|
|
216
|
+
It is not scoped to one port; review [replacement and rollback](docs/upgrades.md#replacing-or-rolling-back-a-running-installation)
|
|
217
|
+
first if another instance must remain running. Do not fetch a different CLI with `npx`
|
|
218
|
+
to remove an existing installation.
|
|
725
219
|
|
|
726
|
-
|
|
727
|
-
|---|---|---|
|
|
728
|
-
| `/status` | GET | Tiny liveness probe: `ok`, `service`, `version`, `port` and the server names. Used to tell a pacemaker bridge apart from a foreign service on the same port |
|
|
729
|
-
| `/api/status` | GET | Full snapshot — every server with sessions, pids, warm count, request count, `health`, `lastError`, connected clients |
|
|
730
|
-
| `/api/doctor` | GET | The health checks, as JSON |
|
|
731
|
-
| `/api/events` | GET (SSE) | The same snapshot pushed every 2s — what the dashboard consumes |
|
|
732
|
-
| `/api/logs` | GET (SSE) | Bridge log: replays the last 100 lines, then streams. For anything older, read `bridge.log` next to your config |
|
|
733
|
-
| `/admin/recycle/<name>` | POST | Restart a server's processes. Omit `<name>` to recycle everything |
|
|
734
|
-
| `/admin/reload` | POST | Flush a pending configuration batch, or re-read an externally edited `servers.json`. Only changed definitions are reconfigured |
|
|
735
|
-
| `/admin/servers/<name>/pooling` | POST | Stage enable/disable or batch cancel/undo with admin nonce, bounded input and a matching config revision |
|
|
736
|
-
| `/<name>/mcp` | POST/GET/DELETE | Streamable HTTP transport for that server |
|
|
737
|
-
| `/<name>/sse` | GET | HTTP+SSE transport for that server |
|
|
738
|
-
| `/.well-known/oauth-protected-resource/<name>` | GET | OAuth discovery relayed from the upstream, for HTTP servers where the client authenticates |
|
|
739
|
-
|
|
740
|
-
Pooling mutations require `x-mcp-pooling-batch: 1`. A staged change returns **HTTP 202** with
|
|
741
|
-
`pending: true`, a batch ID, a private cancel/undo receipt, and the public snapshot. Acceptance
|
|
742
|
-
is not activation. `prewarm.batches` in the snapshot reports bounded pending, applying and
|
|
743
|
-
completed outcomes; it does not expose receipt tokens or full configurations.
|
|
744
|
-
Clients without the batch header are rejected before a change is queued, rather than letting
|
|
745
|
-
an older client report a queued save as already enabled.
|
|
746
|
-
Rich snapshots carry an instance-scoped `snapshotVersion` so a delayed HTTP response or older
|
|
747
|
-
SSE frame cannot replace newer active settings or prematurely expire a batch receipt.
|
|
220
|
+
After the installed service has been removed successfully, remove the global npm package:
|
|
748
221
|
|
|
749
222
|
```bash
|
|
750
|
-
|
|
751
|
-
curl -s http://127.0.0.1:8850/api/status | jq '.servers[] | {name, sessions, pids, clients}'
|
|
752
|
-
|
|
753
|
-
# watch the bridge log
|
|
754
|
-
curl -N http://127.0.0.1:8850/api/logs
|
|
755
|
-
|
|
756
|
-
# force a restart of one server (nonce is written next to servers.json)
|
|
757
|
-
curl -X POST http://127.0.0.1:8850/admin/recycle/filesystem \
|
|
758
|
-
-H "x-mcp-nonce: $(cat ~/.mcp-pacemaker/admin.nonce)"
|
|
759
|
-
```
|
|
760
|
-
|
|
761
|
-
`/admin/*` is guarded twice: the request must arrive on loopback, **and** carry the nonce that the
|
|
762
|
-
bridge writes to `admin.nonce` next to your config at startup. The nonce is new on every start, so
|
|
763
|
-
only same-box tooling that can read that file — the CLI, and the dashboard the bridge itself
|
|
764
|
-
served — can change anything.
|
|
765
|
-
|
|
766
|
-
## servers.json
|
|
767
|
-
|
|
768
|
-
Config lives at `~/.mcp-pacemaker/servers.json`. `import` fills it from your existing client
|
|
769
|
-
config and **carries over `audience`/`auth`/`headers`**, so HTTP servers usually need no hand-editing.
|
|
770
|
-
See [`examples/servers.example.json`](examples/servers.example.json).
|
|
771
|
-
|
|
772
|
-
```jsonc
|
|
773
|
-
{
|
|
774
|
-
"filesystem": { "command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] },
|
|
775
|
-
|
|
776
|
-
"my-api": {
|
|
777
|
-
"type": "http",
|
|
778
|
-
"url": "https://my-service.example.com/mcp",
|
|
779
|
-
"auth": { "type": "command", "command": "my-cli print-token", "refreshMinutes": 50 }
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
```
|
|
783
|
-
|
|
784
|
-
**Auth options** (per HTTP server):
|
|
785
|
-
|
|
786
|
-
| `auth.type` | Behavior |
|
|
787
|
-
|---|---|
|
|
788
|
-
| `command` | Runs any command that prints a token on stdout; the result is cached and auto-refreshed (`refreshMinutes`). Provider-agnostic — see below. |
|
|
789
|
-
| `env` | Uses `process.env[var]`. |
|
|
790
|
-
| `static` | Fixed `header` + `value`. |
|
|
791
|
-
| `none` | No header is added — the **client** authenticates, and the bridge relays OAuth discovery and challenges for it. |
|
|
792
|
-
|
|
793
|
-
`auth.type: "command"` is deliberately just "run this and use what it prints", so it works with
|
|
794
|
-
whatever issues tokens in your environment. Some examples — none of these are special-cased:
|
|
795
|
-
|
|
796
|
-
```jsonc
|
|
797
|
-
// Azure
|
|
798
|
-
{ "type": "command", "command": "az account get-access-token --resource api://<id> --query accessToken -o tsv" }
|
|
799
|
-
// Google Cloud
|
|
800
|
-
{ "type": "command", "command": "gcloud auth print-access-token" }
|
|
801
|
-
// AWS (e.g. a signed token from your own helper)
|
|
802
|
-
{ "type": "command", "command": "aws-token-helper --profile prod" }
|
|
803
|
-
// HashiCorp Vault
|
|
804
|
-
{ "type": "command", "command": "vault read -field=token secret/my-api" }
|
|
805
|
-
// 1Password / any secret manager
|
|
806
|
-
{ "type": "command", "command": "op read op://vault/my-api/token" }
|
|
807
|
-
// A plain script
|
|
808
|
-
{ "type": "command", "command": "./scripts/get-token.sh" }
|
|
223
|
+
npm uninstall -g mcp-pacemaker
|
|
809
224
|
```
|
|
810
225
|
|
|
811
|
-
By default the token is sent as `Authorization: Bearer <token>`; set `header` to send it
|
|
812
|
-
somewhere else (for example `{ "header": "X-Api-Key" }`).
|
|
813
|
-
|
|
814
|
-
**How long a credential is cached.** If the command returns a JWT, its `exp` claim decides —
|
|
815
|
-
`refreshMinutes` is only an upper bound and a fallback for opaque credentials. This matters
|
|
816
|
-
because token CLIs serve from their own cache: `az account get-access-token` will hand back a
|
|
817
|
-
token it minted earlier that may be nearly spent, and trusting a flat window would keep a dead
|
|
818
|
-
token in play. If the upstream rejects a credential the bridge supplied, that 401 is reported on
|
|
819
|
-
the server (`doctor`, `/api/status` → `lastError`) and the credential is discarded rather than
|
|
820
|
-
reused — the client is never asked to log in for a server the bridge authenticates.
|
|
821
|
-
|
|
822
|
-
> **Azure shorthand.** A bare `"audience": "<res>"` expands to
|
|
823
|
-
> `az account get-access-token --resource <res> …`. That is a convenience for Azure users only —
|
|
824
|
-
> every other provider uses `auth.command` above.
|
|
825
|
-
|
|
826
226
|
## Security
|
|
827
227
|
|
|
828
228
|
- The bridge binds **`127.0.0.1` only** (local loopback) — not reachable off-box.
|
|
829
229
|
- Tokens are **minted from your own already-authenticated CLI** and cached in memory; **never written to disk**.
|
|
830
230
|
- The **bridge** (the process that runs 24/7) has **zero third-party dependencies**; the setup CLI uses `commander` / `@clack/prompts` / `picocolors` / `smol-toml`, and the TUI adds `ink` / `react`.
|
|
831
231
|
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
Multiple hosts/agents can share one bridge — each gets its own isolated session by default,
|
|
835
|
-
and HTTP servers share the cached token across all of them. The dashboard, `top`, and
|
|
836
|
-
`mcp-pacemaker status` show **which agents are connected** to each server.
|
|
837
|
-
|
|
838
|
-
| Control | Env / config | Default | Effect |
|
|
839
|
-
|---|---|---|---|
|
|
840
|
-
| Idle reaper | `MCP_IDLE_TIMEOUT_MS` | `1800000` (30 min) | free Streamable HTTP children idle past the timeout. Safe by default because a returning client's session is transparently re-established. `0` disables |
|
|
841
|
-
| Concurrency cap | `MCP_MAX_SESSIONS_PER_SERVER`, or per-server `maxSessions` | `0` (unlimited) | reject new sessions past the cap (HTTP 503) |
|
|
842
|
-
| Queue at the cap | `MCP_QUEUE_TIMEOUT_MS` | `10000` (10s) | at the cap, wait this long for a slot before returning 503, so a burst is absorbed rather than failed. `0` rejects immediately |
|
|
843
|
-
| Sharing policy | per-server `sharing` (+ `minWarm`) | `isolated` | `isolated` = one child per session; `pool` pre-starts exclusive children; `shared` reuses one initialized child for compatible stateless tools sessions. Both alternatives require explicit activation |
|
|
844
|
-
| Cold-start gate | `MCP_MAX_CONCURRENT_SPAWNS`, `MCP_SPAWN_GATE_WAIT_MS` | `2`, `15000` | how many package-manager-backed servers may cold-start at once. `npx`, `uvx`, `dnx` and friends share one cache, and starting several together can corrupt it — this queues them. Detected from the command, or forced either way with per-server `sharedPackageCache`. Servers that use no package manager are never gated. If no slot frees within the wait, the spawn proceeds ungated rather than hold a client request. `0` disables |
|
|
845
|
-
| Pooling advice | `MCP_POOL_ADVICE_MS`, `MCP_WARM_MAX` | 2s, 8 | recommend pooling once a server's measured cold start passes this, and cap the suggested (and auto-sized) pool here. Advice only — the bridge never enables pooling on its own |
|
|
846
|
-
| Session resume | `MCP_RESUME`, `MCP_RESUME_TTL_MS` | on, 24h | re-establish a session id the bridge has not seen — after a restart, recycle or idle reap. `MCP_RESUME=0` disables |
|
|
847
|
-
| Scheduled recycle | per-server `recycleMinutes`, or `MCP_RECYCLE_MINUTES` | off | restart a server periodically, for servers holding a credential they can only refresh interactively |
|
|
848
|
-
| Request timeouts | `MCP_REQUEST_TIMEOUT_MS` / `MCP_INIT_TIMEOUT_MS` | 30s / 180s | `initialize` also pays for spawning the server, so it gets a longer budget than ordinary calls |
|
|
849
|
-
| Credential refresh | `MCP_TOKEN_REFRESH_LEAD_MS` | `300000` (5 min) | renew a cached token before it expires instead of discovering the expiry on a request. A JWT's own `exp` still caps how long it is cached |
|
|
850
|
-
| Durable log | `MCP_LOG_MAX_BYTES` | `5242880` (5 MB) | `bridge.log` next to your config, rolled to `bridge.log.1` at this size. `0` disables. The dashboard only keeps the last few hundred lines in memory, which a chatty client fills in minutes |
|
|
851
|
-
| Config watching | `MCP_CONFIG_WATCH` | on | reload `servers.json` when it is saved. `0` requires an explicit `mcp-pacemaker reload`. Either way, only servers whose definition changed are restarted, and an unparseable file is rejected without disturbing anything |
|
|
852
|
-
| Health probing | `MCP_HEALTH_INTERVAL_MS`, or per-server `healthIntervalMinutes` | off | periodically call an HTTP server so it has a health verdict before a client needs it. Passive health from real traffic is always on and costs nothing |
|
|
853
|
-
|
|
854
|
-
### Measuring process churn
|
|
855
|
-
|
|
856
|
-
For stdio servers, `/api/status` reports `spawn.total` (successful process launches),
|
|
857
|
-
`spawn.attempts`, `spawn.failures`, `spawn.warmAdoptions`, `spawn.sessionStarts`, and
|
|
858
|
-
`spawn.sessionResumes`. Launches include classic SSE, Streamable HTTP, warm-pool refill,
|
|
859
|
-
and replacements after recycle or resume. Adopting an existing warm child does not count
|
|
860
|
-
as another launch. The unit is one bridge-launched process, not all of its descendants.
|
|
861
|
-
|
|
862
|
-
These cumulative counters do not share the 50-sample latency limit. They reset when the
|
|
863
|
-
bridge process restarts; compare snapshots only within the same `instanceId`. `startedAt`
|
|
864
|
-
identifies that interval. Save the final snapshot before a planned restart for a before/after
|
|
865
|
-
comparison. HTTP-proxied servers have no local process counters (`spawn: null`).
|
|
866
|
-
|
|
867
|
-
`spawn.samples` and p50/p95/max still describe the latest 50 directly observed, successful
|
|
868
|
-
cold initializations, not all launches. Latencies are `null` until observed; a warm child's
|
|
869
|
-
idle lifetime is not a cold-start sample. The existing `requests` count covers routed HTTP
|
|
870
|
-
requests, not just `tools/call`; do not label it as a tool-call count.
|
|
871
|
-
|
|
872
|
-
### Shared mode
|
|
873
|
-
|
|
874
|
-
Set `"sharing": "shared"` on a stdio server only when its tools are stateless, operate on
|
|
875
|
-
explicit inputs and use one common credential context. This is an operator assertion, not
|
|
876
|
-
something latency or matching capabilities can prove. Leave workspace-, conversation- or
|
|
877
|
-
client-account-dependent servers isolated.
|
|
878
|
-
|
|
879
|
-
Compatible sessions have identical full initialization parameters, including `clientInfo`,
|
|
880
|
-
and empty client capabilities. One child receives one real initialization; each virtual session
|
|
881
|
-
gets its own IDs, progress, cancellation and tools-list cursors. Only the upstream's actual
|
|
882
|
-
tools capability is exposed. Roots, sampling, elicitation, tasks, resource subscriptions and
|
|
883
|
-
other stateful methods are not supported. Incompatible clients fail explicitly, without
|
|
884
|
-
silently falling back to a different mode.
|
|
885
|
-
|
|
886
|
-
Deleting one session leaves the others running. The initialized child is retained between
|
|
887
|
-
compatible reconnects for up to `sharedLingerMs` (30 minutes by default). Recycle drains work,
|
|
888
|
-
then replaces the generation on later use. A timeout or child exit never triggers automatic
|
|
889
|
-
replay of a potentially executed tool call.
|
|
890
|
-
|
|
891
|
-
The server keeps its existing credential provider and configured `recycleMinutes`. Shared
|
|
892
|
-
mode does not promise silent credential renewal. The dashboard and `prewarm` CLI show the
|
|
893
|
-
actual shared-child state, members, queued work and cumulative process launches.
|
|
894
|
-
See [the shared-session contract](docs/shared-sessions.md) for limits and failure semantics.
|
|
232
|
+
See [SECURITY.md](SECURITY.md) for the threat model and how to report a vulnerability. The
|
|
233
|
+
[HTTP API](docs/http-api.md) page explains how administrative requests are guarded.
|
|
895
234
|
|
|
896
|
-
##
|
|
235
|
+
## Documentation
|
|
897
236
|
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
```bash
|
|
911
|
-
npm uninstall -g mcp-pacemaker
|
|
912
|
-
```
|
|
237
|
+
- [Install details](docs/install.md) — Node and Windows requirements, the ordinary-user rule and npm channels
|
|
238
|
+
- [Configuration](docs/configuration.md) — `servers.json`, auth, live edits, resource controls and shared mode
|
|
239
|
+
- [Commands and operations](docs/operations.md) — every command, start and stop, durable logs and server health
|
|
240
|
+
- [Dashboards](docs/dashboards.md) — the web dashboard, pre-warming controls, the health tab and `top`
|
|
241
|
+
- [HTTP API](docs/http-api.md) — endpoints, pooling batches and the admin nonce
|
|
242
|
+
- [Upgrades and rollback](docs/upgrades.md) — managed upgrades, `--sxs`, recovery and the Windows 1.3 migration
|
|
243
|
+
- [Version compatibility](docs/compatibility.md) — 1.3.x and 2.x pairs, and upgrading from 1.3.0
|
|
244
|
+
- [User-runtime contract](docs/execution-context.md) — what the ordinary-user check verifies
|
|
245
|
+
- [Shared sessions](docs/shared-sessions.md) — the full shared-mode contract
|
|
246
|
+
- [Account upgrades](docs/account-upgrades.md) — explicit original-account upgrades on Windows
|
|
247
|
+
- [Roadmap](docs/roadmap.md) — planned work, including socket handoff
|
|
913
248
|
|
|
914
249
|
## Project
|
|
915
250
|
|
|
916
251
|
- [CHANGELOG.md](CHANGELOG.md) — release notes
|
|
917
252
|
- [CONTRIBUTING.md](CONTRIBUTING.md) — development setup and how regression tests are expected to be proven
|
|
918
253
|
- [SECURITY.md](SECURITY.md) — threat model and how to report a vulnerability
|
|
254
|
+
- Publishing and qualification: [npm publishing](docs/npm-publishing.md),
|
|
255
|
+
[local publication gate](docs/local-publication-gate.md) and
|
|
256
|
+
[cross-host live validation](docs/cross-host-live-validation.md)
|
|
919
257
|
|
|
920
258
|
## License
|
|
921
259
|
|