@intutic/mcp-governance-proxy 1.0.3

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 (87) hide show
  1. package/.turbo/turbo-build.log +1 -0
  2. package/.turbo/turbo-test.log +51 -0
  3. package/.turbo/turbo-typecheck.log +1 -0
  4. package/LICENSE +21 -0
  5. package/README.md +87 -0
  6. package/dist/config.d.ts +40 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +100 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/daemon/healthMonitor.d.ts +18 -0
  11. package/dist/daemon/healthMonitor.d.ts.map +1 -0
  12. package/dist/daemon/healthMonitor.js +185 -0
  13. package/dist/daemon/healthMonitor.js.map +1 -0
  14. package/dist/daemon/index.d.ts +2 -0
  15. package/dist/daemon/index.d.ts.map +1 -0
  16. package/dist/daemon/index.js +77 -0
  17. package/dist/daemon/index.js.map +1 -0
  18. package/dist/daemon/policyCache.d.ts +20 -0
  19. package/dist/daemon/policyCache.d.ts.map +1 -0
  20. package/dist/daemon/policyCache.js +124 -0
  21. package/dist/daemon/policyCache.js.map +1 -0
  22. package/dist/daemon/socketServer.d.ts +13 -0
  23. package/dist/daemon/socketServer.d.ts.map +1 -0
  24. package/dist/daemon/socketServer.js +90 -0
  25. package/dist/daemon/socketServer.js.map +1 -0
  26. package/dist/daemon/telemetryBatcher.d.ts +15 -0
  27. package/dist/daemon/telemetryBatcher.d.ts.map +1 -0
  28. package/dist/daemon/telemetryBatcher.js +143 -0
  29. package/dist/daemon/telemetryBatcher.js.map +1 -0
  30. package/dist/daemonClient.d.ts +5 -0
  31. package/dist/daemonClient.d.ts.map +1 -0
  32. package/dist/daemonClient.js +67 -0
  33. package/dist/daemonClient.js.map +1 -0
  34. package/dist/dlp.d.ts +29 -0
  35. package/dist/dlp.d.ts.map +1 -0
  36. package/dist/dlp.js +58 -0
  37. package/dist/dlp.js.map +1 -0
  38. package/dist/emitter.d.ts +34 -0
  39. package/dist/emitter.d.ts.map +1 -0
  40. package/dist/emitter.js +121 -0
  41. package/dist/emitter.js.map +1 -0
  42. package/dist/index.d.ts +24 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +50 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/interceptor.d.ts +38 -0
  47. package/dist/interceptor.d.ts.map +1 -0
  48. package/dist/interceptor.js +93 -0
  49. package/dist/interceptor.js.map +1 -0
  50. package/dist/policy.d.ts +41 -0
  51. package/dist/policy.d.ts.map +1 -0
  52. package/dist/policy.js +130 -0
  53. package/dist/policy.js.map +1 -0
  54. package/dist/proxy.d.ts +43 -0
  55. package/dist/proxy.d.ts.map +1 -0
  56. package/dist/proxy.js +238 -0
  57. package/dist/proxy.js.map +1 -0
  58. package/dist/stderrLog.d.ts +32 -0
  59. package/dist/stderrLog.d.ts.map +1 -0
  60. package/dist/stderrLog.js +45 -0
  61. package/dist/stderrLog.js.map +1 -0
  62. package/dummy-path.jsonl +2 -0
  63. package/package.json +32 -0
  64. package/src/__tests__/config.test.ts +82 -0
  65. package/src/__tests__/dlp.test.ts +103 -0
  66. package/src/__tests__/integration/daemon-shim.test.ts +125 -0
  67. package/src/__tests__/interceptor.test.ts +229 -0
  68. package/src/__tests__/unit/policyCache.test.ts +95 -0
  69. package/src/__tests__/unit/socketServer.test.ts +220 -0
  70. package/src/__tests__/unit/telemetryBatcher.test.ts +78 -0
  71. package/src/config.ts +145 -0
  72. package/src/daemon/healthMonitor.ts +210 -0
  73. package/src/daemon/index.ts +76 -0
  74. package/src/daemon/policyCache.ts +143 -0
  75. package/src/daemon/socketServer.ts +111 -0
  76. package/src/daemon/telemetryBatcher.ts +147 -0
  77. package/src/daemonClient.ts +71 -0
  78. package/src/dlp.ts +72 -0
  79. package/src/emitter.ts +144 -0
  80. package/src/index.ts +53 -0
  81. package/src/interceptor.ts +103 -0
  82. package/src/policy.ts +152 -0
  83. package/src/proxy.ts +327 -0
  84. package/src/stderrLog.ts +56 -0
  85. package/tsconfig.json +10 -0
  86. package/tsconfig.tsbuildinfo +1 -0
  87. package/vitest.config.ts +9 -0
@@ -0,0 +1 @@
1
+ $ tsc
@@ -0,0 +1,51 @@
1
+ $ vitest run
2
+
3
+ RUN v3.2.6 /Users/ishangupta/intutic-enterprise/packages/mcp-proxy
4
+
5
+ ✓ src/__tests__/dlp.test.ts (14 tests) 9ms
6
+ stderr | src/__tests__/config.test.ts > loadConfig > loads variables from runtime.env and parses CLI arguments
7
+ [mcp-proxy] INTUTIC_MCP_PROXY_MODE=daemon is stored but not yet active — running in per-session mode (Phase 4). Will be activated in Phase 5.
8
+
9
+ ✓ src/__tests__/config.test.ts (2 tests) 20ms
10
+ {"level":"info","time":"2026-07-22T18:22:42.382Z","pid":56861,"hostname":"Ishans-MacBook-Pro-9.local","name":"mcp-proxy.policyCache","workspaceId":"ws_test_cache_miss","msg":"policy_cache.invalidated"}
11
+ {"level":"info","time":"2026-07-22T18:22:42.419Z","pid":56861,"hostname":"Ishans-MacBook-Pro-9.local","name":"mcp-proxy.policyCache","workspaceId":"ws_test_cache_miss","msg":"policy_cache.invalidated"}
12
+ ✓ src/__tests__/unit/policyCache.test.ts (4 tests) 210ms
13
+ {"level":"debug","time":"2026-07-22T18:22:42.429Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Bash","msg":"Evaluating tool call"}
14
+ {"level":"warn","time":"2026-07-22T18:22:42.429Z","name":"mcp-proxy-interceptor","action":"dlp_block","toolName":"Bash","findings":[{"pattern":"sk-[A-Za-z0-9]{20,}","description":"OpenAI API key pattern"}],"msg":"DLP block"}
15
+ {"level":"debug","time":"2026-07-22T18:22:42.430Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Write","msg":"Evaluating tool call"}
16
+ {"level":"warn","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"dlp_block","toolName":"Write","findings":[{"pattern":"sk-ant-[A-Za-z0-9\\-_]{20,}","description":"Anthropic API key pattern"}],"msg":"DLP block"}
17
+ {"level":"debug","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Bash","msg":"Evaluating tool call"}
18
+ {"level":"warn","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"dlp_block","toolName":"Bash","findings":[{"pattern":"rm\\s+-rf?\\s+\\/","description":"Destructive rm -rf / command"}],"msg":"DLP block"}
19
+ {"level":"debug","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"mcp__database__execute","msg":"Evaluating tool call"}
20
+ {"level":"warn","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"dlp_block","toolName":"mcp__database__execute","findings":[{"pattern":"DROP\\s+TABLE","description":"SQL DROP TABLE statement"}],"msg":"DLP block"}
21
+ {"level":"debug","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Read","msg":"Evaluating tool call"}
22
+ {"level":"debug","time":"2026-07-22T18:22:42.431Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Bash","msg":"Evaluating tool call"}
23
+ {"level":"warn","time":"2026-07-22T18:22:42.432Z","name":"mcp-proxy-interceptor","action":"policy_block","toolName":"Bash","ruleId":"rule-1","reason":"Bash execution not permitted in this workspace","msg":"Policy block"}
24
+ {"level":"debug","time":"2026-07-22T18:22:42.433Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Read","msg":"Evaluating tool call"}
25
+ {"level":"debug","time":"2026-07-22T18:22:42.433Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"mcp__postgres__query","msg":"Evaluating tool call"}
26
+ {"level":"warn","time":"2026-07-22T18:22:42.433Z","name":"mcp-proxy-interceptor","action":"policy_block","toolName":"mcp__postgres__query","ruleId":"rule-2","reason":"Production database access blocked","msg":"Policy block"}
27
+ {"level":"debug","time":"2026-07-22T18:22:42.433Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"mcp__postgres__query","msg":"Evaluating tool call"}
28
+ {"level":"debug","time":"2026-07-22T18:22:42.436Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Write","msg":"Evaluating tool call"}
29
+ {"level":"warn","time":"2026-07-22T18:22:42.438Z","name":"mcp-proxy-interceptor","action":"policy_approval_required","toolName":"Write","ruleId":"rule-3","msg":"Tool requires human approval per SOP rule rule-3: File writes require review"}
30
+ {"level":"debug","time":"2026-07-22T18:22:42.439Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Read","msg":"Evaluating tool call"}
31
+ {"level":"error","time":"2026-07-22T18:22:42.439Z","name":"mcp-proxy-interceptor","action":"policy_error","err":"Policy engine down","msg":"Policy evaluation error"}
32
+ {"level":"debug","time":"2026-07-22T18:22:42.440Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Read","msg":"Evaluating tool call"}
33
+ {"level":"error","time":"2026-07-22T18:22:42.440Z","name":"mcp-proxy-interceptor","action":"policy_error","err":"Policy engine down","msg":"Policy evaluation error"}
34
+ {"level":"debug","time":"2026-07-22T18:22:42.440Z","name":"mcp-proxy-interceptor","action":"interceptor_decide","toolName":"Read","msg":"Evaluating tool call"}
35
+ {"level":"error","time":"2026-07-22T18:22:42.446Z","name":"mcp-proxy-interceptor","action":"dlp_error","err":"Converting circular structure to JSON\n --> starting at object with constructor 'Object'\n --- property 'self' closes the circle","msg":"DLP scan error — skipping"}
36
+ ✓ src/__tests__/interceptor.test.ts (13 tests) 19ms
37
+ {"level":"info","time":"2026-07-22T18:22:42.562Z","pid":56863,"hostname":"Ishans-MacBook-Pro-9.local","name":"mcp-proxy.policyCache","workspaceId":"ws_test","msg":"policy_cache.invalidated"}
38
+ {"level":"info","time":"2026-07-22T18:22:42.570Z","name":"mcp-proxy-policy","action":"policy_refreshed_from_daemon","ruleCount":1,"msg":"SOP rules refreshed from daemon"}
39
+ ✓ src/__tests__/unit/socketServer.test.ts (5 tests) 144ms
40
+ {"level":"debug","time":"2026-07-22T18:22:42.571Z","name":"mcp-proxy-emitter","action":"telemetry_enqueued","msg":"Telemetry successfully enqueued to daemon"}
41
+ {"level":"warn","time":"2026-07-22T18:22:42.572Z","name":"mcp-proxy-policy","action":"policy_daemon_failed","err":"connect ENOENT /tmp/nonexistent-1784744562571.sock","msg":"Failed to refresh policy from daemon socket — falling back to HTTP"}
42
+ {"level":"debug","time":"2026-07-22T18:22:42.572Z","name":"mcp-proxy-policy","action":"policy_refresh","url":"http://127.0.0.1:51982/api/v1/sop/rules?workspaceId=ws_fallback&active=true","msg":"Fetching SOP rules from control plane"}
43
+ {"level":"info","time":"2026-07-22T18:22:42.573Z","name":"mcp-proxy-policy","action":"policy_refreshed","ruleCount":1,"msg":"SOP rules refreshed"}
44
+ ✓ src/__tests__/integration/daemon-shim.test.ts (4 tests) 141ms
45
+ ✓ src/__tests__/unit/telemetryBatcher.test.ts (1 test) 446ms
46
+
47
+ Test Files 7 passed (7)
48
+ Tests 43 passed (43)
49
+ Start at 14:22:41
50
+ Duration 1.15s (transform 482ms, setup 0ms, collect 1.25s, tests 989ms, environment 1ms, prepare 1.18s)
51
+
@@ -0,0 +1 @@
1
+ $ tsc --noEmit
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Intutic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPORTED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,87 @@
1
+ # @intutic/mcp-governance-proxy
2
+
3
+ > Transparent stdio proxy that intercepts MCP `tools/call` JSON-RPC messages and applies workspace SOP policy before forwarding to real MCP servers.
4
+
5
+ ## Overview
6
+
7
+ The MCP Governance Proxy sits between an AI coding agent and its MCP tool servers. It intercepts every `tools/call` JSON-RPC message on stdin/stdout, evaluates the call against workspace governance policies, and either forwards, blocks, redacts, or flags for approval — all without requiring changes to the agent or the tool server.
8
+
9
+ ## Modes
10
+
11
+ ### Proxy Mode
12
+
13
+ Intercepts stdio between the agent and an existing MCP server process:
14
+
15
+ ```
16
+ Agent ↔ intutic-mcp-proxy ↔ Real MCP Server (stdio)
17
+ ```
18
+
19
+ The proxy spawns the real MCP server as a child process and relays JSON-RPC messages bidirectionally, applying policy checks on every `tools/call` request.
20
+
21
+ ### Standalone Mode
22
+
23
+ Runs as its own MCP server that exposes governance-aware tool wrappers:
24
+
25
+ ```
26
+ Agent ↔ intutic-mcp-daemon (MCP server)
27
+ ```
28
+
29
+ ## Features
30
+
31
+ ### DLP Scanning
32
+
33
+ 14 regex patterns detect and block sensitive data in tool arguments and responses:
34
+
35
+ - API keys and tokens (AWS, GCP, GitHub, Stripe, etc.)
36
+ - Secrets and passwords
37
+ - Private keys and certificates
38
+ - SQL injection patterns
39
+ - PII indicators
40
+
41
+ ### Policy Enforcement
42
+
43
+ - **SOP rule matching**: Maps tool names and argument patterns to workspace SOP rules
44
+ - **Enforcement actions**: `allow`, `block`, `redact`, `require_approval`
45
+ - **Audit logging**: Every intercepted call is logged with policy evaluation results
46
+
47
+ ## Binaries
48
+
49
+ | Binary | Description |
50
+ |--------|-------------|
51
+ | `intutic-mcp-proxy` | Stdio proxy — wraps an existing MCP server |
52
+ | `intutic-mcp-daemon` | Standalone MCP server with governance built in |
53
+
54
+ ## Installation
55
+
56
+ ```bash
57
+ npm install @intutic/mcp-governance-proxy
58
+ ```
59
+
60
+ ## Usage
61
+
62
+ ### Proxy Mode
63
+
64
+ ```json
65
+ {
66
+ "mcpServers": {
67
+ "filesystem": {
68
+ "command": "intutic-mcp-proxy",
69
+ "args": ["--", "npx", "@modelcontextprotocol/server-filesystem", "/path"]
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### Standalone Mode
76
+
77
+ ```bash
78
+ intutic-mcp-daemon --port 3100
79
+ ```
80
+
81
+ ## Part of Intutic
82
+
83
+ This package is part of the [Intutic](https://github.com/intutic/intutic) monorepo — an open-core AI governance control plane for developer teams.
84
+
85
+ ## License
86
+
87
+ MIT — see [LICENSE](../../LICENSE) for details.
@@ -0,0 +1,40 @@
1
+ /**
2
+ * config.ts — MCP Governance Proxy configuration.
3
+ *
4
+ * Reads from ~/.intutic/env/runtime.env (written by sync-daemon runtimeEnv.ts)
5
+ * and from CLI arguments passed after `--`.
6
+ *
7
+ * @module
8
+ */
9
+ export interface ProxyConfig {
10
+ /** Workspace ID for policy lookups and event attribution */
11
+ workspaceId: string;
12
+ /** Control plane base URL (e.g. http://localhost:3001) */
13
+ controlPlaneUrl: string;
14
+ /** API key for control plane requests */
15
+ apiKey: string;
16
+ /** Real MCP server command + args (everything after --). Empty = standalone mode. */
17
+ realServerCommand: string[];
18
+ /** Path to write hook-events JSONL (Path B) */
19
+ eventsFilePath: string;
20
+ /** Policy cache TTL in milliseconds (default: 60_000) */
21
+ policyTtlMs: number;
22
+ /** Whether to fail-open when control plane is unreachable (default: true) */
23
+ failOpen: boolean;
24
+ /**
25
+ * MCP proxy deployment model ('per-session' | 'daemon').
26
+ * WS-5 Q2 — stored and propagated but 'daemon' is not yet active in Phase 4.
27
+ */
28
+ mcpProxyMode: string;
29
+ /**
30
+ * Standalone mode — when true, the proxy acts as the Intutic MCP server
31
+ * directly (no downstream server to proxy). Used for the `intutic` harness
32
+ * entry that exposes Intutic governance tools to the harness.
33
+ */
34
+ standalone: boolean;
35
+ }
36
+ /**
37
+ * Load the proxy configuration from runtime.env + CLI args + environment.
38
+ */
39
+ export declare function loadConfig(argv?: string[]): Promise<ProxyConfig>;
40
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,WAAW;IAC1B,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAA;IACnB,0DAA0D;IAC1D,eAAe,EAAE,MAAM,CAAA;IACvB,yCAAyC;IACzC,MAAM,EAAE,MAAM,CAAA;IACd,qFAAqF;IACrF,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAA;IACtB,yDAAyD;IACzD,WAAW,EAAE,MAAM,CAAA;IACnB,6EAA6E;IAC7E,QAAQ,EAAE,OAAO,CAAA;IACjB;;;OAGG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAA;CACpB;AAqDD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CAiD7F"}
package/dist/config.js ADDED
@@ -0,0 +1,100 @@
1
+ /**
2
+ * config.ts — MCP Governance Proxy configuration.
3
+ *
4
+ * Reads from ~/.intutic/env/runtime.env (written by sync-daemon runtimeEnv.ts)
5
+ * and from CLI arguments passed after `--`.
6
+ *
7
+ * @module
8
+ */
9
+ import * as node_fs from 'node:fs/promises';
10
+ import * as node_os from 'node:os';
11
+ import * as node_path from 'node:path';
12
+ const DEFAULT_EVENTS_PATH = node_path.join(node_os.homedir(), '.intutic', 'events', 'hook-events.jsonl');
13
+ const RUNTIME_ENV_PATH = node_path.join(node_os.homedir(), '.intutic', 'env', 'runtime.env');
14
+ /**
15
+ * Parse a .env-format file into a key→value record.
16
+ * Lines starting with # are comments; blank lines are skipped.
17
+ */
18
+ async function parseEnvFile(filePath) {
19
+ const result = {};
20
+ try {
21
+ const content = await node_fs.readFile(filePath, 'utf-8');
22
+ for (const line of content.split('\n')) {
23
+ const trimmed = line.trim();
24
+ if (!trimmed || trimmed.startsWith('#'))
25
+ continue;
26
+ const eq = trimmed.indexOf('=');
27
+ if (eq === -1)
28
+ continue;
29
+ const key = trimmed.slice(0, eq).trim();
30
+ // Strip surrounding quotes from value
31
+ let value = trimmed.slice(eq + 1).trim();
32
+ if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
33
+ value = value.slice(1, -1);
34
+ }
35
+ result[key] = value;
36
+ }
37
+ }
38
+ catch {
39
+ // File may not exist — return empty record
40
+ }
41
+ return result;
42
+ }
43
+ /**
44
+ * Parse CLI arguments for --workspace-id and the real server command (after --).
45
+ */
46
+ function parseCliArgs(argv) {
47
+ let workspaceId;
48
+ let separatorIndex = -1;
49
+ for (let i = 0; i < argv.length; i++) {
50
+ if (argv[i] === '--workspace-id' && i + 1 < argv.length) {
51
+ workspaceId = argv[i + 1];
52
+ i++; // skip value
53
+ }
54
+ else if (argv[i] === '--') {
55
+ separatorIndex = i;
56
+ break;
57
+ }
58
+ }
59
+ const realServerCommand = separatorIndex !== -1 ? argv.slice(separatorIndex + 1) : [];
60
+ return { workspaceId, realServerCommand };
61
+ }
62
+ /**
63
+ * Load the proxy configuration from runtime.env + CLI args + environment.
64
+ */
65
+ export async function loadConfig(argv = process.argv.slice(2)) {
66
+ const runtimeEnv = await parseEnvFile(RUNTIME_ENV_PATH);
67
+ const cli = parseCliArgs(argv);
68
+ const controlPlaneUrl = process.env['INTUTIC_CONTROL_PLANE_URL'] ??
69
+ runtimeEnv['INTUTIC_CONTROL_PLANE_URL'] ??
70
+ 'http://localhost:3001';
71
+ const apiKey = process.env['INTUTIC_API_KEY'] ??
72
+ runtimeEnv['INTUTIC_API_KEY'] ??
73
+ '';
74
+ const workspaceId = cli.workspaceId ??
75
+ process.env['INTUTIC_WORKSPACE_ID'] ??
76
+ runtimeEnv['INTUTIC_WORKSPACE_ID'] ??
77
+ 'unknown';
78
+ const eventsFilePath = process.env['INTUTIC_EVENTS_FILE'] ??
79
+ runtimeEnv['INTUTIC_EVENTS_FILE'] ??
80
+ DEFAULT_EVENTS_PATH;
81
+ const failOpen = (runtimeEnv['INTUTIC_MCP_FAIL_OPEN'] ?? 'true').toLowerCase() !== 'false';
82
+ const mcpProxyMode = runtimeEnv['INTUTIC_MCP_PROXY_MODE'] ?? 'per-session';
83
+ if (mcpProxyMode === 'daemon') {
84
+ console.warn('[mcp-proxy] INTUTIC_MCP_PROXY_MODE=daemon is stored but not yet active — '
85
+ + 'running in per-session mode (Phase 4). Will be activated in Phase 5.');
86
+ }
87
+ const standalone = cli.realServerCommand.length === 0;
88
+ return {
89
+ workspaceId,
90
+ controlPlaneUrl,
91
+ apiKey,
92
+ realServerCommand: cli.realServerCommand,
93
+ eventsFilePath,
94
+ policyTtlMs: 60_000,
95
+ failOpen,
96
+ mcpProxyMode,
97
+ standalone,
98
+ };
99
+ }
100
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AA8BtC,MAAM,mBAAmB,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,QAAQ,EAAE,mBAAmB,CAAC,CAAA;AACxG,MAAM,gBAAgB,GAAG,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,aAAa,CAAC,CAAA;AAE5F;;;GAGG;AACH,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC1C,MAAM,MAAM,GAA2B,EAAE,CAAA;IACzC,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;QACzD,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACvC,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,CAAA;YAC3B,IAAI,CAAC,OAAO,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC;gBAAE,SAAQ;YACjD,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,CAAA;YAC/B,IAAI,EAAE,KAAK,CAAC,CAAC;gBAAE,SAAQ;YACvB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,EAAE,CAAA;YACvC,sCAAsC;YACtC,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,CAAA;YACxC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;gBACrG,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAA;YAC5B,CAAC;YACD,MAAM,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;QACrB,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,2CAA2C;IAC7C,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;AAED;;GAEG;AACH,SAAS,YAAY,CAAC,IAAc;IAClC,IAAI,WAA+B,CAAA;IACnC,IAAI,cAAc,GAAG,CAAC,CAAC,CAAA;IAEvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,gBAAgB,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACxD,WAAW,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YACzB,CAAC,EAAE,CAAA,CAAC,aAAa;QACnB,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YAC5B,cAAc,GAAG,CAAC,CAAA;YAClB,MAAK;QACP,CAAC;IACH,CAAC;IAED,MAAM,iBAAiB,GAAG,cAAc,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;IACrF,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,CAAA;AAC3C,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAAiB,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IACrE,MAAM,UAAU,GAAG,MAAM,YAAY,CAAC,gBAAgB,CAAC,CAAA;IACvD,MAAM,GAAG,GAAG,YAAY,CAAC,IAAI,CAAC,CAAA;IAE9B,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC;QACxC,UAAU,CAAC,2BAA2B,CAAC;QACvC,uBAAuB,CAAA;IAEzB,MAAM,MAAM,GACV,OAAO,CAAC,GAAG,CAAC,iBAAiB,CAAC;QAC9B,UAAU,CAAC,iBAAiB,CAAC;QAC7B,EAAE,CAAA;IAEJ,MAAM,WAAW,GACf,GAAG,CAAC,WAAW;QACf,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;QACnC,UAAU,CAAC,sBAAsB,CAAC;QAClC,SAAS,CAAA;IAEX,MAAM,cAAc,GAClB,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QAClC,UAAU,CAAC,qBAAqB,CAAC;QACjC,mBAAmB,CAAA;IAErB,MAAM,QAAQ,GACZ,CAAC,UAAU,CAAC,uBAAuB,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAA;IAE3E,MAAM,YAAY,GAAG,UAAU,CAAC,wBAAwB,CAAC,IAAI,aAAa,CAAA;IAC1E,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,CAAC,IAAI,CACV,2EAA2E;cACzE,sEAAsE,CACzE,CAAA;IACH,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,CAAA;IAErD,OAAO;QACL,WAAW;QACX,eAAe;QACf,MAAM;QACN,iBAAiB,EAAE,GAAG,CAAC,iBAAiB;QACxC,cAAc;QACd,WAAW,EAAE,MAAM;QACnB,QAAQ;QACR,YAAY;QACZ,UAAU;KACX,CAAA;AACH,CAAC"}
@@ -0,0 +1,18 @@
1
+ export interface McpServerConfig {
2
+ name: string;
3
+ url: string;
4
+ credentialExpiryAt?: Date;
5
+ }
6
+ export interface McpServerHealth {
7
+ serverName: string;
8
+ status: 'healthy' | 'degraded' | 'unreachable';
9
+ p95LatencyMs?: number;
10
+ errorRatePct?: number;
11
+ credentialExpiryAt?: string;
12
+ checkedAt: string;
13
+ }
14
+ export declare function registerServer(server: McpServerConfig): void;
15
+ export declare function startHealthMonitor(): void;
16
+ export declare function stopHealthMonitor(): void;
17
+ export declare function getHealthSnapshot(): McpServerHealth[];
18
+ //# sourceMappingURL=healthMonitor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthMonitor.d.ts","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"AA0BA,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAM,MAAM,CAAA;IAChB,GAAG,EAAO,MAAM,CAAA;IAChB,kBAAkB,CAAC,EAAE,IAAI,CAAA;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAW,MAAM,CAAA;IAC3B,MAAM,EAAe,SAAS,GAAG,UAAU,GAAG,aAAa,CAAA;IAC3D,YAAY,CAAC,EAAQ,MAAM,CAAA;IAC3B,YAAY,CAAC,EAAQ,MAAM,CAAA;IAC3B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,EAAY,MAAM,CAAA;CAC5B;AAuID,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,GAAG,IAAI,CAE5D;AAED,wBAAgB,kBAAkB,IAAI,IAAI,CAuBzC;AAED,wBAAgB,iBAAiB,IAAI,IAAI,CAExC;AAED,wBAAgB,iBAAiB,IAAI,eAAe,EAAE,CAErD"}
@@ -0,0 +1,185 @@
1
+ /**
2
+ * MCP Server Health Monitor
3
+ *
4
+ * Probes MCP servers every 30s and records health snapshots.
5
+ * Emits mcp_daemon.mcp_server_down when a server becomes unreachable.
6
+ *
7
+ * LLD #28: MCP Daemon Mode, WS-5MCP
8
+ * @module
9
+ */
10
+ import https from 'node:https';
11
+ import http from 'node:http';
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import os from 'node:os';
15
+ import { execSync } from 'node:child_process';
16
+ import { createLogger } from '@intutic/logger';
17
+ const logger = createLogger('mcp-proxy.healthMonitor');
18
+ const HEARTBEAT_MS = 30_000;
19
+ const PROBE_TIMEOUT = 5_000;
20
+ const CP_URL = process.env['CONTROL_PLANE_URL'] ?? 'http://localhost:3001';
21
+ const DAEMON_API_KEY = process.env['INTUTIC_API_KEY'] ?? '';
22
+ const WORKSPACE_ID = process.env['INTUTIC_WORKSPACE_ID'] ?? '';
23
+ const servers = [];
24
+ const latestHealth = new Map();
25
+ let timer = null;
26
+ function discoverServers() {
27
+ const discovered = [];
28
+ const homedir = os.homedir();
29
+ const configPaths = [
30
+ path.join(homedir, '.claude', 'mcp.json'),
31
+ process.platform === 'darwin'
32
+ ? path.join(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
33
+ : process.platform === 'win32'
34
+ ? path.join(process.env['APPDATA'] ?? '', 'Claude', 'claude_desktop_config.json')
35
+ : path.join(homedir, '.config', 'Claude', 'claude_desktop_config.json'),
36
+ process.platform === 'darwin'
37
+ ? path.join(homedir, 'Library', 'Application Support', 'Cursor', 'User', 'globalSettings.json')
38
+ : process.platform === 'win32'
39
+ ? path.join(process.env['APPDATA'] ?? '', 'Cursor', 'User', 'globalSettings.json')
40
+ : path.join(homedir, '.config', 'Cursor', 'User', 'globalSettings.json'),
41
+ ];
42
+ for (const configPath of configPaths) {
43
+ try {
44
+ if (!fs.existsSync(configPath))
45
+ continue;
46
+ const raw = fs.readFileSync(configPath, 'utf8');
47
+ const parsed = JSON.parse(raw);
48
+ const mcpServers = parsed.mcpServers ?? parsed.mcp ?? {};
49
+ for (const [name, entry] of Object.entries(mcpServers)) {
50
+ if (name === 'intutic')
51
+ continue; // Skip self
52
+ const serverEntry = entry;
53
+ let url = '';
54
+ if (serverEntry.url) {
55
+ url = serverEntry.url;
56
+ }
57
+ else if (serverEntry.command) {
58
+ url = `stdio://${serverEntry.command}`;
59
+ }
60
+ if (url && !discovered.some(s => s.name === name)) {
61
+ discovered.push({
62
+ name,
63
+ url,
64
+ credentialExpiryAt: undefined
65
+ });
66
+ }
67
+ }
68
+ }
69
+ catch { }
70
+ }
71
+ return discovered;
72
+ }
73
+ async function probeServer(server) {
74
+ const start = Date.now();
75
+ if (server.url.startsWith('stdio://')) {
76
+ const cmd = server.url.replace('stdio://', '');
77
+ let status = 'healthy';
78
+ try {
79
+ const whichCmd = process.platform === 'win32' ? `where ${cmd}` : `which ${cmd}`;
80
+ execSync(whichCmd, { stdio: 'ignore' });
81
+ }
82
+ catch {
83
+ status = 'unreachable';
84
+ }
85
+ return {
86
+ serverName: server.name,
87
+ status,
88
+ p95LatencyMs: Date.now() - start,
89
+ credentialExpiryAt: server.credentialExpiryAt?.toISOString(),
90
+ checkedAt: new Date().toISOString()
91
+ };
92
+ }
93
+ return new Promise((resolve) => {
94
+ try {
95
+ const url = new URL(server.url);
96
+ const isHttps = url.protocol === 'https:';
97
+ const lib = isHttps ? https : http;
98
+ const req = lib.request({ hostname: url.hostname, port: url.port, path: url.pathname + url.search, method: 'GET' }, (res) => {
99
+ res.resume();
100
+ const latency = Date.now() - start;
101
+ const status = res.statusCode && res.statusCode < 500 ? 'healthy' : 'degraded';
102
+ resolve({ serverName: server.name, status, p95LatencyMs: latency,
103
+ credentialExpiryAt: server.credentialExpiryAt?.toISOString(),
104
+ checkedAt: new Date().toISOString() });
105
+ });
106
+ req.on('error', () => resolve({ serverName: server.name, status: 'unreachable',
107
+ checkedAt: new Date().toISOString() }));
108
+ req.setTimeout(PROBE_TIMEOUT, () => {
109
+ req.destroy();
110
+ resolve({
111
+ serverName: server.name, status: 'unreachable', checkedAt: new Date().toISOString()
112
+ });
113
+ });
114
+ req.end();
115
+ }
116
+ catch {
117
+ resolve({ serverName: server.name, status: 'unreachable', checkedAt: new Date().toISOString() });
118
+ }
119
+ });
120
+ }
121
+ async function uploadSnapshots(snapshots) {
122
+ if (snapshots.length === 0 || !WORKSPACE_ID)
123
+ return;
124
+ const body = JSON.stringify({ mcpServers: snapshots });
125
+ const url = new URL('/api/v1/mcp-daemon/health-snapshot', CP_URL);
126
+ const isHttps = url.protocol === 'https:';
127
+ const lib = isHttps ? https : http;
128
+ return new Promise((resolve) => {
129
+ const req = lib.request({
130
+ hostname: url.hostname,
131
+ port: url.port,
132
+ path: url.pathname,
133
+ method: 'POST',
134
+ headers: {
135
+ 'Content-Type': 'application/json',
136
+ 'Content-Length': Buffer.byteLength(body),
137
+ 'Authorization': `Bearer ${DAEMON_API_KEY}`,
138
+ 'x-workspace-id': WORKSPACE_ID
139
+ }
140
+ }, (res) => {
141
+ res.resume();
142
+ resolve();
143
+ });
144
+ req.on('error', () => resolve());
145
+ req.setTimeout(5000, () => { req.destroy(); resolve(); });
146
+ req.write(body);
147
+ req.end();
148
+ });
149
+ }
150
+ export function registerServer(server) {
151
+ servers.push(server);
152
+ }
153
+ export function startHealthMonitor() {
154
+ // Run config discovery
155
+ const discovered = discoverServers();
156
+ for (const s of discovered) {
157
+ registerServer(s);
158
+ }
159
+ timer = setInterval(async () => {
160
+ const snapshots = [];
161
+ for (const server of servers) {
162
+ const health = await probeServer(server);
163
+ const prev = latestHealth.get(server.name);
164
+ latestHealth.set(server.name, health);
165
+ snapshots.push(health);
166
+ if (health.status === 'unreachable' && prev?.status !== 'unreachable') {
167
+ logger.warn({ serverName: server.name }, 'mcp_daemon.mcp_server_down');
168
+ }
169
+ }
170
+ if (snapshots.length > 0) {
171
+ await uploadSnapshots(snapshots);
172
+ }
173
+ }, HEARTBEAT_MS);
174
+ timer.unref();
175
+ }
176
+ export function stopHealthMonitor() {
177
+ if (timer) {
178
+ clearInterval(timer);
179
+ timer = null;
180
+ }
181
+ }
182
+ export function getHealthSnapshot() {
183
+ return Array.from(latestHealth.values());
184
+ }
185
+ //# sourceMappingURL=healthMonitor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"healthMonitor.js","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * intutic-mcp-daemon entry point
3
+ *
4
+ * Long-lived process listening on ~/.intutic/mcp-proxy.sock.
5
+ * Manages policy cache, telemetry batching, and MCP server health monitoring.
6
+ *
7
+ * LLD #28: MCP Daemon Mode, WS-5MCP
8
+ *
9
+ * Usage:
10
+ * node dist/daemon/index.js
11
+ * (managed by LaunchAgent / systemd via `intutic daemon mcp start`)
12
+ *
13
+ * @module
14
+ */
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import os from 'node:os';
18
+ import { createLogger } from '@intutic/logger';
19
+ import { startBatcher, stopBatcher } from './telemetryBatcher.js';
20
+ import { startHealthMonitor, stopHealthMonitor } from './healthMonitor.js';
21
+ import { createSocketServer, getSocketPath } from './socketServer.js';
22
+ const logger = createLogger('intutic-mcp-daemon');
23
+ const PID_FILE = path.join(os.homedir(), '.intutic', 'mcp-daemon.pid');
24
+ function ensureDir(dir) {
25
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
26
+ }
27
+ async function main() {
28
+ const socketPath = getSocketPath();
29
+ logger.info({ pid: process.pid, socketPath }, 'mcp_daemon.starting');
30
+ // Ensure ~/.intutic directory exists with correct permissions
31
+ ensureDir(path.dirname(socketPath));
32
+ // Remove stale socket file
33
+ try {
34
+ fs.unlinkSync(socketPath);
35
+ }
36
+ catch { }
37
+ // Write PID file
38
+ fs.writeFileSync(PID_FILE, String(process.pid), { mode: 0o600 });
39
+ // Start subsystems
40
+ startBatcher();
41
+ startHealthMonitor();
42
+ const server = createSocketServer();
43
+ server.listen(socketPath, () => {
44
+ // Restrict socket to owner only
45
+ try {
46
+ fs.chmodSync(socketPath, 0o600);
47
+ }
48
+ catch { }
49
+ logger.info({ socketPath, pid: process.pid }, 'mcp_daemon.running');
50
+ process.stdout.write(JSON.stringify({ event: 'mcp_daemon.started', pid: process.pid }) + '\n');
51
+ });
52
+ server.on('error', (err) => {
53
+ logger.error({ err }, 'mcp_daemon.socket_error');
54
+ process.exit(1);
55
+ });
56
+ // Graceful shutdown
57
+ const shutdown = async (signal) => {
58
+ logger.info({ signal }, 'mcp_daemon.stopping');
59
+ server.close();
60
+ stopHealthMonitor();
61
+ await stopBatcher();
62
+ try {
63
+ fs.unlinkSync(socketPath);
64
+ }
65
+ catch { }
66
+ try {
67
+ fs.unlinkSync(PID_FILE);
68
+ }
69
+ catch { }
70
+ logger.info('mcp_daemon.stopped');
71
+ process.exit(0);
72
+ };
73
+ process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
74
+ process.on('SIGINT', () => { void shutdown('SIGINT'); });
75
+ }
76
+ void main();
77
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/daemon/index.ts"],"names":[],"mappings":"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"}