@vibekiln/cutline-mcp-cli 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/Dockerfile +11 -0
- package/README.md +248 -0
- package/dist/auth/callback.d.ts +6 -0
- package/dist/auth/callback.js +97 -0
- package/dist/auth/keychain.d.ts +3 -0
- package/dist/auth/keychain.js +16 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +309 -0
- package/dist/commands/login.d.ts +7 -0
- package/dist/commands/login.js +166 -0
- package/dist/commands/logout.d.ts +1 -0
- package/dist/commands/logout.js +25 -0
- package/dist/commands/serve.d.ts +1 -0
- package/dist/commands/serve.js +38 -0
- package/dist/commands/setup.d.ts +5 -0
- package/dist/commands/setup.js +278 -0
- package/dist/commands/status.d.ts +3 -0
- package/dist/commands/status.js +127 -0
- package/dist/commands/upgrade.d.ts +3 -0
- package/dist/commands/upgrade.js +112 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +64 -0
- package/dist/servers/chunk-DE7R7WKY.js +331 -0
- package/dist/servers/chunk-KMUSQOTJ.js +47 -0
- package/dist/servers/chunk-OP4EO6FV.js +454 -0
- package/dist/servers/chunk-UBBAYTW3.js +946 -0
- package/dist/servers/chunk-ZVWDXO6M.js +1063 -0
- package/dist/servers/cutline-server.js +10448 -0
- package/dist/servers/data-client-FPUZBUO3.js +160 -0
- package/dist/servers/exploration-server.js +930 -0
- package/dist/servers/graph-metrics-DCNR7JZN.js +12 -0
- package/dist/servers/integrations-server.js +107 -0
- package/dist/servers/output-server.js +107 -0
- package/dist/servers/premortem-server.js +971 -0
- package/dist/servers/tools-server.js +287 -0
- package/dist/utils/config-store.d.ts +8 -0
- package/dist/utils/config-store.js +35 -0
- package/dist/utils/config.d.ts +22 -0
- package/dist/utils/config.js +48 -0
- package/mcpb/manifest.json +77 -0
- package/package.json +76 -0
- package/server.json +42 -0
- package/smithery.yaml +10 -0
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
// ../mcp/dist/mcp/src/shared/sanitize.js
|
|
2
|
+
var NULL_AND_CONTROL_RE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
|
|
3
|
+
var HTML_TAG_RE = /<\/?[a-z][^>]*>/gi;
|
|
4
|
+
var SCRIPT_RE = /<script[\s>][\s\S]*?<\/script>/gi;
|
|
5
|
+
var EVENT_HANDLER_RE = /\s+on\w+\s*=\s*["'][^"']*["']/gi;
|
|
6
|
+
function stripControlChars(input) {
|
|
7
|
+
return input.replace(NULL_AND_CONTROL_RE, "");
|
|
8
|
+
}
|
|
9
|
+
function stripHtmlTags(input) {
|
|
10
|
+
return input.replace(SCRIPT_RE, "").replace(EVENT_HANDLER_RE, "").replace(HTML_TAG_RE, "");
|
|
11
|
+
}
|
|
12
|
+
var DEFAULT_MAX_LENGTH = 5e4;
|
|
13
|
+
function sanitizeText(input, options = {}) {
|
|
14
|
+
const { maxLength = DEFAULT_MAX_LENGTH, stripHtml = true } = options;
|
|
15
|
+
let result = stripControlChars(input);
|
|
16
|
+
if (stripHtml) {
|
|
17
|
+
result = stripHtmlTags(result);
|
|
18
|
+
}
|
|
19
|
+
result = result.trim();
|
|
20
|
+
if (result.length > maxLength) {
|
|
21
|
+
result = result.slice(0, maxLength);
|
|
22
|
+
}
|
|
23
|
+
return result;
|
|
24
|
+
}
|
|
25
|
+
function sanitizeArgs(value, options = {}) {
|
|
26
|
+
if (typeof value === "string") {
|
|
27
|
+
return sanitizeText(value, options);
|
|
28
|
+
}
|
|
29
|
+
if (Array.isArray(value)) {
|
|
30
|
+
return value.map((item) => sanitizeArgs(item, options));
|
|
31
|
+
}
|
|
32
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date)) {
|
|
33
|
+
const result = {};
|
|
34
|
+
for (const [key, val] of Object.entries(value)) {
|
|
35
|
+
result[key] = sanitizeArgs(val, options);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
return value;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// ../mcp/dist/mcp/src/shared/pii-scanner.js
|
|
43
|
+
var patterns = [
|
|
44
|
+
// ── Secrets ──────────────────────────────────────────────────────────────
|
|
45
|
+
{
|
|
46
|
+
type: "aws_key",
|
|
47
|
+
pattern: /AKIA[0-9A-Z]{16}/g,
|
|
48
|
+
severity: "critical",
|
|
49
|
+
description: "AWS Access Key ID",
|
|
50
|
+
validate: (m) => m.length === 20
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
type: "aws_secret",
|
|
54
|
+
pattern: /(?:aws_secret_access_key|secret_access_key|aws_secret)\s*[=:]\s*[A-Za-z0-9/+=]{40}/g,
|
|
55
|
+
severity: "critical",
|
|
56
|
+
description: "AWS Secret Access Key"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: "stripe_key",
|
|
60
|
+
pattern: /[sr]k_(live|test)_[0-9a-zA-Z]{24,}/g,
|
|
61
|
+
severity: "critical",
|
|
62
|
+
description: "Stripe API key"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: "stripe_key",
|
|
66
|
+
pattern: /pk_(live|test)_[0-9a-zA-Z]{24,}/g,
|
|
67
|
+
severity: "critical",
|
|
68
|
+
description: "Stripe publishable key"
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: "github_token",
|
|
72
|
+
pattern: /gh[ps]_[A-Za-z0-9]{36,}/g,
|
|
73
|
+
severity: "critical",
|
|
74
|
+
description: "GitHub personal access token"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "github_token",
|
|
78
|
+
pattern: /github_pat_[A-Za-z0-9_]{20,}/g,
|
|
79
|
+
severity: "critical",
|
|
80
|
+
description: "GitHub fine-grained personal access token"
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: "bearer_token",
|
|
84
|
+
pattern: /Bearer\s+[A-Za-z0-9\-._~+/]+=*/g,
|
|
85
|
+
severity: "critical",
|
|
86
|
+
description: "Bearer authorization token"
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: "generic_api_key",
|
|
90
|
+
pattern: /(?:api[_-]?key|apikey|api[_-]?secret|secret[_-]?key)\s*[=:"']\s*["']?([A-Za-z0-9\-._~+/]{20,})["']?/gi,
|
|
91
|
+
severity: "warning",
|
|
92
|
+
description: "Generic API key/secret assignment"
|
|
93
|
+
},
|
|
94
|
+
// ── PII ──────────────────────────────────────────────────────────────────
|
|
95
|
+
{
|
|
96
|
+
type: "credit_card",
|
|
97
|
+
pattern: /\b(?:4[0-9]{3}|5[1-5][0-9]{2}|3[47][0-9]{2}|6(?:011|5[0-9]{2}))[- ]?[0-9]{4}[- ]?[0-9]{4}[- ]?[0-9]{4}\b/g,
|
|
98
|
+
severity: "critical",
|
|
99
|
+
description: "Credit card number (Visa, MC, Amex, Discover)"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: "ssn",
|
|
103
|
+
pattern: /(?:ssn|social\s*security(?:\s*number)?|ss#)\s*(?:number\s*)?[:\s]?\s*(\d{3})-?(\d{2})-?(\d{4})/gi,
|
|
104
|
+
severity: "critical",
|
|
105
|
+
description: "US Social Security Number (context-required)",
|
|
106
|
+
validate: (_m, _full, _idx) => true
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: "phone_number",
|
|
110
|
+
pattern: /(?:\+?1[-.\s]?)?\(?\d{3}\)?[-.\s]\d{3}[-.\s]\d{4}/g,
|
|
111
|
+
severity: "warning",
|
|
112
|
+
description: "US phone number"
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: "email",
|
|
116
|
+
pattern: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/g,
|
|
117
|
+
severity: "info",
|
|
118
|
+
description: "Email address"
|
|
119
|
+
}
|
|
120
|
+
];
|
|
121
|
+
var DEFAULT_PATTERNS = Object.freeze(patterns.map((p) => ({ ...p })));
|
|
122
|
+
var REDACT_LABELS = {
|
|
123
|
+
aws_key: "[AWS_KEY]",
|
|
124
|
+
aws_secret: "[AWS_SECRET]",
|
|
125
|
+
stripe_key: "[STRIPE_KEY]",
|
|
126
|
+
github_token: "[GITHUB_TOKEN]",
|
|
127
|
+
bearer_token: "[BEARER_TOKEN]",
|
|
128
|
+
generic_api_key: "[API_KEY]",
|
|
129
|
+
credit_card: "[CREDIT_CARD]",
|
|
130
|
+
ssn: "[SSN]",
|
|
131
|
+
phone_number: "[PHONE]",
|
|
132
|
+
email: "[EMAIL]"
|
|
133
|
+
};
|
|
134
|
+
function scanForPii(input) {
|
|
135
|
+
return runScan(input, patterns);
|
|
136
|
+
}
|
|
137
|
+
function runScan(input, defs) {
|
|
138
|
+
if (!input) {
|
|
139
|
+
return { matches: [], flags_by_type: {}, scanned_chars: 0, redacted: "" };
|
|
140
|
+
}
|
|
141
|
+
const allMatches = [];
|
|
142
|
+
for (const def of defs) {
|
|
143
|
+
const re = new RegExp(def.pattern.source, def.pattern.flags);
|
|
144
|
+
let m;
|
|
145
|
+
while ((m = re.exec(input)) !== null) {
|
|
146
|
+
const text = m[0];
|
|
147
|
+
const start = m.index;
|
|
148
|
+
const end = start + text.length;
|
|
149
|
+
if (def.validate && !def.validate(text, input, start)) {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
allMatches.push({
|
|
153
|
+
type: def.type,
|
|
154
|
+
text,
|
|
155
|
+
start,
|
|
156
|
+
end,
|
|
157
|
+
severity: def.severity
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
allMatches.sort((a, b) => a.start - b.start);
|
|
162
|
+
const deduped = deduplicateOverlaps(allMatches);
|
|
163
|
+
const flags_by_type = {};
|
|
164
|
+
for (const match of deduped) {
|
|
165
|
+
flags_by_type[match.type] = (flags_by_type[match.type] || 0) + 1;
|
|
166
|
+
}
|
|
167
|
+
const redacted = buildRedacted(input, deduped);
|
|
168
|
+
return {
|
|
169
|
+
matches: deduped,
|
|
170
|
+
flags_by_type,
|
|
171
|
+
scanned_chars: input.length,
|
|
172
|
+
redacted
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
function deduplicateOverlaps(sorted) {
|
|
176
|
+
if (sorted.length <= 1)
|
|
177
|
+
return sorted;
|
|
178
|
+
const severityRank = {
|
|
179
|
+
critical: 3,
|
|
180
|
+
warning: 2,
|
|
181
|
+
info: 1
|
|
182
|
+
};
|
|
183
|
+
const result = [sorted[0]];
|
|
184
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
185
|
+
const prev = result[result.length - 1];
|
|
186
|
+
const curr = sorted[i];
|
|
187
|
+
if (curr.start < prev.end) {
|
|
188
|
+
const prevRank = severityRank[prev.severity];
|
|
189
|
+
const currRank = severityRank[curr.severity];
|
|
190
|
+
if (currRank > prevRank || currRank === prevRank && curr.end - curr.start > prev.end - prev.start) {
|
|
191
|
+
result[result.length - 1] = curr;
|
|
192
|
+
}
|
|
193
|
+
} else {
|
|
194
|
+
result.push(curr);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return result;
|
|
198
|
+
}
|
|
199
|
+
function buildRedacted(input, matches) {
|
|
200
|
+
if (matches.length === 0)
|
|
201
|
+
return input;
|
|
202
|
+
const parts = [];
|
|
203
|
+
let cursor = 0;
|
|
204
|
+
for (const match of matches) {
|
|
205
|
+
if (match.start > cursor) {
|
|
206
|
+
parts.push(input.slice(cursor, match.start));
|
|
207
|
+
}
|
|
208
|
+
parts.push(REDACT_LABELS[match.type] || `[${match.type.toUpperCase()}]`);
|
|
209
|
+
cursor = match.end;
|
|
210
|
+
}
|
|
211
|
+
if (cursor < input.length) {
|
|
212
|
+
parts.push(input.slice(cursor));
|
|
213
|
+
}
|
|
214
|
+
return parts.join("");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ../mcp/dist/mcp/src/shared/boundary-guard.js
|
|
218
|
+
var DEFAULT_OPTS = { stripHtml: false, maxLength: 1e5 };
|
|
219
|
+
function guardBoundary(toolName, rawArgs, opts = DEFAULT_OPTS) {
|
|
220
|
+
const args = sanitizeArgs(rawArgs, opts);
|
|
221
|
+
const piiResults = [];
|
|
222
|
+
for (const [key, val] of Object.entries(args)) {
|
|
223
|
+
if (typeof val === "string" && val.length > 0) {
|
|
224
|
+
const scan = scanForPii(val);
|
|
225
|
+
if (scan.matches.length > 0) {
|
|
226
|
+
piiResults.push({ field: key, result: scan });
|
|
227
|
+
auditLog("pii_detected", "tool_call", toolName, {
|
|
228
|
+
field: key,
|
|
229
|
+
flags_by_type: scan.flags_by_type,
|
|
230
|
+
match_count: scan.matches.length,
|
|
231
|
+
scanned_chars: scan.scanned_chars
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return {
|
|
237
|
+
args,
|
|
238
|
+
piiDetected: piiResults.length > 0,
|
|
239
|
+
piiResults
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
function guardOutput(toolName, response) {
|
|
243
|
+
let totalRedactions = 0;
|
|
244
|
+
const content = response.content.map((block) => {
|
|
245
|
+
if (block.type !== "text" || !block.text)
|
|
246
|
+
return block;
|
|
247
|
+
const scan = scanForPii(block.text);
|
|
248
|
+
if (scan.matches.length === 0)
|
|
249
|
+
return block;
|
|
250
|
+
totalRedactions += scan.matches.length;
|
|
251
|
+
return { ...block, text: scan.redacted };
|
|
252
|
+
});
|
|
253
|
+
if (totalRedactions > 0) {
|
|
254
|
+
auditLog("secret_in_output", "tool_response", toolName, {
|
|
255
|
+
redaction_count: totalRedactions
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
...response,
|
|
260
|
+
content,
|
|
261
|
+
_secretsRedacted: totalRedactions > 0,
|
|
262
|
+
_redactionCount: totalRedactions
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
function auditLog(eventType, resourceType, resourceId, data) {
|
|
266
|
+
console.error(JSON.stringify({
|
|
267
|
+
audit: true,
|
|
268
|
+
severity: "INFO",
|
|
269
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
270
|
+
eventType,
|
|
271
|
+
resourceType,
|
|
272
|
+
resourceId,
|
|
273
|
+
data
|
|
274
|
+
}));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// ../mcp/dist/mcp/src/shared/perf-tracker.js
|
|
278
|
+
var PerfTracker = class {
|
|
279
|
+
entries = /* @__PURE__ */ new Map();
|
|
280
|
+
windowMs;
|
|
281
|
+
failureAlertThreshold;
|
|
282
|
+
onFailureAlert;
|
|
283
|
+
latencyThresholds;
|
|
284
|
+
maxEntriesPerTool;
|
|
285
|
+
startedAt = Date.now();
|
|
286
|
+
alertActive = false;
|
|
287
|
+
constructor(options = {}) {
|
|
288
|
+
this.windowMs = options.windowMs ?? 5 * 60 * 1e3;
|
|
289
|
+
this.failureAlertThreshold = options.failureAlertThreshold ?? 0.05;
|
|
290
|
+
this.onFailureAlert = options.onFailureAlert;
|
|
291
|
+
this.latencyThresholds = options.latencyThresholds ?? [];
|
|
292
|
+
this.maxEntriesPerTool = options.maxEntriesPerTool ?? 1e4;
|
|
293
|
+
}
|
|
294
|
+
record(tool, latencyMs, success) {
|
|
295
|
+
if (!this.entries.has(tool)) {
|
|
296
|
+
this.entries.set(tool, []);
|
|
297
|
+
}
|
|
298
|
+
const bucket = this.entries.get(tool);
|
|
299
|
+
bucket.push({ timestamp: Date.now(), latencyMs, success });
|
|
300
|
+
if (bucket.length > this.maxEntriesPerTool) {
|
|
301
|
+
bucket.splice(0, bucket.length - this.maxEntriesPerTool);
|
|
302
|
+
}
|
|
303
|
+
this.checkFailureAlert();
|
|
304
|
+
}
|
|
305
|
+
getToolMetrics(tool) {
|
|
306
|
+
const active = this.getActiveEntries(tool);
|
|
307
|
+
if (!active || active.length === 0)
|
|
308
|
+
return void 0;
|
|
309
|
+
return this.computeMetrics(active);
|
|
310
|
+
}
|
|
311
|
+
getSnapshot() {
|
|
312
|
+
const tools = {};
|
|
313
|
+
let totalCalls = 0;
|
|
314
|
+
let totalFailures = 0;
|
|
315
|
+
for (const tool of this.entries.keys()) {
|
|
316
|
+
const active = this.getActiveEntries(tool);
|
|
317
|
+
if (active.length === 0)
|
|
318
|
+
continue;
|
|
319
|
+
const metrics = this.computeMetrics(active);
|
|
320
|
+
tools[tool] = metrics;
|
|
321
|
+
totalCalls += metrics.totalCalls;
|
|
322
|
+
totalFailures += metrics.failureCount;
|
|
323
|
+
}
|
|
324
|
+
return {
|
|
325
|
+
tools,
|
|
326
|
+
totalCalls,
|
|
327
|
+
globalFailureRate: totalCalls > 0 ? totalFailures / totalCalls : 0,
|
|
328
|
+
uptimeMs: Date.now() - this.startedAt,
|
|
329
|
+
windowMs: this.windowMs
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
getThresholdBreaches() {
|
|
333
|
+
const breaches = [];
|
|
334
|
+
for (const threshold of this.latencyThresholds) {
|
|
335
|
+
const metrics = this.getToolMetrics(threshold.tool);
|
|
336
|
+
if (!metrics)
|
|
337
|
+
continue;
|
|
338
|
+
const actual = metrics[threshold.percentile];
|
|
339
|
+
if (actual > threshold.maxMs) {
|
|
340
|
+
breaches.push({ ...threshold, actual });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
return breaches;
|
|
344
|
+
}
|
|
345
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
346
|
+
// INTERNALS
|
|
347
|
+
// ═════════════════════════════════════════════════════════════════════════════
|
|
348
|
+
getActiveEntries(tool) {
|
|
349
|
+
const bucket = this.entries.get(tool);
|
|
350
|
+
if (!bucket)
|
|
351
|
+
return [];
|
|
352
|
+
const cutoff = Date.now() - this.windowMs;
|
|
353
|
+
const active = bucket.filter((e) => e.timestamp >= cutoff);
|
|
354
|
+
this.entries.set(tool, active);
|
|
355
|
+
return active;
|
|
356
|
+
}
|
|
357
|
+
computeMetrics(entries) {
|
|
358
|
+
const latencies = entries.map((e) => e.latencyMs).sort((a, b) => a - b);
|
|
359
|
+
const successCount = entries.filter((e) => e.success).length;
|
|
360
|
+
const failureCount = entries.length - successCount;
|
|
361
|
+
const sum = latencies.reduce((a, b) => a + b, 0);
|
|
362
|
+
return {
|
|
363
|
+
totalCalls: entries.length,
|
|
364
|
+
successCount,
|
|
365
|
+
failureCount,
|
|
366
|
+
failureRate: entries.length > 0 ? failureCount / entries.length : 0,
|
|
367
|
+
p50: percentile(latencies, 0.5),
|
|
368
|
+
p95: percentile(latencies, 0.95),
|
|
369
|
+
p99: percentile(latencies, 0.99),
|
|
370
|
+
avgMs: Math.round(sum / entries.length),
|
|
371
|
+
minMs: latencies[0],
|
|
372
|
+
maxMs: latencies[latencies.length - 1]
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
checkFailureAlert() {
|
|
376
|
+
const snap = this.getSnapshot();
|
|
377
|
+
if (snap.totalCalls === 0)
|
|
378
|
+
return;
|
|
379
|
+
if (snap.globalFailureRate > this.failureAlertThreshold) {
|
|
380
|
+
if (!this.alertActive) {
|
|
381
|
+
this.alertActive = true;
|
|
382
|
+
auditLog2("perf_failure_alert", snap);
|
|
383
|
+
this.onFailureAlert?.(snap);
|
|
384
|
+
}
|
|
385
|
+
} else {
|
|
386
|
+
this.alertActive = false;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
function percentile(sorted, p) {
|
|
391
|
+
if (sorted.length === 0)
|
|
392
|
+
return 0;
|
|
393
|
+
if (sorted.length === 1)
|
|
394
|
+
return sorted[0];
|
|
395
|
+
const idx = (sorted.length - 1) * p;
|
|
396
|
+
const lo = Math.floor(idx);
|
|
397
|
+
const hi = Math.ceil(idx);
|
|
398
|
+
if (lo === hi)
|
|
399
|
+
return sorted[lo];
|
|
400
|
+
return sorted[lo] + (sorted[hi] - sorted[lo]) * (idx - lo);
|
|
401
|
+
}
|
|
402
|
+
function auditLog2(eventType, snapshot) {
|
|
403
|
+
const toolSummary = {};
|
|
404
|
+
for (const [tool, m] of Object.entries(snapshot.tools)) {
|
|
405
|
+
toolSummary[tool] = { calls: m.totalCalls, failRate: m.failureRate, p95: m.p95 };
|
|
406
|
+
}
|
|
407
|
+
console.error(JSON.stringify({
|
|
408
|
+
audit: true,
|
|
409
|
+
severity: "ERROR",
|
|
410
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
411
|
+
eventType,
|
|
412
|
+
resourceType: "mcp_perf",
|
|
413
|
+
data: {
|
|
414
|
+
globalFailureRate: snapshot.globalFailureRate,
|
|
415
|
+
totalCalls: snapshot.totalCalls,
|
|
416
|
+
tools: toolSummary
|
|
417
|
+
}
|
|
418
|
+
}));
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ../mcp/dist/mcp/src/shared/perf-tracker-instance.js
|
|
422
|
+
var LATENCY_THRESHOLDS = [
|
|
423
|
+
{ tool: "exploration_start", percentile: "p99", maxMs: 500 },
|
|
424
|
+
{ tool: "discovery_start", percentile: "p99", maxMs: 500 },
|
|
425
|
+
{ tool: "constraints_query", percentile: "p95", maxMs: 800 },
|
|
426
|
+
{ tool: "constraints_semantic_query", percentile: "p95", maxMs: 800 },
|
|
427
|
+
{ tool: "template_discover", percentile: "p95", maxMs: 500 },
|
|
428
|
+
{ tool: "template_list", percentile: "p95", maxMs: 500 }
|
|
429
|
+
];
|
|
430
|
+
var perfTracker = new PerfTracker({
|
|
431
|
+
windowMs: 5 * 60 * 1e3,
|
|
432
|
+
failureAlertThreshold: 0.05,
|
|
433
|
+
latencyThresholds: LATENCY_THRESHOLDS,
|
|
434
|
+
maxEntriesPerTool: 5e3
|
|
435
|
+
});
|
|
436
|
+
async function withPerfTracking(toolName, fn) {
|
|
437
|
+
const start = Date.now();
|
|
438
|
+
let success = true;
|
|
439
|
+
try {
|
|
440
|
+
return await fn();
|
|
441
|
+
} catch (err) {
|
|
442
|
+
success = false;
|
|
443
|
+
throw err;
|
|
444
|
+
} finally {
|
|
445
|
+
perfTracker.record(toolName, Date.now() - start, success);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
export {
|
|
450
|
+
guardBoundary,
|
|
451
|
+
guardOutput,
|
|
452
|
+
perfTracker,
|
|
453
|
+
withPerfTracking
|
|
454
|
+
};
|