@provos/ironcurtain 0.1.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.
Files changed (160) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +311 -0
  3. package/dist/agent/index.d.ts +10 -0
  4. package/dist/agent/index.js +71 -0
  5. package/dist/agent/index.js.map +1 -0
  6. package/dist/agent/prompts.d.ts +5 -0
  7. package/dist/agent/prompts.js +26 -0
  8. package/dist/agent/prompts.js.map +1 -0
  9. package/dist/agent/tools.d.ts +13 -0
  10. package/dist/agent/tools.js +51 -0
  11. package/dist/agent/tools.js.map +1 -0
  12. package/dist/cli.d.ts +2 -0
  13. package/dist/cli.js +78 -0
  14. package/dist/cli.js.map +1 -0
  15. package/dist/config/constitution.md +16 -0
  16. package/dist/config/generated/compiled-policy.json +236 -0
  17. package/dist/config/generated/test-scenarios.json +765 -0
  18. package/dist/config/generated/tool-annotations.json +955 -0
  19. package/dist/config/index.d.ts +25 -0
  20. package/dist/config/index.js +151 -0
  21. package/dist/config/index.js.map +1 -0
  22. package/dist/config/mcp-servers.json +22 -0
  23. package/dist/config/model-provider.d.ts +49 -0
  24. package/dist/config/model-provider.js +78 -0
  25. package/dist/config/model-provider.js.map +1 -0
  26. package/dist/config/paths.d.ts +59 -0
  27. package/dist/config/paths.js +96 -0
  28. package/dist/config/paths.js.map +1 -0
  29. package/dist/config/types.d.ts +89 -0
  30. package/dist/config/types.js +2 -0
  31. package/dist/config/types.js.map +1 -0
  32. package/dist/config/user-config.d.ts +93 -0
  33. package/dist/config/user-config.js +309 -0
  34. package/dist/config/user-config.js.map +1 -0
  35. package/dist/hash.d.ts +17 -0
  36. package/dist/hash.js +34 -0
  37. package/dist/hash.js.map +1 -0
  38. package/dist/index.d.ts +1 -0
  39. package/dist/index.js +61 -0
  40. package/dist/index.js.map +1 -0
  41. package/dist/logger.d.ts +11 -0
  42. package/dist/logger.js +93 -0
  43. package/dist/logger.js.map +1 -0
  44. package/dist/pipeline/annotate.d.ts +9 -0
  45. package/dist/pipeline/annotate.js +136 -0
  46. package/dist/pipeline/annotate.js.map +1 -0
  47. package/dist/pipeline/compile.d.ts +23 -0
  48. package/dist/pipeline/compile.js +386 -0
  49. package/dist/pipeline/compile.js.map +1 -0
  50. package/dist/pipeline/constitution-compiler.d.ts +22 -0
  51. package/dist/pipeline/constitution-compiler.js +197 -0
  52. package/dist/pipeline/constitution-compiler.js.map +1 -0
  53. package/dist/pipeline/generate-with-repair.d.ts +22 -0
  54. package/dist/pipeline/generate-with-repair.js +64 -0
  55. package/dist/pipeline/generate-with-repair.js.map +1 -0
  56. package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
  57. package/dist/pipeline/handwritten-scenarios.js +321 -0
  58. package/dist/pipeline/handwritten-scenarios.js.map +1 -0
  59. package/dist/pipeline/llm-logger.d.ts +42 -0
  60. package/dist/pipeline/llm-logger.js +78 -0
  61. package/dist/pipeline/llm-logger.js.map +1 -0
  62. package/dist/pipeline/pipeline-shared.d.ts +47 -0
  63. package/dist/pipeline/pipeline-shared.js +145 -0
  64. package/dist/pipeline/pipeline-shared.js.map +1 -0
  65. package/dist/pipeline/policy-verifier.d.ts +46 -0
  66. package/dist/pipeline/policy-verifier.js +277 -0
  67. package/dist/pipeline/policy-verifier.js.map +1 -0
  68. package/dist/pipeline/scenario-generator.d.ts +11 -0
  69. package/dist/pipeline/scenario-generator.js +128 -0
  70. package/dist/pipeline/scenario-generator.js.map +1 -0
  71. package/dist/pipeline/tool-annotator.d.ts +24 -0
  72. package/dist/pipeline/tool-annotator.js +201 -0
  73. package/dist/pipeline/tool-annotator.js.map +1 -0
  74. package/dist/pipeline/types.d.ts +122 -0
  75. package/dist/pipeline/types.js +10 -0
  76. package/dist/pipeline/types.js.map +1 -0
  77. package/dist/sandbox/index.d.ts +39 -0
  78. package/dist/sandbox/index.js +178 -0
  79. package/dist/sandbox/index.js.map +1 -0
  80. package/dist/session/agent-session.d.ts +83 -0
  81. package/dist/session/agent-session.js +382 -0
  82. package/dist/session/agent-session.js.map +1 -0
  83. package/dist/session/cli-transport.d.ts +61 -0
  84. package/dist/session/cli-transport.js +320 -0
  85. package/dist/session/cli-transport.js.map +1 -0
  86. package/dist/session/errors.d.ts +19 -0
  87. package/dist/session/errors.js +33 -0
  88. package/dist/session/errors.js.map +1 -0
  89. package/dist/session/index.d.ts +29 -0
  90. package/dist/session/index.js +104 -0
  91. package/dist/session/index.js.map +1 -0
  92. package/dist/session/message-compactor.d.ts +32 -0
  93. package/dist/session/message-compactor.js +81 -0
  94. package/dist/session/message-compactor.js.map +1 -0
  95. package/dist/session/prompts.d.ts +5 -0
  96. package/dist/session/prompts.js +62 -0
  97. package/dist/session/prompts.js.map +1 -0
  98. package/dist/session/resource-budget-tracker.d.ts +124 -0
  99. package/dist/session/resource-budget-tracker.js +327 -0
  100. package/dist/session/resource-budget-tracker.js.map +1 -0
  101. package/dist/session/step-loop-detector.d.ts +63 -0
  102. package/dist/session/step-loop-detector.js +136 -0
  103. package/dist/session/step-loop-detector.js.map +1 -0
  104. package/dist/session/transport.d.ts +24 -0
  105. package/dist/session/transport.js +2 -0
  106. package/dist/session/transport.js.map +1 -0
  107. package/dist/session/truncate-result.d.ts +35 -0
  108. package/dist/session/truncate-result.js +71 -0
  109. package/dist/session/truncate-result.js.map +1 -0
  110. package/dist/session/types.d.ts +220 -0
  111. package/dist/session/types.js +6 -0
  112. package/dist/session/types.js.map +1 -0
  113. package/dist/trusted-process/audit-log.d.ts +7 -0
  114. package/dist/trusted-process/audit-log.js +21 -0
  115. package/dist/trusted-process/audit-log.js.map +1 -0
  116. package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
  117. package/dist/trusted-process/call-circuit-breaker.js +61 -0
  118. package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
  119. package/dist/trusted-process/escalation.d.ts +7 -0
  120. package/dist/trusted-process/escalation.js +38 -0
  121. package/dist/trusted-process/escalation.js.map +1 -0
  122. package/dist/trusted-process/index.d.ts +32 -0
  123. package/dist/trusted-process/index.js +151 -0
  124. package/dist/trusted-process/index.js.map +1 -0
  125. package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
  126. package/dist/trusted-process/mcp-client-manager.js +90 -0
  127. package/dist/trusted-process/mcp-client-manager.js.map +1 -0
  128. package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
  129. package/dist/trusted-process/mcp-proxy-server.js +451 -0
  130. package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
  131. package/dist/trusted-process/path-utils.d.ts +50 -0
  132. package/dist/trusted-process/path-utils.js +158 -0
  133. package/dist/trusted-process/path-utils.js.map +1 -0
  134. package/dist/trusted-process/policy-engine.d.ts +88 -0
  135. package/dist/trusted-process/policy-engine.js +523 -0
  136. package/dist/trusted-process/policy-engine.js.map +1 -0
  137. package/dist/trusted-process/policy-roots.d.ts +50 -0
  138. package/dist/trusted-process/policy-roots.js +67 -0
  139. package/dist/trusted-process/policy-roots.js.map +1 -0
  140. package/dist/trusted-process/policy-types.d.ts +6 -0
  141. package/dist/trusted-process/policy-types.js +2 -0
  142. package/dist/trusted-process/policy-types.js.map +1 -0
  143. package/dist/trusted-process/sandbox-integration.d.ts +92 -0
  144. package/dist/trusted-process/sandbox-integration.js +184 -0
  145. package/dist/trusted-process/sandbox-integration.js.map +1 -0
  146. package/dist/types/argument-roles.d.ts +112 -0
  147. package/dist/types/argument-roles.js +344 -0
  148. package/dist/types/argument-roles.js.map +1 -0
  149. package/dist/types/audit.d.ts +18 -0
  150. package/dist/types/audit.js +2 -0
  151. package/dist/types/audit.js.map +1 -0
  152. package/dist/types/mcp.d.ts +20 -0
  153. package/dist/types/mcp.js +2 -0
  154. package/dist/types/mcp.js.map +1 -0
  155. package/package.json +83 -0
  156. package/src/config/constitution.md +16 -0
  157. package/src/config/generated/compiled-policy.json +236 -0
  158. package/src/config/generated/test-scenarios.json +765 -0
  159. package/src/config/generated/tool-annotations.json +955 -0
  160. package/src/config/mcp-servers.json +22 -0
@@ -0,0 +1,61 @@
1
+ /**
2
+ * CallCircuitBreaker -- Proxy-level rate limiter for MCP tool calls.
3
+ *
4
+ * Protects against runaway sandbox code that hammers the same tool
5
+ * with identical arguments. Uses a sliding-window approach: if the
6
+ * same (tool, argsHash) pair appears more than `threshold` times
7
+ * within `windowMs`, the call is denied.
8
+ *
9
+ * Runs AFTER policy evaluation so every call is always audited.
10
+ */
11
+ import { computeHash } from '../hash.js';
12
+ const DEFAULT_CONFIG = {
13
+ windowMs: 60_000,
14
+ threshold: 20,
15
+ };
16
+ export class CallCircuitBreaker {
17
+ config;
18
+ windows = new Map();
19
+ constructor(config) {
20
+ this.config = { ...DEFAULT_CONFIG, ...config };
21
+ }
22
+ /**
23
+ * Check whether a tool call should be allowed.
24
+ *
25
+ * @returns `{ allowed: true }` or `{ allowed: false, reason: string }`
26
+ */
27
+ check(toolName, args) {
28
+ const argsHash = computeHash(args);
29
+ const key = `${toolName}:${argsHash}`;
30
+ const now = Date.now();
31
+ const cutoff = now - this.config.windowMs;
32
+ let timestamps = this.windows.get(key);
33
+ if (!timestamps) {
34
+ timestamps = [];
35
+ this.windows.set(key, timestamps);
36
+ }
37
+ // Prune entries older than the window
38
+ const firstValid = timestamps.findIndex((t) => t > cutoff);
39
+ if (firstValid > 0) {
40
+ timestamps.splice(0, firstValid);
41
+ }
42
+ else if (firstValid === -1) {
43
+ timestamps.length = 0;
44
+ }
45
+ if (timestamps.length >= this.config.threshold) {
46
+ return {
47
+ allowed: false,
48
+ reason: `CIRCUIT BREAKER: Tool '${toolName}' called ${this.config.threshold} times ` +
49
+ `with identical arguments within ${this.config.windowMs / 1000}s window. ` +
50
+ 'Try a different approach.',
51
+ };
52
+ }
53
+ timestamps.push(now);
54
+ return { allowed: true };
55
+ }
56
+ /** Reset all state. */
57
+ reset() {
58
+ this.windows.clear();
59
+ }
60
+ }
61
+ //# sourceMappingURL=call-circuit-breaker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"call-circuit-breaker.js","sourceRoot":"","sources":["../../src/trusted-process/call-circuit-breaker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAOzC,MAAM,cAAc,GAAyB;IAC3C,QAAQ,EAAE,MAAM;IAChB,SAAS,EAAE,EAAE;CACd,CAAC;AAMF,MAAM,OAAO,kBAAkB;IACZ,MAAM,CAAuB;IACtC,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;IAE9C,YAAY,MAAsC;QAChD,IAAI,CAAC,MAAM,GAAG,EAAE,GAAG,cAAc,EAAE,GAAG,MAAM,EAAE,CAAC;IACjD,CAAC;IAED;;;;OAIG;IACH,KAAK,CACH,QAAgB,EAChB,IAA6B;QAE7B,MAAM,QAAQ,GAAG,WAAW,CAAC,IAAI,CAAC,CAAC;QACnC,MAAM,GAAG,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;QAE1C,IAAI,UAAU,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,IAAI,CAAC,UAAU,EAAE,CAAC;YAChB,UAAU,GAAG,EAAE,CAAC;YAChB,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;QACpC,CAAC;QAED,sCAAsC;QACtC,MAAM,UAAU,GAAG,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;QAC3D,IAAI,UAAU,GAAG,CAAC,EAAE,CAAC;YACnB,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC;QACnC,CAAC;aAAM,IAAI,UAAU,KAAK,CAAC,CAAC,EAAE,CAAC;YAC7B,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;QACxB,CAAC;QAED,IAAI,UAAU,CAAC,MAAM,IAAI,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAC/C,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EACJ,0BAA0B,QAAQ,YAAY,IAAI,CAAC,MAAM,CAAC,SAAS,SAAS;oBAC5E,mCAAmC,IAAI,CAAC,MAAM,CAAC,QAAQ,GAAG,IAAI,YAAY;oBAC1E,2BAA2B;aAC9B,CAAC;QACJ,CAAC;QAED,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC3B,CAAC;IAED,uBAAuB;IACvB,KAAK;QACH,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IACvB,CAAC;CACF"}
@@ -0,0 +1,7 @@
1
+ import type { ToolCallRequest } from '../types/mcp.js';
2
+ export declare class EscalationHandler {
3
+ private rl;
4
+ private getReadline;
5
+ prompt(request: ToolCallRequest, reason: string): Promise<'approved' | 'denied'>;
6
+ close(): void;
7
+ }
@@ -0,0 +1,38 @@
1
+ import { createInterface } from 'node:readline';
2
+ export class EscalationHandler {
3
+ rl = null;
4
+ getReadline() {
5
+ if (!this.rl) {
6
+ this.rl = createInterface({
7
+ input: process.stdin,
8
+ output: process.stderr,
9
+ });
10
+ }
11
+ return this.rl;
12
+ }
13
+ async prompt(request, reason) {
14
+ const rl = this.getReadline();
15
+ process.stderr.write('\n========================================\n');
16
+ process.stderr.write(' ESCALATION: Human approval required\n');
17
+ process.stderr.write('========================================\n');
18
+ process.stderr.write(` Tool: ${request.serverName}/${request.toolName}\n`);
19
+ process.stderr.write(` Arguments: ${JSON.stringify(request.arguments, null, 2)}\n`);
20
+ process.stderr.write(` Reason: ${reason}\n`);
21
+ process.stderr.write('========================================\n');
22
+ return new Promise((resolve) => {
23
+ rl.question(' Approve? (y/N): ', (answer) => {
24
+ const approved = answer.trim().toLowerCase() === 'y';
25
+ process.stderr.write(approved ? ' -> APPROVED\n' : ' -> DENIED\n');
26
+ process.stderr.write('========================================\n\n');
27
+ resolve(approved ? 'approved' : 'denied');
28
+ });
29
+ });
30
+ }
31
+ close() {
32
+ if (this.rl) {
33
+ this.rl.close();
34
+ this.rl = null;
35
+ }
36
+ }
37
+ }
38
+ //# sourceMappingURL=escalation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"escalation.js","sourceRoot":"","sources":["../../src/trusted-process/escalation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAkB,MAAM,eAAe,CAAC;AAGhE,MAAM,OAAO,iBAAiB;IACpB,EAAE,GAAqB,IAAI,CAAC;IAE5B,WAAW;QACjB,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;YACb,IAAI,CAAC,EAAE,GAAG,eAAe,CAAC;gBACxB,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,MAAM,EAAE,OAAO,CAAC,MAAM;aACvB,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,CAAC,EAAE,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,OAAwB,EAAE,MAAc;QACnD,MAAM,EAAE,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;QAE9B,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;QACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;QAChE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QACnE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,QAAQ,IAAI,CAAC,CAAC;QACjF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC;QACrF,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,gBAAgB,MAAM,IAAI,CAAC,CAAC;QACjD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;QAEnE,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;YAC7B,EAAE,CAAC,QAAQ,CAAC,oBAAoB,EAAE,CAAC,MAAM,EAAE,EAAE;gBAC3C,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,KAAK,GAAG,CAAC;gBACrD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC;gBACrE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;gBACrE,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC5C,CAAC,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK;QACH,IAAI,IAAI,CAAC,EAAE,EAAE,CAAC;YACZ,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAChB,IAAI,CAAC,EAAE,GAAG,IAAI,CAAC;QACjB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,32 @@
1
+ import type { IronCurtainConfig } from '../config/types.js';
2
+ import type { ToolCallRequest, ToolCallResult } from '../types/mcp.js';
3
+ export type EscalationPromptFn = (request: ToolCallRequest, reason: string) => Promise<'approved' | 'denied'>;
4
+ export interface TrustedProcessOptions {
5
+ onEscalation?: EscalationPromptFn;
6
+ }
7
+ export declare class TrustedProcess {
8
+ private config;
9
+ private policyEngine;
10
+ private mcpManager;
11
+ private mcpRoots;
12
+ private auditLog;
13
+ private escalation;
14
+ private onEscalation?;
15
+ constructor(config: IronCurtainConfig, options?: TrustedProcessOptions);
16
+ initialize(): Promise<void>;
17
+ listTools(serverName: string): Promise<{
18
+ name: string;
19
+ description?: string;
20
+ inputSchema: unknown;
21
+ }[]>;
22
+ /**
23
+ * Handles a tool call request through the full trusted process lifecycle:
24
+ * 1. Evaluate policy (allow / deny / escalate)
25
+ * 2. If escalated, prompt the human for approval
26
+ * 3. If allowed, forward to the real MCP server
27
+ * 4. Log the complete decision trace to the audit log
28
+ * 5. Return the result to the caller
29
+ */
30
+ handleToolCall(request: ToolCallRequest): Promise<ToolCallResult>;
31
+ shutdown(): Promise<void>;
32
+ }
@@ -0,0 +1,151 @@
1
+ import { loadGeneratedPolicy, extractServerDomainAllowlists } from '../config/index.js';
2
+ import { PolicyEngine } from './policy-engine.js';
3
+ import { MCPClientManager } from './mcp-client-manager.js';
4
+ import { AuditLog } from './audit-log.js';
5
+ import { EscalationHandler } from './escalation.js';
6
+ import { prepareToolArgs } from './path-utils.js';
7
+ import { extractPolicyRoots, toMcpRoots, directoryForPath } from './policy-roots.js';
8
+ import * as logger from '../logger.js';
9
+ export class TrustedProcess {
10
+ config;
11
+ policyEngine;
12
+ mcpManager;
13
+ mcpRoots;
14
+ auditLog;
15
+ escalation;
16
+ onEscalation;
17
+ constructor(config, options) {
18
+ this.config = config;
19
+ const { compiledPolicy, toolAnnotations } = loadGeneratedPolicy(config.generatedDir);
20
+ const serverDomainAllowlists = extractServerDomainAllowlists(config.mcpServers);
21
+ this.policyEngine = new PolicyEngine(compiledPolicy, toolAnnotations, config.protectedPaths, config.allowedDirectory, serverDomainAllowlists);
22
+ const policyRoots = extractPolicyRoots(compiledPolicy, config.allowedDirectory);
23
+ this.mcpRoots = toMcpRoots(policyRoots);
24
+ this.mcpManager = new MCPClientManager();
25
+ this.auditLog = new AuditLog(config.auditLogPath);
26
+ this.escalation = new EscalationHandler();
27
+ this.onEscalation = options?.onEscalation;
28
+ }
29
+ async initialize() {
30
+ for (const [name, serverConfig] of Object.entries(this.config.mcpServers)) {
31
+ logger.info(`Connecting to MCP server: ${name}...`);
32
+ await this.mcpManager.connect(name, serverConfig, this.mcpRoots);
33
+ logger.info(`Connected to MCP server: ${name}`);
34
+ }
35
+ }
36
+ async listTools(serverName) {
37
+ return this.mcpManager.listTools(serverName);
38
+ }
39
+ /**
40
+ * Handles a tool call request through the full trusted process lifecycle:
41
+ * 1. Evaluate policy (allow / deny / escalate)
42
+ * 2. If escalated, prompt the human for approval
43
+ * 3. If allowed, forward to the real MCP server
44
+ * 4. Log the complete decision trace to the audit log
45
+ * 5. Return the result to the caller
46
+ */
47
+ async handleToolCall(request) {
48
+ const startTime = Date.now();
49
+ // Annotation-driven normalization: split into transport vs policy args
50
+ const annotation = this.policyEngine.getAnnotation(request.serverName, request.toolName);
51
+ const { argsForTransport, argsForPolicy } = prepareToolArgs(request.arguments, annotation, this.config.allowedDirectory);
52
+ const policyRequest = { ...request, arguments: argsForPolicy };
53
+ const transportRequest = { ...request, arguments: argsForTransport };
54
+ // Step 1: Evaluate request against the policy rule chain
55
+ const evaluation = this.policyEngine.evaluate(policyRequest);
56
+ const policyDecision = {
57
+ status: evaluation.decision,
58
+ rule: evaluation.rule,
59
+ reason: evaluation.reason,
60
+ };
61
+ let escalationResult;
62
+ let resultContent;
63
+ let resultStatus;
64
+ let resultError;
65
+ try {
66
+ // Step 2: Handle escalation via human approval
67
+ if (evaluation.decision === 'escalate') {
68
+ escalationResult = this.onEscalation
69
+ ? await this.onEscalation(transportRequest, evaluation.reason)
70
+ : await this.escalation.prompt(transportRequest, evaluation.reason);
71
+ if (escalationResult === 'approved') {
72
+ policyDecision.status = 'allow';
73
+ policyDecision.reason = 'Approved by human during escalation';
74
+ // Expand roots to include target directories so the filesystem
75
+ // server accepts the forwarded call.
76
+ const paths = Object.values(transportRequest.arguments).filter((v) => typeof v === 'string');
77
+ for (const p of paths) {
78
+ const dir = directoryForPath(p);
79
+ await this.mcpManager.addRoot(transportRequest.serverName, {
80
+ uri: `file://${dir}`,
81
+ name: 'escalation-approved',
82
+ });
83
+ }
84
+ }
85
+ else {
86
+ policyDecision.status = 'deny';
87
+ policyDecision.reason = 'Denied by human during escalation';
88
+ }
89
+ }
90
+ // Step 3: Forward to MCP server or deny (using transport args)
91
+ if (policyDecision.status === 'allow') {
92
+ const mcpResult = await this.mcpManager.callTool(transportRequest.serverName, transportRequest.toolName, transportRequest.arguments);
93
+ resultContent = mcpResult;
94
+ if (mcpResult.isError) {
95
+ resultStatus = 'error';
96
+ const content = mcpResult.content;
97
+ if (Array.isArray(content)) {
98
+ resultError = content
99
+ .filter((c) => c.type === 'text' && typeof c.text === 'string')
100
+ .map((c) => c.text)
101
+ .join('\n');
102
+ }
103
+ }
104
+ else {
105
+ resultStatus = 'success';
106
+ }
107
+ }
108
+ else {
109
+ resultContent = { denied: true, reason: policyDecision.reason };
110
+ resultStatus = 'denied';
111
+ }
112
+ }
113
+ catch (err) {
114
+ resultStatus = 'error';
115
+ resultError = err instanceof Error ? err.message : String(err);
116
+ resultContent = { error: resultError };
117
+ }
118
+ const durationMs = Date.now() - startTime;
119
+ // Step 4: Append-only audit log (records argsForTransport -- what was sent to MCP server)
120
+ const auditEntry = {
121
+ timestamp: new Date().toISOString(),
122
+ requestId: transportRequest.requestId,
123
+ serverName: transportRequest.serverName,
124
+ toolName: transportRequest.toolName,
125
+ arguments: transportRequest.arguments,
126
+ policyDecision,
127
+ escalationResult,
128
+ result: {
129
+ status: resultStatus,
130
+ content: resultStatus === 'success' ? resultContent : undefined,
131
+ error: resultError,
132
+ },
133
+ durationMs,
134
+ };
135
+ this.auditLog.log(auditEntry);
136
+ // Step 5: Return result to caller
137
+ return {
138
+ requestId: transportRequest.requestId,
139
+ status: resultStatus,
140
+ content: resultContent,
141
+ policyDecision,
142
+ durationMs,
143
+ };
144
+ }
145
+ async shutdown() {
146
+ this.escalation.close();
147
+ await this.mcpManager.closeAll();
148
+ await this.auditLog.close();
149
+ }
150
+ }
151
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/trusted-process/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ import type { MCPServerConfig } from '../config/types.js';
2
+ export interface McpRoot {
3
+ uri: string;
4
+ name: string;
5
+ }
6
+ export declare class MCPClientManager {
7
+ private servers;
8
+ connect(name: string, config: MCPServerConfig, roots?: McpRoot[]): Promise<void>;
9
+ /**
10
+ * Adds a root directory to a connected server and waits for the
11
+ * server to fetch the updated root list. This ensures the server's
12
+ * allowed directories include the new root before any tool call
13
+ * that depends on it is forwarded.
14
+ *
15
+ * No-op if the root URI is already present.
16
+ */
17
+ addRoot(serverName: string, root: McpRoot): Promise<void>;
18
+ listTools(serverName: string): Promise<{
19
+ name: string;
20
+ description?: string;
21
+ inputSchema: unknown;
22
+ }[]>;
23
+ callTool(serverName: string, toolName: string, args: Record<string, unknown>): Promise<unknown>;
24
+ closeAll(): Promise<void>;
25
+ }
@@ -0,0 +1,90 @@
1
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
2
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
3
+ import { CompatibilityCallToolResultSchema, ListRootsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
4
+ import * as logger from '../logger.js';
5
+ export class MCPClientManager {
6
+ servers = new Map();
7
+ async connect(name, config, roots) {
8
+ const transport = new StdioClientTransport({
9
+ command: config.command,
10
+ args: config.args,
11
+ env: config.env ? { ...process.env, ...config.env } : undefined,
12
+ });
13
+ const client = new Client({ name: 'ironcurtain', version: '0.1.0' }, roots ? { capabilities: { roots: { listChanged: true } } } : {});
14
+ // Mutable copy -- addRoot() pushes to this array.
15
+ const mutableRoots = roots ? [...roots] : undefined;
16
+ const managed = { client, transport, roots: mutableRoots };
17
+ // When the server asks for roots, return the current set.
18
+ // If a rootsRefreshed callback is registered (from addRoot),
19
+ // resolve it so the caller knows the server has the latest roots.
20
+ if (mutableRoots) {
21
+ client.setRequestHandler(ListRootsRequestSchema, async () => {
22
+ if (managed.rootsRefreshed) {
23
+ managed.rootsRefreshed();
24
+ managed.rootsRefreshed = undefined;
25
+ }
26
+ return { roots: mutableRoots };
27
+ });
28
+ }
29
+ await client.connect(transport);
30
+ this.servers.set(name, managed);
31
+ }
32
+ /**
33
+ * Adds a root directory to a connected server and waits for the
34
+ * server to fetch the updated root list. This ensures the server's
35
+ * allowed directories include the new root before any tool call
36
+ * that depends on it is forwarded.
37
+ *
38
+ * No-op if the root URI is already present.
39
+ */
40
+ async addRoot(serverName, root) {
41
+ const server = this.servers.get(serverName);
42
+ if (!server?.roots)
43
+ return;
44
+ // Deduplicate
45
+ if (server.roots.some(r => r.uri === root.uri))
46
+ return;
47
+ server.roots.push(root);
48
+ // Wait for the server to call roots/list after we notify it.
49
+ const refreshed = new Promise(resolve => {
50
+ server.rootsRefreshed = resolve;
51
+ });
52
+ await server.client.sendRootsListChanged();
53
+ await refreshed;
54
+ }
55
+ async listTools(serverName) {
56
+ const server = this.servers.get(serverName);
57
+ if (!server) {
58
+ throw new Error(`MCP server "${serverName}" not connected`);
59
+ }
60
+ const result = await server.client.listTools();
61
+ return result.tools.map(tool => ({
62
+ name: tool.name,
63
+ description: tool.description,
64
+ inputSchema: tool.inputSchema,
65
+ }));
66
+ }
67
+ async callTool(serverName, toolName, args) {
68
+ const server = this.servers.get(serverName);
69
+ if (!server) {
70
+ throw new Error(`MCP server "${serverName}" not connected`);
71
+ }
72
+ // TODO(workaround): Remove once @cyanheads/git-mcp-server fixes outputSchema declarations.
73
+ // See the detailed comment in mcp-proxy-server.ts for full context.
74
+ // CompatibilityCallToolResultSchema bypasses client-side output schema validation that
75
+ // fails when servers declare outputSchema but return non-conforming structuredContent.
76
+ return server.client.callTool({ name: toolName, arguments: args }, CompatibilityCallToolResultSchema);
77
+ }
78
+ async closeAll() {
79
+ for (const [name, server] of this.servers) {
80
+ try {
81
+ await server.client.close();
82
+ }
83
+ catch (err) {
84
+ logger.error(`Error closing MCP server "${name}": ${err}`);
85
+ }
86
+ }
87
+ this.servers.clear();
88
+ }
89
+ }
90
+ //# sourceMappingURL=mcp-client-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-client-manager.js","sourceRoot":"","sources":["../../src/trusted-process/mcp-client-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * MCP Proxy Server -- The trusted process running as a standalone MCP server.
3
+ *
4
+ * Code Mode spawns this as a child process via stdio transport. It acts
5
+ * as the security boundary between the sandbox and real MCP servers:
6
+ *
7
+ * 1. Connects to real MCP servers as a client
8
+ * 2. Exposes their tools with passthrough schemas
9
+ * 3. Evaluates every tool call against the policy engine
10
+ * 4. Forwards allowed calls to real servers, denies or escalates others
11
+ * 5. Logs every request and decision to the append-only audit log
12
+ *
13
+ * Configuration via environment variables:
14
+ * AUDIT_LOG_PATH -- path to the audit log file
15
+ * MCP_SERVERS_CONFIG -- JSON string of MCP server configs to proxy
16
+ * GENERATED_DIR -- path to the generated artifacts directory
17
+ * PROTECTED_PATHS -- JSON array of protected paths
18
+ * ALLOWED_DIRECTORY -- (optional) sandbox directory for structural containment check
19
+ * ESCALATION_DIR -- (optional) directory for file-based escalation IPC
20
+ * SESSION_LOG_PATH -- (optional) path for capturing child process stderr
21
+ * SANDBOX_POLICY -- (optional) "enforce" | "warn" (default: "warn")
22
+ * SERVER_FILTER -- (optional) when set, only connect to this single server name
23
+ */
24
+ export {};