@diegotsi/flint-mcp 1.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/README.md +68 -0
- package/dist/index.js +736 -0
- package/dist/index.js.map +1 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# @diegotsi/flint-mcp
|
|
2
|
+
|
|
3
|
+
MCP (Model Context Protocol) server for [Flint](https://github.com/diegotsi/flint) — work with your bug reports directly from Claude Code, Cursor, Claude Desktop or any MCP client.
|
|
4
|
+
|
|
5
|
+
## Tools
|
|
6
|
+
|
|
7
|
+
| Tool | Description |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `list_bugs` | List bug reports with filters (status, severity, release, …) |
|
|
10
|
+
| `get_bug` | Full report detail — AI root cause, console logs, environment |
|
|
11
|
+
| `search_code` | Search the indexed codebase (not yet available via MCP) |
|
|
12
|
+
| `suggest_fix` | AI-generated fix suggestion for a bug |
|
|
13
|
+
| `triage_bug` | Set assignee, severity, area or notes |
|
|
14
|
+
| `resolve_bug` | Mark a bug as resolved |
|
|
15
|
+
| `create_fix_pr` | Open a GitHub PR with an AI-generated fix |
|
|
16
|
+
| `get_analytics` | Project analytics (volume, severity, releases, SLA) |
|
|
17
|
+
| `chat_about_bug` | Ask questions about a bug with full AI context |
|
|
18
|
+
|
|
19
|
+
## Configuration
|
|
20
|
+
|
|
21
|
+
| Env var | Required | Description |
|
|
22
|
+
|---|---|---|
|
|
23
|
+
| `FLINT_SERVER_URL` | yes | URL of your Flint server, e.g. `https://api.yourflint.com` |
|
|
24
|
+
| `FLINT_ADMIN_KEY` | yes | Admin key (`X-Admin-Key`) — **not** the project key used by the widget SDK |
|
|
25
|
+
| `FLINT_PROJECT_ID` | no | Default project — avoids passing `projectId` on every call |
|
|
26
|
+
|
|
27
|
+
> `FLINT_API_KEY` is still accepted as a fallback for `FLINT_ADMIN_KEY`.
|
|
28
|
+
|
|
29
|
+
## Install
|
|
30
|
+
|
|
31
|
+
### Claude Code
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
claude mcp add flint \
|
|
35
|
+
-e FLINT_SERVER_URL=https://api.yourflint.com \
|
|
36
|
+
-e FLINT_ADMIN_KEY=your-admin-key \
|
|
37
|
+
-e FLINT_PROJECT_ID=your-project-id \
|
|
38
|
+
-- npx -y @diegotsi/flint-mcp
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Cursor / Windsurf / Claude Desktop
|
|
42
|
+
|
|
43
|
+
Add to your MCP config (`.cursor/mcp.json`, `~/.codeium/windsurf/mcp_config.json` or `claude_desktop_config.json`):
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"mcpServers": {
|
|
48
|
+
"flint": {
|
|
49
|
+
"command": "npx",
|
|
50
|
+
"args": ["-y", "@diegotsi/flint-mcp"],
|
|
51
|
+
"env": {
|
|
52
|
+
"FLINT_SERVER_URL": "https://api.yourflint.com",
|
|
53
|
+
"FLINT_ADMIN_KEY": "your-admin-key",
|
|
54
|
+
"FLINT_PROJECT_ID": "your-project-id"
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Development
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
bun run dev # run from source (tsx)
|
|
65
|
+
bun run build # bundle to dist/ (tsup)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Tests live in `src/tools/mcpTools.test.ts` and run via the root vitest workspace (`bunx vitest run --project mcp`).
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,736 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/index.ts
|
|
4
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
|
+
|
|
7
|
+
// src/tools/chatAboutBug.ts
|
|
8
|
+
import { z } from "zod";
|
|
9
|
+
|
|
10
|
+
// src/client.ts
|
|
11
|
+
var serverUrl = (process.env.FLINT_SERVER_URL ?? "http://localhost:3333").replace(/\/$/, "");
|
|
12
|
+
var adminKey = process.env.FLINT_ADMIN_KEY ?? process.env.FLINT_API_KEY ?? "";
|
|
13
|
+
var defaultProjectId = process.env.FLINT_PROJECT_ID ?? "";
|
|
14
|
+
function resolveProjectId(pid) {
|
|
15
|
+
return pid || defaultProjectId || null;
|
|
16
|
+
}
|
|
17
|
+
if (!serverUrl) {
|
|
18
|
+
console.error("[Flint MCP] FLINT_SERVER_URL is required");
|
|
19
|
+
process.exit(1);
|
|
20
|
+
}
|
|
21
|
+
if (!adminKey) {
|
|
22
|
+
console.error("[Flint MCP] FLINT_ADMIN_KEY is required");
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
function headers() {
|
|
26
|
+
return {
|
|
27
|
+
"X-Admin-Key": adminKey,
|
|
28
|
+
"Content-Type": "application/json"
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
async function apiGet(path) {
|
|
32
|
+
const res = await fetch(`${serverUrl}${path}`, { headers: headers() });
|
|
33
|
+
if (!res.ok) {
|
|
34
|
+
const body = await res.text().catch(() => "");
|
|
35
|
+
throw new Error(`API ${res.status}: ${body.slice(0, 200)}`);
|
|
36
|
+
}
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
async function apiPatch(path, body) {
|
|
40
|
+
const res = await fetch(`${serverUrl}${path}`, {
|
|
41
|
+
method: "PATCH",
|
|
42
|
+
headers: headers(),
|
|
43
|
+
body: body ? JSON.stringify(body) : void 0
|
|
44
|
+
});
|
|
45
|
+
if (!res.ok) {
|
|
46
|
+
const text = await res.text().catch(() => "");
|
|
47
|
+
throw new Error(`API ${res.status}: ${text.slice(0, 200)}`);
|
|
48
|
+
}
|
|
49
|
+
return res.json();
|
|
50
|
+
}
|
|
51
|
+
async function apiPost(path, body) {
|
|
52
|
+
const res = await fetch(`${serverUrl}${path}`, {
|
|
53
|
+
method: "POST",
|
|
54
|
+
headers: headers(),
|
|
55
|
+
body: body ? JSON.stringify(body) : void 0
|
|
56
|
+
});
|
|
57
|
+
if (!res.ok) {
|
|
58
|
+
const text = await res.text().catch(() => "");
|
|
59
|
+
throw new Error(`API ${res.status}: ${text.slice(0, 200)}`);
|
|
60
|
+
}
|
|
61
|
+
return res.json();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/tools/chatAboutBug.ts
|
|
65
|
+
function registerChatAboutBug(server2) {
|
|
66
|
+
server2.registerTool(
|
|
67
|
+
"chat_about_bug",
|
|
68
|
+
{
|
|
69
|
+
description: "Ask a question about a specific bug report. Uses AI with full context (code, similar bugs, fix history) to answer.",
|
|
70
|
+
inputSchema: {
|
|
71
|
+
projectId: z.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
72
|
+
reportId: z.string().describe("The bug report ID"),
|
|
73
|
+
message: z.string().describe("The message/question to ask about this bug")
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
async ({ projectId: pid, reportId, message }) => {
|
|
77
|
+
const projectId = resolveProjectId(pid);
|
|
78
|
+
if (!projectId)
|
|
79
|
+
return {
|
|
80
|
+
content: [
|
|
81
|
+
{
|
|
82
|
+
type: "text",
|
|
83
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
84
|
+
}
|
|
85
|
+
]
|
|
86
|
+
};
|
|
87
|
+
let result;
|
|
88
|
+
try {
|
|
89
|
+
result = await apiPost(`/api/v1/admin/projects/${projectId}/reports/${reportId}/chat`, { message });
|
|
90
|
+
} catch (err) {
|
|
91
|
+
const errorMsg = err.message ?? "Unknown error";
|
|
92
|
+
return {
|
|
93
|
+
content: [
|
|
94
|
+
{
|
|
95
|
+
type: "text",
|
|
96
|
+
text: [
|
|
97
|
+
`Failed to chat about bug #${reportId}: ${errorMsg}`,
|
|
98
|
+
"",
|
|
99
|
+
"The AI chat feature may not be enabled for this project, or the bug report may not exist.",
|
|
100
|
+
"Check the Flint Admin Dashboard for more details."
|
|
101
|
+
].join("\n")
|
|
102
|
+
}
|
|
103
|
+
]
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
let responseText;
|
|
107
|
+
if (typeof result === "string") {
|
|
108
|
+
responseText = result;
|
|
109
|
+
} else if (result?.response) {
|
|
110
|
+
responseText = result.response;
|
|
111
|
+
} else if (result?.text) {
|
|
112
|
+
responseText = result.text;
|
|
113
|
+
} else if (result?.message) {
|
|
114
|
+
responseText = result.message;
|
|
115
|
+
} else if (result?.content) {
|
|
116
|
+
if (Array.isArray(result.content)) {
|
|
117
|
+
responseText = result.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
|
|
118
|
+
} else {
|
|
119
|
+
responseText = String(result.content);
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
responseText = JSON.stringify(result, null, 2);
|
|
123
|
+
}
|
|
124
|
+
if (!responseText || responseText === "null" || responseText === "undefined") {
|
|
125
|
+
responseText = "No response was generated. The AI may need more context about this bug.";
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
content: [{ type: "text", text: responseText }]
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// src/tools/createFixPR.ts
|
|
135
|
+
import { z as z2 } from "zod";
|
|
136
|
+
function registerCreateFixPR(server2) {
|
|
137
|
+
server2.registerTool(
|
|
138
|
+
"create_fix_pr",
|
|
139
|
+
{
|
|
140
|
+
description: "Create a GitHub Pull Request with the AI-suggested fix for a bug report. Requires the bug to have a suggested fix.",
|
|
141
|
+
inputSchema: {
|
|
142
|
+
projectId: z2.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
143
|
+
reportId: z2.string().describe("The bug report ID"),
|
|
144
|
+
branch: z2.string().optional().describe("Branch name (default: fix/<reportId>)"),
|
|
145
|
+
commitMessage: z2.string().optional().describe("Custom commit message")
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
async ({ projectId: pid, reportId, branch, commitMessage }) => {
|
|
149
|
+
const projectId = resolveProjectId(pid);
|
|
150
|
+
if (!projectId)
|
|
151
|
+
return {
|
|
152
|
+
content: [
|
|
153
|
+
{
|
|
154
|
+
type: "text",
|
|
155
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
156
|
+
}
|
|
157
|
+
]
|
|
158
|
+
};
|
|
159
|
+
const body = {};
|
|
160
|
+
if (branch) body.branch = branch;
|
|
161
|
+
if (commitMessage) body.commitMessage = commitMessage;
|
|
162
|
+
let result;
|
|
163
|
+
try {
|
|
164
|
+
result = await apiPost(
|
|
165
|
+
`/api/v1/admin/projects/${projectId}/reports/${reportId}/create-fix-pr`,
|
|
166
|
+
Object.keys(body).length > 0 ? body : void 0
|
|
167
|
+
);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return {
|
|
170
|
+
content: [
|
|
171
|
+
{
|
|
172
|
+
type: "text",
|
|
173
|
+
text: `Failed to create fix PR for bug #${reportId}: ${err.message ?? "Unknown error"}`
|
|
174
|
+
}
|
|
175
|
+
]
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
const prUrl = result?.pr_url ?? result?.prUrl ?? result?.url ?? null;
|
|
179
|
+
const text = prUrl ? [
|
|
180
|
+
`## Pull Request Created for Bug #${reportId}`,
|
|
181
|
+
"",
|
|
182
|
+
`**PR URL:** ${prUrl}`,
|
|
183
|
+
"",
|
|
184
|
+
"The PR has been created with the AI-suggested fix. Review and merge when ready."
|
|
185
|
+
].join("\n") : [
|
|
186
|
+
`## Fix PR Request Submitted for Bug #${reportId}`,
|
|
187
|
+
"",
|
|
188
|
+
"The request to create a fix PR was accepted.",
|
|
189
|
+
result?.message ? `Server response: ${result.message}` : "Check the Flint Admin Dashboard or your GitHub repository for the PR."
|
|
190
|
+
].join("\n");
|
|
191
|
+
return {
|
|
192
|
+
content: [{ type: "text", text }]
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// src/tools/getAnalytics.ts
|
|
199
|
+
import { z as z3 } from "zod";
|
|
200
|
+
function registerGetAnalytics(server2) {
|
|
201
|
+
server2.registerTool(
|
|
202
|
+
"get_analytics",
|
|
203
|
+
{
|
|
204
|
+
description: "Get project analytics: total reports, open/critical counts, resolution time, triage rate, top errors, and trends.",
|
|
205
|
+
inputSchema: {
|
|
206
|
+
projectId: z3.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
207
|
+
period: z3.string().optional().describe("Time period: 7d, 30d, or 90d (default 30d)")
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
async ({ projectId: pid, period }) => {
|
|
211
|
+
const projectId = resolveProjectId(pid);
|
|
212
|
+
if (!projectId)
|
|
213
|
+
return {
|
|
214
|
+
content: [
|
|
215
|
+
{
|
|
216
|
+
type: "text",
|
|
217
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
const periodStr = period ?? "30d";
|
|
222
|
+
let data;
|
|
223
|
+
try {
|
|
224
|
+
data = await apiGet(`/api/v1/admin/projects/${projectId}/analytics?period=${periodStr}`);
|
|
225
|
+
} catch (err) {
|
|
226
|
+
return {
|
|
227
|
+
content: [
|
|
228
|
+
{
|
|
229
|
+
type: "text",
|
|
230
|
+
text: `Failed to fetch analytics for project ${projectId}: ${err.message ?? "Unknown error"}`
|
|
231
|
+
}
|
|
232
|
+
]
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
const total = data.total ?? data.totalReports ?? "\u2014";
|
|
236
|
+
const open = data.open ?? data.openCount ?? "\u2014";
|
|
237
|
+
const inProgress = data.inProgress ?? data.inProgressCount ?? "\u2014";
|
|
238
|
+
const resolved = data.resolved ?? data.resolvedCount ?? "\u2014";
|
|
239
|
+
const critical = data.critical ?? data.criticalCount ?? "\u2014";
|
|
240
|
+
const avgResolution = data.avgResolutionHours ?? data.avgResolution ?? "\u2014";
|
|
241
|
+
const triageRate = data.triageRate ?? data.triagedPercent ?? "\u2014";
|
|
242
|
+
const sections = [
|
|
243
|
+
`## Analytics (${periodStr})`,
|
|
244
|
+
"",
|
|
245
|
+
`**Total:** ${total} reports | **Open:** ${open} | **In Progress:** ${inProgress} | **Critical (P1/P2):** ${critical} | **Resolved:** ${resolved}`,
|
|
246
|
+
`**Avg Resolution:** ${avgResolution}${typeof avgResolution === "number" ? "h" : ""} | **Triage Rate:** ${triageRate}${typeof triageRate === "number" ? "%" : ""}`
|
|
247
|
+
];
|
|
248
|
+
const topErrors = data.topErrors ?? data.errorGroups ?? data.topErrorGroups ?? [];
|
|
249
|
+
if (topErrors.length > 0) {
|
|
250
|
+
sections.push("", "### Top Errors");
|
|
251
|
+
topErrors.forEach((e, i) => {
|
|
252
|
+
const title = e.title ?? e.message ?? "Unknown error";
|
|
253
|
+
const events = e.error_count ?? e.errorCount ?? e.count ?? 0;
|
|
254
|
+
const users = e.user_count ?? e.userCount ?? 0;
|
|
255
|
+
sections.push(`${i + 1}. ${title} (${events} events${users ? `, ${users} users` : ""})`);
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
const trends = data.trends ?? data.dailyCounts ?? [];
|
|
259
|
+
if (trends.length > 0) {
|
|
260
|
+
sections.push("", "### Trend (daily bug count)");
|
|
261
|
+
const recent = trends.slice(-7);
|
|
262
|
+
const trendLine = recent.map((t) => {
|
|
263
|
+
const date = t.date ?? t.day ?? t.label ?? "?";
|
|
264
|
+
const count = t.count ?? t.total ?? 0;
|
|
265
|
+
return `${date}: ${count}`;
|
|
266
|
+
}).join(" | ");
|
|
267
|
+
sections.push(trendLine);
|
|
268
|
+
}
|
|
269
|
+
return {
|
|
270
|
+
content: [{ type: "text", text: sections.join("\n") }]
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// src/tools/getBug.ts
|
|
277
|
+
import { z as z4 } from "zod";
|
|
278
|
+
function timeAgo(iso) {
|
|
279
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
280
|
+
const mins = Math.floor(diff / 6e4);
|
|
281
|
+
if (mins < 1) return "just now";
|
|
282
|
+
if (mins < 60) return `${mins}m ago`;
|
|
283
|
+
const hours = Math.floor(mins / 60);
|
|
284
|
+
if (hours < 24) return `${hours}h ago`;
|
|
285
|
+
const days = Math.floor(hours / 24);
|
|
286
|
+
return `${days}d ago`;
|
|
287
|
+
}
|
|
288
|
+
function registerGetBug(server2) {
|
|
289
|
+
server2.registerTool(
|
|
290
|
+
"get_bug",
|
|
291
|
+
{
|
|
292
|
+
description: "Get full details of a bug report including AI analysis, root cause, suggested fix, console errors, network errors, and environment info.",
|
|
293
|
+
inputSchema: {
|
|
294
|
+
projectId: z4.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
295
|
+
reportId: z4.string().describe("Bug report ID")
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
async ({ projectId: pid, reportId }) => {
|
|
299
|
+
const projectId = resolveProjectId(pid);
|
|
300
|
+
if (!projectId)
|
|
301
|
+
return {
|
|
302
|
+
content: [
|
|
303
|
+
{
|
|
304
|
+
type: "text",
|
|
305
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
306
|
+
}
|
|
307
|
+
]
|
|
308
|
+
};
|
|
309
|
+
let r;
|
|
310
|
+
try {
|
|
311
|
+
r = await apiGet(`/api/v1/admin/projects/${projectId}/reports/${reportId}`);
|
|
312
|
+
} catch {
|
|
313
|
+
return { content: [{ type: "text", text: `Bug report "${reportId}" not found.` }] };
|
|
314
|
+
}
|
|
315
|
+
const meta = r.meta;
|
|
316
|
+
const aiReasoning = r.ai_reasoning;
|
|
317
|
+
const consoleLogs = meta?.consoleLogs ?? [];
|
|
318
|
+
const networkErrors = meta?.networkErrors ?? [];
|
|
319
|
+
const envInfo = meta?.environment;
|
|
320
|
+
const fix = r.fix ?? (r.ai_suggested_fix ? JSON.parse(r.ai_suggested_fix) : null);
|
|
321
|
+
const sections = [];
|
|
322
|
+
sections.push(`## Bug #${r.id.slice(-8)} \u2014 ${r.ai_title ?? r.title}`);
|
|
323
|
+
sections.push(
|
|
324
|
+
`**Severity:** ${r.severity} | **Status:** ${r.status} | **Assignee:** ${r.assignee ?? "unassigned"}`
|
|
325
|
+
);
|
|
326
|
+
sections.push(
|
|
327
|
+
`**Reporter:** ${r.reporter_name} | **Created:** ${timeAgo(r.created_at)} | **URL:** ${r.url ?? "N/A"}`
|
|
328
|
+
);
|
|
329
|
+
if (r.app_version || r.release) {
|
|
330
|
+
sections.push(`**Version:** ${r.app_version ?? "N/A"} | **Release:** ${r.release ?? "N/A"}`);
|
|
331
|
+
}
|
|
332
|
+
if (r.ai_description) sections.push(`
|
|
333
|
+
### AI Summary
|
|
334
|
+
${r.ai_description}`);
|
|
335
|
+
if (r.ai_root_cause) {
|
|
336
|
+
const conf = aiReasoning?.rootCauseConfidence ?? null;
|
|
337
|
+
const confStr = conf !== null ? ` (${conf}% confidence)` : "";
|
|
338
|
+
sections.push(`
|
|
339
|
+
### Root Cause${confStr}
|
|
340
|
+
${r.ai_root_cause}`);
|
|
341
|
+
}
|
|
342
|
+
if (fix) {
|
|
343
|
+
const filePath = fix.filePath ?? fix.file_path ?? "unknown";
|
|
344
|
+
sections.push(
|
|
345
|
+
`
|
|
346
|
+
### Suggested Fix (${fix.confidence ?? "unknown"} confidence)
|
|
347
|
+
**File:** ${filePath}
|
|
348
|
+
|
|
349
|
+
${fix.explanation ?? ""}
|
|
350
|
+
|
|
351
|
+
\`\`\`diff
|
|
352
|
+
${fix.diff ?? ""}
|
|
353
|
+
\`\`\``
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
const errors = consoleLogs.filter((e) => e.level === "error").slice(-5);
|
|
357
|
+
if (errors.length > 0) {
|
|
358
|
+
sections.push(`
|
|
359
|
+
### Console Errors (last ${errors.length})
|
|
360
|
+
${errors.map((e) => `- ${e.args}`).join("\n")}`);
|
|
361
|
+
}
|
|
362
|
+
if (networkErrors.length > 0) {
|
|
363
|
+
sections.push(
|
|
364
|
+
`
|
|
365
|
+
### Failed Network Requests
|
|
366
|
+
${networkErrors.map((e) => `- ${e.method} ${e.url} \u2192 ${e.status}`).join("\n")}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
if (envInfo) {
|
|
370
|
+
sections.push(
|
|
371
|
+
`
|
|
372
|
+
### Environment
|
|
373
|
+
${envInfo.browser} | ${envInfo.os} | ${envInfo.viewport} | ${envInfo.timezone}`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
const steps = r.steps_to_reproduce;
|
|
377
|
+
if (steps && steps.length > 0) {
|
|
378
|
+
sections.push(
|
|
379
|
+
`
|
|
380
|
+
### Steps to Reproduce
|
|
381
|
+
${steps.map((s, i) => `${i + 1}. ${s.action}${s.result ? ` \u2192 ${s.result}` : ""}`).join("\n")}`
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
if (r.ai_repro_steps) sections.push(`
|
|
385
|
+
### AI-Generated Repro Steps
|
|
386
|
+
${r.ai_repro_steps}`);
|
|
387
|
+
if (r.fixPatterns?.length > 0) {
|
|
388
|
+
sections.push(`
|
|
389
|
+
### How Similar Bugs Were Fixed`);
|
|
390
|
+
for (const fp of r.fixPatterns) {
|
|
391
|
+
sections.push(
|
|
392
|
+
`- **PR #${fp.pr_number}**${fp.author_name ? ` by ${fp.author_name}` : ""} \u2014 ${fp.bug_title.slice(0, 50)}
|
|
393
|
+
${fp.diff_summary.slice(0, 120)}`
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return { content: [{ type: "text", text: sections.join("\n") }] };
|
|
398
|
+
}
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// src/tools/listBugs.ts
|
|
403
|
+
import { z as z5 } from "zod";
|
|
404
|
+
function timeAgo2(iso) {
|
|
405
|
+
const diff = Date.now() - new Date(iso).getTime();
|
|
406
|
+
const mins = Math.floor(diff / 6e4);
|
|
407
|
+
if (mins < 1) return "just now";
|
|
408
|
+
if (mins < 60) return `${mins}m ago`;
|
|
409
|
+
const hours = Math.floor(mins / 60);
|
|
410
|
+
if (hours < 24) return `${hours}h ago`;
|
|
411
|
+
const days = Math.floor(hours / 24);
|
|
412
|
+
return `${days}d ago`;
|
|
413
|
+
}
|
|
414
|
+
function registerListBugs(server2) {
|
|
415
|
+
server2.registerTool(
|
|
416
|
+
"list_bugs",
|
|
417
|
+
{
|
|
418
|
+
description: "List bug reports for a project with optional filters. Returns a summary table with ID, title, severity, status, assignee, and creation time.",
|
|
419
|
+
inputSchema: {
|
|
420
|
+
projectId: z5.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
421
|
+
severity: z5.string().optional().describe("Filter by severity: P1, P2, P3, P4 (comma-separated)"),
|
|
422
|
+
status: z5.string().optional().describe("Filter by status: OPEN, IN_PROGRESS, TESTING, RESOLVED (comma-separated). Defaults to OPEN"),
|
|
423
|
+
assignee: z5.string().optional().describe("Filter by assignee name"),
|
|
424
|
+
area: z5.string().optional().describe("Filter by area: FRONTEND, BACKEND, DESIGN"),
|
|
425
|
+
q: z5.string().optional().describe("Search in title, reporter name, URL"),
|
|
426
|
+
limit: z5.number().optional().describe("Max results (default 10, max 50)")
|
|
427
|
+
}
|
|
428
|
+
},
|
|
429
|
+
async ({ projectId: pid, severity, status, assignee, area, q, limit }) => {
|
|
430
|
+
const projectId = resolveProjectId(pid);
|
|
431
|
+
if (!projectId)
|
|
432
|
+
return {
|
|
433
|
+
content: [
|
|
434
|
+
{
|
|
435
|
+
type: "text",
|
|
436
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
437
|
+
}
|
|
438
|
+
]
|
|
439
|
+
};
|
|
440
|
+
const params = new URLSearchParams({ page: "1", limit: String(Math.min(limit ?? 10, 50)) });
|
|
441
|
+
if (severity) params.set("severity", severity);
|
|
442
|
+
params.set("status", status ?? "OPEN");
|
|
443
|
+
if (assignee) params.set("assignee", assignee);
|
|
444
|
+
if (area) params.set("area", area);
|
|
445
|
+
if (q) params.set("q", q);
|
|
446
|
+
const data = await apiGet(
|
|
447
|
+
`/api/v1/admin/projects/${projectId}/reports?${params}`
|
|
448
|
+
);
|
|
449
|
+
if (data.reports.length === 0) {
|
|
450
|
+
return { content: [{ type: "text", text: "No bugs found matching the filters." }] };
|
|
451
|
+
}
|
|
452
|
+
const lines = data.reports.map((r) => {
|
|
453
|
+
const title = (r.ai_title ?? r.title).slice(0, 60);
|
|
454
|
+
const assigneeStr = r.assignee ?? "unassigned";
|
|
455
|
+
const quality = r.quality_score ? ` Q:${r.quality_score}%` : "";
|
|
456
|
+
return `- **${r.severity}** \`${r.id}\` ${title} \u2014 ${r.status} \u2014 ${assigneeStr} \u2014 ${timeAgo2(r.created_at)}${quality}`;
|
|
457
|
+
});
|
|
458
|
+
const text = `## Bug Reports (${data.total} total, showing ${data.reports.length})
|
|
459
|
+
|
|
460
|
+
${lines.join("\n")}
|
|
461
|
+
|
|
462
|
+
Use \`get_bug\` with a report ID for full details.`;
|
|
463
|
+
return { content: [{ type: "text", text }] };
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
// src/tools/resolveBug.ts
|
|
469
|
+
import { z as z6 } from "zod";
|
|
470
|
+
function registerResolveBug(server2) {
|
|
471
|
+
server2.registerTool(
|
|
472
|
+
"resolve_bug",
|
|
473
|
+
{
|
|
474
|
+
description: "Mark a bug report as resolved.",
|
|
475
|
+
inputSchema: {
|
|
476
|
+
projectId: z6.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
477
|
+
reportId: z6.string().describe("The bug report ID")
|
|
478
|
+
}
|
|
479
|
+
},
|
|
480
|
+
async ({ projectId: pid, reportId }) => {
|
|
481
|
+
const projectId = resolveProjectId(pid);
|
|
482
|
+
if (!projectId)
|
|
483
|
+
return {
|
|
484
|
+
content: [
|
|
485
|
+
{
|
|
486
|
+
type: "text",
|
|
487
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
488
|
+
}
|
|
489
|
+
]
|
|
490
|
+
};
|
|
491
|
+
try {
|
|
492
|
+
await apiPatch(`/api/v1/admin/projects/${projectId}/reports/${reportId}/resolve`);
|
|
493
|
+
} catch (err) {
|
|
494
|
+
return {
|
|
495
|
+
content: [
|
|
496
|
+
{
|
|
497
|
+
type: "text",
|
|
498
|
+
text: `Failed to resolve bug #${reportId}: ${err.message ?? "Unknown error"}`
|
|
499
|
+
}
|
|
500
|
+
]
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
return {
|
|
504
|
+
content: [
|
|
505
|
+
{
|
|
506
|
+
type: "text",
|
|
507
|
+
text: `Bug #${reportId} marked as RESOLVED.`
|
|
508
|
+
}
|
|
509
|
+
]
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
// src/tools/searchCode.ts
|
|
516
|
+
import { z as z7 } from "zod";
|
|
517
|
+
function registerSearchCode(server2) {
|
|
518
|
+
server2.registerTool(
|
|
519
|
+
"search_code",
|
|
520
|
+
{
|
|
521
|
+
description: "Search the project's indexed codebase by semantic query. Returns relevant code chunks with file paths and line numbers.",
|
|
522
|
+
inputSchema: {
|
|
523
|
+
projectId: z7.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
524
|
+
query: z7.string().describe("Semantic search query"),
|
|
525
|
+
limit: z7.number().optional().describe("Max results to return (default 5)")
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
async ({ projectId: pid, query, limit }) => {
|
|
529
|
+
const projectId = resolveProjectId(pid) ?? "unknown";
|
|
530
|
+
const text = [
|
|
531
|
+
"## Code Search \u2014 Not Available via MCP",
|
|
532
|
+
"",
|
|
533
|
+
"Semantic code search requires direct access to the vector embedding pipeline (Voyage AI + pgvector), which is not exposed through the HTTP API.",
|
|
534
|
+
"",
|
|
535
|
+
"**Alternatives:**",
|
|
536
|
+
`- Use the **Flint Admin Dashboard** \u2192 Project \u2192 Code Index to search the indexed codebase for project \`${projectId}\`.`,
|
|
537
|
+
`- Use \`chat_about_bug\` with a specific bug report ID \u2014 the AI chat endpoint includes relevant code context automatically.`,
|
|
538
|
+
"",
|
|
539
|
+
`Your query was: "${query}" (limit: ${limit ?? 5})`
|
|
540
|
+
].join("\n");
|
|
541
|
+
return {
|
|
542
|
+
content: [{ type: "text", text }]
|
|
543
|
+
};
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
// src/tools/suggestFix.ts
|
|
549
|
+
import { z as z8 } from "zod";
|
|
550
|
+
function registerSuggestFix(server2) {
|
|
551
|
+
server2.registerTool(
|
|
552
|
+
"suggest_fix",
|
|
553
|
+
{
|
|
554
|
+
description: "Generate an AI-suggested code fix for a bug report. Uses the project's indexed code and AI analysis to produce a diff.",
|
|
555
|
+
inputSchema: {
|
|
556
|
+
projectId: z8.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
557
|
+
reportId: z8.string().describe("The bug report ID")
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
async ({ projectId: pid, reportId }) => {
|
|
561
|
+
const projectId = resolveProjectId(pid);
|
|
562
|
+
if (!projectId)
|
|
563
|
+
return {
|
|
564
|
+
content: [
|
|
565
|
+
{
|
|
566
|
+
type: "text",
|
|
567
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
568
|
+
}
|
|
569
|
+
]
|
|
570
|
+
};
|
|
571
|
+
let report;
|
|
572
|
+
try {
|
|
573
|
+
report = await apiGet(`/api/v1/admin/projects/${projectId}/reports/${reportId}`);
|
|
574
|
+
} catch (err) {
|
|
575
|
+
return {
|
|
576
|
+
content: [
|
|
577
|
+
{
|
|
578
|
+
type: "text",
|
|
579
|
+
text: `Bug report ${reportId} not found in project ${projectId}. ${err.message ?? ""}`
|
|
580
|
+
}
|
|
581
|
+
]
|
|
582
|
+
};
|
|
583
|
+
}
|
|
584
|
+
const rawFix = report.ai_suggested_fix;
|
|
585
|
+
if (!rawFix) {
|
|
586
|
+
return {
|
|
587
|
+
content: [
|
|
588
|
+
{
|
|
589
|
+
type: "text",
|
|
590
|
+
text: [
|
|
591
|
+
`## No Fix Available for Bug #${reportId}`,
|
|
592
|
+
"",
|
|
593
|
+
"An AI-suggested fix has not been generated yet for this bug report.",
|
|
594
|
+
"The fix is generated automatically during background processing when the bug is first created.",
|
|
595
|
+
"",
|
|
596
|
+
"**Possible reasons:**",
|
|
597
|
+
"- The bug was recently created and background processing hasn't completed yet.",
|
|
598
|
+
"- The project's codebase may not be indexed (Code Index is required for fix generation).",
|
|
599
|
+
"- The AI analysis did not produce a fix for this particular bug.",
|
|
600
|
+
"",
|
|
601
|
+
"Check the Flint Admin Dashboard for the latest status."
|
|
602
|
+
].join("\n")
|
|
603
|
+
}
|
|
604
|
+
]
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
let fix;
|
|
608
|
+
try {
|
|
609
|
+
fix = typeof rawFix === "string" ? JSON.parse(rawFix) : rawFix;
|
|
610
|
+
} catch {
|
|
611
|
+
return {
|
|
612
|
+
content: [
|
|
613
|
+
{
|
|
614
|
+
type: "text",
|
|
615
|
+
text: "Failed to parse the suggested fix data."
|
|
616
|
+
}
|
|
617
|
+
]
|
|
618
|
+
};
|
|
619
|
+
}
|
|
620
|
+
const filePath = fix.file_path ?? fix.filePath ?? "unknown";
|
|
621
|
+
const explanation = fix.explanation ?? "No explanation provided.";
|
|
622
|
+
const diff = fix.diff ?? fix.code ?? "";
|
|
623
|
+
const confidence = fix.confidence ?? "unknown";
|
|
624
|
+
const text = [
|
|
625
|
+
`## Suggested Fix for Bug #${reportId}`,
|
|
626
|
+
"",
|
|
627
|
+
`**File:** \`${filePath}\``,
|
|
628
|
+
`**Confidence:** ${confidence}`,
|
|
629
|
+
"",
|
|
630
|
+
`### Explanation`,
|
|
631
|
+
explanation,
|
|
632
|
+
"",
|
|
633
|
+
"### Diff",
|
|
634
|
+
"```diff",
|
|
635
|
+
diff,
|
|
636
|
+
"```"
|
|
637
|
+
].join("\n");
|
|
638
|
+
return {
|
|
639
|
+
content: [{ type: "text", text }]
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
// src/tools/triageBug.ts
|
|
646
|
+
import { z as z9 } from "zod";
|
|
647
|
+
function registerTriageBug(server2) {
|
|
648
|
+
server2.registerTool(
|
|
649
|
+
"triage_bug",
|
|
650
|
+
{
|
|
651
|
+
description: "Update triage fields on a bug report: assignee, severity, area, and notes.",
|
|
652
|
+
inputSchema: {
|
|
653
|
+
projectId: z9.string().optional().describe("Project ID (uses FLINT_PROJECT_ID env if omitted)"),
|
|
654
|
+
reportId: z9.string().describe("The bug report ID"),
|
|
655
|
+
assignee: z9.string().optional().describe("Assignee name or identifier"),
|
|
656
|
+
severity: z9.string().optional().describe("Severity level: P1, P2, P3, or P4"),
|
|
657
|
+
area: z9.string().optional().describe("Affected area: FRONTEND, BACKEND, or DESIGN"),
|
|
658
|
+
notes: z9.string().optional().describe("Triage notes")
|
|
659
|
+
}
|
|
660
|
+
},
|
|
661
|
+
async ({ projectId: pid, reportId, assignee, severity, area, notes }) => {
|
|
662
|
+
const projectId = resolveProjectId(pid);
|
|
663
|
+
if (!projectId)
|
|
664
|
+
return {
|
|
665
|
+
content: [
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
text: "Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId."
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
};
|
|
672
|
+
const data = {};
|
|
673
|
+
if (assignee !== void 0) data.assignee = assignee;
|
|
674
|
+
if (severity !== void 0) data.severity = severity;
|
|
675
|
+
if (area !== void 0) data.area = area;
|
|
676
|
+
if (notes !== void 0) data.notes = notes;
|
|
677
|
+
if (Object.keys(data).length === 0) {
|
|
678
|
+
return {
|
|
679
|
+
content: [
|
|
680
|
+
{
|
|
681
|
+
type: "text",
|
|
682
|
+
text: "No triage fields provided. Specify at least one of: assignee, severity, area, notes."
|
|
683
|
+
}
|
|
684
|
+
]
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
await apiPatch(`/api/v1/admin/projects/${projectId}/reports/${reportId}/triage`, data);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
return {
|
|
691
|
+
content: [
|
|
692
|
+
{
|
|
693
|
+
type: "text",
|
|
694
|
+
text: `Failed to triage bug #${reportId}: ${err.message ?? "Unknown error"}`
|
|
695
|
+
}
|
|
696
|
+
]
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
const parts = [];
|
|
700
|
+
if (assignee !== void 0) parts.push(`Assignee: ${assignee}`);
|
|
701
|
+
if (severity !== void 0) parts.push(`Severity: ${severity}`);
|
|
702
|
+
if (area !== void 0) parts.push(`Area: ${area}`);
|
|
703
|
+
if (notes !== void 0) parts.push(`Notes: ${notes}`);
|
|
704
|
+
const text = `Bug #${reportId} triaged: ${parts.join(", ")}`;
|
|
705
|
+
return {
|
|
706
|
+
content: [{ type: "text", text }]
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
);
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
// src/index.ts
|
|
713
|
+
var server = new McpServer({
|
|
714
|
+
name: "flint",
|
|
715
|
+
version: "1.0.0",
|
|
716
|
+
description: "Flint bug tracking & error monitoring \u2014 list bugs, search code, suggest fixes, triage issues directly from your editor."
|
|
717
|
+
});
|
|
718
|
+
registerListBugs(server);
|
|
719
|
+
registerGetBug(server);
|
|
720
|
+
registerSearchCode(server);
|
|
721
|
+
registerSuggestFix(server);
|
|
722
|
+
registerTriageBug(server);
|
|
723
|
+
registerResolveBug(server);
|
|
724
|
+
registerCreateFixPR(server);
|
|
725
|
+
registerGetAnalytics(server);
|
|
726
|
+
registerChatAboutBug(server);
|
|
727
|
+
async function main() {
|
|
728
|
+
const transport = new StdioServerTransport();
|
|
729
|
+
await server.connect(transport);
|
|
730
|
+
console.error("Flint MCP Server running on stdio");
|
|
731
|
+
}
|
|
732
|
+
main().catch((error) => {
|
|
733
|
+
console.error("Fatal error in MCP server:", error);
|
|
734
|
+
process.exit(1);
|
|
735
|
+
});
|
|
736
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tools/chatAboutBug.ts","../src/client.ts","../src/tools/createFixPR.ts","../src/tools/getAnalytics.ts","../src/tools/getBug.ts","../src/tools/listBugs.ts","../src/tools/resolveBug.ts","../src/tools/searchCode.ts","../src/tools/suggestFix.ts","../src/tools/triageBug.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport {\n registerChatAboutBug,\n registerCreateFixPR,\n registerGetAnalytics,\n registerGetBug,\n registerListBugs,\n registerResolveBug,\n registerSearchCode,\n registerSuggestFix,\n registerTriageBug,\n} from \"./tools/index.js\";\n\nconst server = new McpServer({\n name: \"flint\",\n version: \"1.0.0\",\n description:\n \"Flint bug tracking & error monitoring — list bugs, search code, suggest fixes, triage issues directly from your editor.\",\n});\n\nregisterListBugs(server);\nregisterGetBug(server);\nregisterSearchCode(server);\nregisterSuggestFix(server);\nregisterTriageBug(server);\nregisterResolveBug(server);\nregisterCreateFixPR(server);\nregisterGetAnalytics(server);\nregisterChatAboutBug(server);\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(\"Flint MCP Server running on stdio\");\n}\n\nmain().catch((error) => {\n console.error(\"Fatal error in MCP server:\", error);\n process.exit(1);\n});\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiPost, resolveProjectId } from \"../client.js\";\n\nexport function registerChatAboutBug(server: McpServer) {\n server.registerTool(\n \"chat_about_bug\",\n {\n description:\n \"Ask a question about a specific bug report. Uses AI with full context (code, similar bugs, fix history) to answer.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"The bug report ID\"),\n message: z.string().describe(\"The message/question to ask about this bug\"),\n },\n },\n async ({ projectId: pid, reportId, message }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n let result: any;\n try {\n result = await apiPost(`/api/v1/admin/projects/${projectId}/reports/${reportId}/chat`, { message });\n } catch (err: any) {\n const errorMsg = err.message ?? \"Unknown error\";\n return {\n content: [\n {\n type: \"text\" as const,\n text: [\n `Failed to chat about bug #${reportId}: ${errorMsg}`,\n \"\",\n \"The AI chat feature may not be enabled for this project, or the bug report may not exist.\",\n \"Check the Flint Admin Dashboard for more details.\",\n ].join(\"\\n\"),\n },\n ],\n };\n }\n\n // The chat endpoint may return the response in different formats:\n // - { response: \"...\" } or { text: \"...\" } or { message: \"...\" }\n // - or the result itself could be a string (if the response was plain text parsed as JSON string)\n let responseText: string;\n if (typeof result === \"string\") {\n responseText = result;\n } else if (result?.response) {\n responseText = result.response;\n } else if (result?.text) {\n responseText = result.text;\n } else if (result?.message) {\n responseText = result.message;\n } else if (result?.content) {\n // Handle case where the API returns an array of content blocks (Anthropic-style)\n if (Array.isArray(result.content)) {\n responseText = result.content\n .filter((block: any) => block.type === \"text\")\n .map((block: any) => block.text)\n .join(\"\\n\");\n } else {\n responseText = String(result.content);\n }\n } else {\n responseText = JSON.stringify(result, null, 2);\n }\n\n if (!responseText || responseText === \"null\" || responseText === \"undefined\") {\n responseText = \"No response was generated. The AI may need more context about this bug.\";\n }\n\n return {\n content: [{ type: \"text\" as const, text: responseText }],\n };\n },\n );\n}\n","/**\n * HTTP client for the Flint Admin API.\n * Used by MCP tools instead of direct Prisma access.\n */\n\nconst serverUrl = (process.env.FLINT_SERVER_URL ?? \"http://localhost:3333\").replace(/\\/$/, \"\");\n// FLINT_API_KEY kept as fallback for setups created before the rename\nconst adminKey = process.env.FLINT_ADMIN_KEY ?? process.env.FLINT_API_KEY ?? \"\";\nexport const defaultProjectId = process.env.FLINT_PROJECT_ID ?? \"\";\n\nexport function resolveProjectId(pid?: string): string | null {\n return pid || defaultProjectId || null;\n}\n\nif (!serverUrl) {\n console.error(\"[Flint MCP] FLINT_SERVER_URL is required\");\n process.exit(1);\n}\nif (!adminKey) {\n console.error(\"[Flint MCP] FLINT_ADMIN_KEY is required\");\n process.exit(1);\n}\n\nfunction headers(): Record<string, string> {\n return {\n \"X-Admin-Key\": adminKey,\n \"Content-Type\": \"application/json\",\n };\n}\n\nexport async function apiGet<T = unknown>(path: string): Promise<T> {\n const res = await fetch(`${serverUrl}${path}`, { headers: headers() });\n if (!res.ok) {\n const body = await res.text().catch(() => \"\");\n throw new Error(`API ${res.status}: ${body.slice(0, 200)}`);\n }\n return res.json() as Promise<T>;\n}\n\nexport async function apiPatch<T = unknown>(path: string, body?: unknown): Promise<T> {\n const res = await fetch(`${serverUrl}${path}`, {\n method: \"PATCH\",\n headers: headers(),\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`API ${res.status}: ${text.slice(0, 200)}`);\n }\n return res.json() as Promise<T>;\n}\n\nexport async function apiPost<T = unknown>(path: string, body?: unknown): Promise<T> {\n const res = await fetch(`${serverUrl}${path}`, {\n method: \"POST\",\n headers: headers(),\n body: body ? JSON.stringify(body) : undefined,\n });\n if (!res.ok) {\n const text = await res.text().catch(() => \"\");\n throw new Error(`API ${res.status}: ${text.slice(0, 200)}`);\n }\n return res.json() as Promise<T>;\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiPost, resolveProjectId } from \"../client.js\";\n\nexport function registerCreateFixPR(server: McpServer) {\n server.registerTool(\n \"create_fix_pr\",\n {\n description:\n \"Create a GitHub Pull Request with the AI-suggested fix for a bug report. Requires the bug to have a suggested fix.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"The bug report ID\"),\n branch: z.string().optional().describe(\"Branch name (default: fix/<reportId>)\"),\n commitMessage: z.string().optional().describe(\"Custom commit message\"),\n },\n },\n async ({ projectId: pid, reportId, branch, commitMessage }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n const body: Record<string, string> = {};\n if (branch) body.branch = branch;\n if (commitMessage) body.commitMessage = commitMessage;\n\n let result: any;\n try {\n result = await apiPost(\n `/api/v1/admin/projects/${projectId}/reports/${reportId}/create-fix-pr`,\n Object.keys(body).length > 0 ? body : undefined,\n );\n } catch (err: any) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to create fix PR for bug #${reportId}: ${err.message ?? \"Unknown error\"}`,\n },\n ],\n };\n }\n\n const prUrl = result?.pr_url ?? result?.prUrl ?? result?.url ?? null;\n\n const text = prUrl\n ? [\n `## Pull Request Created for Bug #${reportId}`,\n \"\",\n `**PR URL:** ${prUrl}`,\n \"\",\n \"The PR has been created with the AI-suggested fix. Review and merge when ready.\",\n ].join(\"\\n\")\n : [\n `## Fix PR Request Submitted for Bug #${reportId}`,\n \"\",\n \"The request to create a fix PR was accepted.\",\n result?.message\n ? `Server response: ${result.message}`\n : \"Check the Flint Admin Dashboard or your GitHub repository for the PR.\",\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\" as const, text }],\n };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiGet, resolveProjectId } from \"../client.js\";\n\nexport function registerGetAnalytics(server: McpServer) {\n server.registerTool(\n \"get_analytics\",\n {\n description:\n \"Get project analytics: total reports, open/critical counts, resolution time, triage rate, top errors, and trends.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n period: z.string().optional().describe(\"Time period: 7d, 30d, or 90d (default 30d)\"),\n },\n },\n async ({ projectId: pid, period }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n const periodStr = period ?? \"30d\";\n\n let data: any;\n try {\n data = await apiGet(`/api/v1/admin/projects/${projectId}/analytics?period=${periodStr}`);\n } catch (err: any) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to fetch analytics for project ${projectId}: ${err.message ?? \"Unknown error\"}`,\n },\n ],\n };\n }\n\n // The analytics endpoint may return data in different shapes.\n // We handle the most common fields gracefully.\n const total = data.total ?? data.totalReports ?? \"—\";\n const open = data.open ?? data.openCount ?? \"—\";\n const inProgress = data.inProgress ?? data.inProgressCount ?? \"—\";\n const resolved = data.resolved ?? data.resolvedCount ?? \"—\";\n const critical = data.critical ?? data.criticalCount ?? \"—\";\n const avgResolution = data.avgResolutionHours ?? data.avgResolution ?? \"—\";\n const triageRate = data.triageRate ?? data.triagedPercent ?? \"—\";\n\n const sections: string[] = [\n `## Analytics (${periodStr})`,\n \"\",\n `**Total:** ${total} reports | **Open:** ${open} | **In Progress:** ${inProgress} | **Critical (P1/P2):** ${critical} | **Resolved:** ${resolved}`,\n `**Avg Resolution:** ${avgResolution}${typeof avgResolution === \"number\" ? \"h\" : \"\"} | **Triage Rate:** ${triageRate}${typeof triageRate === \"number\" ? \"%\" : \"\"}`,\n ];\n\n // Top errors\n const topErrors: any[] = data.topErrors ?? data.errorGroups ?? data.topErrorGroups ?? [];\n if (topErrors.length > 0) {\n sections.push(\"\", \"### Top Errors\");\n topErrors.forEach((e: any, i: number) => {\n const title = e.title ?? e.message ?? \"Unknown error\";\n const events = e.error_count ?? e.errorCount ?? e.count ?? 0;\n const users = e.user_count ?? e.userCount ?? 0;\n sections.push(`${i + 1}. ${title} (${events} events${users ? `, ${users} users` : \"\"})`);\n });\n }\n\n // Trends\n const trends: any[] = data.trends ?? data.dailyCounts ?? [];\n if (trends.length > 0) {\n sections.push(\"\", \"### Trend (daily bug count)\");\n const recent = trends.slice(-7);\n const trendLine = recent\n .map((t: any) => {\n const date = t.date ?? t.day ?? t.label ?? \"?\";\n const count = t.count ?? t.total ?? 0;\n return `${date}: ${count}`;\n })\n .join(\" | \");\n sections.push(trendLine);\n }\n\n return {\n content: [{ type: \"text\" as const, text: sections.join(\"\\n\") }],\n };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiGet, resolveProjectId } from \"../client.js\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60_000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nexport function registerGetBug(server: McpServer) {\n server.registerTool(\n \"get_bug\",\n {\n description:\n \"Get full details of a bug report including AI analysis, root cause, suggested fix, console errors, network errors, and environment info.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"Bug report ID\"),\n },\n },\n async ({ projectId: pid, reportId }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n let r: any;\n try {\n r = await apiGet(`/api/v1/admin/projects/${projectId}/reports/${reportId}`);\n } catch {\n return { content: [{ type: \"text\" as const, text: `Bug report \"${reportId}\" not found.` }] };\n }\n\n const meta = r.meta as Record<string, unknown> | null;\n const aiReasoning = r.ai_reasoning as Record<string, unknown> | null;\n const consoleLogs = (meta?.consoleLogs ?? []) as { level: string; args: string }[];\n const networkErrors = (meta?.networkErrors ?? []) as { method: string; url: string; status: number }[];\n const envInfo = meta?.environment as Record<string, unknown> | null;\n const fix = r.fix ?? (r.ai_suggested_fix ? JSON.parse(r.ai_suggested_fix) : null);\n\n const sections: string[] = [];\n\n sections.push(`## Bug #${r.id.slice(-8)} — ${r.ai_title ?? r.title}`);\n sections.push(\n `**Severity:** ${r.severity} | **Status:** ${r.status} | **Assignee:** ${r.assignee ?? \"unassigned\"}`,\n );\n sections.push(\n `**Reporter:** ${r.reporter_name} | **Created:** ${timeAgo(r.created_at)} | **URL:** ${r.url ?? \"N/A\"}`,\n );\n if (r.app_version || r.release) {\n sections.push(`**Version:** ${r.app_version ?? \"N/A\"} | **Release:** ${r.release ?? \"N/A\"}`);\n }\n\n if (r.ai_description) sections.push(`\\n### AI Summary\\n${r.ai_description}`);\n\n if (r.ai_root_cause) {\n const conf = (aiReasoning?.rootCauseConfidence as number) ?? null;\n const confStr = conf !== null ? ` (${conf}% confidence)` : \"\";\n sections.push(`\\n### Root Cause${confStr}\\n${r.ai_root_cause}`);\n }\n\n if (fix) {\n const filePath = fix.filePath ?? fix.file_path ?? \"unknown\";\n sections.push(\n `\\n### Suggested Fix (${fix.confidence ?? \"unknown\"} confidence)\\n**File:** ${filePath}\\n\\n${fix.explanation ?? \"\"}\\n\\n\\`\\`\\`diff\\n${fix.diff ?? \"\"}\\n\\`\\`\\``,\n );\n }\n\n const errors = consoleLogs.filter((e) => e.level === \"error\").slice(-5);\n if (errors.length > 0) {\n sections.push(`\\n### Console Errors (last ${errors.length})\\n${errors.map((e) => `- ${e.args}`).join(\"\\n\")}`);\n }\n\n if (networkErrors.length > 0) {\n sections.push(\n `\\n### Failed Network Requests\\n${networkErrors.map((e) => `- ${e.method} ${e.url} → ${e.status}`).join(\"\\n\")}`,\n );\n }\n\n if (envInfo) {\n sections.push(\n `\\n### Environment\\n${envInfo.browser} | ${envInfo.os} | ${envInfo.viewport} | ${envInfo.timezone}`,\n );\n }\n\n const steps = r.steps_to_reproduce as { action: string; result: string }[] | null;\n if (steps && steps.length > 0) {\n sections.push(\n `\\n### Steps to Reproduce\\n${steps.map((s, i) => `${i + 1}. ${s.action}${s.result ? ` → ${s.result}` : \"\"}`).join(\"\\n\")}`,\n );\n }\n\n if (r.ai_repro_steps) sections.push(`\\n### AI-Generated Repro Steps\\n${r.ai_repro_steps}`);\n\n // Fix patterns (D9)\n if (r.fixPatterns?.length > 0) {\n sections.push(`\\n### How Similar Bugs Were Fixed`);\n for (const fp of r.fixPatterns) {\n sections.push(\n `- **PR #${fp.pr_number}**${fp.author_name ? ` by ${fp.author_name}` : \"\"} — ${fp.bug_title.slice(0, 50)}\\n ${fp.diff_summary.slice(0, 120)}`,\n );\n }\n }\n\n return { content: [{ type: \"text\" as const, text: sections.join(\"\\n\") }] };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiGet, resolveProjectId } from \"../client.js\";\n\nfunction timeAgo(iso: string): string {\n const diff = Date.now() - new Date(iso).getTime();\n const mins = Math.floor(diff / 60_000);\n if (mins < 1) return \"just now\";\n if (mins < 60) return `${mins}m ago`;\n const hours = Math.floor(mins / 60);\n if (hours < 24) return `${hours}h ago`;\n const days = Math.floor(hours / 24);\n return `${days}d ago`;\n}\n\nexport function registerListBugs(server: McpServer) {\n server.registerTool(\n \"list_bugs\",\n {\n description:\n \"List bug reports for a project with optional filters. Returns a summary table with ID, title, severity, status, assignee, and creation time.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n severity: z.string().optional().describe(\"Filter by severity: P1, P2, P3, P4 (comma-separated)\"),\n status: z\n .string()\n .optional()\n .describe(\"Filter by status: OPEN, IN_PROGRESS, TESTING, RESOLVED (comma-separated). Defaults to OPEN\"),\n assignee: z.string().optional().describe(\"Filter by assignee name\"),\n area: z.string().optional().describe(\"Filter by area: FRONTEND, BACKEND, DESIGN\"),\n q: z.string().optional().describe(\"Search in title, reporter name, URL\"),\n limit: z.number().optional().describe(\"Max results (default 10, max 50)\"),\n },\n },\n async ({ projectId: pid, severity, status, assignee, area, q, limit }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n const params = new URLSearchParams({ page: \"1\", limit: String(Math.min(limit ?? 10, 50)) });\n if (severity) params.set(\"severity\", severity);\n params.set(\"status\", status ?? \"OPEN\");\n if (assignee) params.set(\"assignee\", assignee);\n if (area) params.set(\"area\", area);\n if (q) params.set(\"q\", q);\n\n const data = await apiGet<{ reports: any[]; total: number }>(\n `/api/v1/admin/projects/${projectId}/reports?${params}`,\n );\n\n if (data.reports.length === 0) {\n return { content: [{ type: \"text\" as const, text: \"No bugs found matching the filters.\" }] };\n }\n\n const lines = data.reports.map((r: any) => {\n const title = (r.ai_title ?? r.title).slice(0, 60);\n const assigneeStr = r.assignee ?? \"unassigned\";\n const quality = r.quality_score ? ` Q:${r.quality_score}%` : \"\";\n return `- **${r.severity}** \\`${r.id}\\` ${title} — ${r.status} — ${assigneeStr} — ${timeAgo(r.created_at)}${quality}`;\n });\n\n const text = `## Bug Reports (${data.total} total, showing ${data.reports.length})\\n\\n${lines.join(\"\\n\")}\\n\\nUse \\`get_bug\\` with a report ID for full details.`;\n return { content: [{ type: \"text\" as const, text }] };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiPatch, resolveProjectId } from \"../client.js\";\n\nexport function registerResolveBug(server: McpServer) {\n server.registerTool(\n \"resolve_bug\",\n {\n description: \"Mark a bug report as resolved.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"The bug report ID\"),\n },\n },\n async ({ projectId: pid, reportId }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n try {\n await apiPatch(`/api/v1/admin/projects/${projectId}/reports/${reportId}/resolve`);\n } catch (err: any) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to resolve bug #${reportId}: ${err.message ?? \"Unknown error\"}`,\n },\n ],\n };\n }\n\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Bug #${reportId} marked as RESOLVED.`,\n },\n ],\n };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { resolveProjectId } from \"../client.js\";\n\nexport function registerSearchCode(server: McpServer) {\n server.registerTool(\n \"search_code\",\n {\n description:\n \"Search the project's indexed codebase by semantic query. Returns relevant code chunks with file paths and line numbers.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n query: z.string().describe(\"Semantic search query\"),\n limit: z.number().optional().describe(\"Max results to return (default 5)\"),\n },\n },\n async ({ projectId: pid, query, limit }) => {\n const projectId = resolveProjectId(pid) ?? \"unknown\";\n const text = [\n \"## Code Search — Not Available via MCP\",\n \"\",\n \"Semantic code search requires direct access to the vector embedding pipeline (Voyage AI + pgvector), which is not exposed through the HTTP API.\",\n \"\",\n \"**Alternatives:**\",\n `- Use the **Flint Admin Dashboard** → Project → Code Index to search the indexed codebase for project \\`${projectId}\\`.`,\n `- Use \\`chat_about_bug\\` with a specific bug report ID — the AI chat endpoint includes relevant code context automatically.`,\n \"\",\n `Your query was: \"${query}\" (limit: ${limit ?? 5})`,\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\" as const, text }],\n };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiGet, resolveProjectId } from \"../client.js\";\n\nexport function registerSuggestFix(server: McpServer) {\n server.registerTool(\n \"suggest_fix\",\n {\n description:\n \"Generate an AI-suggested code fix for a bug report. Uses the project's indexed code and AI analysis to produce a diff.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"The bug report ID\"),\n },\n },\n async ({ projectId: pid, reportId }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n let report: any;\n try {\n report = await apiGet(`/api/v1/admin/projects/${projectId}/reports/${reportId}`);\n } catch (err: any) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Bug report ${reportId} not found in project ${projectId}. ${err.message ?? \"\"}`,\n },\n ],\n };\n }\n\n const rawFix = report.ai_suggested_fix;\n\n if (!rawFix) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: [\n `## No Fix Available for Bug #${reportId}`,\n \"\",\n \"An AI-suggested fix has not been generated yet for this bug report.\",\n \"The fix is generated automatically during background processing when the bug is first created.\",\n \"\",\n \"**Possible reasons:**\",\n \"- The bug was recently created and background processing hasn't completed yet.\",\n \"- The project's codebase may not be indexed (Code Index is required for fix generation).\",\n \"- The AI analysis did not produce a fix for this particular bug.\",\n \"\",\n \"Check the Flint Admin Dashboard for the latest status.\",\n ].join(\"\\n\"),\n },\n ],\n };\n }\n\n let fix: any;\n try {\n fix = typeof rawFix === \"string\" ? JSON.parse(rawFix) : rawFix;\n } catch {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Failed to parse the suggested fix data.\",\n },\n ],\n };\n }\n\n const filePath = fix.file_path ?? fix.filePath ?? \"unknown\";\n const explanation = fix.explanation ?? \"No explanation provided.\";\n const diff = fix.diff ?? fix.code ?? \"\";\n const confidence = fix.confidence ?? \"unknown\";\n\n const text = [\n `## Suggested Fix for Bug #${reportId}`,\n \"\",\n `**File:** \\`${filePath}\\``,\n `**Confidence:** ${confidence}`,\n \"\",\n `### Explanation`,\n explanation,\n \"\",\n \"### Diff\",\n \"```diff\",\n diff,\n \"```\",\n ].join(\"\\n\");\n\n return {\n content: [{ type: \"text\" as const, text }],\n };\n },\n );\n}\n","import type { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport { apiPatch, resolveProjectId } from \"../client.js\";\n\nexport function registerTriageBug(server: McpServer) {\n server.registerTool(\n \"triage_bug\",\n {\n description: \"Update triage fields on a bug report: assignee, severity, area, and notes.\",\n inputSchema: {\n projectId: z.string().optional().describe(\"Project ID (uses FLINT_PROJECT_ID env if omitted)\"),\n reportId: z.string().describe(\"The bug report ID\"),\n assignee: z.string().optional().describe(\"Assignee name or identifier\"),\n severity: z.string().optional().describe(\"Severity level: P1, P2, P3, or P4\"),\n area: z.string().optional().describe(\"Affected area: FRONTEND, BACKEND, or DESIGN\"),\n notes: z.string().optional().describe(\"Triage notes\"),\n },\n },\n async ({ projectId: pid, reportId, assignee, severity, area, notes }) => {\n const projectId = resolveProjectId(pid);\n if (!projectId)\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"Error: projectId is required. Set FLINT_PROJECT_ID env or pass projectId.\",\n },\n ],\n };\n const data: Record<string, unknown> = {};\n if (assignee !== undefined) data.assignee = assignee;\n if (severity !== undefined) data.severity = severity;\n if (area !== undefined) data.area = area;\n if (notes !== undefined) data.notes = notes;\n\n if (Object.keys(data).length === 0) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: \"No triage fields provided. Specify at least one of: assignee, severity, area, notes.\",\n },\n ],\n };\n }\n\n try {\n await apiPatch(`/api/v1/admin/projects/${projectId}/reports/${reportId}/triage`, data);\n } catch (err: any) {\n return {\n content: [\n {\n type: \"text\" as const,\n text: `Failed to triage bug #${reportId}: ${err.message ?? \"Unknown error\"}`,\n },\n ],\n };\n }\n\n const parts: string[] = [];\n if (assignee !== undefined) parts.push(`Assignee: ${assignee}`);\n if (severity !== undefined) parts.push(`Severity: ${severity}`);\n if (area !== undefined) parts.push(`Area: ${area}`);\n if (notes !== undefined) parts.push(`Notes: ${notes}`);\n\n const text = `Bug #${reportId} triaged: ${parts.join(\", \")}`;\n\n return {\n content: [{ type: \"text\" as const, text }],\n };\n },\n );\n}\n"],"mappings":"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","names":["server","z","server","z","z","server","z","z","server","z","z","timeAgo","server","z","z","server","z","z","server","z","z","server","z","z","server","z"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@diegotsi/flint-mcp",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "Flint MCP server — list bugs, suggest fixes, triage issues and pull analytics from your editor via the Model Context Protocol.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"flint-mcp": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"access": "public"
|
|
11
|
+
},
|
|
12
|
+
"files": [
|
|
13
|
+
"dist"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsup",
|
|
17
|
+
"dev": "tsx src/index.ts"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"mcp",
|
|
21
|
+
"model-context-protocol",
|
|
22
|
+
"bug-tracking",
|
|
23
|
+
"flint"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
27
|
+
"zod": "^3.25.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"tsup": "^8.4.0",
|
|
31
|
+
"tsx": "^4.19.0",
|
|
32
|
+
"typescript": "^5.6.0"
|
|
33
|
+
}
|
|
34
|
+
}
|