sovr-mcp-proxy 7.0.0 → 7.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.
@@ -0,0 +1,370 @@
1
+ // src/auditDashboard.ts
2
+ var AuditDashboard = class {
3
+ entries = [];
4
+ config;
5
+ saveTimer = null;
6
+ quotaCounter = 0;
7
+ quotaPeriodStart = Date.now();
8
+ constructor(config = {}) {
9
+ this.config = {
10
+ maxEntries: config.maxEntries ?? 5e4,
11
+ persistPath: config.persistPath,
12
+ autoSaveInterval: config.autoSaveInterval ?? 6e4,
13
+ quotaLimit: config.quotaLimit ?? 1e4,
14
+ quotaPeriod: config.quotaPeriod ?? 30 * 24 * 60 * 60 * 1e3,
15
+ // 30 days
16
+ redactPatterns: config.redactPatterns ?? [
17
+ /password/i,
18
+ /secret/i,
19
+ /token/i,
20
+ /api.?key/i,
21
+ /credential/i,
22
+ /authorization/i,
23
+ /cookie/i,
24
+ /session/i
25
+ ]
26
+ };
27
+ if (this.config.persistPath && this.config.autoSaveInterval > 0) {
28
+ this.saveTimer = setInterval(() => this.persist(), this.config.autoSaveInterval);
29
+ }
30
+ }
31
+ // ─── Core Operations ───────────────────────────────────────────────────
32
+ /** Record a new audit entry */
33
+ record(entry) {
34
+ const now = Date.now();
35
+ const fullEntry = {
36
+ ...entry,
37
+ id: `ae_${now}_${Math.random().toString(36).slice(2, 8)}`,
38
+ timestamp: new Date(now).toISOString(),
39
+ epochMs: now,
40
+ arguments: this.redactSensitive(entry.arguments)
41
+ };
42
+ this.entries.push(fullEntry);
43
+ this.quotaCounter++;
44
+ if (this.entries.length > this.config.maxEntries) {
45
+ this.entries = this.entries.slice(-this.config.maxEntries);
46
+ }
47
+ return fullEntry;
48
+ }
49
+ /** Get entries with optional filters */
50
+ query(filters = {}) {
51
+ let filtered = this.entries;
52
+ if (filters.from) filtered = filtered.filter((e) => e.epochMs >= filters.from);
53
+ if (filters.to) filtered = filtered.filter((e) => e.epochMs <= filters.to);
54
+ if (filters.toolName) filtered = filtered.filter((e) => e.toolName === filters.toolName);
55
+ if (filters.decision) filtered = filtered.filter((e) => e.decision === filters.decision);
56
+ if (filters.riskLevel) filtered = filtered.filter((e) => e.riskLevel === filters.riskLevel);
57
+ if (filters.sessionId) filtered = filtered.filter((e) => e.sessionId === filters.sessionId);
58
+ if (filters.agentId) filtered = filtered.filter((e) => e.agentId === filters.agentId);
59
+ const total = filtered.length;
60
+ const offset = filters.offset ?? 0;
61
+ const limit = filters.limit ?? 100;
62
+ const paged = filtered.slice(offset, offset + limit);
63
+ return { entries: paged, total };
64
+ }
65
+ /** Get a single entry by ID */
66
+ getById(id) {
67
+ return this.entries.find((e) => e.id === id);
68
+ }
69
+ // ─── Statistics ────────────────────────────────────────────────────────
70
+ /** Get dashboard statistics for a time range */
71
+ getStats(from, to) {
72
+ const now = Date.now();
73
+ const rangeFrom = from ?? now - 24 * 60 * 60 * 1e3;
74
+ const rangeTo = to ?? now;
75
+ const filtered = this.entries.filter((e) => e.epochMs >= rangeFrom && e.epochMs <= rangeTo);
76
+ const byDecision = {};
77
+ const byRiskLevel = {};
78
+ const byTool = {};
79
+ const byType = {};
80
+ const policyCount = {};
81
+ const blockedTools = {};
82
+ const latencies = [];
83
+ for (const entry of filtered) {
84
+ byDecision[entry.decision] = (byDecision[entry.decision] || 0) + 1;
85
+ byRiskLevel[entry.riskLevel] = (byRiskLevel[entry.riskLevel] || 0) + 1;
86
+ byTool[entry.toolName] = (byTool[entry.toolName] || 0) + 1;
87
+ byType[entry.type] = (byType[entry.type] || 0) + 1;
88
+ latencies.push(entry.durationMs);
89
+ if (entry.policyId) {
90
+ policyCount[entry.policyId] = (policyCount[entry.policyId] || 0) + 1;
91
+ }
92
+ if (entry.decision === "block") {
93
+ blockedTools[entry.toolName] = (blockedTools[entry.toolName] || 0) + 1;
94
+ }
95
+ }
96
+ const sortedLatencies = latencies.sort((a, b) => a - b);
97
+ const p95Index = Math.floor(sortedLatencies.length * 0.95);
98
+ const p99Index = Math.floor(sortedLatencies.length * 0.99);
99
+ const topBlocked = Object.entries(blockedTools).sort(([, a], [, b]) => b - a).slice(0, 10).map(([tool, count]) => ({ tool, count }));
100
+ const topPolicies = Object.entries(policyCount).sort(([, a], [, b]) => b - a).slice(0, 10).map(([policyId, count]) => ({ policyId, count }));
101
+ const riskHistogram = [
102
+ { range: "0-20", count: filtered.filter((e) => e.riskScore < 20).length },
103
+ { range: "20-40", count: filtered.filter((e) => e.riskScore >= 20 && e.riskScore < 40).length },
104
+ { range: "40-60", count: filtered.filter((e) => e.riskScore >= 40 && e.riskScore < 60).length },
105
+ { range: "60-80", count: filtered.filter((e) => e.riskScore >= 60 && e.riskScore < 80).length },
106
+ { range: "80-100", count: filtered.filter((e) => e.riskScore >= 80).length }
107
+ ];
108
+ const durationMinutes = Math.max(1, (rangeTo - rangeFrom) / 6e4);
109
+ const allowed = byDecision["allow"] || 0;
110
+ return {
111
+ timeRange: { from: new Date(rangeFrom).toISOString(), to: new Date(rangeTo).toISOString() },
112
+ totalEvents: filtered.length,
113
+ byDecision,
114
+ byRiskLevel,
115
+ byTool,
116
+ byType,
117
+ successRate: filtered.length > 0 ? Math.round(allowed / filtered.length * 1e4) / 100 : 100,
118
+ avgLatencyMs: latencies.length > 0 ? Math.round(latencies.reduce((a, b) => a + b, 0) / latencies.length) : 0,
119
+ p95LatencyMs: sortedLatencies[p95Index] ?? 0,
120
+ p99LatencyMs: sortedLatencies[p99Index] ?? 0,
121
+ eventsPerMinute: Math.round(filtered.length / durationMinutes * 100) / 100,
122
+ topBlocked,
123
+ topPolicies,
124
+ riskHistogram
125
+ };
126
+ }
127
+ // ─── Trends ────────────────────────────────────────────────────────────
128
+ /** Get time-series trend data */
129
+ getTrends(granularity = "hourly", from, to) {
130
+ const now = Date.now();
131
+ const rangeFrom = from ?? now - 7 * 24 * 60 * 60 * 1e3;
132
+ const rangeTo = to ?? now;
133
+ const bucketSize = granularity === "hourly" ? 36e5 : granularity === "daily" ? 864e5 : 6048e5;
134
+ const filtered = this.entries.filter((e) => e.epochMs >= rangeFrom && e.epochMs <= rangeTo);
135
+ const buckets = /* @__PURE__ */ new Map();
136
+ for (const entry of filtered) {
137
+ const bucketStart = Math.floor(entry.epochMs / bucketSize) * bucketSize;
138
+ if (!buckets.has(bucketStart)) buckets.set(bucketStart, []);
139
+ buckets.get(bucketStart).push(entry);
140
+ }
141
+ const points = [];
142
+ let current = Math.floor(rangeFrom / bucketSize) * bucketSize;
143
+ while (current <= rangeTo) {
144
+ const entries = buckets.get(current) || [];
145
+ const allowed = entries.filter((e) => e.decision === "allow" || e.decision === "transform").length;
146
+ const blocked = entries.filter((e) => e.decision === "block" || e.decision === "rate-limited").length;
147
+ points.push({
148
+ label: this.formatBucketLabel(current, granularity),
149
+ epochMs: current,
150
+ total: entries.length,
151
+ allowed,
152
+ blocked,
153
+ avgRisk: entries.length > 0 ? Math.round(entries.reduce((sum, e) => sum + e.riskScore, 0) / entries.length) : 0,
154
+ avgLatency: entries.length > 0 ? Math.round(entries.reduce((sum, e) => sum + e.durationMs, 0) / entries.length) : 0
155
+ });
156
+ current += bucketSize;
157
+ }
158
+ return points;
159
+ }
160
+ // ─── Quota ─────────────────────────────────────────────────────────────
161
+ /** Get current quota status with prediction */
162
+ getQuotaStatus() {
163
+ const now = Date.now();
164
+ if (now - this.quotaPeriodStart > this.config.quotaPeriod) {
165
+ this.quotaCounter = 0;
166
+ this.quotaPeriodStart = now;
167
+ }
168
+ const elapsed = now - this.quotaPeriodStart;
169
+ const elapsedDays = Math.max(1, elapsed / (24 * 60 * 60 * 1e3));
170
+ const dailyAverage = Math.round(this.quotaCounter / elapsedDays);
171
+ const remaining = this.config.quotaLimit - this.quotaCounter;
172
+ const daysRemaining = dailyAverage > 0 ? Math.round(remaining / dailyAverage) : void 0;
173
+ let predictedExhaustion;
174
+ if (daysRemaining !== void 0 && daysRemaining > 0) {
175
+ predictedExhaustion = new Date(now + daysRemaining * 24 * 60 * 60 * 1e3).toISOString();
176
+ }
177
+ return {
178
+ used: this.quotaCounter,
179
+ limit: this.config.quotaLimit,
180
+ percentage: Math.round(this.quotaCounter / this.config.quotaLimit * 1e4) / 100,
181
+ predictedExhaustion,
182
+ dailyAverage,
183
+ daysRemaining
184
+ };
185
+ }
186
+ // ─── Export ────────────────────────────────────────────────────────────
187
+ /** Export audit log as CSV */
188
+ exportCSV(filters) {
189
+ const { entries } = this.query({ from: filters?.from, to: filters?.to, limit: this.config.maxEntries });
190
+ const headers = [
191
+ "id",
192
+ "timestamp",
193
+ "type",
194
+ "toolName",
195
+ "decision",
196
+ "riskScore",
197
+ "riskLevel",
198
+ "policyId",
199
+ "reason",
200
+ "durationMs",
201
+ "sessionId",
202
+ "agentId",
203
+ "error"
204
+ ];
205
+ const rows = entries.map((e) => [
206
+ e.id,
207
+ e.timestamp,
208
+ e.type,
209
+ e.toolName,
210
+ e.decision,
211
+ e.riskScore,
212
+ e.riskLevel,
213
+ e.policyId || "",
214
+ `"${(e.reason || "").replace(/"/g, '""')}"`,
215
+ e.durationMs,
216
+ e.sessionId || "",
217
+ e.agentId || "",
218
+ e.error ? `"${e.error.replace(/"/g, '""')}"` : ""
219
+ ].join(","));
220
+ return [headers.join(","), ...rows].join("\n");
221
+ }
222
+ /** Export audit log as JSON */
223
+ exportJSON(filters) {
224
+ const { entries } = this.query({ from: filters?.from, to: filters?.to, limit: this.config.maxEntries });
225
+ return JSON.stringify({
226
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
227
+ totalEntries: entries.length,
228
+ entries
229
+ }, null, 2);
230
+ }
231
+ /** Export Trust Bundle (evidence package) */
232
+ exportTrustBundle(sessionId) {
233
+ const { entries } = sessionId ? this.query({ sessionId, limit: this.config.maxEntries }) : this.query({ limit: 1e3 });
234
+ const stats = this.getStats();
235
+ const dataStr = JSON.stringify(entries);
236
+ let hash = 0;
237
+ for (let i = 0; i < dataStr.length; i++) {
238
+ const char = dataStr.charCodeAt(i);
239
+ hash = (hash << 5) - hash + char;
240
+ hash |= 0;
241
+ }
242
+ return {
243
+ bundleId: `tb_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
244
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
245
+ entries,
246
+ stats,
247
+ integrity: `sha256:${Math.abs(hash).toString(16).padStart(16, "0")}`
248
+ };
249
+ }
250
+ // ─── Session Management ────────────────────────────────────────────────
251
+ /** Start a new audit session */
252
+ startSession(agentId) {
253
+ const sessionId = `ses_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
254
+ this.record({
255
+ type: "session_start",
256
+ toolName: "__session__",
257
+ arguments: {},
258
+ decision: "allow",
259
+ riskScore: 0,
260
+ riskLevel: "safe",
261
+ reason: "Session started",
262
+ durationMs: 0,
263
+ sessionId,
264
+ agentId
265
+ });
266
+ return sessionId;
267
+ }
268
+ /** End an audit session */
269
+ endSession(sessionId) {
270
+ this.record({
271
+ type: "session_end",
272
+ toolName: "__session__",
273
+ arguments: {},
274
+ decision: "allow",
275
+ riskScore: 0,
276
+ riskLevel: "safe",
277
+ reason: "Session ended",
278
+ durationMs: 0,
279
+ sessionId
280
+ });
281
+ }
282
+ // ─── Lifecycle ─────────────────────────────────────────────────────────
283
+ /** Get total entry count */
284
+ get size() {
285
+ return this.entries.length;
286
+ }
287
+ /** Clear all entries */
288
+ clear() {
289
+ this.entries = [];
290
+ }
291
+ /** Destroy and cleanup */
292
+ destroy() {
293
+ if (this.saveTimer) {
294
+ clearInterval(this.saveTimer);
295
+ this.saveTimer = null;
296
+ }
297
+ this.persist();
298
+ }
299
+ // ─── Private ─────────────────────────────────────────────────────────────
300
+ redactSensitive(args) {
301
+ const result = JSON.parse(JSON.stringify(args));
302
+ const redact = (obj) => {
303
+ for (const key of Object.keys(obj)) {
304
+ if (this.config.redactPatterns.some((p) => p.test(key))) {
305
+ obj[key] = "[REDACTED]";
306
+ } else if (typeof obj[key] === "object" && obj[key] !== null) {
307
+ redact(obj[key]);
308
+ } else if (typeof obj[key] === "string") {
309
+ let val = obj[key];
310
+ for (const pattern of this.config.redactPatterns) {
311
+ if (pattern.test(val)) {
312
+ obj[key] = "[REDACTED]";
313
+ break;
314
+ }
315
+ }
316
+ }
317
+ }
318
+ };
319
+ redact(result);
320
+ return result;
321
+ }
322
+ formatBucketLabel(epochMs, granularity) {
323
+ const d = new Date(epochMs);
324
+ switch (granularity) {
325
+ case "hourly":
326
+ return `${d.getUTCMonth() + 1}/${d.getUTCDate()} ${d.getUTCHours().toString().padStart(2, "0")}:00`;
327
+ case "daily":
328
+ return `${d.getUTCFullYear()}-${(d.getUTCMonth() + 1).toString().padStart(2, "0")}-${d.getUTCDate().toString().padStart(2, "0")}`;
329
+ case "weekly":
330
+ return `Week of ${d.getUTCMonth() + 1}/${d.getUTCDate()}`;
331
+ default:
332
+ return d.toISOString();
333
+ }
334
+ }
335
+ persist() {
336
+ if (!this.config.persistPath) return;
337
+ try {
338
+ const data = JSON.stringify({
339
+ entries: this.entries.slice(-this.config.maxEntries),
340
+ quotaCounter: this.quotaCounter,
341
+ quotaPeriodStart: this.quotaPeriodStart
342
+ });
343
+ void data;
344
+ } catch {
345
+ }
346
+ }
347
+ };
348
+ function createAuditDashboard(overrides = {}) {
349
+ return new AuditDashboard(overrides);
350
+ }
351
+ function createFreeTierDashboard() {
352
+ return new AuditDashboard({
353
+ maxEntries: 1e3,
354
+ quotaLimit: 500,
355
+ quotaPeriod: 30 * 24 * 60 * 60 * 1e3
356
+ });
357
+ }
358
+ function createProDashboard() {
359
+ return new AuditDashboard({
360
+ maxEntries: 5e4,
361
+ quotaLimit: 5e4,
362
+ quotaPeriod: 30 * 24 * 60 * 60 * 1e3
363
+ });
364
+ }
365
+ export {
366
+ AuditDashboard,
367
+ createAuditDashboard,
368
+ createFreeTierDashboard,
369
+ createProDashboard
370
+ };
@@ -0,0 +1,256 @@
1
+ /**
2
+ * SOVR MCP Proxy Interceptor — Universal Tool Call Interception
3
+ *
4
+ * P1-1: SOVR sits between the MCP client (LLM) and the MCP server (tools),
5
+ * intercepting ALL tool calls regardless of transport (stdio/SSE/HTTP).
6
+ *
7
+ * Architecture:
8
+ * [LLM Client] → [SOVR Proxy] → [Upstream MCP Server]
9
+ * ↓
10
+ * [Policy Engine]
11
+ * [Audit Trail]
12
+ * [Rate Limiter]
13
+ * [Semantic Analyzer]
14
+ *
15
+ * Unlike Tool Replacement (which replaces tools), this module transparently
16
+ * proxies ALL tools and applies policies at the protocol level.
17
+ */
18
+ interface InterceptorConfig {
19
+ /** Policy mode: block | warn | audit-only */
20
+ mode: 'block' | 'warn' | 'audit';
21
+ /** Maximum concurrent tool calls */
22
+ maxConcurrent: number;
23
+ /** Per-tool rate limits (calls per minute) */
24
+ rateLimits: Record<string, number>;
25
+ /** Global rate limit (calls per minute) */
26
+ globalRateLimit: number;
27
+ /** Tool-specific policies */
28
+ toolPolicies: ToolPolicy[];
29
+ /** Enable semantic analysis for tool arguments */
30
+ enableSemanticAnalysis: boolean;
31
+ /** Callback for human approval */
32
+ onApprovalRequired?: (request: InterceptedCall) => Promise<ApprovalResult>;
33
+ /** Callback for audit events */
34
+ onAuditEvent?: (event: AuditEvent) => void;
35
+ /** Timeout for upstream tool calls (ms) */
36
+ upstreamTimeout: number;
37
+ /** Max retries for transient failures */
38
+ maxRetries: number;
39
+ }
40
+ interface ToolPolicy {
41
+ /** Tool name pattern (glob) */
42
+ toolPattern: string;
43
+ /** Action to take */
44
+ action: 'allow' | 'block' | 'require-approval' | 'rate-limit' | 'transform';
45
+ /** Conditions for this policy */
46
+ conditions?: PolicyCondition[];
47
+ /** Argument transformations (for 'transform' action) */
48
+ transforms?: ArgumentTransform[];
49
+ /** Priority (higher = evaluated first) */
50
+ priority: number;
51
+ /** Human-readable description */
52
+ description: string;
53
+ }
54
+ interface PolicyCondition {
55
+ /** Field path in tool arguments (dot notation) */
56
+ field: string;
57
+ /** Operator */
58
+ operator: 'contains' | 'matches' | 'equals' | 'gt' | 'lt' | 'exists' | 'not-exists';
59
+ /** Value to compare against */
60
+ value: string | number | boolean;
61
+ }
62
+ interface ArgumentTransform {
63
+ /** Field path to transform */
64
+ field: string;
65
+ /** Transform type */
66
+ type: 'redact' | 'mask' | 'replace' | 'remove';
67
+ /** Replacement value (for 'replace') */
68
+ replacement?: string;
69
+ }
70
+ interface InterceptedCall {
71
+ /** Unique call ID */
72
+ id: string;
73
+ /** Timestamp */
74
+ timestamp: number;
75
+ /** Tool name */
76
+ toolName: string;
77
+ /** Original arguments */
78
+ arguments: Record<string, unknown>;
79
+ /** Matched policies */
80
+ matchedPolicies: ToolPolicy[];
81
+ /** Decision */
82
+ decision: InterceptDecision;
83
+ /** Semantic analysis result (if enabled) */
84
+ semanticResult?: SemanticAnalysisResult;
85
+ }
86
+ interface InterceptDecision {
87
+ /** Final action */
88
+ action: 'allow' | 'block' | 'require-approval' | 'transform';
89
+ /** Reason for decision */
90
+ reason: string;
91
+ /** Policy that triggered this decision */
92
+ triggeringPolicy?: ToolPolicy;
93
+ /** Risk score (0-100) */
94
+ riskScore: number;
95
+ /** Transformed arguments (if action is 'transform') */
96
+ transformedArguments?: Record<string, unknown>;
97
+ }
98
+ interface SemanticAnalysisResult {
99
+ /** Detected intent */
100
+ intent: string;
101
+ /** Risk category */
102
+ riskCategory: 'safe' | 'suspicious' | 'dangerous' | 'critical';
103
+ /** Confidence (0-1) */
104
+ confidence: number;
105
+ /** Explanation */
106
+ explanation: string;
107
+ }
108
+ interface ApprovalResult {
109
+ approved: boolean;
110
+ approver?: string;
111
+ reason?: string;
112
+ timestamp: number;
113
+ }
114
+ interface AuditEvent {
115
+ id: string;
116
+ timestamp: number;
117
+ type: 'intercept' | 'allow' | 'block' | 'approval-requested' | 'approval-granted' | 'approval-denied' | 'error' | 'rate-limited';
118
+ toolName: string;
119
+ arguments: Record<string, unknown>;
120
+ decision: InterceptDecision;
121
+ duration?: number;
122
+ error?: string;
123
+ upstreamResult?: unknown;
124
+ }
125
+ interface InterceptorStats {
126
+ totalCalls: number;
127
+ allowed: number;
128
+ blocked: number;
129
+ approvalRequested: number;
130
+ rateLimited: number;
131
+ errors: number;
132
+ avgLatencyMs: number;
133
+ callsByTool: Record<string, number>;
134
+ blocksByPolicy: Record<string, number>;
135
+ riskDistribution: {
136
+ safe: number;
137
+ suspicious: number;
138
+ dangerous: number;
139
+ critical: number;
140
+ };
141
+ }
142
+ declare class McpProxyInterceptor {
143
+ private config;
144
+ private rateLimiter;
145
+ private stats;
146
+ private auditLog;
147
+ private maxAuditLogSize;
148
+ private activeCalls;
149
+ private cleanupInterval;
150
+ constructor(config?: Partial<InterceptorConfig>);
151
+ /**
152
+ * Intercept a tool call — the core entry point.
153
+ * Returns the decision and optionally transformed arguments.
154
+ */
155
+ intercept(toolName: string, args: Record<string, unknown>): Promise<InterceptedCall>;
156
+ /**
157
+ * Wrap an upstream tool call with interception.
158
+ * If allowed, executes the upstream function; if blocked, returns error.
159
+ */
160
+ wrapToolCall<T>(toolName: string, args: Record<string, unknown>, upstream: (args: Record<string, unknown>) => Promise<T>): Promise<{
161
+ result?: T;
162
+ intercepted: InterceptedCall;
163
+ error?: string;
164
+ }>;
165
+ /** Get current statistics */
166
+ getStats(): InterceptorStats;
167
+ /** Get recent audit log */
168
+ getAuditLog(limit?: number): AuditEvent[];
169
+ /** Export audit log as CSV */
170
+ exportAuditCSV(): string;
171
+ /** Add a policy at runtime */
172
+ addPolicy(policy: ToolPolicy): void;
173
+ /** Remove policies matching a pattern */
174
+ removePolicies(toolPattern: string): number;
175
+ /** Reset statistics */
176
+ resetStats(): void;
177
+ /** Cleanup resources */
178
+ destroy(): void;
179
+ private emitAudit;
180
+ private trimAuditLog;
181
+ }
182
+ declare const PRESET_POLICIES: {
183
+ /** Block all destructive file operations */
184
+ noDestructiveFiles: {
185
+ toolPattern: string;
186
+ action: "block";
187
+ conditions: {
188
+ field: string;
189
+ operator: "matches";
190
+ value: string;
191
+ }[];
192
+ priority: number;
193
+ description: string;
194
+ };
195
+ /** Block all database write operations */
196
+ readOnlyDatabase: {
197
+ toolPattern: string;
198
+ action: "block";
199
+ conditions: {
200
+ field: string;
201
+ operator: "matches";
202
+ value: string;
203
+ }[];
204
+ priority: number;
205
+ description: string;
206
+ };
207
+ /** Require approval for payment operations */
208
+ approvePayments: {
209
+ toolPattern: string;
210
+ action: "require-approval";
211
+ priority: number;
212
+ description: string;
213
+ };
214
+ /** Redact secrets in tool arguments */
215
+ redactSecrets: {
216
+ toolPattern: string;
217
+ action: "transform";
218
+ conditions: {
219
+ field: string;
220
+ operator: "matches";
221
+ value: string;
222
+ }[];
223
+ transforms: ({
224
+ field: string;
225
+ type: "redact";
226
+ } | {
227
+ field: string;
228
+ type: "mask";
229
+ })[];
230
+ priority: number;
231
+ description: string;
232
+ };
233
+ /** Rate limit shell commands */
234
+ rateLimitShell: {
235
+ toolPattern: string;
236
+ action: "rate-limit";
237
+ priority: number;
238
+ description: string;
239
+ };
240
+ /** Allow read-only operations */
241
+ allowReadOnly: {
242
+ toolPattern: string;
243
+ action: "allow";
244
+ conditions: {
245
+ field: string;
246
+ operator: "matches";
247
+ value: string;
248
+ }[];
249
+ priority: number;
250
+ description: string;
251
+ };
252
+ };
253
+ /** Create a pre-configured interceptor with common security policies */
254
+ declare function createSecureInterceptor(overrides?: Partial<InterceptorConfig>): McpProxyInterceptor;
255
+
256
+ export { type ApprovalResult, type ArgumentTransform, type AuditEvent, type InterceptDecision, type InterceptedCall, type InterceptorConfig, type InterceptorStats, McpProxyInterceptor, PRESET_POLICIES, type PolicyCondition, type SemanticAnalysisResult, type ToolPolicy, createSecureInterceptor };