zopkit-bug-resolution-mcp 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +19 -6
  2. package/index.js +120 -2
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # bug-resolution MCP server
2
2
 
3
- A two-tool MCP server so Claude Code (or Cursor, or any other MCP client) can
4
- work a Zopkit bug report end to end as native tool calls, instead of
3
+ A four-tool MCP server so Claude Code (or Cursor, or any other MCP client)
4
+ can work a Zopkit bug report end to end as native tool calls, instead of
5
5
  hand-rolling curl commands:
6
6
 
7
7
  - **`fetch_bug_context`** — pulls the full report (title, description,
@@ -11,8 +11,13 @@ hand-rolling curl commands:
11
11
  - **`submit_bug_resolution`** — writes back how it was fixed as a Markdown
12
12
  writeup and moves the report to `resolved`, via
13
13
  `POST /api/admin/bug-reports/resolution/:id`.
14
+ - **`fetch_assigned_bugs`** — lists reports currently assigned to the API
15
+ key's owner ("what's on my plate"), optionally filtered by status, via
16
+ `GET /api/admin/bug-reports/assigned`.
17
+ - **`create_bug_report`** — files a new bug report (plain-text description),
18
+ optionally self-assigning it, via `POST /api/admin/bug-reports/report`.
14
19
 
15
- Both are thin wrappers — see `backend/src/features/bug-reports/README.md`
20
+ All four are thin wrappers — see `backend/src/features/bug-reports/README.md`
16
21
  for what each endpoint actually does server-side.
17
22
 
18
23
  Published to npm as
@@ -89,6 +94,14 @@ whoever asked you to fix it).
89
94
  and a Markdown writeup. It sets the report's resolution notes to that
90
95
  writeup and moves its status straight to `resolved`.
91
96
 
97
+ Two more, independent of the fetch/resolve flow above:
98
+
99
+ - Ask it to call `fetch_assigned_bugs` (optionally with a `status` filter) to
100
+ see what's currently assigned to you — no id needed.
101
+ - Ask it to call `create_bug_report` with a `title` and plain-text
102
+ `description` to file something it noticed — pass `assignToSelf: true` to
103
+ claim it immediately.
104
+
92
105
  ## Notes
93
106
 
94
107
  - Not part of the pnpm workspace (`pnpm-workspace.yaml`) or the deployed
@@ -98,6 +111,6 @@ whoever asked you to fix it).
98
111
  via the `BUG_REPORT_API_KEY` env var, never embedded.
99
112
  - The key is per-developer and individually revocable from the same API Keys
100
113
  panel — revoking one never affects any other person's key.
101
- - This server only exposes the two operations it needs. It can't list or
102
- search bug reports — the person/tool using it needs the report's id
103
- already.
114
+ - `fetch_bug_context` and `submit_bug_resolution` still need a report id —
115
+ this server doesn't search or browse. `fetch_assigned_bugs` is the one tool
116
+ that doesn't require knowing an id up front.
package/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // bug-resolution MCP server — two tools wrapping the bug-reports resolution
2
+ // bug-resolution MCP server — four tools wrapping the bug-reports resolution
3
3
  // API (see backend/src/features/bug-reports/README.md) so a developer's
4
4
  // Claude Code / Cursor session can work a bug report end to end as native
5
5
  // tool calls instead of hand-rolling curl commands:
@@ -8,6 +8,10 @@
8
8
  // inline screenshot as real image content.
9
9
  // - submit_bug_resolution: POST /api/admin/bug-reports/resolution/:id —
10
10
  // writes back the fix as a Markdown resolution and marks it resolved.
11
+ // - fetch_assigned_bugs: GET /api/admin/bug-reports/assigned — "what's on
12
+ // my plate", always the API key owner's own assigned reports.
13
+ // - create_bug_report: POST /api/admin/bug-reports/report — file a new bug
14
+ // the tool found, optionally self-assigning it.
11
15
  // Thin wrapper: all the actual logic (auth, scoping, the DB writes) lives
12
16
  // server-side; this just forwards the HTTP calls the README documents.
13
17
  //
@@ -25,7 +29,11 @@ if (!API_KEY) {
25
29
  'Generate one from the company-admin panel (Bug Reports → API Keys) and set it when registering this server.');
26
30
  }
27
31
 
28
- const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.1.0' });
32
+ const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.2.0' });
33
+
34
+ const BUG_SEVERITIES = ['low', 'medium', 'high', 'critical'];
35
+ const BUG_STATUSES = ['open', 'in_progress', 'blocked', 'further_discussion', 'resolved'];
36
+ const BUG_PROJECTS = ['wrapper', 'crm', 'fa', 'accounting', 'academy', 'itsm', 'studio'];
29
37
 
30
38
  const IMG_SRC = /<img\b[^>]*\bsrc="([^"]+)"[^>]*>/gi;
31
39
 
@@ -189,5 +197,115 @@ server.registerTool(
189
197
  }
190
198
  );
191
199
 
200
+ server.registerTool(
201
+ 'fetch_assigned_bugs',
202
+ {
203
+ title: 'Fetch bugs assigned to me',
204
+ description:
205
+ "List Zopkit bug reports currently assigned to this API key's owner — \"what's on my plate\". " +
206
+ 'Always the key owner\'s own assigned reports, never anyone else\'s, regardless of what the key ' +
207
+ 'can otherwise see. Optionally filter by status.',
208
+ inputSchema: {
209
+ status: z.enum(BUG_STATUSES).optional().describe('Only return reports in this status.'),
210
+ },
211
+ },
212
+ async ({ status }) => {
213
+ if (!API_KEY) {
214
+ return {
215
+ isError: true,
216
+ content: [{ type: 'text', text: 'BUG_REPORT_API_KEY is not set on this MCP server — see README.md.' }],
217
+ };
218
+ }
219
+
220
+ const url = new URL(`${BASE_URL}/api/admin/bug-reports/assigned`);
221
+ if (status) url.searchParams.set('status', status);
222
+
223
+ let res;
224
+ try {
225
+ res = await fetch(url, { headers: { 'X-API-Key': API_KEY } });
226
+ } catch (err) {
227
+ return {
228
+ isError: true,
229
+ content: [{ type: 'text', text: `Could not reach ${BASE_URL}: ${err instanceof Error ? err.message : String(err)}` }],
230
+ };
231
+ }
232
+
233
+ const body = await res.json().catch(() => null);
234
+
235
+ if (!res.ok) {
236
+ const message = body?.message || body?.error || `HTTP ${res.status}`;
237
+ return { isError: true, content: [{ type: 'text', text: `Failed to fetch assigned bugs: ${message}` }] };
238
+ }
239
+
240
+ const reports = body?.data || [];
241
+ if (reports.length === 0) {
242
+ return { content: [{ type: 'text', text: 'No bug reports currently assigned to you.' }] };
243
+ }
244
+
245
+ const summary = reports
246
+ .map((r) => `- [${r.severity}/${r.status}] ${r.title} (id: ${r.id})${r.project ? ` — project: ${r.project}` : ''}`)
247
+ .join('\n');
248
+
249
+ return { content: [{ type: 'text', text: `${reports.length} assigned report(s):\n${summary}` }] };
250
+ }
251
+ );
252
+
253
+ server.registerTool(
254
+ 'create_bug_report',
255
+ {
256
+ title: 'Create a bug report',
257
+ description:
258
+ 'File a new Zopkit bug report — e.g. something this tool noticed while working on something else. ' +
259
+ 'Description is plain text (paragraphs separated by a blank line), not rich-text HTML.',
260
+ inputSchema: {
261
+ title: z.string().min(1).max(255).describe('A short, specific summary of the bug.'),
262
+ description: z.string().min(1).describe('The bug, in plain text — what happened, expected vs actual, repro steps.'),
263
+ severity: z.enum(BUG_SEVERITIES).optional().describe('Defaults to "medium".'),
264
+ project: z.enum(BUG_PROJECTS).optional().describe('Which product this bug is in, if known.'),
265
+ pageUrl: z.string().max(2048).optional().describe('The URL where the bug was observed, if applicable.'),
266
+ assignToSelf: z.boolean().optional().describe('Self-assign this report to the API key owner. Defaults to false (unassigned).'),
267
+ },
268
+ },
269
+ async ({ title, description, severity, project, pageUrl, assignToSelf }) => {
270
+ if (!API_KEY) {
271
+ return {
272
+ isError: true,
273
+ content: [{ type: 'text', text: 'BUG_REPORT_API_KEY is not set on this MCP server — see README.md.' }],
274
+ };
275
+ }
276
+
277
+ let res;
278
+ try {
279
+ res = await fetch(`${BASE_URL}/api/admin/bug-reports/report`, {
280
+ method: 'POST',
281
+ headers: { 'Content-Type': 'application/json', 'X-API-Key': API_KEY },
282
+ body: JSON.stringify({ title, description, severity, project, pageUrl, assignToSelf }),
283
+ });
284
+ } catch (err) {
285
+ return {
286
+ isError: true,
287
+ content: [{ type: 'text', text: `Could not reach ${BASE_URL}: ${err instanceof Error ? err.message : String(err)}` }],
288
+ };
289
+ }
290
+
291
+ const body = await res.json().catch(() => null);
292
+
293
+ if (!res.ok) {
294
+ const message = body?.message || body?.error || `HTTP ${res.status}`;
295
+ return { isError: true, content: [{ type: 'text', text: `Failed to create bug report: ${message}` }] };
296
+ }
297
+
298
+ const report = body?.data;
299
+ return {
300
+ content: [{
301
+ type: 'text',
302
+ text: report
303
+ ? `Bug report created: "${report.title}" (id: ${report.id}).`
304
+ : 'Bug report created.',
305
+ }],
306
+ };
307
+ }
308
+ );
309
+
192
310
  const transport = new StdioServerTransport();
193
311
  await server.connect(transport);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "zopkit-bug-resolution-mcp",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "type": "module",
5
- "description": "MCP server for Zopkit wrapper bug reports: fetch full bug context (with screenshots) and submit how a bug was fixed. Requires a BUG_REPORT_API_KEY (generated in the company-admin panel) — see README.md.",
5
+ "description": "MCP server for Zopkit wrapper bug reports: fetch full bug context (with screenshots), submit how a bug was fixed, list bugs assigned to you, and file new bug reports. Requires a BUG_REPORT_API_KEY (generated in the company-admin panel) — see README.md.",
6
6
  "license": "UNLICENSED",
7
7
  "engines": {
8
8
  "node": ">=18"