@intutic/mcp-governance-proxy 2.1.0 → 2.2.0
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/README.md +33 -18
- package/dist/anomaly/index.d.ts +2 -1
- package/dist/anomaly/index.d.ts.map +1 -1
- package/dist/anomaly/index.js.map +1 -1
- package/dist/approvalHold.d.ts +65 -0
- package/dist/approvalHold.d.ts.map +1 -0
- package/dist/approvalHold.js +142 -0
- package/dist/approvalHold.js.map +1 -0
- package/dist/config.d.ts +6 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -1
- package/dist/config.js.map +1 -1
- package/dist/daemon/healthMonitor.d.ts +2 -0
- package/dist/daemon/healthMonitor.d.ts.map +1 -1
- package/dist/daemon/healthMonitor.js +60 -26
- package/dist/daemon/healthMonitor.js.map +1 -1
- package/dist/daemon/instrumentation.d.ts +2 -2
- package/dist/daemon/instrumentation.js +2 -2
- package/dist/daemon/policyCache.d.ts +23 -0
- package/dist/daemon/policyCache.d.ts.map +1 -1
- package/dist/daemon/policyCache.js +28 -7
- package/dist/daemon/policyCache.js.map +1 -1
- package/dist/emitter.d.ts +43 -9
- package/dist/emitter.d.ts.map +1 -1
- package/dist/emitter.js +23 -43
- package/dist/emitter.js.map +1 -1
- package/dist/httpJson.d.ts +19 -0
- package/dist/httpJson.d.ts.map +1 -0
- package/dist/httpJson.js +62 -0
- package/dist/httpJson.js.map +1 -0
- package/dist/identity.d.ts +27 -0
- package/dist/identity.d.ts.map +1 -0
- package/dist/identity.js +36 -0
- package/dist/identity.js.map +1 -0
- package/dist/interceptor.d.ts +88 -4
- package/dist/interceptor.d.ts.map +1 -1
- package/dist/interceptor.js +161 -11
- package/dist/interceptor.js.map +1 -1
- package/dist/policy.d.ts +84 -1
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +160 -39
- package/dist/policy.js.map +1 -1
- package/dist/proxy.d.ts +12 -7
- package/dist/proxy.d.ts.map +1 -1
- package/dist/proxy.js +81 -26
- package/dist/proxy.js.map +1 -1
- package/dist/registryObserver.d.ts +33 -0
- package/dist/registryObserver.d.ts.map +1 -0
- package/dist/registryObserver.js +59 -0
- package/dist/registryObserver.js.map +1 -0
- package/dist/version.d.ts +12 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +13 -0
- package/dist/version.js.map +1 -0
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
## Overview
|
|
8
8
|
|
|
9
|
-
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
|
|
9
|
+
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 it, blocks it, or holds it for a person's approval, and redacts secrets out of results on the way back — all without requiring changes to the agent or the tool server.
|
|
10
10
|
|
|
11
11
|
## Modes
|
|
12
12
|
|
|
@@ -22,12 +22,14 @@ The proxy spawns the real MCP server as a child process and relays JSON-RPC mess
|
|
|
22
22
|
|
|
23
23
|
### Standalone Mode
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
With no server to wrap (no `--` command, no `--remote-url`), `intutic-mcp-proxy` is itself an MCP server named `intutic`, exposing three read-only tools: `intutic_governance_status`, `intutic_list_sops` and `intutic_list_incidents`. The two list tools take a `limit` (1–50, default 10). The control plane lists incidents only for the OWNER, ADMIN or EM role; any other role gets a message saying so instead.
|
|
26
26
|
|
|
27
27
|
```
|
|
28
|
-
Agent ↔ intutic-mcp-
|
|
28
|
+
Agent ↔ intutic-mcp-proxy (MCP server) ↔ Intutic control plane
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
+
It needs `INTUTIC_API_KEY`, the control plane's address (`INTUTIC_CONTROL_PLANE_URL`, or `INTUTIC_HOST` in `~/.intutic/env/runtime.env`; default `http://localhost:3001`) and a workspace id (`--workspace-id` or `INTUTIC_WORKSPACE_ID`).
|
|
32
|
+
|
|
31
33
|
### Remote Bridge Mode
|
|
32
34
|
|
|
33
35
|
Fronts a remote (HTTP/SSE-transport) MCP server instead of a spawned child process. The harness still spawns this proxy as a normal stdio child — no new listener, no new port — but the proxy's upstream side talks to the remote server over HTTP or Server-Sent Events using the MCP SDK's client transports directly:
|
|
@@ -42,26 +44,27 @@ Every governance check proxy mode applies to a stdio server — allowlists, `too
|
|
|
42
44
|
|
|
43
45
|
### DLP Scanning
|
|
44
46
|
|
|
45
|
-
|
|
47
|
+
16 built-in regex patterns, plus any the workspace adds. A match in a call's arguments blocks the call; a credential in a result is redacted before the agent reads it:
|
|
46
48
|
|
|
47
|
-
- API keys and tokens
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
- PII indicators
|
|
49
|
+
- API keys and tokens: OpenAI, Anthropic, Google, GitHub, Slack, AWS access key IDs
|
|
50
|
+
- PEM and EC private keys, long hex strings that look like secrets
|
|
51
|
+
- US Social Security numbers
|
|
52
|
+
- Destructive commands (`rm -rf /`, `DROP TABLE`, `DROP DATABASE`, `TRUNCATE TABLE`) — arguments only
|
|
52
53
|
|
|
53
54
|
### Policy Enforcement
|
|
54
55
|
|
|
55
|
-
- **
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
56
|
+
- **MCP server registry**: a server an owner or admin blocked is refused; under the workspace's `deny` default, so is any server not yet approved; a tool switched off within a server is hidden and refused
|
|
57
|
+
- **Allowlists**: `mcpAllowedServers` / `mcpAllowedTools`, empty meaning unrestricted
|
|
58
|
+
- **SSO group policy**: the workspace's `sso_group_policy`, applied to the member the API key belongs to
|
|
59
|
+
- **SOP rules** matched on tool name and arguments: `block`, `warn`, or `require_approval` — a hold recorded in the workspace's review queue; the identical call passes once approved
|
|
60
|
+
- **Audit events** for every decision, carrying the caller: API key prefix, OS user, harness session and server
|
|
58
61
|
|
|
59
62
|
## Binaries
|
|
60
63
|
|
|
61
64
|
| Binary | Description |
|
|
62
65
|
|--------|-------------|
|
|
63
|
-
| `intutic-mcp-proxy` |
|
|
64
|
-
| `intutic-mcp-daemon` |
|
|
66
|
+
| `intutic-mcp-proxy` | Wraps an MCP server (stdio or remote), or with no server to wrap, runs the standalone `intutic` MCP server |
|
|
67
|
+
| `intutic-mcp-daemon` | Long-lived local daemon on a Unix socket (`~/.intutic/mcp-proxy.sock`, or `MCP_DAEMON_SOCKET`) that caches policy and batches telemetry for proxies running with `INTUTIC_MCP_PROXY_MODE=daemon`. Not an MCP server. |
|
|
65
68
|
|
|
66
69
|
## Installation
|
|
67
70
|
|
|
@@ -78,7 +81,7 @@ npm install @intutic/mcp-governance-proxy
|
|
|
78
81
|
"mcpServers": {
|
|
79
82
|
"filesystem": {
|
|
80
83
|
"command": "intutic-mcp-proxy",
|
|
81
|
-
"args": ["--", "npx", "@modelcontextprotocol/server-filesystem", "/path"]
|
|
84
|
+
"args": ["--server-name", "filesystem", "--", "npx", "@modelcontextprotocol/server-filesystem", "/path"]
|
|
82
85
|
}
|
|
83
86
|
}
|
|
84
87
|
}
|
|
@@ -86,10 +89,20 @@ npm install @intutic/mcp-governance-proxy
|
|
|
86
89
|
|
|
87
90
|
### Standalone Mode
|
|
88
91
|
|
|
89
|
-
```
|
|
90
|
-
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"mcpServers": {
|
|
95
|
+
"intutic": {
|
|
96
|
+
"command": "npx",
|
|
97
|
+
"args": ["-y", "-p", "@intutic/mcp-governance-proxy", "intutic-mcp-proxy", "--workspace-id", "wk_your_workspace"],
|
|
98
|
+
"env": { "INTUTIC_API_KEY": "<your API key>" }
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
91
102
|
```
|
|
92
103
|
|
|
104
|
+
The package has two binaries, so `npx` needs `-p @intutic/mcp-governance-proxy` and the binary name.
|
|
105
|
+
|
|
93
106
|
### Remote Bridge Mode
|
|
94
107
|
|
|
95
108
|
```json
|
|
@@ -97,7 +110,7 @@ intutic-mcp-daemon --port 3100
|
|
|
97
110
|
"mcpServers": {
|
|
98
111
|
"linear": {
|
|
99
112
|
"command": "intutic-mcp-proxy",
|
|
100
|
-
"args": ["--remote-url", "https://mcp.linear.app/sse", "--remote-transport", "sse"],
|
|
113
|
+
"args": ["--server-name", "linear", "--remote-url", "https://mcp.linear.app/sse", "--remote-transport", "sse"],
|
|
101
114
|
"env": { "INTUTIC_REMOTE_HEADERS": "{\"Authorization\":\"Bearer <token>\"}" }
|
|
102
115
|
}
|
|
103
116
|
}
|
|
@@ -106,6 +119,8 @@ intutic-mcp-daemon --port 3100
|
|
|
106
119
|
|
|
107
120
|
`--remote-transport` accepts `sse` or `http` (default `http`, the SDK's current non-deprecated `StreamableHTTPClientTransport`). `--remote-url` and a positional stdio command (`--` followed by a command) are mutually exclusive — pass one or the other. Auth headers ride via the `INTUTIC_REMOTE_HEADERS` environment variable (a JSON object string), never as a CLI argument — argv is visible to any local process via `ps`.
|
|
108
121
|
|
|
122
|
+
`--server-name` is the name the registry, the server allowlist and the TOFU pin file (`~/.intutic/mcp-pins/<workspace>__<server>.json`) know the server by; without it the server is `unknown`. `intutic connect` sets it when it wraps a harness's servers. Every environment variable and flag is listed in the [MCP Server Governance guide](https://docs.intutic.ai/guide/mcp-governance#configuration-reference).
|
|
123
|
+
|
|
109
124
|
## Part of Intutic
|
|
110
125
|
|
|
111
126
|
This package is part of the [Intutic](https://github.com/intutic/intutic) monorepo — an open-core AI governance control plane for developer teams.
|
package/dist/anomaly/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
* @module
|
|
8
8
|
*/
|
|
9
9
|
import type { AnomalyFinding, Disposition } from './detectors.js';
|
|
10
|
+
import type { McpAnomalyDetectorId } from '@intutic/shared-types';
|
|
10
11
|
export * from './detectors.js';
|
|
11
12
|
/**
|
|
12
13
|
* Ported from `packages/proxy/src/plugins/anomaly/mod.rs`'s
|
|
@@ -23,7 +24,7 @@ export declare const REASK_MAX_ATTEMPTS = 3;
|
|
|
23
24
|
* even when no finding fired yet (an `'off'` override skips evaluation
|
|
24
25
|
* entirely — see `resolveEffectiveDisposition`).
|
|
25
26
|
*/
|
|
26
|
-
export declare const DETECTOR_BASE_DISPOSITION: Readonly<Record<
|
|
27
|
+
export declare const DETECTOR_BASE_DISPOSITION: Readonly<Record<McpAnomalyDetectorId, Disposition>>;
|
|
27
28
|
export type AnomalyMode = 'enforce' | 'warn' | 'off';
|
|
28
29
|
/**
|
|
29
30
|
* Resolves the EFFECTIVE disposition for one finding, honoring:
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/anomaly/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAEjE,cAAc,gBAAgB,CAAA;AAE9B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/anomaly/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAEjE,cAAc,gBAAgB,CAAA;AAE9B;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,IAAI,CAAA;AAEnC;;;;;;;GAOG;AACH,eAAO,MAAM,yBAAyB,EAAE,QAAQ,CAAC,MAAM,CAAC,oBAAoB,EAAE,WAAW,CAAC,CAQzF,CAAA;AAID,MAAM,MAAM,WAAW,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,CAAA;AAEpD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CAAC,CAAC,GACvD,WAAW,GAAG,KAAK,CAarB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/anomaly/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/anomaly/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,cAAc,gBAAgB,CAAA;AAE9B;;;;;GAKG;AACH,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAA;AAEnC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAwD;IAC5F,kBAAkB,EAAE,OAAO;IAC3B,eAAe,EAAE,OAAO;IACxB,cAAc,EAAE,OAAO;IACvB,uBAAuB,EAAE,OAAO;IAChC,cAAc,EAAE,MAAM;IACtB,cAAc,EAAE,OAAO;IACvB,cAAc,EAAE,MAAM;CACvB,CAAA;AAED,MAAM,QAAQ,GAAkD,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,CAAA;AAIvG;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAM,UAAU,2BAA2B,CACzC,OAAuB,EACvB,IAAiB,EACjB,SAAwD;IAExD,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,KAAK,CAAA;IAEhC,MAAM,WAAW,GAAwB,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,CAAA;IACxF,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC,UAAU,CAAC,CAAA;IAE9C,MAAM,UAAU,GAA4B,CAAC,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAA;IAC9E,IAAI,QAAQ,KAAK,SAAS;QAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;IAErD,yEAAyE;IACzE,0EAA0E;IAC1E,2DAA2D;IAC3D,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;AAC/E,CAAC"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approvalHold.ts — `require_approval` rules as real holds, not blocks.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the harness hook gates' hold tier through the same control-plane
|
|
5
|
+
* decisions API:
|
|
6
|
+
*
|
|
7
|
+
* 1. A call that matches a `require_approval` rule is looked up in the
|
|
8
|
+
* workspace's approved bypasses (`GET /api/v1/decisions/approved-bypasses`).
|
|
9
|
+
* An exact, unexpired match — same rule, same server and tool, same
|
|
10
|
+
* arguments — lets the call through.
|
|
11
|
+
* 2. Otherwise the call is refused and a hold is recorded
|
|
12
|
+
* (`POST /api/v1/decisions`), which puts it in the review queue, notifies
|
|
13
|
+
* the workspace (`decision.pending`, Slack included), and gives the agent a
|
|
14
|
+
* hold id to quote.
|
|
15
|
+
* 3. An approver approves it (`intutic decision approve <holdId>`, the
|
|
16
|
+
* dashboard, or Slack). When the workspace has `reviewHoldBypassEnabled`,
|
|
17
|
+
* approving writes the bypass step 1 finds, valid for
|
|
18
|
+
* `reviewHoldBypassTtlMinutes` — so the agent's identical retry passes.
|
|
19
|
+
* Without that setting an approval records the decision only, exactly as it
|
|
20
|
+
* does for a hook-gate hold.
|
|
21
|
+
*
|
|
22
|
+
* "Identical" is the bypass key: the rule id, `mcp__<server>__<tool>`
|
|
23
|
+
* lower-cased, and a SHA-256 of the arguments serialised with sorted keys, so
|
|
24
|
+
* the same arguments in a different key order still match and any other
|
|
25
|
+
* argument value does not.
|
|
26
|
+
*
|
|
27
|
+
* A hold needs the control plane both ways — to find an approval and to
|
|
28
|
+
* record a request for one — so an unreachable control plane means the call
|
|
29
|
+
* stays held, whatever the fail-open setting: a rule that says a person must
|
|
30
|
+
* approve is not satisfied by nobody being reachable to ask.
|
|
31
|
+
*
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
import type { SopRule } from './policy.js';
|
|
35
|
+
import type { CallerIdentity } from './identity.js';
|
|
36
|
+
export type HoldOutcome = {
|
|
37
|
+
kind: 'bypassed';
|
|
38
|
+
holdId: string;
|
|
39
|
+
decidedBy: string;
|
|
40
|
+
} | {
|
|
41
|
+
kind: 'held';
|
|
42
|
+
holdId: string;
|
|
43
|
+
recorded: boolean;
|
|
44
|
+
};
|
|
45
|
+
/** JSON with object keys sorted at every level, so key order never changes a hash. */
|
|
46
|
+
export declare function canonicalJson(value: unknown): string;
|
|
47
|
+
export declare function holdKey(serverName: string, toolName: string, toolInput: unknown): {
|
|
48
|
+
toolNameNormalized: string;
|
|
49
|
+
targetHash: string;
|
|
50
|
+
};
|
|
51
|
+
export declare class ApprovalHolds {
|
|
52
|
+
private readonly controlPlaneUrl;
|
|
53
|
+
private readonly apiKey;
|
|
54
|
+
private readonly workspaceId;
|
|
55
|
+
private readonly serverName;
|
|
56
|
+
/** Rides on the hold record so the reviewer sees who asked (identity.ts). */
|
|
57
|
+
private readonly identity;
|
|
58
|
+
constructor(controlPlaneUrl: string, apiKey: string, workspaceId: string, serverName: string,
|
|
59
|
+
/** Rides on the hold record so the reviewer sees who asked (identity.ts). */
|
|
60
|
+
identity?: CallerIdentity | undefined);
|
|
61
|
+
/** Lets the call through on an approved bypass, or records a hold and returns its id. Never throws. */
|
|
62
|
+
request(rule: SopRule, toolName: string, toolInput: unknown): Promise<HoldOutcome>;
|
|
63
|
+
private findBypass;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=approvalHold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalHold.d.ts","sourceRoot":"","sources":["../src/approvalHold.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;AAKH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAOnD,MAAM,MAAM,WAAW,GACnB;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GACvD;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,OAAO,CAAA;CAAE,CAAA;AAEvD,sFAAsF;AACtF,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CASpD;AAED,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG;IAAE,kBAAkB,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAKpI;AAgBD,qBAAa,aAAa;IAEtB,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAC5B,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,QAAQ,CAAC,QAAQ;gBALR,eAAe,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM;IACnC,6EAA6E;IAC5D,QAAQ,GAAE,cAAc,GAAG,SAAqB;IAGnE,uGAAuG;IACjG,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,WAAW,CAAC;YAoC1E,UAAU;CA4BzB"}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* approvalHold.ts — `require_approval` rules as real holds, not blocks.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the harness hook gates' hold tier through the same control-plane
|
|
5
|
+
* decisions API:
|
|
6
|
+
*
|
|
7
|
+
* 1. A call that matches a `require_approval` rule is looked up in the
|
|
8
|
+
* workspace's approved bypasses (`GET /api/v1/decisions/approved-bypasses`).
|
|
9
|
+
* An exact, unexpired match — same rule, same server and tool, same
|
|
10
|
+
* arguments — lets the call through.
|
|
11
|
+
* 2. Otherwise the call is refused and a hold is recorded
|
|
12
|
+
* (`POST /api/v1/decisions`), which puts it in the review queue, notifies
|
|
13
|
+
* the workspace (`decision.pending`, Slack included), and gives the agent a
|
|
14
|
+
* hold id to quote.
|
|
15
|
+
* 3. An approver approves it (`intutic decision approve <holdId>`, the
|
|
16
|
+
* dashboard, or Slack). When the workspace has `reviewHoldBypassEnabled`,
|
|
17
|
+
* approving writes the bypass step 1 finds, valid for
|
|
18
|
+
* `reviewHoldBypassTtlMinutes` — so the agent's identical retry passes.
|
|
19
|
+
* Without that setting an approval records the decision only, exactly as it
|
|
20
|
+
* does for a hook-gate hold.
|
|
21
|
+
*
|
|
22
|
+
* "Identical" is the bypass key: the rule id, `mcp__<server>__<tool>`
|
|
23
|
+
* lower-cased, and a SHA-256 of the arguments serialised with sorted keys, so
|
|
24
|
+
* the same arguments in a different key order still match and any other
|
|
25
|
+
* argument value does not.
|
|
26
|
+
*
|
|
27
|
+
* A hold needs the control plane both ways — to find an approval and to
|
|
28
|
+
* record a request for one — so an unreachable control plane means the call
|
|
29
|
+
* stays held, whatever the fail-open setting: a rule that says a person must
|
|
30
|
+
* approve is not satisfied by nobody being reachable to ask.
|
|
31
|
+
*
|
|
32
|
+
* @module
|
|
33
|
+
*/
|
|
34
|
+
import * as node_crypto from 'node:crypto';
|
|
35
|
+
import { createStderrLogger as createLogger } from './stderrLog.js';
|
|
36
|
+
import { getJson, postJson } from './httpJson.js';
|
|
37
|
+
const log = createLogger('mcp-proxy-hold');
|
|
38
|
+
/** The hold-record version the control plane accepts (`POST /api/v1/decisions`). */
|
|
39
|
+
const HOLD_RECORD_VERSION = 1;
|
|
40
|
+
/** JSON with object keys sorted at every level, so key order never changes a hash. */
|
|
41
|
+
export function canonicalJson(value) {
|
|
42
|
+
if (Array.isArray(value))
|
|
43
|
+
return `[${value.map(canonicalJson).join(',')}]`;
|
|
44
|
+
if (value !== null && typeof value === 'object') {
|
|
45
|
+
const entries = Object.entries(value)
|
|
46
|
+
.filter(([, v]) => v !== undefined)
|
|
47
|
+
.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0));
|
|
48
|
+
return `{${entries.map(([k, v]) => `${JSON.stringify(k)}:${canonicalJson(v)}`).join(',')}}`;
|
|
49
|
+
}
|
|
50
|
+
return JSON.stringify(value ?? null);
|
|
51
|
+
}
|
|
52
|
+
export function holdKey(serverName, toolName, toolInput) {
|
|
53
|
+
return {
|
|
54
|
+
toolNameNormalized: `mcp__${serverName}__${toolName}`.trim().toLowerCase(),
|
|
55
|
+
targetHash: node_crypto.createHash('sha256').update(canonicalJson(toolInput ?? {})).digest('hex'),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function newHoldId() {
|
|
59
|
+
return `hold_${Date.now().toString(36)}_${node_crypto.randomBytes(4).toString('hex')}`;
|
|
60
|
+
}
|
|
61
|
+
export class ApprovalHolds {
|
|
62
|
+
controlPlaneUrl;
|
|
63
|
+
apiKey;
|
|
64
|
+
workspaceId;
|
|
65
|
+
serverName;
|
|
66
|
+
identity;
|
|
67
|
+
constructor(controlPlaneUrl, apiKey, workspaceId, serverName,
|
|
68
|
+
/** Rides on the hold record so the reviewer sees who asked (identity.ts). */
|
|
69
|
+
identity = undefined) {
|
|
70
|
+
this.controlPlaneUrl = controlPlaneUrl;
|
|
71
|
+
this.apiKey = apiKey;
|
|
72
|
+
this.workspaceId = workspaceId;
|
|
73
|
+
this.serverName = serverName;
|
|
74
|
+
this.identity = identity;
|
|
75
|
+
}
|
|
76
|
+
/** Lets the call through on an approved bypass, or records a hold and returns its id. Never throws. */
|
|
77
|
+
async request(rule, toolName, toolInput) {
|
|
78
|
+
const { toolNameNormalized, targetHash } = holdKey(this.serverName, toolName, toolInput);
|
|
79
|
+
const bypass = await this.findBypass(rule.id, toolNameNormalized, targetHash);
|
|
80
|
+
if (bypass)
|
|
81
|
+
return bypass;
|
|
82
|
+
const holdId = newHoldId();
|
|
83
|
+
try {
|
|
84
|
+
await postJson(`${this.controlPlaneUrl}/api/v1/decisions`, this.apiKey, {
|
|
85
|
+
holds: [
|
|
86
|
+
{
|
|
87
|
+
v: HOLD_RECORD_VERSION,
|
|
88
|
+
holdId,
|
|
89
|
+
reason: rule.id,
|
|
90
|
+
tool: toolNameNormalized,
|
|
91
|
+
sessionId: '',
|
|
92
|
+
at: new Date().toISOString(),
|
|
93
|
+
toolNameNormalized,
|
|
94
|
+
targetHash,
|
|
95
|
+
context: {
|
|
96
|
+
source: 'mcp_proxy',
|
|
97
|
+
server: this.serverName,
|
|
98
|
+
tool: toolName,
|
|
99
|
+
rule: rule.reason,
|
|
100
|
+
...(this.identity ? { principal: this.identity } : {}),
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
});
|
|
105
|
+
return { kind: 'held', holdId, recorded: true };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
log.warn({ action: 'hold_record_failed', ruleId: rule.id, err: err.message }, 'Could not record the hold');
|
|
109
|
+
return { kind: 'held', holdId, recorded: false };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async findBypass(ruleId, toolNameNormalized, targetHash) {
|
|
113
|
+
let body;
|
|
114
|
+
try {
|
|
115
|
+
body = await getJson(`${this.controlPlaneUrl}/api/v1/decisions/approved-bypasses`, this.apiKey);
|
|
116
|
+
}
|
|
117
|
+
catch (err) {
|
|
118
|
+
log.warn({ action: 'bypass_lookup_failed', err: err.message }, 'Could not look up approved bypasses — the call stays held');
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const list = body?.bypasses;
|
|
122
|
+
if (!Array.isArray(list))
|
|
123
|
+
return null;
|
|
124
|
+
const now = Date.now();
|
|
125
|
+
for (const raw of list) {
|
|
126
|
+
if (raw.workspaceId !== this.workspaceId || raw.sopRuleId !== ruleId)
|
|
127
|
+
continue;
|
|
128
|
+
if (raw.toolNameNormalized !== toolNameNormalized || raw.targetHash !== targetHash)
|
|
129
|
+
continue;
|
|
130
|
+
const expires = typeof raw.expiresAt === 'string' ? Date.parse(raw.expiresAt) : NaN;
|
|
131
|
+
if (Number.isNaN(expires) || now >= expires)
|
|
132
|
+
continue;
|
|
133
|
+
return {
|
|
134
|
+
kind: 'bypassed',
|
|
135
|
+
holdId: typeof raw.holdId === 'string' ? raw.holdId : '',
|
|
136
|
+
decidedBy: typeof raw.decidedBy === 'string' ? raw.decidedBy : '',
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
//# sourceMappingURL=approvalHold.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"approvalHold.js","sourceRoot":"","sources":["../src/approvalHold.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -19,7 +19,12 @@ export interface ProxyConfig {
|
|
|
19
19
|
eventsFilePath: string;
|
|
20
20
|
/** Policy cache TTL in milliseconds (default: 60_000) */
|
|
21
21
|
policyTtlMs: number;
|
|
22
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* Whether a governance check that cannot complete lets the call through
|
|
24
|
+
* (default: true), from `INTUTIC_MCP_FAIL_OPEN`. The local fallback only:
|
|
25
|
+
* once the control plane delivers the workspace's `mcpProxyFailBehavior`,
|
|
26
|
+
* that wins (interceptor.ts's `failOpen`).
|
|
27
|
+
*/
|
|
23
28
|
failOpen: boolean;
|
|
24
29
|
/**
|
|
25
30
|
* MCP proxy deployment model ('per-session' | 'daemon').
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,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
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAOH,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;;;;;OAKG;IACH,QAAQ,EAAE,OAAO,CAAA;IACjB;;;;;;;OAOG;IACH,YAAY,EAAE,MAAM,CAAA;IACpB;;;;OAIG;IACH,UAAU,EAAE,OAAO,CAAA;IACnB;;;;;;;;OAQG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;;OAMG;IACH,eAAe,CAAC,EAAE,KAAK,GAAG,MAAM,CAAA;IAChC;;;;;;;OAOG;IACH,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC;;;;;;;;;;;OAWG;IACH,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAA;IACpC;;;;;;;;;;;;OAYG;IACH,cAAc,EAAE,SAAS,GAAG,MAAM,GAAG,KAAK,CAAA;IAC1C;;;;;;;;;;;OAWG;IACH,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC,CAAA;IACvE;;;;;;;;;;;OAWG;IACH,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IAC9B;;;;;;;OAOG;IACH,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IAC7B;;;OAGG;IACH,YAAY,EAAE,MAAM,GAAG,SAAS,CAAA;CACjC;AAyHD;;GAEG;AACH,wBAAsB,UAAU,CAAC,IAAI,GAAE,MAAM,EAA0B,GAAG,OAAO,CAAC,WAAW,CAAC,CA+G7F"}
|
package/dist/config.js
CHANGED
|
@@ -154,7 +154,7 @@ export async function loadConfig(argv = process.argv.slice(2)) {
|
|
|
154
154
|
const eventsFilePath = process.env['INTUTIC_EVENTS_FILE'] ??
|
|
155
155
|
runtimeEnv['INTUTIC_EVENTS_FILE'] ??
|
|
156
156
|
DEFAULT_EVENTS_PATH;
|
|
157
|
-
const failOpen = (runtimeEnv['INTUTIC_MCP_FAIL_OPEN'] ?? 'true').toLowerCase() !== 'false';
|
|
157
|
+
const failOpen = (process.env['INTUTIC_MCP_FAIL_OPEN'] ?? runtimeEnv['INTUTIC_MCP_FAIL_OPEN'] ?? 'true').toLowerCase() !== 'false';
|
|
158
158
|
// 'daemon' is honoured downstream: policy.ts routes policy lookups through
|
|
159
159
|
// the daemon's Unix socket when this is set. No warning here — the mode does
|
|
160
160
|
// what it says.
|
package/dist/config.js.map
CHANGED
|
@@ -1 +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,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AACtD,OAAO,KAAK,OAAO,MAAM,SAAS,CAAA;AAClC,OAAO,KAAK,SAAS,MAAM,WAAW,CAAA;AA6ItC,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;;;GAGG;AACH,SAAS,YAAY,CAAC,IAAc;IAOlC,IAAI,WAA+B,CAAA;IACnC,IAAI,UAA8B,CAAA;IAClC,IAAI,SAA6B,CAAA;IACjC,IAAI,eAAmC,CAAA;IACvC,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,eAAe,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC9D,UAAU,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YACxB,CAAC,EAAE,CAAA,CAAC,aAAa;QACnB,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,cAAc,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YAC7D,SAAS,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YACvB,CAAC,EAAE,CAAA,CAAC,aAAa;QACnB,CAAC;aAAM,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,oBAAoB,IAAI,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;YACnE,eAAe,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAA;YAC7B,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,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,iBAAiB,EAAE,CAAA;AACnF,CAAC;AAED;;;;;;GAMG;AACH,SAAS,kBAAkB,CAAC,GAAuB;IACjD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAA;IACnB,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IACrF,MAAM,OAAO,GAA2B,EAAE,CAAA;IAC1C,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;QAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ;YAAE,OAAO,CAAC,GAAG,CAAC,GAAG,KAAK,CAAA;IACrD,CAAC;IACD,OAAO,OAAO,CAAA;AAChB,CAAC;AAED,MAAM,6BAA6B,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAA;AAEhF;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,GAAuB;IACpD,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAA;IACnB,IAAI,MAAe,CAAA;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,CAAA;IACX,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,IAAI,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IACrF,MAAM,SAAS,GAAuD,EAAE,CAAA;IACxE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;QAC7E,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,6BAA6B,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC1E,SAAS,CAAC,GAAG,CAAC,GAAG,KAA2C,CAAA;QAC9D,CAAC;IACH,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,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,wEAAwE;IACxE,0EAA0E;IAC1E,oEAAoE;IACpE,+DAA+D;IAC/D,4EAA4E;IAC5E,MAAM,eAAe,GACnB,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC;QACxC,UAAU,CAAC,2BAA2B,CAAC;QACvC,UAAU,CAAC,cAAc,CAAC;QAC1B,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,OAAO,CAAC,GAAG,CAAC,uBAAuB,CAAC,IAAI,UAAU,CAAC,uBAAuB,CAAC,IAAI,MAAM,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAA;IAEnH,2EAA2E;IAC3E,6EAA6E;IAC7E,gBAAgB;IAChB,MAAM,YAAY,GAAG,UAAU,CAAC,wBAAwB,CAAC,IAAI,aAAa,CAAA;IAE1E,MAAM,UAAU,GAAG,GAAG,CAAC,UAAU,IAAI,SAAS,CAAA;IAE9C,kEAAkE;IAClE,uEAAuE;IACvE,sEAAsE;IACtE,0EAA0E;IAC1E,uEAAuE;IACvE,uEAAuE;IACvE,yEAAyE;IACzE,mDAAmD;IACnD,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,GAAG,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACpE,MAAM,IAAI,KAAK,CACb,8EAA8E;YAC5E,gFAAgF;YAChF,6EAA6E,CAChF,CAAA;IACH,CAAC;IAED,IAAI,eAA2C,CAAA;IAC/C,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;YACtC,qEAAqE;YACrE,uEAAuE;YACvE,2EAA2E;YAC3E,eAAe,GAAG,MAAM,CAAA;QAC1B,CAAC;aAAM,IAAI,GAAG,CAAC,eAAe,KAAK,KAAK,IAAI,GAAG,CAAC,eAAe,KAAK,MAAM,EAAE,CAAC;YAC3E,eAAe,GAAG,GAAG,CAAC,eAAe,CAAA;QACvC,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,yEAAyE,GAAG,CAAC,eAAe,KAAK,CAClG,CAAA;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,GAAG,CAAC,iBAAiB,CAAC,MAAM,KAAK,CAAC,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,CAAA;IAEpF,MAAM,kBAAkB,GACtB,CAAC,OAAO,CAAC,GAAG,CAAC,8BAA8B,CAAC,IAAI,UAAU,CAAC,8BAA8B,CAAC,CAAC,KAAK,OAAO;QACrG,CAAC,CAAC,OAAO;QACT,CAAC,CAAC,MAAM,CAAA;IAEZ,MAAM,SAAS,GACb,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,UAAU,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,IAAI,SAAS,CAAA;IAChI,4EAA4E;IAC5E,sDAAsD;IACtD,MAAM,YAAY,GAAG,MAAM,kBAAkB,CAAC,WAAW,CAAC,CAAA;IAE1D,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,CAAC,0BAA0B,CAAC,IAAI,UAAU,CAAC,0BAA0B,CAAC,CAAA;IACxG,MAAM,cAAc,GAClB,cAAc,KAAK,MAAM,IAAI,cAAc,KAAK,KAAK,CAAC,CAAC,CAAC,cAAc,CAAC,CAAC,CAAC,SAAS,CAAA;IAEpF,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;QACV,UAAU;QACV,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,eAAe;QACf,aAAa,EAAE,kBAAkB,CAAC,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;QACxE,kBAAkB;QAClB,cAAc;QACd,mBAAmB,EAAE,qBAAqB,CAAC,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;QACxF,UAAU,EAAE,UAAU,CAAC,wBAAwB,CAAC,IAAI,SAAS;QAC7D,SAAS;QACT,YAAY;KACb,CAAA;AACH,CAAC"}
|
|
@@ -11,6 +11,8 @@ export interface McpServerHealth {
|
|
|
11
11
|
credentialExpiryAt?: string;
|
|
12
12
|
checkedAt: string;
|
|
13
13
|
}
|
|
14
|
+
/** Exported for tests; `home` defaults to the user's home directory. */
|
|
15
|
+
export declare function discoverServers(home?: string): McpServerConfig[];
|
|
14
16
|
export declare function registerServer(server: McpServerConfig): void;
|
|
15
17
|
export declare function startHealthMonitor(): void;
|
|
16
18
|
export declare function stopHealthMonitor(): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"healthMonitor.d.ts","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"AA+BA,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;
|
|
1
|
+
{"version":3,"file":"healthMonitor.d.ts","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"AA+BA,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;AAyED,wEAAwE;AACxE,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAqB,GAAG,eAAe,EAAE,CA0C9E;AAkDD,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"}
|
|
@@ -50,40 +50,66 @@ function entryToUrl(entry) {
|
|
|
50
50
|
return `stdio://${command}`;
|
|
51
51
|
return '';
|
|
52
52
|
}
|
|
53
|
-
function
|
|
53
|
+
function readJson(file) {
|
|
54
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* The server maps Claude Code reads: `~/.claude.json` holds the user-scope
|
|
58
|
+
* `mcpServers` and, under `projects[<path>]`, each project's local-scope
|
|
59
|
+
* `mcpServers`; the project-scope ones live in `<path>/.mcp.json`, which the
|
|
60
|
+
* daemon finds through the same project paths. (`~/.claude/mcp.json`, read
|
|
61
|
+
* here before, is not a file Claude Code uses.) The sync daemon wraps the
|
|
62
|
+
* first two (services/sync-daemon/src/harness/mcpAutoWrite.ts) and governs a
|
|
63
|
+
* project's `.mcp.json` through local-scope shadows of its approved servers,
|
|
64
|
+
* never writing the shared file — so a server not yet approved is unwrapped,
|
|
65
|
+
* and belongs in the registry report all the same.
|
|
66
|
+
*/
|
|
67
|
+
function claudeCodeServerMaps(home) {
|
|
68
|
+
let state;
|
|
69
|
+
try {
|
|
70
|
+
state = readJson(path.join(home, '.claude.json'));
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
return []; // Claude Code not set up on this machine
|
|
74
|
+
}
|
|
75
|
+
if (!isRecord(state))
|
|
76
|
+
return [];
|
|
77
|
+
const maps = [readServerMap(state)];
|
|
78
|
+
const projects = state['projects'];
|
|
79
|
+
if (isRecord(projects)) {
|
|
80
|
+
for (const [projectPath, project] of Object.entries(projects)) {
|
|
81
|
+
maps.push(readServerMap(project));
|
|
82
|
+
try {
|
|
83
|
+
maps.push(readServerMap(readJson(path.join(projectPath, '.mcp.json'))));
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Most projects have no .mcp.json; an unreadable one costs only its own servers.
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return maps;
|
|
91
|
+
}
|
|
92
|
+
/** Exported for tests; `home` defaults to the user's home directory. */
|
|
93
|
+
export function discoverServers(home = os.homedir()) {
|
|
54
94
|
const discovered = [];
|
|
55
|
-
const homedir = os.homedir();
|
|
56
95
|
const configPaths = [
|
|
57
|
-
path.join(homedir, '.claude', 'mcp.json'),
|
|
58
96
|
process.platform === 'darwin'
|
|
59
|
-
? path.join(
|
|
97
|
+
? path.join(home, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json')
|
|
60
98
|
: process.platform === 'win32'
|
|
61
99
|
? path.join(process.env['APPDATA'] ?? '', 'Claude', 'claude_desktop_config.json')
|
|
62
|
-
: path.join(
|
|
100
|
+
: path.join(home, '.config', 'Claude', 'claude_desktop_config.json'),
|
|
63
101
|
process.platform === 'darwin'
|
|
64
|
-
? path.join(
|
|
102
|
+
? path.join(home, 'Library', 'Application Support', 'Cursor', 'User', 'globalSettings.json')
|
|
65
103
|
: process.platform === 'win32'
|
|
66
104
|
? path.join(process.env['APPDATA'] ?? '', 'Cursor', 'User', 'globalSettings.json')
|
|
67
|
-
: path.join(
|
|
105
|
+
: path.join(home, '.config', 'Cursor', 'User', 'globalSettings.json'),
|
|
68
106
|
];
|
|
107
|
+
const maps = [...claudeCodeServerMaps(home)];
|
|
69
108
|
for (const configPath of configPaths) {
|
|
70
109
|
try {
|
|
71
110
|
if (!fs.existsSync(configPath))
|
|
72
111
|
continue;
|
|
73
|
-
|
|
74
|
-
const parsed = JSON.parse(raw);
|
|
75
|
-
for (const [name, entry] of Object.entries(readServerMap(parsed))) {
|
|
76
|
-
if (name === 'intutic')
|
|
77
|
-
continue; // Skip self
|
|
78
|
-
const url = entryToUrl(entry);
|
|
79
|
-
if (url && !discovered.some(s => s.name === name)) {
|
|
80
|
-
discovered.push({
|
|
81
|
-
name,
|
|
82
|
-
url,
|
|
83
|
-
credentialExpiryAt: undefined
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
}
|
|
112
|
+
maps.push(readServerMap(readJson(configPath)));
|
|
87
113
|
}
|
|
88
114
|
catch {
|
|
89
115
|
// This config path belongs to a harness the user may not have installed,
|
|
@@ -94,6 +120,16 @@ function discoverServers() {
|
|
|
94
120
|
// declared in the rest.
|
|
95
121
|
}
|
|
96
122
|
}
|
|
123
|
+
for (const map of maps) {
|
|
124
|
+
for (const [name, entry] of Object.entries(map)) {
|
|
125
|
+
if (name === 'intutic')
|
|
126
|
+
continue; // Skip self
|
|
127
|
+
const url = entryToUrl(entry);
|
|
128
|
+
if (url && !discovered.some((s) => s.name === name)) {
|
|
129
|
+
discovered.push({ name, url, credentialExpiryAt: undefined });
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
97
133
|
return discovered;
|
|
98
134
|
}
|
|
99
135
|
async function probeServer(server) {
|
|
@@ -164,11 +200,9 @@ export function startHealthMonitor() {
|
|
|
164
200
|
logger.warn({ serverName: server.name }, 'mcp_daemon.mcp_server_down');
|
|
165
201
|
}
|
|
166
202
|
}
|
|
167
|
-
//
|
|
168
|
-
//
|
|
169
|
-
//
|
|
170
|
-
// plane — every 30s heartbeat silently 404'd and the snapshots were
|
|
171
|
-
// discarded. Restore an ingest route before re-adding an upload.
|
|
203
|
+
// statusReporter.ts uploads these through getHealthSnapshot() to
|
|
204
|
+
// POST /api/v1/mcp-daemon/report, which also records each server in the
|
|
205
|
+
// workspace's MCP server registry.
|
|
172
206
|
}, HEARTBEAT_MS);
|
|
173
207
|
timer.unref();
|
|
174
208
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"healthMonitor.js","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,KAAK,MAAM,YAAY,CAAA;AAC9B,OAAO,IAAI,MAAO,WAAW,CAAA;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AACxB,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAC7C,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAE9C,MAAM,MAAM,GAAG,YAAY,CAAC,yBAAyB,CAAC,CAAA;AAEtD,MAAM,YAAY,GAAG,MAAM,CAAA;AAC3B,MAAM,aAAa,GAAG,KAAK,CAAA;AA0B3B,MAAM,OAAO,GAAsB,EAAE,CAAA;AACrC,MAAM,YAAY,GAAG,IAAI,GAAG,EAA2B,CAAA;AACvD,IAAI,KAAK,GAA0C,IAAI,CAAA;AAEvD,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAA;AAC7E,CAAC;AAED;;;;;GAKG;AACH,SAAS,aAAa,CAAC,MAAe;IACpC,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAAE,OAAO,EAAE,CAAA;IAChC,MAAM,GAAG,GAAG,MAAM,CAAC,YAAY,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,CAAA;IACjD,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAA;AACjC,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU,CAAC,KAAc;IAChC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/B,MAAM,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,CAAA;IACxB,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,GAAG,CAAA;IACzD,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,CAAA;IAChC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,WAAW,OAAO,EAAE,CAAA;IAClF,OAAO,EAAE,CAAA;AACX,CAAC;AAED,SAAS,
|
|
1
|
+
{"version":3,"file":"healthMonitor.js","sourceRoot":"","sources":["../../src/daemon/healthMonitor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* OpenTelemetry Code-Level Instrumentation — mcp-daemon only.
|
|
3
3
|
*
|
|
4
|
-
* Same pattern as
|
|
5
|
-
*
|
|
4
|
+
* Same pattern as services/control-plane/src/instrumentation.ts.
|
|
5
|
+
* Deliberately NOT imported
|
|
6
6
|
* by src/index.ts (the `intutic-mcp-proxy` stdio JSON-RPC proxy) -- that
|
|
7
7
|
* process reserves stdout exclusively for protocol frames, and
|
|
8
8
|
* auto-instrumentations-node's console/process patching is not a risk worth
|