@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.
- package/LICENSE +202 -0
- package/README.md +311 -0
- package/dist/agent/index.d.ts +10 -0
- package/dist/agent/index.js +71 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/prompts.d.ts +5 -0
- package/dist/agent/prompts.js +26 -0
- package/dist/agent/prompts.js.map +1 -0
- package/dist/agent/tools.d.ts +13 -0
- package/dist/agent/tools.js +51 -0
- package/dist/agent/tools.js.map +1 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +78 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/constitution.md +16 -0
- package/dist/config/generated/compiled-policy.json +236 -0
- package/dist/config/generated/test-scenarios.json +765 -0
- package/dist/config/generated/tool-annotations.json +955 -0
- package/dist/config/index.d.ts +25 -0
- package/dist/config/index.js +151 -0
- package/dist/config/index.js.map +1 -0
- package/dist/config/mcp-servers.json +22 -0
- package/dist/config/model-provider.d.ts +49 -0
- package/dist/config/model-provider.js +78 -0
- package/dist/config/model-provider.js.map +1 -0
- package/dist/config/paths.d.ts +59 -0
- package/dist/config/paths.js +96 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/types.d.ts +89 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/user-config.d.ts +93 -0
- package/dist/config/user-config.js +309 -0
- package/dist/config/user-config.js.map +1 -0
- package/dist/hash.d.ts +17 -0
- package/dist/hash.js +34 -0
- package/dist/hash.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +61 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +11 -0
- package/dist/logger.js +93 -0
- package/dist/logger.js.map +1 -0
- package/dist/pipeline/annotate.d.ts +9 -0
- package/dist/pipeline/annotate.js +136 -0
- package/dist/pipeline/annotate.js.map +1 -0
- package/dist/pipeline/compile.d.ts +23 -0
- package/dist/pipeline/compile.js +386 -0
- package/dist/pipeline/compile.js.map +1 -0
- package/dist/pipeline/constitution-compiler.d.ts +22 -0
- package/dist/pipeline/constitution-compiler.js +197 -0
- package/dist/pipeline/constitution-compiler.js.map +1 -0
- package/dist/pipeline/generate-with-repair.d.ts +22 -0
- package/dist/pipeline/generate-with-repair.js +64 -0
- package/dist/pipeline/generate-with-repair.js.map +1 -0
- package/dist/pipeline/handwritten-scenarios.d.ts +9 -0
- package/dist/pipeline/handwritten-scenarios.js +321 -0
- package/dist/pipeline/handwritten-scenarios.js.map +1 -0
- package/dist/pipeline/llm-logger.d.ts +42 -0
- package/dist/pipeline/llm-logger.js +78 -0
- package/dist/pipeline/llm-logger.js.map +1 -0
- package/dist/pipeline/pipeline-shared.d.ts +47 -0
- package/dist/pipeline/pipeline-shared.js +145 -0
- package/dist/pipeline/pipeline-shared.js.map +1 -0
- package/dist/pipeline/policy-verifier.d.ts +46 -0
- package/dist/pipeline/policy-verifier.js +277 -0
- package/dist/pipeline/policy-verifier.js.map +1 -0
- package/dist/pipeline/scenario-generator.d.ts +11 -0
- package/dist/pipeline/scenario-generator.js +128 -0
- package/dist/pipeline/scenario-generator.js.map +1 -0
- package/dist/pipeline/tool-annotator.d.ts +24 -0
- package/dist/pipeline/tool-annotator.js +201 -0
- package/dist/pipeline/tool-annotator.js.map +1 -0
- package/dist/pipeline/types.d.ts +122 -0
- package/dist/pipeline/types.js +10 -0
- package/dist/pipeline/types.js.map +1 -0
- package/dist/sandbox/index.d.ts +39 -0
- package/dist/sandbox/index.js +178 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/session/agent-session.d.ts +83 -0
- package/dist/session/agent-session.js +382 -0
- package/dist/session/agent-session.js.map +1 -0
- package/dist/session/cli-transport.d.ts +61 -0
- package/dist/session/cli-transport.js +320 -0
- package/dist/session/cli-transport.js.map +1 -0
- package/dist/session/errors.d.ts +19 -0
- package/dist/session/errors.js +33 -0
- package/dist/session/errors.js.map +1 -0
- package/dist/session/index.d.ts +29 -0
- package/dist/session/index.js +104 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/message-compactor.d.ts +32 -0
- package/dist/session/message-compactor.js +81 -0
- package/dist/session/message-compactor.js.map +1 -0
- package/dist/session/prompts.d.ts +5 -0
- package/dist/session/prompts.js +62 -0
- package/dist/session/prompts.js.map +1 -0
- package/dist/session/resource-budget-tracker.d.ts +124 -0
- package/dist/session/resource-budget-tracker.js +327 -0
- package/dist/session/resource-budget-tracker.js.map +1 -0
- package/dist/session/step-loop-detector.d.ts +63 -0
- package/dist/session/step-loop-detector.js +136 -0
- package/dist/session/step-loop-detector.js.map +1 -0
- package/dist/session/transport.d.ts +24 -0
- package/dist/session/transport.js +2 -0
- package/dist/session/transport.js.map +1 -0
- package/dist/session/truncate-result.d.ts +35 -0
- package/dist/session/truncate-result.js +71 -0
- package/dist/session/truncate-result.js.map +1 -0
- package/dist/session/types.d.ts +220 -0
- package/dist/session/types.js +6 -0
- package/dist/session/types.js.map +1 -0
- package/dist/trusted-process/audit-log.d.ts +7 -0
- package/dist/trusted-process/audit-log.js +21 -0
- package/dist/trusted-process/audit-log.js.map +1 -0
- package/dist/trusted-process/call-circuit-breaker.d.ts +33 -0
- package/dist/trusted-process/call-circuit-breaker.js +61 -0
- package/dist/trusted-process/call-circuit-breaker.js.map +1 -0
- package/dist/trusted-process/escalation.d.ts +7 -0
- package/dist/trusted-process/escalation.js +38 -0
- package/dist/trusted-process/escalation.js.map +1 -0
- package/dist/trusted-process/index.d.ts +32 -0
- package/dist/trusted-process/index.js +151 -0
- package/dist/trusted-process/index.js.map +1 -0
- package/dist/trusted-process/mcp-client-manager.d.ts +25 -0
- package/dist/trusted-process/mcp-client-manager.js +90 -0
- package/dist/trusted-process/mcp-client-manager.js.map +1 -0
- package/dist/trusted-process/mcp-proxy-server.d.ts +24 -0
- package/dist/trusted-process/mcp-proxy-server.js +451 -0
- package/dist/trusted-process/mcp-proxy-server.js.map +1 -0
- package/dist/trusted-process/path-utils.d.ts +50 -0
- package/dist/trusted-process/path-utils.js +158 -0
- package/dist/trusted-process/path-utils.js.map +1 -0
- package/dist/trusted-process/policy-engine.d.ts +88 -0
- package/dist/trusted-process/policy-engine.js +523 -0
- package/dist/trusted-process/policy-engine.js.map +1 -0
- package/dist/trusted-process/policy-roots.d.ts +50 -0
- package/dist/trusted-process/policy-roots.js +67 -0
- package/dist/trusted-process/policy-roots.js.map +1 -0
- package/dist/trusted-process/policy-types.d.ts +6 -0
- package/dist/trusted-process/policy-types.js +2 -0
- package/dist/trusted-process/policy-types.js.map +1 -0
- package/dist/trusted-process/sandbox-integration.d.ts +92 -0
- package/dist/trusted-process/sandbox-integration.js +184 -0
- package/dist/trusted-process/sandbox-integration.js.map +1 -0
- package/dist/types/argument-roles.d.ts +112 -0
- package/dist/types/argument-roles.js +344 -0
- package/dist/types/argument-roles.js.map +1 -0
- package/dist/types/audit.d.ts +18 -0
- package/dist/types/audit.js +2 -0
- package/dist/types/audit.js.map +1 -0
- package/dist/types/mcp.d.ts +20 -0
- package/dist/types/mcp.js +2 -0
- package/dist/types/mcp.js.map +1 -0
- package/package.json +83 -0
- package/src/config/constitution.md +16 -0
- package/src/config/generated/compiled-policy.json +236 -0
- package/src/config/generated/test-scenarios.json +765 -0
- package/src/config/generated/tool-annotations.json +955 -0
- 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;
|