brakit 0.8.4 → 0.8.5
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/api.d.ts +26 -9
- package/dist/api.js +209 -99
- package/dist/bin/brakit.js +509 -218
- package/dist/dashboard.html +2652 -0
- package/dist/mcp/server.js +195 -90
- package/dist/runtime/index.js +854 -388
- package/package.json +3 -2
package/dist/mcp/server.js
CHANGED
|
@@ -9,20 +9,39 @@ import {
|
|
|
9
9
|
} from "@modelcontextprotocol/sdk/types.js";
|
|
10
10
|
|
|
11
11
|
// src/constants/routes.ts
|
|
12
|
-
var
|
|
13
|
-
var
|
|
14
|
-
var
|
|
15
|
-
var
|
|
16
|
-
var
|
|
17
|
-
var
|
|
18
|
-
var
|
|
19
|
-
var
|
|
20
|
-
var
|
|
21
|
-
var
|
|
12
|
+
var DASHBOARD_PREFIX = "/__brakit";
|
|
13
|
+
var DASHBOARD_API_REQUESTS = `${DASHBOARD_PREFIX}/api/requests`;
|
|
14
|
+
var DASHBOARD_API_EVENTS = `${DASHBOARD_PREFIX}/api/events`;
|
|
15
|
+
var DASHBOARD_API_FLOWS = `${DASHBOARD_PREFIX}/api/flows`;
|
|
16
|
+
var DASHBOARD_API_CLEAR = `${DASHBOARD_PREFIX}/api/clear`;
|
|
17
|
+
var DASHBOARD_API_LOGS = `${DASHBOARD_PREFIX}/api/logs`;
|
|
18
|
+
var DASHBOARD_API_FETCHES = `${DASHBOARD_PREFIX}/api/fetches`;
|
|
19
|
+
var DASHBOARD_API_ERRORS = `${DASHBOARD_PREFIX}/api/errors`;
|
|
20
|
+
var DASHBOARD_API_QUERIES = `${DASHBOARD_PREFIX}/api/queries`;
|
|
21
|
+
var DASHBOARD_API_INGEST = `${DASHBOARD_PREFIX}/api/ingest`;
|
|
22
|
+
var DASHBOARD_API_METRICS = `${DASHBOARD_PREFIX}/api/metrics`;
|
|
23
|
+
var DASHBOARD_API_ACTIVITY = `${DASHBOARD_PREFIX}/api/activity`;
|
|
24
|
+
var DASHBOARD_API_METRICS_LIVE = `${DASHBOARD_PREFIX}/api/metrics/live`;
|
|
25
|
+
var DASHBOARD_API_INSIGHTS = `${DASHBOARD_PREFIX}/api/insights`;
|
|
26
|
+
var DASHBOARD_API_SECURITY = `${DASHBOARD_PREFIX}/api/security`;
|
|
27
|
+
var DASHBOARD_API_TAB = `${DASHBOARD_PREFIX}/api/tab`;
|
|
28
|
+
var DASHBOARD_API_FINDINGS = `${DASHBOARD_PREFIX}/api/findings`;
|
|
29
|
+
var DASHBOARD_API_FINDINGS_REPORT = `${DASHBOARD_PREFIX}/api/findings/report`;
|
|
30
|
+
var VALID_TABS_TUPLE = [
|
|
31
|
+
"overview",
|
|
32
|
+
"actions",
|
|
33
|
+
"requests",
|
|
34
|
+
"fetches",
|
|
35
|
+
"queries",
|
|
36
|
+
"errors",
|
|
37
|
+
"logs",
|
|
38
|
+
"performance",
|
|
39
|
+
"security"
|
|
40
|
+
];
|
|
41
|
+
var VALID_TABS = new Set(VALID_TABS_TUPLE);
|
|
22
42
|
|
|
23
43
|
// src/constants/mcp.ts
|
|
24
44
|
var MCP_SERVER_NAME = "brakit";
|
|
25
|
-
var MCP_SERVER_VERSION = "0.8.4";
|
|
26
45
|
var INITIAL_DISCOVERY_TIMEOUT_MS = 5e3;
|
|
27
46
|
var LAZY_DISCOVERY_TIMEOUT_MS = 2e3;
|
|
28
47
|
var CLIENT_FETCH_TIMEOUT_MS = 1e4;
|
|
@@ -32,6 +51,7 @@ var MAX_DISCOVERY_DEPTH = 5;
|
|
|
32
51
|
var MAX_TIMELINE_EVENTS = 20;
|
|
33
52
|
var MAX_RESOLVED_DISPLAY = 5;
|
|
34
53
|
var ENRICHMENT_SEVERITY_FILTER = ["critical", "warning"];
|
|
54
|
+
var MCP_SERVER_VERSION = "0.8.5";
|
|
35
55
|
|
|
36
56
|
// src/mcp/client.ts
|
|
37
57
|
var BrakitClient = class {
|
|
@@ -79,6 +99,19 @@ var BrakitClient = class {
|
|
|
79
99
|
if (state) url.searchParams.set("state", state);
|
|
80
100
|
return this.fetchJson(url);
|
|
81
101
|
}
|
|
102
|
+
async reportFix(findingId, status, notes) {
|
|
103
|
+
const res = await fetch(`${this.baseUrl}${DASHBOARD_API_FINDINGS_REPORT}`, {
|
|
104
|
+
method: "POST",
|
|
105
|
+
headers: { "Content-Type": "application/json" },
|
|
106
|
+
body: JSON.stringify({ findingId, status, notes }),
|
|
107
|
+
signal: AbortSignal.timeout(CLIENT_FETCH_TIMEOUT_MS)
|
|
108
|
+
});
|
|
109
|
+
if (!res.ok) return false;
|
|
110
|
+
const contentType = res.headers.get("content-type") ?? "";
|
|
111
|
+
if (!contentType.includes("application/json")) return false;
|
|
112
|
+
const body = await res.json();
|
|
113
|
+
return body.ok === true;
|
|
114
|
+
}
|
|
82
115
|
async clearAll() {
|
|
83
116
|
const res = await fetch(`${this.baseUrl}${DASHBOARD_API_CLEAR}`, {
|
|
84
117
|
method: "POST",
|
|
@@ -108,59 +141,74 @@ var BrakitClient = class {
|
|
|
108
141
|
};
|
|
109
142
|
|
|
110
143
|
// src/mcp/discovery.ts
|
|
111
|
-
import {
|
|
144
|
+
import { readFile, readdir, stat } from "fs/promises";
|
|
112
145
|
import { resolve, dirname } from "path";
|
|
113
146
|
|
|
114
147
|
// src/constants/limits.ts
|
|
115
|
-
var
|
|
148
|
+
var FINDING_ID_HASH_LENGTH = 16;
|
|
116
149
|
|
|
117
150
|
// src/constants/metrics.ts
|
|
118
151
|
var PORT_FILE = ".brakit/port";
|
|
119
152
|
|
|
120
|
-
// src/constants/
|
|
121
|
-
var
|
|
122
|
-
var
|
|
123
|
-
var
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
// src/constants/network.ts
|
|
154
|
+
var RECOVERY_WINDOW_MS = 5 * 60 * 1e3;
|
|
155
|
+
var PORT_MIN = 1;
|
|
156
|
+
var PORT_MAX = 65535;
|
|
157
|
+
|
|
158
|
+
// src/constants/lifecycle.ts
|
|
159
|
+
var VALID_FINDING_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved"]);
|
|
160
|
+
var VALID_AI_FIX_STATUSES = /* @__PURE__ */ new Set(["fixed", "wont_fix"]);
|
|
161
|
+
var VALID_SECURITY_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
|
|
162
|
+
|
|
163
|
+
// src/utils/log.ts
|
|
164
|
+
var PREFIX = "[brakit]";
|
|
165
|
+
function brakitDebug(message) {
|
|
166
|
+
if (process.env.DEBUG_BRAKIT) {
|
|
167
|
+
process.stderr.write(`${PREFIX}:debug ${message}
|
|
168
|
+
`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
129
171
|
|
|
130
172
|
// src/mcp/discovery.ts
|
|
131
|
-
function readPort(portPath) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
async function readPort(portPath) {
|
|
174
|
+
try {
|
|
175
|
+
const raw = (await readFile(portPath, "utf-8")).trim();
|
|
176
|
+
const port = parseInt(raw, 10);
|
|
177
|
+
return isNaN(port) || port < PORT_MIN || port > PORT_MAX ? null : port;
|
|
178
|
+
} catch {
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
136
181
|
}
|
|
137
|
-
function portInDir(dir) {
|
|
182
|
+
async function portInDir(dir) {
|
|
138
183
|
return readPort(resolve(dir, PORT_FILE));
|
|
139
184
|
}
|
|
140
|
-
function portInChildren(dir) {
|
|
185
|
+
async function portInChildren(dir) {
|
|
141
186
|
try {
|
|
142
|
-
|
|
187
|
+
const entries = await readdir(dir);
|
|
188
|
+
for (const entry of entries) {
|
|
143
189
|
if (entry.startsWith(".") || entry === "node_modules") continue;
|
|
144
190
|
const child = resolve(dir, entry);
|
|
145
191
|
try {
|
|
146
|
-
if (!
|
|
147
|
-
} catch {
|
|
192
|
+
if (!(await stat(child)).isDirectory()) continue;
|
|
193
|
+
} catch (err) {
|
|
194
|
+
brakitDebug(`discovery: stat failed for ${child}: ${err}`);
|
|
148
195
|
continue;
|
|
149
196
|
}
|
|
150
|
-
const port = portInDir(child);
|
|
197
|
+
const port = await portInDir(child);
|
|
151
198
|
if (port) return port;
|
|
152
199
|
}
|
|
153
|
-
} catch {
|
|
200
|
+
} catch (err) {
|
|
201
|
+
brakitDebug(`discovery: readdir failed for ${dir}: ${err}`);
|
|
154
202
|
}
|
|
155
203
|
return null;
|
|
156
204
|
}
|
|
157
|
-
function searchForPort(startDir) {
|
|
205
|
+
async function searchForPort(startDir) {
|
|
158
206
|
const start = resolve(startDir);
|
|
159
|
-
const initial = portInDir(start) ?? portInChildren(start);
|
|
207
|
+
const initial = await portInDir(start) ?? await portInChildren(start);
|
|
160
208
|
if (initial) return initial;
|
|
161
209
|
let dir = dirname(start);
|
|
162
210
|
for (let depth = 0; depth < MAX_DISCOVERY_DEPTH; depth++) {
|
|
163
|
-
const port = portInDir(dir);
|
|
211
|
+
const port = await portInDir(dir) ?? await portInChildren(dir);
|
|
164
212
|
if (port) return port;
|
|
165
213
|
const parent = dirname(dir);
|
|
166
214
|
if (parent === dir) break;
|
|
@@ -168,8 +216,8 @@ function searchForPort(startDir) {
|
|
|
168
216
|
}
|
|
169
217
|
return null;
|
|
170
218
|
}
|
|
171
|
-
function discoverBrakitPort(cwd) {
|
|
172
|
-
const port = searchForPort(cwd ?? process.cwd());
|
|
219
|
+
async function discoverBrakitPort(cwd) {
|
|
220
|
+
const port = await searchForPort(cwd ?? process.cwd());
|
|
173
221
|
if (!port) {
|
|
174
222
|
throw new Error(
|
|
175
223
|
"Brakit is not running. Start your app with brakit enabled first."
|
|
@@ -181,7 +229,7 @@ async function waitForBrakit(cwd, timeoutMs = 1e4, pollMs = DISCOVERY_POLL_INTER
|
|
|
181
229
|
const deadline = Date.now() + timeoutMs;
|
|
182
230
|
while (Date.now() < deadline) {
|
|
183
231
|
try {
|
|
184
|
-
const result = discoverBrakitPort(cwd);
|
|
232
|
+
const result = await discoverBrakitPort(cwd);
|
|
185
233
|
const res = await fetch(`${result.baseUrl}${DASHBOARD_API_REQUESTS}?limit=1`);
|
|
186
234
|
if (res.ok) return result;
|
|
187
235
|
} catch {
|
|
@@ -193,14 +241,11 @@ async function waitForBrakit(cwd, timeoutMs = 1e4, pollMs = DISCOVERY_POLL_INTER
|
|
|
193
241
|
);
|
|
194
242
|
}
|
|
195
243
|
|
|
196
|
-
// src/mcp/enrichment.ts
|
|
197
|
-
import { createHash as createHash2 } from "crypto";
|
|
198
|
-
|
|
199
244
|
// src/store/finding-id.ts
|
|
200
245
|
import { createHash } from "crypto";
|
|
201
|
-
function
|
|
202
|
-
const key = `${
|
|
203
|
-
return createHash("sha256").update(key).digest("hex").slice(0,
|
|
246
|
+
function computeInsightId(type, endpoint, desc) {
|
|
247
|
+
const key = `${type}:${endpoint}:${desc}`;
|
|
248
|
+
return createHash("sha256").update(key).digest("hex").slice(0, FINDING_ID_HASH_LENGTH);
|
|
204
249
|
}
|
|
205
250
|
|
|
206
251
|
// src/utils/endpoint.ts
|
|
@@ -213,44 +258,46 @@ function parseEndpointKey(endpoint) {
|
|
|
213
258
|
}
|
|
214
259
|
|
|
215
260
|
// src/mcp/enrichment.ts
|
|
216
|
-
function computeInsightId(type, endpoint, desc) {
|
|
217
|
-
const key = `${type}:${endpoint}:${desc}`;
|
|
218
|
-
return createHash2("sha256").update(key).digest("hex").slice(0, 16);
|
|
219
|
-
}
|
|
220
261
|
async function enrichFindings(client) {
|
|
221
262
|
const [securityData, insightsData] = await Promise.all([
|
|
222
263
|
client.getSecurityFindings(),
|
|
223
264
|
client.getInsights()
|
|
224
265
|
]);
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
266
|
+
const contexts = await Promise.all(
|
|
267
|
+
securityData.findings.map(async (sf) => {
|
|
268
|
+
try {
|
|
269
|
+
const { path } = parseEndpointKey(sf.finding.endpoint);
|
|
270
|
+
const reqData = await client.getRequests({ search: path, limit: 1 });
|
|
271
|
+
if (reqData.requests.length > 0) {
|
|
272
|
+
const req = reqData.requests[0];
|
|
273
|
+
if (req.id) {
|
|
274
|
+
const activity = await client.getActivity(req.id);
|
|
275
|
+
const queryCount = activity.counts?.queries ?? 0;
|
|
276
|
+
const fetchCount = activity.counts?.fetches ?? 0;
|
|
277
|
+
return `Request took ${req.durationMs}ms. ${queryCount} DB queries, ${fetchCount} fetches.`;
|
|
278
|
+
}
|
|
238
279
|
}
|
|
280
|
+
} catch {
|
|
281
|
+
return "(context unavailable)";
|
|
239
282
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
283
|
+
return "";
|
|
284
|
+
})
|
|
285
|
+
);
|
|
286
|
+
const enriched = securityData.findings.map((sf, i) => {
|
|
287
|
+
const f = sf.finding;
|
|
288
|
+
return {
|
|
289
|
+
findingId: sf.findingId,
|
|
245
290
|
severity: f.severity,
|
|
246
291
|
title: f.title,
|
|
247
292
|
endpoint: f.endpoint,
|
|
248
293
|
description: f.desc,
|
|
249
294
|
hint: f.hint,
|
|
250
295
|
occurrences: f.count,
|
|
251
|
-
context
|
|
252
|
-
|
|
253
|
-
|
|
296
|
+
context: contexts[i],
|
|
297
|
+
aiStatus: sf.aiStatus,
|
|
298
|
+
aiNotes: sf.aiNotes
|
|
299
|
+
};
|
|
300
|
+
});
|
|
254
301
|
for (const si of insightsData.insights) {
|
|
255
302
|
if (si.state === "resolved") continue;
|
|
256
303
|
const i = si.insight;
|
|
@@ -264,7 +311,9 @@ async function enrichFindings(client) {
|
|
|
264
311
|
description: i.desc,
|
|
265
312
|
hint: i.hint,
|
|
266
313
|
occurrences: 1,
|
|
267
|
-
context: i.detail ?? ""
|
|
314
|
+
context: i.detail ?? "",
|
|
315
|
+
aiStatus: si.aiStatus,
|
|
316
|
+
aiNotes: si.aiNotes
|
|
268
317
|
});
|
|
269
318
|
}
|
|
270
319
|
return enriched;
|
|
@@ -317,9 +366,18 @@ async function buildRequestDetail(client, req) {
|
|
|
317
366
|
};
|
|
318
367
|
}
|
|
319
368
|
|
|
369
|
+
// src/utils/type-guards.ts
|
|
370
|
+
function isNonEmptyString(val) {
|
|
371
|
+
return typeof val === "string" && val.trim().length > 0;
|
|
372
|
+
}
|
|
373
|
+
function isValidFindingState(val) {
|
|
374
|
+
return typeof val === "string" && VALID_FINDING_STATES.has(val);
|
|
375
|
+
}
|
|
376
|
+
function isValidAiFixStatus(val) {
|
|
377
|
+
return typeof val === "string" && VALID_AI_FIX_STATUSES.has(val);
|
|
378
|
+
}
|
|
379
|
+
|
|
320
380
|
// src/mcp/tools/get-findings.ts
|
|
321
|
-
var VALID_SEVERITIES = /* @__PURE__ */ new Set(["critical", "warning"]);
|
|
322
|
-
var VALID_STATES = /* @__PURE__ */ new Set(["open", "fixing", "resolved"]);
|
|
323
381
|
var getFindings = {
|
|
324
382
|
name: "get_findings",
|
|
325
383
|
description: "Get all security findings and performance insights from the running app. Returns enriched findings with actionable fix hints, endpoint context, and evidence. Use this to understand what issues exist in the running application.",
|
|
@@ -341,10 +399,10 @@ var getFindings = {
|
|
|
341
399
|
async handler(client, args) {
|
|
342
400
|
const severity = args.severity;
|
|
343
401
|
const state = args.state;
|
|
344
|
-
if (severity && !
|
|
402
|
+
if (severity && !VALID_SECURITY_SEVERITIES.has(severity)) {
|
|
345
403
|
return { content: [{ type: "text", text: `Invalid severity "${severity}". Use: critical, warning.` }], isError: true };
|
|
346
404
|
}
|
|
347
|
-
if (state && !
|
|
405
|
+
if (state && !isValidFindingState(state)) {
|
|
348
406
|
return { content: [{ type: "text", text: `Invalid state "${state}". Use: open, fixing, resolved.` }], isError: true };
|
|
349
407
|
}
|
|
350
408
|
let findings = await enrichFindings(client);
|
|
@@ -363,10 +421,18 @@ var getFindings = {
|
|
|
363
421
|
`];
|
|
364
422
|
for (const f of findings) {
|
|
365
423
|
lines.push(`[${f.severity.toUpperCase()}] ${f.title}`);
|
|
424
|
+
lines.push(` ID: ${f.findingId}`);
|
|
366
425
|
lines.push(` Endpoint: ${f.endpoint}`);
|
|
367
426
|
lines.push(` Issue: ${f.description}`);
|
|
368
427
|
if (f.context) lines.push(` Context: ${f.context}`);
|
|
369
428
|
lines.push(` Fix: ${f.hint}`);
|
|
429
|
+
if (f.aiStatus === "fixed") {
|
|
430
|
+
lines.push(` AI Status: fixed (awaiting verification)`);
|
|
431
|
+
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
432
|
+
} else if (f.aiStatus === "wont_fix") {
|
|
433
|
+
lines.push(` AI Status: won't fix`);
|
|
434
|
+
if (f.aiNotes) lines.push(` AI Notes: ${f.aiNotes}`);
|
|
435
|
+
}
|
|
370
436
|
lines.push("");
|
|
371
437
|
}
|
|
372
438
|
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
@@ -659,9 +725,57 @@ var clearFindings = {
|
|
|
659
725
|
}
|
|
660
726
|
};
|
|
661
727
|
|
|
728
|
+
// src/mcp/tools/report-fix.ts
|
|
729
|
+
var reportFix = {
|
|
730
|
+
name: "report_fix",
|
|
731
|
+
description: "Report the result of fixing a brakit finding. Call this after attempting to fix each finding to update the dashboard with the outcome. Use status 'fixed' when you've applied a fix, or 'wont_fix' when the issue can't be resolved (e.g. third-party library, by design).",
|
|
732
|
+
inputSchema: {
|
|
733
|
+
type: "object",
|
|
734
|
+
properties: {
|
|
735
|
+
finding_id: {
|
|
736
|
+
type: "string",
|
|
737
|
+
description: "The finding ID to report on"
|
|
738
|
+
},
|
|
739
|
+
status: {
|
|
740
|
+
type: "string",
|
|
741
|
+
description: "Whether the fix was applied or can't be fixed",
|
|
742
|
+
enum: ["fixed", "wont_fix"]
|
|
743
|
+
},
|
|
744
|
+
summary: {
|
|
745
|
+
type: "string",
|
|
746
|
+
description: "Brief description of what was done or why it can't be fixed"
|
|
747
|
+
}
|
|
748
|
+
},
|
|
749
|
+
required: ["finding_id", "status", "summary"]
|
|
750
|
+
},
|
|
751
|
+
async handler(client, args) {
|
|
752
|
+
const { finding_id, status, summary } = args;
|
|
753
|
+
if (!isNonEmptyString(finding_id)) {
|
|
754
|
+
return { content: [{ type: "text", text: "finding_id is required." }], isError: true };
|
|
755
|
+
}
|
|
756
|
+
if (!isValidAiFixStatus(status)) {
|
|
757
|
+
return { content: [{ type: "text", text: "status must be 'fixed' or 'wont_fix'." }], isError: true };
|
|
758
|
+
}
|
|
759
|
+
if (!isNonEmptyString(summary)) {
|
|
760
|
+
return { content: [{ type: "text", text: "summary is required." }], isError: true };
|
|
761
|
+
}
|
|
762
|
+
const ok = await client.reportFix(finding_id, status, summary);
|
|
763
|
+
if (!ok) {
|
|
764
|
+
return {
|
|
765
|
+
content: [{ type: "text", text: `Finding ${finding_id} not found. It may have already been resolved.` }],
|
|
766
|
+
isError: true
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
const label = status === "fixed" ? "marked as fixed (awaiting verification)" : "marked as won't fix";
|
|
770
|
+
return {
|
|
771
|
+
content: [{ type: "text", text: `Finding ${finding_id} ${label}. Dashboard updated.` }]
|
|
772
|
+
};
|
|
773
|
+
}
|
|
774
|
+
};
|
|
775
|
+
|
|
662
776
|
// src/mcp/tools/index.ts
|
|
663
777
|
var TOOL_MAP = new Map(
|
|
664
|
-
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings].map((t) => [t.name, t])
|
|
778
|
+
[getFindings, getEndpoints, getRequestDetail, verifyFix, getReport, clearFindings, reportFix].map((t) => [t.name, t])
|
|
665
779
|
);
|
|
666
780
|
function getToolDefinitions() {
|
|
667
781
|
return [...TOOL_MAP.values()].map((t) => ({
|
|
@@ -682,6 +796,7 @@ function handleToolCall(client, name, args) {
|
|
|
682
796
|
}
|
|
683
797
|
|
|
684
798
|
// src/mcp/prompts.ts
|
|
799
|
+
var SERVER_INSTRUCTIONS = "Brakit is a dev tool that monitors your running app for security vulnerabilities, performance issues, and query problems. IMPORTANT: After fixing any brakit finding, always call the report_fix tool with the finding_id, status ('fixed' or 'wont_fix'), and a brief summary of what you did. This updates the dashboard in real-time so the developer can track AI-driven fixes.";
|
|
685
800
|
var PROMPTS = [
|
|
686
801
|
{
|
|
687
802
|
name: "check-app",
|
|
@@ -693,18 +808,8 @@ var PROMPTS = [
|
|
|
693
808
|
}
|
|
694
809
|
];
|
|
695
810
|
var PROMPT_MESSAGES = {
|
|
696
|
-
"check-app":
|
|
697
|
-
|
|
698
|
-
"First get all findings, then get the endpoint summary.",
|
|
699
|
-
"For any critical or warning findings, get the request detail to understand the root cause.",
|
|
700
|
-
"Give me a clear report of what's wrong and offer to fix each issue."
|
|
701
|
-
].join(" "),
|
|
702
|
-
"fix-findings": [
|
|
703
|
-
"Get all open brakit findings.",
|
|
704
|
-
"For each finding, get the request detail to understand the exact issue.",
|
|
705
|
-
"Then find the source code responsible and fix it.",
|
|
706
|
-
"After fixing, ask me to re-trigger the endpoint so you can verify the fix with brakit."
|
|
707
|
-
].join(" ")
|
|
811
|
+
"check-app": "Check my running app for security and performance issues using brakit. First get all findings, then get the endpoint summary. For any critical or warning findings, get the request detail to understand the root cause. Give me a clear report of what's wrong and offer to fix each issue. After fixing any issue, call report_fix with the finding_id, status, and summary.",
|
|
812
|
+
"fix-findings": "Get all open brakit findings. For each finding:\n1. Get the request detail to understand the exact issue\n2. Find the source code responsible and fix it\n3. Call report_fix with the finding_id, status ('fixed' or 'wont_fix'), and a brief summary of what you did\n4. Move to the next finding\n\nAfter all findings are processed, ask me to re-trigger the endpoints so brakit can verify the fixes."
|
|
708
813
|
};
|
|
709
814
|
|
|
710
815
|
// src/mcp/server.ts
|
|
@@ -718,7 +823,7 @@ async function startMcpServer() {
|
|
|
718
823
|
let cachedClient = discovery ? new BrakitClient(discovery.baseUrl) : null;
|
|
719
824
|
const server = new Server(
|
|
720
825
|
{ name: MCP_SERVER_NAME, version: MCP_SERVER_VERSION },
|
|
721
|
-
{ capabilities: { tools: {}, prompts: {} } }
|
|
826
|
+
{ capabilities: { tools: {}, prompts: {} }, instructions: SERVER_INSTRUCTIONS }
|
|
722
827
|
);
|
|
723
828
|
server.setRequestHandler(ListPromptsRequestSchema, async () => ({
|
|
724
829
|
prompts: [...PROMPTS]
|