@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,451 @@
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
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
25
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
26
+ import { CallToolRequestSchema, CompatibilityCallToolResultSchema, ListToolsRequestSchema, ListRootsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
27
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
28
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
29
+ import { appendFileSync, existsSync, mkdtempSync, readFileSync, writeFileSync, unlinkSync } from 'node:fs';
30
+ import { join, resolve } from 'node:path';
31
+ import { tmpdir } from 'node:os';
32
+ import { v4 as uuidv4 } from 'uuid';
33
+ import { loadGeneratedPolicy, extractServerDomainAllowlists } from '../config/index.js';
34
+ import { PolicyEngine } from './policy-engine.js';
35
+ import { AuditLog } from './audit-log.js';
36
+ import { prepareToolArgs } from './path-utils.js';
37
+ import { extractPolicyRoots, toMcpRoots, directoryForPath } from './policy-roots.js';
38
+ import { checkSandboxAvailability, resolveSandboxConfig, writeServerSettings, wrapServerCommand, cleanupSettingsFiles, annotateSandboxViolation, } from './sandbox-integration.js';
39
+ import { CallCircuitBreaker } from './call-circuit-breaker.js';
40
+ /**
41
+ * Adds a root to a client's root list and waits for the server to
42
+ * fetch the updated list. No-op if the root URI is already present.
43
+ */
44
+ async function addRootToClient(state, root) {
45
+ if (state.roots.some(r => r.uri === root.uri))
46
+ return;
47
+ state.roots.push(root);
48
+ const refreshed = new Promise(resolve => {
49
+ state.rootsRefreshed = resolve;
50
+ });
51
+ await state.client.sendRootsListChanged();
52
+ await refreshed;
53
+ }
54
+ /** Appends a timestamped line to the session log file. */
55
+ function logToSessionFile(sessionLogPath, message) {
56
+ const timestamp = new Date().toISOString();
57
+ try {
58
+ appendFileSync(sessionLogPath, `${timestamp} INFO ${message}\n`);
59
+ }
60
+ catch { /* ignore write failures */ }
61
+ }
62
+ /** Extracts concatenated text from MCP content blocks. */
63
+ function extractTextFromContent(content) {
64
+ if (!Array.isArray(content))
65
+ return undefined;
66
+ const texts = content
67
+ .filter((c) => c.type === 'text' && typeof c.text === 'string')
68
+ .map((c) => c.text);
69
+ return texts.length > 0 ? texts.join('\n') : undefined;
70
+ }
71
+ const ESCALATION_POLL_INTERVAL_MS = 500;
72
+ const DEFAULT_ESCALATION_TIMEOUT_SECONDS = 300;
73
+ /** Reads escalation timeout from env var, falling back to default. */
74
+ function getEscalationTimeoutMs() {
75
+ const envValue = process.env.ESCALATION_TIMEOUT_SECONDS;
76
+ if (envValue) {
77
+ const parsed = Number(envValue);
78
+ if (Number.isFinite(parsed) && parsed > 0) {
79
+ return parsed * 1000;
80
+ }
81
+ }
82
+ return DEFAULT_ESCALATION_TIMEOUT_SECONDS * 1000;
83
+ }
84
+ /**
85
+ * Waits for a human decision via file-based IPC.
86
+ *
87
+ * Writes a request file to the escalation directory, then polls
88
+ * for a response file. The session process (on the other side)
89
+ * detects the request, surfaces it to the user, and writes the response.
90
+ *
91
+ * Returns 'approved' or 'denied'. On timeout, returns 'denied'.
92
+ */
93
+ async function waitForEscalationDecision(escalationDir, request) {
94
+ const requestPath = resolve(escalationDir, `request-${request.escalationId}.json`);
95
+ const responsePath = resolve(escalationDir, `response-${request.escalationId}.json`);
96
+ writeFileSync(requestPath, JSON.stringify(request));
97
+ const deadline = Date.now() + getEscalationTimeoutMs();
98
+ while (Date.now() < deadline) {
99
+ if (existsSync(responsePath)) {
100
+ const response = JSON.parse(readFileSync(responsePath, 'utf-8'));
101
+ // Clean up both files
102
+ try {
103
+ unlinkSync(requestPath);
104
+ }
105
+ catch { /* ignore */ }
106
+ try {
107
+ unlinkSync(responsePath);
108
+ }
109
+ catch { /* ignore */ }
110
+ return response.decision;
111
+ }
112
+ await new Promise((r) => setTimeout(r, ESCALATION_POLL_INTERVAL_MS));
113
+ }
114
+ // Timeout -- clean up request file and treat as denied
115
+ try {
116
+ unlinkSync(requestPath);
117
+ }
118
+ catch { /* ignore */ }
119
+ return 'denied';
120
+ }
121
+ async function main() {
122
+ const auditLogPath = process.env.AUDIT_LOG_PATH ?? './audit.jsonl';
123
+ const serversConfigJson = process.env.MCP_SERVERS_CONFIG;
124
+ const generatedDir = process.env.GENERATED_DIR;
125
+ const protectedPathsJson = process.env.PROTECTED_PATHS ?? '[]';
126
+ const sessionLogPath = process.env.SESSION_LOG_PATH;
127
+ const allowedDirectory = process.env.ALLOWED_DIRECTORY;
128
+ const escalationDir = process.env.ESCALATION_DIR;
129
+ if (!serversConfigJson) {
130
+ process.stderr.write('MCP_SERVERS_CONFIG environment variable is required\n');
131
+ process.exit(1);
132
+ }
133
+ if (!generatedDir) {
134
+ process.stderr.write('GENERATED_DIR environment variable is required\n');
135
+ process.exit(1);
136
+ }
137
+ const sandboxPolicy = (process.env.SANDBOX_POLICY ?? 'warn');
138
+ const allServersConfig = JSON.parse(serversConfigJson);
139
+ const protectedPaths = JSON.parse(protectedPathsJson);
140
+ // When SERVER_FILTER is set, only connect to that single backend server.
141
+ // This allows per-server proxy processes with clean tool naming.
142
+ const serverFilter = process.env.SERVER_FILTER;
143
+ const serversConfig = serverFilter
144
+ ? { [serverFilter]: allServersConfig[serverFilter] }
145
+ : allServersConfig;
146
+ if (serverFilter && !allServersConfig[serverFilter]) {
147
+ process.stderr.write(`SERVER_FILTER: unknown server "${serverFilter}"\n`);
148
+ process.exit(1);
149
+ }
150
+ const { compiledPolicy, toolAnnotations } = loadGeneratedPolicy(generatedDir);
151
+ const serverDomainAllowlists = extractServerDomainAllowlists(serversConfig);
152
+ const policyEngine = new PolicyEngine(compiledPolicy, toolAnnotations, protectedPaths, allowedDirectory, serverDomainAllowlists);
153
+ const auditLog = new AuditLog(auditLogPath);
154
+ const circuitBreaker = new CallCircuitBreaker();
155
+ // Compute initial roots from compiled policy for the MCP Roots protocol
156
+ const policyRoots = extractPolicyRoots(compiledPolicy, allowedDirectory ?? '/tmp');
157
+ const mcpRoots = toMcpRoots(policyRoots);
158
+ // ── Sandbox availability check (once for all servers) ──────────────
159
+ const { platformSupported, errors: depErrors, warnings: depWarnings } = checkSandboxAvailability();
160
+ if (sessionLogPath) {
161
+ for (const warning of depWarnings) {
162
+ logToSessionFile(sessionLogPath, `[sandbox] WARNING: ${warning}`);
163
+ }
164
+ }
165
+ if (sandboxPolicy === 'enforce' && (!platformSupported || depErrors.length > 0)) {
166
+ const reasons = !platformSupported
167
+ ? [`Platform ${process.platform} not supported`]
168
+ : depErrors;
169
+ throw new Error(`[sandbox] FATAL: sandboxPolicy is "enforce" but sandboxing is unavailable:\n` +
170
+ reasons.map(r => ` - ${r}`).join('\n') + '\n' +
171
+ `Install with: sudo apt-get install -y bubblewrap socat`);
172
+ }
173
+ const sandboxAvailable = platformSupported && depErrors.length === 0;
174
+ if (!sandboxAvailable && sessionLogPath) {
175
+ const missing = depErrors.length > 0 ? depErrors.join(', ') : `platform ${process.platform}`;
176
+ logToSessionFile(sessionLogPath, `[sandbox] WARNING: OS-level sandboxing unavailable (${missing}). ` +
177
+ `Servers will run without OS containment. ` +
178
+ `Set SANDBOX_POLICY=enforce to require sandboxing.`);
179
+ }
180
+ // ── Resolve sandbox configs and write per-server srt settings ─────
181
+ const resolvedSandboxConfigs = new Map();
182
+ const settingsDir = mkdtempSync(join(tmpdir(), 'ironcurtain-srt-'));
183
+ for (const [serverName, config] of Object.entries(serversConfig)) {
184
+ const resolved = resolveSandboxConfig(config, allowedDirectory ?? '/tmp', sandboxAvailable, sandboxPolicy);
185
+ resolvedSandboxConfigs.set(serverName, resolved);
186
+ if (resolved.sandboxed) {
187
+ writeServerSettings(serverName, resolved.config, settingsDir);
188
+ }
189
+ }
190
+ const clientStates = new Map();
191
+ // Connect to real MCP servers as clients, wrapping sandboxed ones with srt
192
+ const allTools = [];
193
+ for (const [serverName, config] of Object.entries(serversConfig)) {
194
+ const resolved = resolvedSandboxConfigs.get(serverName);
195
+ const wrapped = wrapServerCommand(serverName, config.command, config.args, resolved, settingsDir);
196
+ const transport = new StdioClientTransport({
197
+ command: wrapped.command,
198
+ args: wrapped.args,
199
+ // Always pass full process.env -- never rely on getDefaultEnvironment()
200
+ // which strips vars that srt and MCP servers may need
201
+ env: { ...process.env, ...(config.env ?? {}) },
202
+ stderr: 'pipe', // Prevent child server stderr from leaking to the terminal
203
+ // Sandboxed servers get the sandbox dir as cwd so relative-path writes
204
+ // (e.g., log files) land inside the writable sandbox instead of failing
205
+ // with EROFS on the read-only host filesystem.
206
+ ...(resolved.sandboxed && allowedDirectory ? { cwd: allowedDirectory } : {}),
207
+ });
208
+ // Drain the piped stderr to prevent buffer backpressure from blocking
209
+ // the child process. Write output to the session log if configured.
210
+ if (transport.stderr) {
211
+ transport.stderr.on('data', (chunk) => {
212
+ if (sessionLogPath) {
213
+ const lines = chunk.toString().trimEnd();
214
+ if (lines) {
215
+ const timestamp = new Date().toISOString();
216
+ try {
217
+ appendFileSync(sessionLogPath, `${timestamp} INFO [mcp:${serverName}] ${lines}\n`);
218
+ }
219
+ catch { /* ignore write failures */ }
220
+ }
221
+ }
222
+ });
223
+ }
224
+ const client = new Client({ name: 'ironcurtain-proxy', version: '0.1.0' }, { capabilities: { roots: { listChanged: true } } });
225
+ // Mutable copy per client -- root expansion pushes to this array
226
+ const state = { client, roots: [...mcpRoots] };
227
+ // When the server asks for roots, return the current set.
228
+ // If a rootsRefreshed callback is registered (from escalation-triggered
229
+ // root expansion), resolve it so the caller knows the server has
230
+ // the latest roots.
231
+ client.setRequestHandler(ListRootsRequestSchema, async () => {
232
+ if (state.rootsRefreshed) {
233
+ state.rootsRefreshed();
234
+ state.rootsRefreshed = undefined;
235
+ }
236
+ return { roots: state.roots };
237
+ });
238
+ await client.connect(transport);
239
+ clientStates.set(serverName, state);
240
+ const result = await client.listTools();
241
+ for (const tool of result.tools) {
242
+ allTools.push({
243
+ serverName,
244
+ name: tool.name,
245
+ description: tool.description,
246
+ inputSchema: tool.inputSchema,
247
+ });
248
+ }
249
+ }
250
+ // Build a lookup map for routing tool calls
251
+ const toolMap = new Map();
252
+ for (const tool of allTools) {
253
+ toolMap.set(tool.name, tool);
254
+ }
255
+ // Create the proxy MCP server using the low-level Server API
256
+ // so we can pass through raw JSON schemas without Zod conversion
257
+ const server = new Server({ name: 'ironcurtain-proxy', version: '0.1.0' }, { capabilities: { tools: {} } });
258
+ // Handle tools/list -- return all proxied tool schemas verbatim
259
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
260
+ return {
261
+ tools: allTools.map((t) => ({
262
+ name: t.name,
263
+ description: t.description,
264
+ inputSchema: t.inputSchema,
265
+ })),
266
+ };
267
+ });
268
+ // Handle tools/call -- evaluate policy, then forward or deny
269
+ server.setRequestHandler(CallToolRequestSchema, async (req) => {
270
+ const toolName = req.params.name;
271
+ const rawArgs = (req.params.arguments ?? {});
272
+ const toolInfo = toolMap.get(toolName);
273
+ if (!toolInfo) {
274
+ return {
275
+ content: [{ type: 'text', text: `Unknown tool: ${toolName}` }],
276
+ isError: true,
277
+ };
278
+ }
279
+ // Annotation-driven normalization: split into transport vs policy args
280
+ const annotation = policyEngine.getAnnotation(toolInfo.serverName, toolInfo.name);
281
+ const { argsForTransport, argsForPolicy } = prepareToolArgs(rawArgs, annotation, allowedDirectory);
282
+ const request = {
283
+ requestId: uuidv4(),
284
+ serverName: toolInfo.serverName,
285
+ toolName: toolInfo.name,
286
+ arguments: argsForPolicy,
287
+ timestamp: new Date().toISOString(),
288
+ };
289
+ const evaluation = policyEngine.evaluate(request);
290
+ const policyDecision = {
291
+ status: evaluation.decision,
292
+ rule: evaluation.rule,
293
+ reason: evaluation.reason,
294
+ };
295
+ // Tracks the escalation outcome for audit logging when an approved
296
+ // escalation falls through to the forwarding section below.
297
+ let escalationResult;
298
+ // Look up whether this server is sandboxed for audit logging
299
+ const serverSandboxConfig = resolvedSandboxConfigs.get(toolInfo.serverName);
300
+ const serverIsSandboxed = serverSandboxConfig?.sandboxed === true;
301
+ // Audit log records argsForTransport (what was actually sent to the MCP server)
302
+ function logAudit(result, durationMs, overrideEscalation) {
303
+ const entry = {
304
+ timestamp: request.timestamp,
305
+ requestId: request.requestId,
306
+ serverName: request.serverName,
307
+ toolName: request.toolName,
308
+ arguments: argsForTransport,
309
+ policyDecision,
310
+ escalationResult: overrideEscalation ?? escalationResult,
311
+ result,
312
+ durationMs,
313
+ sandboxed: serverIsSandboxed || undefined,
314
+ };
315
+ auditLog.log(entry);
316
+ }
317
+ if (evaluation.decision === 'escalate') {
318
+ if (!escalationDir) {
319
+ // No escalation directory configured -- auto-deny (backward compatible)
320
+ logAudit({ status: 'denied', error: evaluation.reason }, 0, 'denied');
321
+ return {
322
+ content: [{
323
+ type: 'text',
324
+ text: `ESCALATION REQUIRED: ${evaluation.reason}. Action denied (no escalation handler).`,
325
+ }],
326
+ isError: true,
327
+ };
328
+ }
329
+ // File-based escalation rendezvous: write request, poll for response
330
+ const escalationId = uuidv4();
331
+ const decision = await waitForEscalationDecision(escalationDir, {
332
+ escalationId,
333
+ serverName: request.serverName,
334
+ toolName: request.toolName,
335
+ arguments: argsForTransport,
336
+ reason: evaluation.reason,
337
+ });
338
+ if (decision === 'denied') {
339
+ logAudit({ status: 'denied', error: evaluation.reason }, 0, 'denied');
340
+ return {
341
+ content: [{ type: 'text', text: `ESCALATION DENIED: ${evaluation.reason}` }],
342
+ isError: true,
343
+ };
344
+ }
345
+ // Approved -- update policy decision and fall through to forward the call
346
+ escalationResult = 'approved';
347
+ policyDecision.status = 'allow';
348
+ policyDecision.reason = 'Approved by human during escalation';
349
+ // Expand roots to include target directories so the filesystem
350
+ // server accepts the forwarded call.
351
+ const state = clientStates.get(toolInfo.serverName);
352
+ if (state) {
353
+ const paths = Object.values(argsForTransport).filter((v) => typeof v === 'string');
354
+ for (const p of paths) {
355
+ await addRootToClient(state, {
356
+ uri: `file://${directoryForPath(p)}`,
357
+ name: 'escalation-approved',
358
+ });
359
+ }
360
+ }
361
+ }
362
+ if (evaluation.decision === 'deny') {
363
+ logAudit({ status: 'denied', error: evaluation.reason }, 0);
364
+ return {
365
+ content: [{ type: 'text', text: `DENIED: ${evaluation.reason}` }],
366
+ isError: true,
367
+ };
368
+ }
369
+ // Circuit breaker: deny if the same tool+args is called too many times
370
+ const cbVerdict = circuitBreaker.check(toolInfo.name, argsForTransport);
371
+ if (!cbVerdict.allowed) {
372
+ logAudit({ status: 'denied', error: cbVerdict.reason }, 0);
373
+ return {
374
+ content: [{ type: 'text', text: cbVerdict.reason }],
375
+ isError: true,
376
+ };
377
+ }
378
+ // Policy allows -- forward to the real MCP server with transport args
379
+ const startTime = Date.now();
380
+ try {
381
+ const client = clientStates.get(toolInfo.serverName).client;
382
+ // TODO(workaround): Remove once @cyanheads/git-mcp-server fixes outputSchema declarations.
383
+ //
384
+ // WHY: The git MCP server v2.8.4 declares outputSchema for tools like git_add and
385
+ // git_commit, but the structuredContent it actually returns does not match those schemas.
386
+ // The MCP SDK v1.26.0 Client.callTool() validates responses client-side against the
387
+ // declared outputSchema and throws McpError(-32602, "Structured content does not match
388
+ // the tool's output schema: ...") when there is a mismatch.
389
+ //
390
+ // WHAT: git_add declares required properties {success, stagedFiles, totalFiles, status}
391
+ // in its outputSchema, but actual responses (especially errors) are missing these and
392
+ // include additional undeclared properties. git_commit similarly requires {success,
393
+ // commitHash, author, timestamp, committedFiles, status} but returns different shapes.
394
+ //
395
+ // FIX: Passing CompatibilityCallToolResultSchema instead of the default
396
+ // CallToolResultSchema makes the response parsing more permissive, which avoids the
397
+ // client-side validation failure.
398
+ //
399
+ // CONSEQUENCE: By using CompatibilityCallToolResultSchema we lose client-side output
400
+ // validation for ALL MCP servers proxied through this path, not just the git server.
401
+ const result = await client.callTool({
402
+ name: toolInfo.name,
403
+ arguments: argsForTransport,
404
+ }, CompatibilityCallToolResultSchema);
405
+ if (result.isError) {
406
+ const errorText = extractTextFromContent(result.content) ?? 'Unknown tool error';
407
+ const errorMessage = annotateSandboxViolation(errorText, serverIsSandboxed);
408
+ logAudit({ status: 'error', error: errorMessage }, Date.now() - startTime);
409
+ return { content: result.content, isError: true };
410
+ }
411
+ logAudit({ status: 'success' }, Date.now() - startTime);
412
+ return { content: result.content };
413
+ }
414
+ catch (err) {
415
+ const rawError = err instanceof Error ? err.message : String(err);
416
+ const errorMessage = annotateSandboxViolation(rawError, serverIsSandboxed);
417
+ logAudit({ status: 'error', error: errorMessage }, Date.now() - startTime);
418
+ return {
419
+ content: [{ type: 'text', text: `Error: ${errorMessage}` }],
420
+ isError: true,
421
+ };
422
+ }
423
+ });
424
+ // Start on stdio
425
+ const transport = new StdioServerTransport();
426
+ await server.connect(transport);
427
+ // Clean shutdown -- handle both SIGINT and SIGTERM since this process
428
+ // is spawned as a child by Code Mode and may receive either signal.
429
+ async function shutdown() {
430
+ for (const state of clientStates.values()) {
431
+ try {
432
+ await state.client.close();
433
+ }
434
+ catch { /* ignore */ }
435
+ }
436
+ cleanupSettingsFiles(settingsDir);
437
+ try {
438
+ await server.close();
439
+ }
440
+ catch { /* ignore */ }
441
+ await auditLog.close();
442
+ process.exit(0);
443
+ }
444
+ process.on('SIGINT', shutdown);
445
+ process.on('SIGTERM', shutdown);
446
+ }
447
+ main().catch((err) => {
448
+ process.stderr.write(`MCP Proxy Server fatal error: ${err}\n`);
449
+ process.exit(1);
450
+ });
451
+ //# sourceMappingURL=mcp-proxy-server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-proxy-server.js","sourceRoot":"","sources":["../../src/trusted-process/mcp-proxy-server.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Path normalization utilities for the trusted process security boundary.
3
+ *
4
+ * Provides annotation-driven normalization via `prepareToolArgs()` and a
5
+ * legacy heuristic fallback via `normalizeToolArgPaths()`. The heuristic
6
+ * is retained for defense-in-depth and as a fallback when annotations are
7
+ * unavailable.
8
+ */
9
+ import type { ToolAnnotation } from '../pipeline/types.js';
10
+ export { expandTilde } from '../types/argument-roles.js';
11
+ /**
12
+ * Returns a new arguments object with all path-like string values
13
+ * fully resolved (tilde expanded, relative resolved, traversals collapsed).
14
+ *
15
+ * A string value is considered path-like if it starts with `/`, `.`, or `~`.
16
+ * Array values have each string element checked individually.
17
+ * Non-string and non-path values pass through unchanged.
18
+ *
19
+ * The input object is never mutated.
20
+ *
21
+ * @deprecated Use `prepareToolArgs()` with annotation-driven normalization.
22
+ * Retained as a fallback when annotations are unavailable.
23
+ */
24
+ export declare function normalizeToolArgPaths(args: Record<string, unknown>): Record<string, unknown>;
25
+ export interface PreparedToolArgs {
26
+ /** Canonical args sent to the real MCP server. */
27
+ argsForTransport: Record<string, unknown>;
28
+ /** Args presented to the policy engine (may differ if prepareForPolicy is defined). */
29
+ argsForPolicy: Record<string, unknown>;
30
+ }
31
+ /**
32
+ * Annotation-driven normalization of tool call arguments.
33
+ *
34
+ * For each argument, looks up its annotated roles and applies the
35
+ * corresponding normalizer from the registry. Returns two argument
36
+ * objects: one for transport (MCP server) and one for policy evaluation.
37
+ *
38
+ * For path-category roles, relative paths (not starting with `/` or `~`)
39
+ * are treated differently:
40
+ * - Transport: passed through unchanged (the MCP server resolves them
41
+ * against its own sandbox CWD)
42
+ * - Policy: resolved against `allowedDirectory` so the policy engine
43
+ * has absolute canonical paths for containment checks
44
+ *
45
+ * When `annotation` is undefined (unknown tool), falls back to the
46
+ * heuristic `normalizeToolArgPaths()` for both outputs.
47
+ *
48
+ * The input object is never mutated.
49
+ */
50
+ export declare function prepareToolArgs(args: Record<string, unknown>, annotation: ToolAnnotation | undefined, allowedDirectory?: string): PreparedToolArgs;