@clampd/mcp-proxy 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/fleet.js ADDED
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Fleet orchestrator — runs multiple MCP proxy instances from a single config.
3
+ *
4
+ * Each proxy wraps a different upstream MCP server with a different agent identity,
5
+ * enabling multi-agent demos showing scope isolation, delegation chains, and
6
+ * behavioral correlation.
7
+ *
8
+ * Usage:
9
+ * clampd-mcp-proxy --fleet-config fleet.json
10
+ *
11
+ * Config:
12
+ * {
13
+ * "gateway": "http://ag-gateway:8080",
14
+ * "apiKey": "ag_test_acme_demo_2026",
15
+ * "secret": "ags_...",
16
+ * "dashboardPort": 3000,
17
+ * "agents": [
18
+ * { "name": "Data Analyst", "agentId": "...", "port": 3003, "upstream": "...", "color": "#3b82f6" },
19
+ * { "name": "DevOps Bot", "agentId": "...", "port": 3004, "upstream": "...", "color": "#22c55e" },
20
+ * { "name": "DB Admin", "agentId": "...", "port": 3005, "upstream": "...", "color": "#f59e0b" }
21
+ * ]
22
+ * }
23
+ */
24
+ import { createServer } from "node:http";
25
+ import { readFileSync } from "node:fs";
26
+ import { startProxy } from "./proxy.js";
27
+ import { log, setVerbose } from "./logger.js";
28
+ // ── Fleet State ───────────────────────────────────────────────────────
29
+ const fleetEvents = [];
30
+ const MAX_FLEET_EVENTS = 2000;
31
+ const fleetSubscribers = new Set();
32
+ function pushFleetEvent(event) {
33
+ fleetEvents.push(event);
34
+ if (fleetEvents.length > MAX_FLEET_EVENTS) {
35
+ fleetEvents.shift();
36
+ }
37
+ const payload = `data: ${JSON.stringify(event)}\n\n`;
38
+ for (const res of fleetSubscribers) {
39
+ try {
40
+ res.write(payload);
41
+ }
42
+ catch {
43
+ fleetSubscribers.delete(res);
44
+ }
45
+ }
46
+ }
47
+ // ── Start Fleet ───────────────────────────────────────────────────────
48
+ export async function startFleet(configPath) {
49
+ const raw = readFileSync(configPath, "utf-8");
50
+ const config = JSON.parse(raw);
51
+ if (config.verbose)
52
+ setVerbose(true);
53
+ log("info", "");
54
+ log("info", "=== Clampd MCP Fleet ===");
55
+ log("info", `Agents: ${config.agents.length}`);
56
+ log("info", `Dashboard: http://localhost:${config.dashboardPort}/`);
57
+ log("info", "");
58
+ // Build color map
59
+ const defaultColors = ["#3b82f6", "#22c55e", "#f59e0b", "#ef4444", "#8b5cf6", "#ec4899", "#06b6d4", "#84cc16"];
60
+ const colorMap = new Map();
61
+ config.agents.forEach((a, i) => {
62
+ colorMap.set(a.agentId, a.color ?? defaultColors[i % defaultColors.length]);
63
+ });
64
+ // Start each proxy instance
65
+ for (const agent of config.agents) {
66
+ const opts = {
67
+ upstreamCommand: agent.upstream,
68
+ gatewayUrl: config.gateway,
69
+ apiKey: config.apiKey,
70
+ agentId: agent.agentId,
71
+ port: agent.port,
72
+ secret: agent.secret ?? config.secret,
73
+ dryRun: config.dryRun ?? false,
74
+ verbose: config.verbose ?? false,
75
+ scanInputEnabled: agent.scanInput ?? true,
76
+ scanOutputEnabled: agent.scanOutput ?? true,
77
+ checkResponse: agent.checkResponse ?? false,
78
+ demoPanel: agent.demoPanel ?? false,
79
+ agentName: agent.name,
80
+ onEvent: (event) => {
81
+ pushFleetEvent({
82
+ ...event,
83
+ agentName: agent.name,
84
+ agentColor: colorMap.get(agent.agentId),
85
+ agentId: agent.agentId,
86
+ });
87
+ },
88
+ };
89
+ try {
90
+ await startProxy(opts);
91
+ log("info", `Started agent "${agent.name}" on port ${agent.port} (${agent.agentId.slice(0, 8)}...)`);
92
+ }
93
+ catch (err) {
94
+ log("error", `Failed to start agent "${agent.name}": ${err instanceof Error ? err.message : err}`);
95
+ }
96
+ }
97
+ // Start fleet dashboard server
98
+ const httpServer = createServer((req, res) => {
99
+ const url = new URL(req.url ?? "/", `http://localhost:${config.dashboardPort}`);
100
+ // GET / — Fleet dashboard
101
+ if (url.pathname === "/" && req.method === "GET") {
102
+ const html = renderFleetDashboard(fleetEvents, config);
103
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-cache" });
104
+ res.end(html);
105
+ return;
106
+ }
107
+ // GET /events — Fleet SSE stream
108
+ if (url.pathname === "/events" && req.method === "GET") {
109
+ res.writeHead(200, { "Content-Type": "text/event-stream", "Cache-Control": "no-cache", Connection: "keep-alive" });
110
+ res.write(`data: ${JSON.stringify({ type: "connected", total: fleetEvents.length })}\n\n`);
111
+ fleetSubscribers.add(res);
112
+ req.on("close", () => fleetSubscribers.delete(res));
113
+ return;
114
+ }
115
+ // GET /health — Fleet health
116
+ if (url.pathname === "/health" && req.method === "GET") {
117
+ res.writeHead(200, { "Content-Type": "application/json" });
118
+ res.end(JSON.stringify({
119
+ status: "ok",
120
+ mode: "fleet",
121
+ agents: config.agents.map((a) => ({ name: a.name, agentId: a.agentId, port: a.port })),
122
+ total_events: fleetEvents.length,
123
+ }));
124
+ return;
125
+ }
126
+ res.writeHead(404);
127
+ res.end("Not found");
128
+ });
129
+ httpServer.listen(config.dashboardPort, () => {
130
+ log("info", `Fleet dashboard: http://localhost:${config.dashboardPort}/`);
131
+ for (const agent of config.agents) {
132
+ log("info", ` ${agent.name}: http://localhost:${agent.port}/sse (MCP) | http://localhost:${agent.port}/ (dashboard)`);
133
+ }
134
+ log("info", "");
135
+ log("info", "Claude Desktop config — add each agent as a separate MCP server:");
136
+ log("info", JSON.stringify({
137
+ mcpServers: Object.fromEntries(config.agents.map((a) => [
138
+ a.name.toLowerCase().replace(/\s+/g, "-"),
139
+ { url: `http://localhost:${a.port}/sse` },
140
+ ])),
141
+ }, null, 2));
142
+ log("info", "");
143
+ });
144
+ }
145
+ // ── Fleet Dashboard Renderer ──────────────────────────────────────────
146
+ function renderFleetDashboard(events, config) {
147
+ const agentStats = new Map();
148
+ for (const a of config.agents) {
149
+ const color = a.color ?? "#888";
150
+ agentStats.set(a.agentId, { name: a.name, color, total: 0, blocked: 0, flagged: 0, allowed: 0, errors: 0, port: a.port });
151
+ }
152
+ for (const e of events) {
153
+ const stats = agentStats.get(e.agentId ?? "");
154
+ if (stats) {
155
+ stats.total++;
156
+ if (e.status === "blocked")
157
+ stats.blocked++;
158
+ else if (e.status === "flagged")
159
+ stats.flagged++;
160
+ else if (e.status === "error")
161
+ stats.errors++;
162
+ else
163
+ stats.allowed++;
164
+ }
165
+ }
166
+ // Agent cards
167
+ const agentCards = Array.from(agentStats.values())
168
+ .map((s) => `
169
+ <div style="border:1px solid #222;border-left:3px solid ${s.color};border-radius:6px;padding:12px 16px;min-width:200px">
170
+ <div style="font-size:14px;font-weight:600;color:${s.color}">${esc(s.name)}</div>
171
+ <div style="font-size:11px;color:#555;margin-top:2px">Port ${s.port}</div>
172
+ <div style="display:flex;gap:12px;margin-top:8px;font-size:12px">
173
+ <span style="color:#22c55e">${s.allowed} ok</span>
174
+ <span style="color:#ef4444">${s.blocked} blocked</span>
175
+ <span style="color:#f59e0b">${s.flagged} flagged</span>
176
+ <span style="color:#6b7280">${s.errors} err</span>
177
+ </div>
178
+ </div>`)
179
+ .join("");
180
+ // Event rows (last 100)
181
+ const last100 = events.slice(-100).reverse();
182
+ const rows = last100.map((e, i) => {
183
+ const stats = agentStats.get(e.agentId ?? "");
184
+ const color = stats?.color ?? "#888";
185
+ const agentName = stats?.name ?? e.agentId ?? "?";
186
+ const statusColor = e.status === "blocked" ? "#ef4444" : e.status === "flagged" ? "#f59e0b" : e.status === "error" ? "#6b7280" : "#22c55e";
187
+ const rules = e.matched_rules?.map((r) => `<span style="background:#2d1b69;color:#c4b5fd;padding:1px 5px;border-radius:3px;font-size:10px">${esc(r)}</span>`).join(" ") ?? "";
188
+ const time = e.timestamp.split("T")[1]?.slice(0, 12) ?? "";
189
+ return `
190
+ <tr style="cursor:pointer;border-bottom:1px solid #151515" onclick="toggleDetail(${i})">
191
+ <td style="padding:6px 10px;font-family:monospace;font-size:11px;color:#666">${esc(time)}</td>
192
+ <td style="padding:6px 10px"><span style="display:inline-block;width:8px;height:8px;border-radius:50%;background:${color};margin-right:6px"></span><span style="font-size:12px;color:${color}">${esc(agentName)}</span></td>
193
+ <td style="padding:6px 10px;font-weight:600;font-size:12px">${esc(e.tool)}</td>
194
+ <td style="padding:6px 10px;font-weight:600;color:${statusColor};font-size:12px">${e.status.toUpperCase()}</td>
195
+ <td style="padding:6px 10px;font-family:monospace;font-size:11px">${e.risk_score.toFixed(2)}</td>
196
+ <td style="padding:6px 10px">${rules}</td>
197
+ <td style="padding:6px 10px;font-family:monospace;font-size:11px">${e.latency_ms}ms</td>
198
+ </tr>
199
+ <tr id="detail-${i}" style="display:none">
200
+ <td colspan="7" style="padding:10px 16px;background:#0d0d14;border-bottom:1px solid #1a1a2e;font-size:12px;color:#888">
201
+ ${e.reason ? `<div><strong>Reason:</strong> ${esc(e.reason)}</div>` : ""}
202
+ ${e.reasoning ? `<div><strong>Reasoning:</strong> ${esc(e.reasoning)}</div>` : ""}
203
+ ${e.session_flags?.length ? `<div><strong>Session Flags:</strong> ${e.session_flags.map((f) => `<span style="background:#422006;color:#fbbf24;padding:1px 5px;border-radius:3px;font-size:10px">${esc(f)}</span>`).join(" ")}</div>` : ""}
204
+ ${e.scope_granted ? `<div><strong>Scope:</strong> <span style="background:#042f2e;color:#5eead4;padding:1px 5px;border-radius:3px;font-size:10px">${esc(e.scope_granted)}</span></div>` : ""}
205
+ <div style="margin-top:6px;font-family:monospace;font-size:11px;color:#555;max-height:120px;overflow:auto">${esc(e.params)}</div>
206
+ </td>
207
+ </tr>`;
208
+ }).join("");
209
+ const total = events.length;
210
+ const blocked = events.filter((e) => e.status === "blocked").length;
211
+ return `<!DOCTYPE html>
212
+ <html lang="en">
213
+ <head>
214
+ <meta charset="utf-8">
215
+ <meta name="viewport" content="width=device-width, initial-scale=1">
216
+ <title>Clampd Fleet Dashboard</title>
217
+ <style>
218
+ * { margin:0; padding:0; box-sizing:border-box; }
219
+ body { font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; background:#0a0a0a; color:#e0e0e0; }
220
+ </style>
221
+ </head>
222
+ <body>
223
+ <div style="padding:20px 32px;border-bottom:1px solid #222;display:flex;align-items:center;gap:16px;flex-wrap:wrap">
224
+ <h1 style="font-size:20px;color:#fff">Clampd Fleet</h1>
225
+ <span style="padding:3px 10px;border-radius:4px;font-size:11px;font-weight:600;background:#1a472a;color:#4ade80">${config.agents.length} AGENTS</span>
226
+ <span style="font-size:13px;color:#888">${total} calls | ${blocked} blocked | ${total > 0 ? ((blocked / total) * 100).toFixed(1) : "0"}% threat rate</span>
227
+ <div style="margin-left:auto">
228
+ <button onclick="location.reload()" style="padding:5px 12px;border:1px solid #333;background:#111;color:#aaa;border-radius:4px;cursor:pointer;font-size:11px">Refresh</button>
229
+ </div>
230
+ </div>
231
+
232
+ <div style="display:flex;gap:12px;padding:16px 32px;border-bottom:1px solid #222;flex-wrap:wrap;overflow-x:auto">
233
+ ${agentCards}
234
+ </div>
235
+
236
+ <div style="max-height:600px;overflow-y:auto">
237
+ <table style="width:100%;border-collapse:collapse;font-size:13px">
238
+ <thead>
239
+ <tr style="background:#111;position:sticky;top:0;z-index:1">
240
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Time</th>
241
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Agent</th>
242
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Tool</th>
243
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Status</th>
244
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Risk</th>
245
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Rules</th>
246
+ <th style="text-align:left;padding:7px 10px;color:#666;font-size:10px;text-transform:uppercase">Latency</th>
247
+ </tr>
248
+ </thead>
249
+ <tbody>
250
+ ${rows || '<tr><td colspan="7" style="padding:48px;text-align:center;color:#333">No events yet. Connect Claude Desktop to the agent SSE endpoints.</td></tr>'}
251
+ </tbody>
252
+ </table>
253
+ </div>
254
+
255
+ <script>
256
+ const evtSource = new EventSource('/events');
257
+ let newCount = 0;
258
+ evtSource.onmessage = function() {
259
+ newCount++;
260
+ const btn = document.querySelector('button');
261
+ if (btn) btn.textContent = 'Refresh (' + newCount + ' new)';
262
+ };
263
+ function toggleDetail(idx) {
264
+ const el = document.getElementById('detail-' + idx);
265
+ if (el) el.style.display = el.style.display === 'none' ? 'table-row' : 'none';
266
+ }
267
+ </script>
268
+ </body>
269
+ </html>`;
270
+ }
271
+ function esc(s) {
272
+ return s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
273
+ }
274
+ //# sourceMappingURL=fleet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fleet.js","sourceRoot":"","sources":["../src/fleet.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAEH,OAAO,EAAE,YAAY,EAA6C,MAAM,WAAW,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,UAAU,EAAqB,MAAM,YAAY,CAAC;AAE3D,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAmC9C,yEAAyE;AAEzE,MAAM,WAAW,GAAiB,EAAE,CAAC;AACrC,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAC9B,MAAM,gBAAgB,GAAG,IAAI,GAAG,EAAkB,CAAC;AAEnD,SAAS,cAAc,CAAC,KAAiB;IACvC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACxB,IAAI,WAAW,CAAC,MAAM,GAAG,gBAAgB,EAAE,CAAC;QAC1C,WAAW,CAAC,KAAK,EAAE,CAAC;IACtB,CAAC;IACD,MAAM,OAAO,GAAG,SAAS,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,MAAM,CAAC;IACrD,KAAK,MAAM,GAAG,IAAI,gBAAgB,EAAE,CAAC;QACnC,IAAI,CAAC;YACH,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC/B,CAAC;IACH,CAAC;AACH,CAAC;AAED,yEAAyE;AAEzE,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB;IACjD,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IAC9C,MAAM,MAAM,GAAgB,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAE5C,IAAI,MAAM,CAAC,OAAO;QAAE,UAAU,CAAC,IAAI,CAAC,CAAC;IAErC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAChB,GAAG,CAAC,MAAM,EAAE,0BAA0B,CAAC,CAAC;IACxC,GAAG,CAAC,MAAM,EAAE,WAAW,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IAC/C,GAAG,CAAC,MAAM,EAAE,+BAA+B,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC;IACpE,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAEhB,kBAAkB;IAClB,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAC/G,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC7B,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,KAAK,IAAI,aAAa,CAAC,CAAC,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAClC,MAAM,IAAI,GAAiB;YACzB,eAAe,EAAE,KAAK,CAAC,QAAQ;YAC/B,UAAU,EAAE,MAAM,CAAC,OAAO;YAC1B,MAAM,EAAE,MAAM,CAAC,MAAM;YACrB,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM;YACrC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,KAAK;YAC9B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,KAAK;YAChC,gBAAgB,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;YACzC,iBAAiB,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;YAC3C,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,KAAK;YAC3C,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,KAAK;YACnC,SAAS,EAAE,KAAK,CAAC,IAAI;YACrB,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE;gBACjB,cAAc,CAAC;oBACb,GAAG,KAAK;oBACR,SAAS,EAAE,KAAK,CAAC,IAAI;oBACrB,UAAU,EAAE,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;oBACvC,OAAO,EAAE,KAAK,CAAC,OAAO;iBACvB,CAAC,CAAC;YACL,CAAC;SACF,CAAC;QAEF,IAAI,CAAC;YACH,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;YACvB,GAAG,CAAC,MAAM,EAAE,kBAAkB,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;QACvG,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACtB,GAAG,CAAC,OAAO,EAAE,0BAA0B,KAAK,CAAC,IAAI,MAAM,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC,CAAC;QACrG,CAAC;IACH,CAAC;IAED,+BAA+B;IAC/B,MAAM,UAAU,GAAG,YAAY,CAAC,CAAC,GAAoB,EAAE,GAAmB,EAAE,EAAE;QAC5E,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,GAAG,EAAE,oBAAoB,MAAM,CAAC,aAAa,EAAE,CAAC,CAAC;QAEhF,0BAA0B;QAC1B,IAAI,GAAG,CAAC,QAAQ,KAAK,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACjD,MAAM,IAAI,GAAG,oBAAoB,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;YACvD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,0BAA0B,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC,CAAC;YAChG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACd,OAAO;QACT,CAAC;QAED,iCAAiC;QACjC,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACvD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,mBAAmB,EAAE,eAAe,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;YACnH,GAAG,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,CAAC;YAC3F,gBAAgB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC1B,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YACpD,OAAO;QACT,CAAC;QAED,6BAA6B;QAC7B,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;YACvD,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE,CAAC,CAAC;YAC3D,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC;gBACrB,MAAM,EAAE,IAAI;gBACZ,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;gBACtF,YAAY,EAAE,WAAW,CAAC,MAAM;aACjC,CAAC,CAAC,CAAC;YACJ,OAAO;QACT,CAAC;QAED,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;QACnB,GAAG,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,GAAG,EAAE;QAC3C,GAAG,CAAC,MAAM,EAAE,qCAAqC,MAAM,CAAC,aAAa,GAAG,CAAC,CAAC;QAC1E,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;YAClC,GAAG,CAAC,MAAM,EAAE,KAAK,KAAK,CAAC,IAAI,sBAAsB,KAAK,CAAC,IAAI,iCAAiC,KAAK,CAAC,IAAI,eAAe,CAAC,CAAC;QACzH,CAAC;QACD,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;QAChB,GAAG,CAAC,MAAM,EAAE,kEAAkE,CAAC,CAAC;QAChF,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC;YACzB,UAAU,EAAE,MAAM,CAAC,WAAW,CAC5B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gBACvB,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;gBACzC,EAAE,GAAG,EAAE,oBAAoB,CAAC,CAAC,IAAI,MAAM,EAAE;aAC1C,CAAC,CACH;SACF,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QACb,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAClB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,yEAAyE;AAEzE,SAAS,oBAAoB,CAAC,MAAoB,EAAE,MAAmB;IACrE,MAAM,UAAU,GAAG,IAAI,GAAG,EAA2I,CAAC;IAEtK,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,IAAI,MAAM,CAAC;QAChC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IAC5H,CAAC;IAED,KAAK,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACvB,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC9C,IAAI,KAAK,EAAE,CAAC;YACV,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;iBACvC,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;gBAAE,KAAK,CAAC,OAAO,EAAE,CAAC;iBAC5C,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;gBAAE,KAAK,CAAC,MAAM,EAAE,CAAC;;gBACzC,KAAK,CAAC,OAAO,EAAE,CAAC;QACvB,CAAC;IACH,CAAC;IAED,cAAc;IACd,MAAM,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;SAC/C,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;gEACgD,CAAC,CAAC,KAAK;2DACZ,CAAC,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;qEACb,CAAC,CAAC,IAAI;;wCAEnC,CAAC,CAAC,OAAO;wCACT,CAAC,CAAC,OAAO;wCACT,CAAC,CAAC,OAAO;wCACT,CAAC,CAAC,MAAM;;aAEnC,CAAC;SACT,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,wBAAwB;IACxB,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,CAAC;IAC7C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAChC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,KAAK,EAAE,KAAK,IAAI,MAAM,CAAC;QACrC,MAAM,SAAS,GAAG,KAAK,EAAE,IAAI,IAAI,CAAC,CAAC,OAAO,IAAI,GAAG,CAAC;QAClD,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC;QAC3I,MAAM,KAAK,GAAG,CAAC,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mGAAmG,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9K,MAAM,IAAI,GAAG,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC;QAE3D,OAAO;yFAC8E,CAAC;uFACH,GAAG,CAAC,IAAI,CAAC;2HAC2B,KAAK,+DAA+D,KAAK,KAAK,GAAG,CAAC,SAAS,CAAC;sEACjJ,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC;4DACrB,WAAW,oBAAoB,CAAC,CAAC,MAAM,CAAC,WAAW,EAAE;4EACrC,CAAC,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC;uCAC5D,KAAK;4EACgC,CAAC,CAAC,UAAU;;uBAEjE,CAAC;;YAEZ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,iCAAiC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YACtE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,oCAAoC,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YAC/E,CAAC,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,mGAAmG,GAAG,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE;YACvO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,gIAAgI,GAAG,CAAC,CAAC,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE;uHAC/E,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;;YAExH,CAAC;IACX,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAEZ,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5B,MAAM,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,MAAM,CAAC;IAEpE,OAAO;;;;;;;;;;;;;;uHAc8G,MAAM,CAAC,MAAM,CAAC,MAAM;8CAC7F,KAAK,YAAY,OAAO,cAAc,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,GAAG,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG;;;;;;;MAOpI,UAAU;;;;;;;;;;;;;;;;;UAiBN,IAAI,IAAI,mJAAmJ;;;;;;;;;;;;;;;;;;;QAmB7J,CAAC;AACT,CAAC;AAED,SAAS,GAAG,CAAC,CAAS;IACpB,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AACtG,CAAC"}
@@ -0,0 +1,26 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Clampd MCP Proxy — standalone proxy that wraps any MCP server with
4
+ * Clampd's full security pipeline.
5
+ *
6
+ * Users connect Claude Desktop (or any MCP client) to this proxy via SSE.
7
+ * The proxy intercepts every tool call, classifies it through ag-gateway,
8
+ * and only forwards allowed calls to the upstream MCP server.
9
+ *
10
+ * Usage:
11
+ * clampd-mcp-proxy \
12
+ * --upstream "npx -y @modelcontextprotocol/server-filesystem /tmp" \
13
+ * --gateway http://localhost:8080 \
14
+ * --api-key ag_test_acme_demo_2026 \
15
+ * --agent-id b0000001-0000-0000-0000-000000000001 \
16
+ * --port 3003
17
+ *
18
+ * Claude Desktop config (mcpServers):
19
+ * {
20
+ * "filesystem": {
21
+ * "url": "http://localhost:3003/sse"
22
+ * }
23
+ * }
24
+ */
25
+ export {};
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
package/dist/index.js ADDED
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Clampd MCP Proxy — standalone proxy that wraps any MCP server with
4
+ * Clampd's full security pipeline.
5
+ *
6
+ * Users connect Claude Desktop (or any MCP client) to this proxy via SSE.
7
+ * The proxy intercepts every tool call, classifies it through ag-gateway,
8
+ * and only forwards allowed calls to the upstream MCP server.
9
+ *
10
+ * Usage:
11
+ * clampd-mcp-proxy \
12
+ * --upstream "npx -y @modelcontextprotocol/server-filesystem /tmp" \
13
+ * --gateway http://localhost:8080 \
14
+ * --api-key ag_test_acme_demo_2026 \
15
+ * --agent-id b0000001-0000-0000-0000-000000000001 \
16
+ * --port 3003
17
+ *
18
+ * Claude Desktop config (mcpServers):
19
+ * {
20
+ * "filesystem": {
21
+ * "url": "http://localhost:3003/sse"
22
+ * }
23
+ * }
24
+ */
25
+ import { parseArgs } from "node:util";
26
+ import { startProxy } from "./proxy.js";
27
+ import { startFleet } from "./fleet.js";
28
+ // ── CLI argument parsing ──────────────────────────────────────────────
29
+ function parseCli() {
30
+ const { values } = parseArgs({
31
+ options: {
32
+ upstream: { type: "string", short: "u" },
33
+ gateway: { type: "string", short: "g" },
34
+ "api-key": { type: "string", short: "k" },
35
+ "agent-id": { type: "string", short: "a" },
36
+ port: { type: "string", short: "p" },
37
+ secret: { type: "string", short: "s" },
38
+ "dry-run": { type: "boolean", default: false },
39
+ "scan-input": { type: "boolean", default: false },
40
+ "scan-output": { type: "boolean", default: false },
41
+ "check-response": { type: "boolean", default: false },
42
+ "demo-panel": { type: "boolean", default: false },
43
+ "fleet-config": { type: "string" },
44
+ verbose: { type: "boolean", short: "v", default: false },
45
+ help: { type: "boolean", short: "h", default: false },
46
+ },
47
+ strict: true,
48
+ });
49
+ if (values.help) {
50
+ printUsage();
51
+ process.exit(0);
52
+ }
53
+ // Fleet mode — start multiple proxies from config file
54
+ if (values["fleet-config"]) {
55
+ return { fleetConfig: values["fleet-config"], verbose: values.verbose };
56
+ }
57
+ // CLI args take priority, then env vars, then defaults
58
+ const upstream = values.upstream ?? process.env.UPSTREAM_MCP;
59
+ const gateway = values.gateway ?? process.env.AG_GATEWAY_URL ?? "http://localhost:8080";
60
+ const apiKey = values["api-key"] ?? process.env.AG_API_KEY ?? "clmpd_demo_key";
61
+ const agentId = values["agent-id"] ?? process.env.AG_AGENT_ID;
62
+ const port = values.port ?? process.env.PORT ?? "3003";
63
+ const secret = values.secret ?? process.env.CLAMPD_AGENT_SECRET ?? process.env.JWT_SECRET;
64
+ // Feature flags: CLI flag OR env var
65
+ const scanInput = values["scan-input"] || process.env.CLAMPD_SCAN_INPUT === "true";
66
+ const scanOutput = values["scan-output"] || process.env.CLAMPD_SCAN_OUTPUT === "true";
67
+ const checkResponse = values["check-response"] || process.env.CLAMPD_CHECK_RESPONSE === "true";
68
+ const demoPanel = values["demo-panel"] || process.env.CLAMPD_DEMO_PANEL === "true";
69
+ if (!upstream) {
70
+ console.error("Error: --upstream is required (command to spawn the MCP server)");
71
+ console.error(" Set via --upstream flag or UPSTREAM_MCP env var");
72
+ printUsage();
73
+ process.exit(1);
74
+ }
75
+ if (!agentId) {
76
+ console.error("Error: --agent-id is required");
77
+ console.error(" Set via --agent-id flag or AG_AGENT_ID env var");
78
+ printUsage();
79
+ process.exit(1);
80
+ }
81
+ return {
82
+ upstream,
83
+ gateway,
84
+ apiKey,
85
+ agentId,
86
+ port: parseInt(port, 10),
87
+ secret,
88
+ dryRun: values["dry-run"],
89
+ scanInput,
90
+ scanOutput,
91
+ checkResponse,
92
+ demoPanel,
93
+ verbose: values.verbose,
94
+ };
95
+ }
96
+ function printUsage() {
97
+ console.log(`
98
+ Clampd MCP Proxy — wraps any MCP server with Clampd security
99
+
100
+ Usage:
101
+ clampd-mcp-proxy --upstream <command> --agent-id <uuid> [options]
102
+
103
+ Required:
104
+ --upstream, -u Command to spawn the upstream MCP server
105
+ Example: "npx -y @modelcontextprotocol/server-filesystem /tmp"
106
+ --agent-id, -a Clampd agent UUID
107
+
108
+ Options:
109
+ --gateway, -g Clampd gateway URL (default: http://localhost:8080)
110
+ --api-key, -k Clampd API key (default: clmpd_demo_key)
111
+ --port, -p Port to listen on (default: 3003)
112
+ --secret, -s Signing secret (default: CLAMPD_AGENT_SECRET or JWT_SECRET env var)
113
+ --dry-run Classify but never forward (uses /v1/verify)
114
+ --scan-input Scan tool arguments for prompt injection / PII / secrets
115
+ --scan-output Scan tool responses for PII / secrets leakage
116
+ --check-response Validate responses against granted scope token
117
+ --demo-panel Show attack demo panel in dashboard
118
+ --fleet-config Path to fleet config JSON (multi-agent mode)
119
+ --verbose, -v Enable debug logging
120
+ --help, -h Show this help
121
+
122
+ Environment Variables:
123
+ UPSTREAM_MCP Upstream MCP server command
124
+ AG_GATEWAY_URL Gateway URL
125
+ AG_API_KEY API key
126
+ AG_AGENT_ID Agent UUID
127
+ CLAMPD_AGENT_SECRET Agent signing secret (ags_...)
128
+ JWT_SECRET Global JWT signing secret (fallback)
129
+ CLAMPD_SCAN_INPUT Enable input scanning (true/false)
130
+ CLAMPD_SCAN_OUTPUT Enable output scanning (true/false)
131
+ CLAMPD_CHECK_RESPONSE Enable response checking (true/false)
132
+
133
+ Claude Desktop config:
134
+ {
135
+ "mcpServers": {
136
+ "filesystem": {
137
+ "url": "http://localhost:3003/sse"
138
+ }
139
+ }
140
+ }
141
+ `);
142
+ }
143
+ // ── Main ──────────────────────────────────────────────────────────────
144
+ async function main() {
145
+ const args = parseCli();
146
+ // Fleet mode: start multiple proxies from config file
147
+ if ("fleetConfig" in args) {
148
+ const { setVerbose } = await import("./logger.js");
149
+ if (args.verbose)
150
+ setVerbose(true);
151
+ await startFleet(args.fleetConfig);
152
+ return;
153
+ }
154
+ await startProxy({
155
+ upstreamCommand: args.upstream,
156
+ gatewayUrl: args.gateway,
157
+ apiKey: args.apiKey,
158
+ agentId: args.agentId,
159
+ port: args.port,
160
+ secret: args.secret,
161
+ dryRun: args.dryRun,
162
+ scanInputEnabled: args.scanInput,
163
+ scanOutputEnabled: args.scanOutput,
164
+ checkResponse: args.checkResponse,
165
+ demoPanel: args.demoPanel,
166
+ verbose: args.verbose,
167
+ });
168
+ }
169
+ main().catch((err) => {
170
+ console.error("Fatal error:", err);
171
+ process.exit(1);
172
+ });
173
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Gateway communication — classifies tool calls, scans input/output,
3
+ * and validates responses through ag-gateway.
4
+ *
5
+ * This is the security boundary: every MCP tool call passes through here
6
+ * before reaching the upstream server.
7
+ */
8
+ export interface ClassifyResult {
9
+ /** Whether the tool call is allowed */
10
+ allowed: boolean;
11
+ /** Risk score from the 9-stage pipeline (0.0 - 1.0) */
12
+ risk_score: number;
13
+ /** Rule IDs that matched (e.g. ["R001", "R006"]) */
14
+ matched_rules: string[];
15
+ /** Human-readable denial reason (when blocked) */
16
+ denial_reason?: string;
17
+ /** Scope granted by OPA policy */
18
+ scope_granted?: string;
19
+ /** Gateway request ID for audit trail */
20
+ request_id?: string;
21
+ /** Scope token for response validation */
22
+ scope_token?: string;
23
+ /** Gateway processing latency */
24
+ latency_ms: number;
25
+ /** Pipeline stages that were degraded */
26
+ degraded_stages: string[];
27
+ /** Behavioral session flags */
28
+ session_flags: string[];
29
+ /** Intent action: "pass", "flag", or "block" */
30
+ action?: string;
31
+ /** Human-readable reasoning from the rules engine */
32
+ reasoning?: string;
33
+ }
34
+ export interface ScanResult {
35
+ allowed: boolean;
36
+ risk_score: number;
37
+ matched_rules: string[];
38
+ denial_reason?: string;
39
+ latency_ms: number;
40
+ pii_found?: Array<{
41
+ pii_type: string;
42
+ count: number;
43
+ }>;
44
+ secrets_found?: Array<{
45
+ secret_type: string;
46
+ count: number;
47
+ }>;
48
+ }
49
+ export interface InspectResult {
50
+ allowed: boolean;
51
+ risk_score: number;
52
+ matched_rules: string[];
53
+ denial_reason?: string;
54
+ latency_ms: number;
55
+ }
56
+ /** Tool definition from MCP listTools() */
57
+ export interface ToolDef {
58
+ name: string;
59
+ description?: string;
60
+ inputSchema?: Record<string, unknown>;
61
+ }
62
+ /** Compute SHA-256 hex hash of a tool descriptor for rug-pull detection. */
63
+ export declare function computeToolDescriptorHash(tool: ToolDef): string;
64
+ /** Build a lookup map of tool name → descriptor hash. */
65
+ export declare function buildDescriptorMap(tools: ToolDef[]): Map<string, string>;
66
+ export declare function classifyToolCall(gatewayUrl: string, apiKey: string, secret: string, agentId: string, toolName: string, params: Record<string, unknown>, dryRun?: boolean, toolDescriptorHash?: string, authorizedTools?: string[], toolDescription?: string, toolParamsSchema?: string): Promise<ClassifyResult>;
67
+ export declare function scanInput(gatewayUrl: string, apiKey: string, secret: string, agentId: string, text: string): Promise<ScanResult>;
68
+ export declare function scanOutput(gatewayUrl: string, apiKey: string, secret: string, agentId: string, text: string, requestId?: string): Promise<ScanResult>;
69
+ export declare function inspectResponse(gatewayUrl: string, apiKey: string, secret: string, agentId: string, toolName: string, responseData: unknown, requestId?: string, scopeToken?: string): Promise<InspectResult>;
70
+ export interface RegisterToolsResult {
71
+ registered: number;
72
+ failed: number;
73
+ errors: Array<{
74
+ tool: string;
75
+ error: string;
76
+ }>;
77
+ }
78
+ /**
79
+ * Register discovered tools with the gateway at startup.
80
+ *
81
+ * Sends a lightweight `/v1/proxy` call (evaluate-only, target_url="") for
82
+ * each tool with benign empty params. This triggers the shadow event pipeline
83
+ * in ag-gateway, which ag-control picks up to auto-capture tool descriptors
84
+ * into the `tool_descriptors` table. If the org has `auto_trust` enabled,
85
+ * tools are auto-approved and their scopes synced to Redis — preventing
86
+ * `tool_not_registered` (422) errors on subsequent real calls.
87
+ *
88
+ * Requests run in parallel with a concurrency cap to avoid overwhelming
89
+ * the gateway. Registration is idempotent (safe to re-run on restart).
90
+ */
91
+ export declare function registerTools(gatewayUrl: string, apiKey: string, secret: string, agentId: string, tools: ToolDef[], descriptorMap: Map<string, string>): Promise<RegisterToolsResult>;
92
+ //# sourceMappingURL=interceptor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interceptor.d.ts","sourceRoot":"","sources":["../src/interceptor.ts"],"names":[],"mappings":"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"}