mcp-pacemaker 2.0.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.
Files changed (47) hide show
  1. package/CHANGELOG.md +452 -0
  2. package/CONTRIBUTING.md +222 -0
  3. package/LICENSE +21 -0
  4. package/README.md +621 -0
  5. package/SECURITY.md +120 -0
  6. package/THIRD_PARTY_NOTICES.txt +366 -0
  7. package/autostart/windows/launcher.vbs +11 -0
  8. package/autostart/windows/register-task.ps1 +63 -0
  9. package/bin/cli.mjs +880 -0
  10. package/bin/config-checks.mjs +62 -0
  11. package/bin/logs.mjs +465 -0
  12. package/bin/mcp-bridge.mjs +2162 -0
  13. package/bin/pooling-batch-scheduler.mjs +129 -0
  14. package/bin/pooling-batches.mjs +270 -0
  15. package/bin/pooling-config.mjs +577 -0
  16. package/bin/pooling-editor.mjs +128 -0
  17. package/bin/pooling-errors.mjs +23 -0
  18. package/bin/pooling-execution.mjs +104 -0
  19. package/bin/pooling-files.mjs +541 -0
  20. package/bin/pooling-trace.mjs +218 -0
  21. package/bin/pooling-writer.mjs +242 -0
  22. package/bin/prewarm.mjs +233 -0
  23. package/bin/server-metrics.mjs +69 -0
  24. package/bin/service-control.mjs +124 -0
  25. package/bin/shared-sessions.mjs +1083 -0
  26. package/bin/top.mjs +115 -0
  27. package/bin/update-check.mjs +153 -0
  28. package/bin/windows/PoolingSecurityHelper.build.json +42 -0
  29. package/bin/windows/PoolingSecurityHelper.exe +0 -0
  30. package/bin/windows/src/AssemblyInfo.cs +11 -0
  31. package/bin/windows/src/PoolingNativeFiles.cs +678 -0
  32. package/bin/windows/src/PoolingSecurityHelper.cs +178 -0
  33. package/bin/windows/src/PoolingSecurityReader.cs +50 -0
  34. package/docs/shared-sessions.md +181 -0
  35. package/examples/servers.example.json +31 -0
  36. package/package.json +77 -0
  37. package/supervisor/bridge-child.mjs +14 -0
  38. package/supervisor/supervise.mjs +172 -0
  39. package/supervisor/supervise.ps1 +23 -0
  40. package/supervisor/supervise.sh +11 -0
  41. package/tools/windows-security-helper/README.md +238 -0
  42. package/tools/windows-security-helper/build.ps1 +262 -0
  43. package/ui/dist/THIRD_PARTY_NOTICES.txt +366 -0
  44. package/ui/dist/assets/index-CaLPg0c-.css +1 -0
  45. package/ui/dist/assets/index-CgPruWYi.js +9 -0
  46. package/ui/dist/index.html +14 -0
  47. package/ui/dist/third-party-manifest.json +597 -0
@@ -0,0 +1,178 @@
1
+ using System;
2
+ using System.ComponentModel;
3
+ using System.IO;
4
+ using System.Security.AccessControl;
5
+ using System.Security.Cryptography;
6
+
7
+ public sealed class PoolingSecurityHelper
8
+ {
9
+ private readonly PoolingSecurityReader nativeReader = new PoolingSecurityReader();
10
+ private readonly AccessControlSections sections =
11
+ AccessControlSections.Access | AccessControlSections.Owner | AccessControlSections.Group;
12
+
13
+ /// <summary>Inspects, stages, moves, or preserves Windows file security.</summary>
14
+ /// <param name="args">One action. File paths come from MCP_POOL environment variables.</param>
15
+ /// <returns>Zero on success, three on a source security conflict, or one on failure.</returns>
16
+ public static int Main(string[] args)
17
+ {
18
+ return new PoolingSecurityHelper().Run(args);
19
+ }
20
+
21
+ /// <summary>Runs a native file operation or a legacy security operation.</summary>
22
+ /// <param name="args">One of inspect-access, stage, move-no-replace, inspect, or copy.</param>
23
+ /// <returns>Zero on success, three on a source security conflict, or one on failure.</returns>
24
+ public int Run(string[] args)
25
+ {
26
+ try
27
+ {
28
+ if (args.Length != 1)
29
+ {
30
+ throw new ArgumentException();
31
+ }
32
+
33
+ var sourcePath = this.RequiredEnvironment("MCP_POOL_SOURCE");
34
+ if (args[0] == "inspect-access" ||
35
+ args[0] == "stage" ||
36
+ args[0] == "move-no-replace")
37
+ {
38
+ var files = new PoolingNativeFiles();
39
+ Console.WriteLine(files.Run(args[0], sourcePath));
40
+ return 0;
41
+ }
42
+
43
+ if (args[0] == "inspect")
44
+ {
45
+ Console.WriteLine(this.ReadSecurity(sourcePath).Fingerprint);
46
+ return 0;
47
+ }
48
+
49
+ if (args[0] == "copy")
50
+ {
51
+ return this.CopySecurity(sourcePath);
52
+ }
53
+
54
+ throw new ArgumentException();
55
+ }
56
+ catch (Exception error)
57
+ {
58
+ Console.Error.WriteLine("MCPERR type=" + error.GetType().Name);
59
+ var nativeError = error as Win32Exception;
60
+ if (nativeError != null)
61
+ {
62
+ Console.Error.WriteLine("MCPERR nativeError=" + nativeError.NativeErrorCode);
63
+ }
64
+
65
+ bool conflict = error is PoolingConflictException ||
66
+ (nativeError != null &&
67
+ (nativeError.NativeErrorCode == 80 ||
68
+ nativeError.NativeErrorCode == 183));
69
+ return conflict ? 3 : 1;
70
+ }
71
+ }
72
+
73
+ private string RequiredEnvironment(string name)
74
+ {
75
+ var value = Environment.GetEnvironmentVariable(name);
76
+ if (string.IsNullOrEmpty(value))
77
+ {
78
+ throw new ArgumentException();
79
+ }
80
+
81
+ return value;
82
+ }
83
+
84
+ private SecurityState ReadSecurity(string path)
85
+ {
86
+ var acl = File.GetAccessControl(path);
87
+ var descriptor = acl.GetSecurityDescriptorSddlForm(this.sections);
88
+ var native = this.nativeReader.ReadControlDescriptor(path);
89
+ bool complete = true;
90
+ var audit = new byte[0];
91
+ try
92
+ {
93
+ audit = this.ReadAudit(path);
94
+ }
95
+ catch (PrivilegeNotHeldException)
96
+ {
97
+ complete = false;
98
+ }
99
+ catch (UnauthorizedAccessException)
100
+ {
101
+ complete = false;
102
+ }
103
+
104
+ int attributes = (int)File.GetAttributes(path);
105
+ using (var hash = SHA256.Create())
106
+ using (var buffer = new MemoryStream())
107
+ using (var writer = new BinaryWriter(buffer))
108
+ {
109
+ writer.Write(native.Length);
110
+ writer.Write(native);
111
+ writer.Write(audit.Length);
112
+ writer.Write(audit);
113
+ writer.Write(attributes);
114
+ writer.Flush();
115
+ var scope = complete ? "F:" : "P:";
116
+ return new SecurityState
117
+ {
118
+ Descriptor = descriptor,
119
+ Fingerprint = scope + Convert.ToBase64String(hash.ComputeHash(buffer.ToArray())),
120
+ };
121
+ }
122
+ }
123
+
124
+ private byte[] ReadAudit(string path)
125
+ {
126
+ return File.GetAccessControl(path, AccessControlSections.Audit).GetSecurityDescriptorBinaryForm();
127
+ }
128
+
129
+ private int CopySecurity(string sourcePath)
130
+ {
131
+ var expectedFingerprint = this.RequiredEnvironment("MCP_POOL_SECURITY");
132
+ var source = this.ReadSecurity(sourcePath);
133
+ if (!string.Equals(source.Fingerprint, expectedFingerprint, StringComparison.Ordinal))
134
+ {
135
+ return 3;
136
+ }
137
+
138
+ var paths = new[]
139
+ {
140
+ this.RequiredEnvironment("MCP_POOL_TEMP"),
141
+ this.RequiredEnvironment("MCP_POOL_BACKUP"),
142
+ };
143
+ foreach (var path in paths)
144
+ {
145
+ var actual = File.GetAccessControl(path);
146
+ if (!string.Equals(actual.GetSecurityDescriptorSddlForm(this.sections),
147
+ source.Descriptor, StringComparison.Ordinal))
148
+ {
149
+ var copy = new FileSecurity();
150
+ copy.SetSecurityDescriptorSddlForm(source.Descriptor, this.sections);
151
+ File.SetAccessControl(path, copy);
152
+ actual = File.GetAccessControl(path);
153
+ }
154
+
155
+ if (!string.Equals(actual.GetSecurityDescriptorSddlForm(this.sections),
156
+ source.Descriptor, StringComparison.Ordinal))
157
+ {
158
+ throw new InvalidOperationException();
159
+ }
160
+
161
+ if (!string.Equals(this.ReadSecurity(path).Fingerprint,
162
+ expectedFingerprint, StringComparison.Ordinal))
163
+ {
164
+ throw new InvalidOperationException();
165
+ }
166
+ }
167
+
168
+ return string.Equals(this.ReadSecurity(sourcePath).Fingerprint,
169
+ expectedFingerprint, StringComparison.Ordinal) ? 0 : 3;
170
+ }
171
+
172
+ private sealed class SecurityState
173
+ {
174
+ public string Descriptor { get; set; }
175
+
176
+ public string Fingerprint { get; set; }
177
+ }
178
+ }
@@ -0,0 +1,50 @@
1
+ using System;
2
+ using System.ComponentModel;
3
+ using System.Runtime.InteropServices;
4
+ using System.Security.AccessControl;
5
+
6
+ public sealed class PoolingSecurityReader
7
+ {
8
+ private const uint ReadControlSections = 0x1f7;
9
+
10
+ /// <summary>Reads native owner, DACL and non-audit security sections.</summary>
11
+ /// <param name="path">File whose descriptor is queried.</param>
12
+ /// <returns>The unfiltered sections in canonical binary form.</returns>
13
+ public byte[] ReadControlDescriptor(string path)
14
+ {
15
+ var descriptor = IntPtr.Zero;
16
+ try
17
+ {
18
+ uint status = GetNamedSecurityInfo(path, 1, ReadControlSections,
19
+ IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero, out descriptor);
20
+ if (status != 0)
21
+ {
22
+ throw new Win32Exception((int)status);
23
+ }
24
+
25
+ var native = new byte[GetSecurityDescriptorLength(descriptor)];
26
+ Marshal.Copy(descriptor, native, 0, native.Length);
27
+ var raw = new RawSecurityDescriptor(native, 0);
28
+ var canonical = new byte[raw.BinaryLength];
29
+ raw.GetBinaryForm(canonical, 0);
30
+ return canonical;
31
+ }
32
+ finally
33
+ {
34
+ if (descriptor != IntPtr.Zero)
35
+ {
36
+ LocalFree(descriptor);
37
+ }
38
+ }
39
+ }
40
+
41
+ [DllImport("advapi32.dll", EntryPoint = "GetNamedSecurityInfoW", CharSet = CharSet.Unicode)]
42
+ private static extern uint GetNamedSecurityInfo(string path, uint objectType, uint sections,
43
+ IntPtr owner, IntPtr group, IntPtr dacl, IntPtr sacl, out IntPtr descriptor);
44
+
45
+ [DllImport("advapi32.dll")]
46
+ private static extern uint GetSecurityDescriptorLength(IntPtr descriptor);
47
+
48
+ [DllImport("kernel32.dll")]
49
+ private static extern IntPtr LocalFree(IntPtr memory);
50
+ }
@@ -0,0 +1,181 @@
1
+ # Shared stdio sessions
2
+
3
+ Status: 1.3.0 implementation contract; not a claim that every server is compatible.
4
+
5
+ ## Configuration
6
+
7
+ Add `"sharing": "shared"` to a stdio definition only after confirming the profile below.
8
+ Use `/<name>/mcp`: classic SSE endpoints explicitly reject shared mode.
9
+ Pooling buttons never enable shared mode or replace an existing shared policy.
10
+
11
+ | Setting | Default | Purpose |
12
+ |---|---|---|
13
+ | `sharedLingerMs` | 1800000 | Keep the initialized child between compatible sessions. |
14
+ | `sharedDrainTimeoutMs` | 30000 | Maximum drain time before terminating unresolved work. |
15
+ | `sharedRetryDelayMs` | 1000 | Backoff after a failed generation. |
16
+ | `sharedMaxSessions` | 128 | Virtual members plus joining clients per group. |
17
+ | `sharedMaxInFlight` | 8 | Dispatched operations per child. |
18
+ | `sharedMaxQueued` | 32 | Unsent operations waiting for dispatch. |
19
+ | `sharedMaxIds` | 4096 | Retained request IDs per virtual session. |
20
+ | `sharedMaxCursors` | 256 | Retained tools-list cursors per virtual session. |
21
+ | `sharedMaxLineBytes` | 1048576 | Maximum JSON-line size, including translated fields. |
22
+ | `sharedMaxBufferBytes` | 4194304 | Maximum buffered protocol data per stream. |
23
+
24
+ All settings require bounded integers; invalid values fail explicitly. The bridge's existing
25
+ session cap and request/initialization timeouts also apply. A slot is reserved before waiting
26
+ for shared initialization or the spawn gate. Concurrent resumes of one external session ID
27
+ share one handshake.
28
+ Client and upstream JSON have a maximum depth of 64, counting the root as depth 0.
29
+ An upstream depth violation terminates its generation with explicit failure metadata,
30
+ not an uncaught serializer exception; no business call is replayed.
31
+
32
+ ## Purpose and approval
33
+
34
+ `sharing: "shared"` reuses one initialized stdio child across compatible virtual HTTP sessions.
35
+ This differs from `pool`: a pooled child is uninitialized and belongs exclusively to its first
36
+ session. Both modes remain opt-in. No latency recommendation enables either mode automatically.
37
+
38
+ Shared mode is for a stateless-tools profile: tools operate on explicit inputs, the configured
39
+ credential context is common, and the server has no implicit per-client workspace, conversation,
40
+ account or subscription state. Enabling the mode asserts that profile. Matching capabilities
41
+ alone does not prove the implementation is stateless.
42
+
43
+ The default remains `isolated`. Unsupported clients receive a clear compatibility error before
44
+ their business request is dispatched. There is no silent sharing downgrade, guessed callback
45
+ recipient, or automatic replay on another process.
46
+
47
+ ## Compatibility and initialization
48
+
49
+ One group per configured server and configuration generation is allowed. Its compatibility key
50
+ contains the complete, canonically ordered initialization parameters and configuration. Preserve
51
+ array order, absent properties and all client identity fields. The JSON-RPC request ID is not
52
+ part of the initialization parameters.
53
+
54
+ For the first profile:
55
+
56
+ - Support released protocol versions `2025-03-26`, `2025-06-18` and `2025-11-25`.
57
+ - Require empty client capabilities. Roots, sampling, elicitation, tasks and unknown extensions
58
+ cannot safely be routed from an arbitrary stdio callback to an individual client.
59
+ - Support `tools/list`, `tools/call` and `ping`.
60
+ - Reject stateful protocol methods, subscriptions, logging changes and task-augmented calls.
61
+ - Do not interpret arbitrary tool argument/result fields as request IDs or state handles.
62
+ - Bind tools-list cursors to their originating virtual session and child generation.
63
+ - Retain the actual upstream protocol version, instructions and implementation metadata.
64
+ The upstream may negotiate any of the three supported versions.
65
+ - Expose only the upstream's actual `tools` capability, without inventing flags. This
66
+ explicitly tools-only surface does not advertise optional prompts, resources, logging,
67
+ completions or tasks even if the upstream supports them. An empty `experimental` object
68
+ is harmless; unknown or nonempty experimental extensions are incompatible.
69
+
70
+ Concurrent compatible arrivals share a creation promise installed before awaiting process
71
+ creation. Exactly one upstream `initialize` and one `notifications/initialized` are sent.
72
+ Each virtual session receives the result with its own request ID and must complete its own
73
+ initialized notification before dispatching tools.
74
+
75
+ An incompatible client does not replace or disturb the established group. Failed initialization
76
+ fails all waiters, removes the failed creation promise, and terminates the candidate. A later
77
+ attempt may start a new group with bounded retry behavior.
78
+
79
+ ## Routing
80
+
81
+ Use distinct typed keys: integer `1` and string `"1"` are different client IDs.
82
+
83
+ | Message | Required behavior |
84
+ |---|---|
85
+ | Client request | Translate to a generation-unique upstream ID and retain owner plus original ID. |
86
+ | Child response | Deliver only to its mapped owner, restoring the original ID. |
87
+ | Client progress token | Translate only `_meta.progressToken`; restore it on the owner's progress notifications. |
88
+ | Client cancellation | Translate only that session's request ID. Never cancel another client's request. |
89
+ | Child `ping` | Answer as the bridge peer. It is not a response even if its ID matches an operation. |
90
+ | Other child requests | Reject explicitly; never select the first/recent client or broadcast callbacks. |
91
+ | Tools-list changes | Fan out to members only when the common catalog contract permits it. |
92
+ | Other notifications | Drop unowned logs and unexposed catalog changes with diagnostics. Never broadcast their data; reject unknown extensions. |
93
+
94
+ Classify messages by category (`method`, `result`, `error`) before consulting ID maps. Unknown
95
+ or late responses must not become notifications. Progress associated with a POST must be
96
+ delivered on its originating POST stream; an unrelated GET stream is not a replacement.
97
+
98
+ Bound operations, queued work, input lines, retained IDs/cursors and stream buffers. Reject
99
+ overflow explicitly instead of allocating without limit or silently truncating protocol data.
100
+
101
+ ## Virtual child adapter
102
+
103
+ `bin/shared-sessions.mjs` exports `SharedSessionManager`. It takes:
104
+
105
+ ```js
106
+ {
107
+ spawn: async (name, definition) => actualChild,
108
+ kill: (actualChild) => {},
109
+ onFailure: (name, detail) => {},
110
+ onInitialized: (name, elapsedMs) => {},
111
+ onResponse: (name) => {},
112
+ log: (name, text) => {},
113
+ initTimeoutMs,
114
+ requestTimeoutMs
115
+ }
116
+ ```
117
+
118
+ `await manager.acquire(name, definition, initializeParams)` returns an EventEmitter-compatible
119
+ virtual child with `stdin` accepting newline-delimited JSON-RPC, readable `stdout`, the actual
120
+ group `pid`, `exitCode`, `signalCode`, `__spawnedAt`, `__sharedSession: true`, and `detach()`.
121
+ It plugs into the existing Streamable HTTP per-session response parser without spawning again.
122
+ One actual child owns one stdout parser; virtual stdout contains only that session's messages.
123
+ For already-parsed HTTP JSON, use `child.writeMessage(message)`. It applies the same input,
124
+ depth, ID and ownership guards before serialization. Calling it after detachment throws an
125
+ explicit `SHARED_DETACHED` admission error instead of leaving a request waiting indefinitely.
126
+
127
+ The bridge's process-kill helper must detect a virtual child and detach it instead of killing
128
+ its shared PID. Actual group creation goes through the existing spawn gate and B21 counter.
129
+ Virtual session attachment is not an OS spawn or a cold-initialization sample.
130
+ `onResponse` reports actual upstream responses, not locally generated rejections. Health must
131
+ not classify errors by a numeric JSON-RPC code that an upstream can also legitimately use.
132
+
133
+ Provide `recycle(name)`, `remove(name)`, `shutdown()` and `inspect(name)` methods. Inspection
134
+ exposes only lifecycle facts such as generation, state, members and unresolved operations.
135
+
136
+ ## Lifetime, drain and credentials
137
+
138
+ Deleting one session forgets and tombstones only that session. Remove its unsent work and send
139
+ owner-scoped cancellation for dispatched work. Other clients and their operations remain alive.
140
+ The actual child lingers between compatible sessions, bounded by `sharedLingerMs` (default
141
+ 30 minutes). Unresolved work prevents idle eviction.
142
+
143
+ Cancellation is not proof that execution stopped. Retain execution ownership until the
144
+ response arrives or a bounded failure/drain policy terminates the generation.
145
+
146
+ Recycling marks the generation draining and stops new dispatch to it. Existing work gets a
147
+ bounded opportunity to finish; once drained, terminate promptly rather than waiting for the
148
+ next full recycle interval. A drain deadline fails unresolved work explicitly as potentially
149
+ executed, terminates that generation, and permits a later fresh generation. Never replay those
150
+ business calls. Fence old exit handlers and timers so they cannot delete a replacement.
151
+
152
+ The server retains its existing credential mechanism. The bridge does not freeze a token in
153
+ the shared initialization, infer renewal from a generic cache, or substitute an auth provider.
154
+ Keep configured credential recycling. A synthetic short-expiry test must show: expired call
155
+ fails unchanged; draining/replacement creates a fresh generation; a new call succeeds; the
156
+ failed call was dispatched once. Real provider renewal still requires its own canary.
157
+
158
+ Resume reattaches only compatible stateless sessions. It is not restoration of arbitrary
159
+ application state and does not preserve TCP connections across a bridge restart; that is B14.
160
+
161
+ ## Acceptance gates
162
+
163
+ 1. More than fifty compatible initialize/DELETE cycles reuse one actual child and one upstream
164
+ initialization. Two simultaneously active sessions must also share that same generation.
165
+ 2. Concurrent numeric/string ID collisions, out-of-order results, progress and cancellation
166
+ route to the correct owner. Forged or late messages cannot cross sessions.
167
+ 3. Delete one client while another works, kill a child during a request, and recycle while busy.
168
+ Pending callers fail promptly and dispatched tool calls are never replayed.
169
+ 4. Incompatible initialization and unsupported callbacks/methods fail explicitly. Isolation
170
+ defaults and existing HTTP proxy behavior remain unchanged.
171
+ 5. Counter deltas for identical isolated and shared workloads show actual reduced launches,
172
+ not merely fewer latency samples or a lower instant process count.
173
+ 6. Use an isolated config directory and spare port for real-server and browser/CLI canaries.
174
+ Do not alter another project's client or orchestration configuration.
175
+
176
+ Reference: released MCP
177
+ [lifecycle](https://modelcontextprotocol.io/specification/2025-11-25/basic/lifecycle),
178
+ [transports](https://modelcontextprotocol.io/specification/2025-11-25/basic/transports),
179
+ [progress](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/progress), and
180
+ [cancellation](https://modelcontextprotocol.io/specification/2025-11-25/basic/utilities/cancellation).
181
+ Draft transport changes are not applied to clients speaking released versions.
@@ -0,0 +1,31 @@
1
+ {
2
+ "filesystem": {
3
+ "command": "npx",
4
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "."]
5
+ },
6
+ "github": {
7
+ "command": "npx",
8
+ "args": ["-y", "@modelcontextprotocol/server-github"],
9
+ "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "REPLACE_WITH_YOUR_PAT" }
10
+ },
11
+ "example-http-static-token": {
12
+ "type": "http",
13
+ "url": "https://example.com/mcp",
14
+ "auth": { "type": "env", "var": "EXAMPLE_TOKEN" }
15
+ },
16
+ "example-http-cli-token": {
17
+ "type": "http",
18
+ "url": "https://your-service.example.com/mcp",
19
+ "auth": {
20
+ "type": "command",
21
+ "command": "az account get-access-token --resource api://your-resource-id --query accessToken -o tsv",
22
+ "refreshMinutes": 50
23
+ },
24
+ "headers": { "x-example-header": "value" }
25
+ },
26
+ "example-http-no-auth": {
27
+ "type": "http",
28
+ "url": "https://open.example.com/mcp",
29
+ "auth": { "type": "none" }
30
+ }
31
+ }
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "mcp-pacemaker",
3
+ "version": "2.0.1",
4
+ "description": "Keeps your MCP servers alive across editor restarts, reboots, and token expiry — pluggable auth, cross-platform auto-start, one-command setup. Zero-dependency bridge; a small CLI for setup.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-pacemaker": "bin/cli.mjs",
8
+ "mcp-bridge": "bin/mcp-bridge.mjs"
9
+ },
10
+ "files": [
11
+ "bin/",
12
+ "autostart/",
13
+ "supervisor/",
14
+ "tools/windows-security-helper/",
15
+ "examples/",
16
+ "ui/dist/",
17
+ "README.md",
18
+ "CHANGELOG.md",
19
+ "SECURITY.md",
20
+ "docs/shared-sessions.md",
21
+ "LICENSE",
22
+ "THIRD_PARTY_NOTICES.txt",
23
+ "CONTRIBUTING.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=20"
27
+ },
28
+ "license": "MIT",
29
+ "author": "Girish Konda",
30
+ "homepage": "https://github.com/girishkvs/mcp-pacemaker#readme",
31
+ "bugs": {
32
+ "url": "https://github.com/girishkvs/mcp-pacemaker/issues"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/girishkvs/mcp-pacemaker.git"
37
+ },
38
+ "keywords": [
39
+ "mcp",
40
+ "model-context-protocol",
41
+ "sse",
42
+ "bridge",
43
+ "proxy",
44
+ "vscode",
45
+ "cursor",
46
+ "claude-desktop",
47
+ "supervisor",
48
+ "keep-alive"
49
+ ],
50
+ "scripts": {
51
+ "bridge": "node bin/mcp-bridge.mjs",
52
+ "cli": "node bin/cli.mjs",
53
+ "consumer:check": "node tools/npm-consumer/check.mjs",
54
+ "compat:prepare": "node tools/compatibility/prepare.mjs",
55
+ "compat:prepare:baseline": "node tools/compatibility/prepare.mjs --historical",
56
+ "compat:clean": "node tools/compatibility/prepare.mjs --clean",
57
+ "test:compat": "node --test --test-concurrency=1 test/compat/gates.test.mjs test/compat/cli-api.test.mjs",
58
+ "test:compat:browser": "node --test --test-concurrency=1 test/compat/browser.test.mjs",
59
+ "test": "node --test test/bridge.test.mjs test/bridge-controls.test.mjs test/kill-tree.test.mjs test/keepalive.test.mjs test/auth-token.test.mjs test/cold-start.test.mjs test/crash-report.test.mjs test/resume.test.mjs test/config-checks.test.mjs test/config-reload.test.mjs test/health.test.mjs test/cli.test.mjs test/logs.test.mjs test/prewarm.test.mjs test/pooling-batch-scheduler.test.mjs test/pooling-batch-api.test.mjs test/pooling-batch-store.test.mjs test/pooling-batches.test.mjs test/pooling-stage-publication.test.mjs test/pooling-batch-startup.test.mjs test/pooling-postcommit-recovery.test.mjs test/pooling-config.test.mjs test/pooling-writer.test.mjs test/pooling-api.test.mjs test/pooling-deadlines.test.mjs test/pooling-queue-budget.test.mjs test/windows-security-helper.test.mjs test/windows-batch-security.test.mjs test/tui.test.mjs test/warm-pool.test.mjs test/server-metrics.test.mjs test/admission.test.mjs test/shared-sessions.test.mjs test/shared-http.test.mjs test/cross-host.test.mjs test/suite-integrity.test.mjs test/consumer-package.test.mjs test/notices.test.mjs test/notices-runtime.test.mjs test/npm-publication.test.mjs test/npm-publication-proof.test.mjs test/update-check.test.mjs test/service-lifecycle.test.mjs test/service-supervisor.test.mjs test/npm-publication-gates.test.mjs test/npm-publication-matrix.test.mjs test/publication-scanners-advisories.test.mjs test/publication-scanners.test.mjs test/service-replacement.test.mjs test/consumer-environment.test.mjs test/npm-publication-external.test.mjs test/npm-publication-peer.test.mjs test/publication-scanners-diagnostics.test.mjs test/publication-scanners-publication.test.mjs test/publication-scanners-paths.test.mjs test/npm-publication-local-enforcement.test.mjs test/publication-scanners-admission.test.mjs",
60
+ "notices:check": "node tools/third-party-notices/check.mjs",
61
+ "publication:source-gates": "node tools/npm-publication/source-gates.mjs",
62
+ "publication:artifact-gates": "node tools/npm-publication/artifact-gates.mjs",
63
+ "service:replacement:check": "node tools/service-replacement/check.mjs",
64
+ "publication:external-gates": "node tools/npm-publication/external-gates.mjs",
65
+ "publication:local-gate": "node tools/npm-publication/local-windows.mjs",
66
+ "publication:verify-local": "node tools/npm-publication/verify-local.mjs"
67
+ },
68
+ "dependencies": {
69
+ "@clack/prompts": "^1.7.0",
70
+ "commander": "^15.0.0",
71
+ "ink": "^7.1.1",
72
+ "picocolors": "^1.1.1",
73
+ "react": "^19.2.7",
74
+ "semver": "^7.7.3",
75
+ "smol-toml": "^1.8.0"
76
+ }
77
+ }
@@ -0,0 +1,14 @@
1
+ // IPC reaches the bridge's existing graceful shutdown handler on Windows too.
2
+ let loaded = false;
3
+ let stopRequested = false;
4
+ function shutdown() {
5
+ stopRequested = true;
6
+ if (loaded) process.emit('SIGTERM');
7
+ }
8
+ process.on('message', (message) => {
9
+ if (message === 'stop') shutdown();
10
+ });
11
+ process.on('disconnect', shutdown);
12
+ await import('../bin/mcp-bridge.mjs');
13
+ loaded = true;
14
+ if (stopRequested) shutdown();