zopkit-bug-resolution-mcp 1.0.0 → 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.
Files changed (3) hide show
  1. package/README.md +79 -23
  2. package/index.js +114 -8
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -1,47 +1,103 @@
1
1
  # bug-resolution MCP server
2
2
 
3
- A single-tool MCP server so Claude Code (or Cursor, or any other MCP client)
4
- can report back how it fixed a Zopkit bug report as a native tool call,
5
- instead of hand-rolling a curl command. It's a thin wrapper around
6
- `POST /api/admin/bug-reports/resolution/:id` — see
7
- `backend/src/features/bug-reports/README.md` for what that endpoint
8
- actually does server-side.
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
5
+ hand-rolling curl commands:
6
+
7
+ - **`fetch_bug_context`** — pulls the full report (title, description,
8
+ severity, status, page URL, browser info, discussion comments) plus every
9
+ inline screenshot as real image content, via
10
+ `GET /api/admin/bug-reports/context/:id`.
11
+ - **`submit_bug_resolution`** — writes back how it was fixed as a Markdown
12
+ writeup and moves the report to `resolved`, via
13
+ `POST /api/admin/bug-reports/resolution/:id`.
14
+
15
+ Both are thin wrappers — see `backend/src/features/bug-reports/README.md`
16
+ for what each endpoint actually does server-side.
17
+
18
+ Published to npm as
19
+ [`zopkit-bug-resolution-mcp`](https://www.npmjs.com/package/zopkit-bug-resolution-mcp)
20
+ — employees don't need this repo at all, just `npx` and a key.
9
21
 
10
22
  ## Setup
11
23
 
12
24
  1. **Get a key.** In the company-admin panel → Bug Reports → **API Keys**
13
- (full `bug-reports` staff access only), generate one and copy it — it's
14
- shown exactly once.
25
+ (any platform staff with `bug-reports` or `bug-reports-own` access can
26
+ generate their own), generate one and copy it — it's shown exactly once.
27
+ The key's reach (every report, or only reports you created/are assigned
28
+ to) always tracks your *current* staff role — it's resolved live on every
29
+ call, not fixed at generation time.
15
30
 
16
- 2. **Register the server:**
31
+ 2. **Register the server** — either via the CLI:
17
32
 
18
33
  ```bash
19
- cd deploy/mcp/bug-resolution
20
- npm install
21
34
  claude mcp add bug-resolution \
22
35
  --env BUG_REPORT_API_KEY=<the key you copied> \
23
- -- node "$(pwd)/index.js"
36
+ -- npx -y zopkit-bug-resolution-mcp
37
+ ```
38
+
39
+ or by adding this to your MCP config JSON (Claude Desktop's
40
+ `claude_desktop_config.json`, or Claude Code's own MCP settings):
41
+
42
+ ```json
43
+ {
44
+ "mcpServers": {
45
+ "bug-resolution": {
46
+ "command": "npx",
47
+ "args": ["-y", "zopkit-bug-resolution-mcp"],
48
+ "env": {
49
+ "BUG_REPORT_API_KEY": "<the key you copied>"
50
+ }
51
+ }
52
+ }
53
+ }
24
54
  ```
25
55
 
26
56
  By default it talks to production (`https://api.zopkit.com`). Point it
27
- elsewhere (e.g. a local dev backend) with an extra
28
- `--env BUG_REPORT_API_BASE_URL=http://localhost:3000`.
57
+ elsewhere (e.g. a local dev backend) with an extra env var,
58
+ `BUG_REPORT_API_BASE_URL=http://localhost:3000`.
59
+
60
+ 3. Restart Claude Code (or Claude Desktop) and approve `bug-resolution` when
61
+ prompted.
62
+
63
+ <details>
64
+ <summary>Running from a local checkout instead (this repo, not npm)</summary>
29
65
 
30
- 3. Restart Claude Code and approve `bug-resolution` when prompted.
66
+ ```bash
67
+ cd deploy/mcp/bug-resolution
68
+ npm install
69
+ claude mcp add bug-resolution \
70
+ --env BUG_REPORT_API_KEY=<the key you copied> \
71
+ -- node "$(pwd)/index.js"
72
+ ```
73
+
74
+ Useful if you're changing `index.js` itself — the npm package is what
75
+ everyone else should use.
76
+ </details>
31
77
 
32
78
  ## Usage
33
79
 
34
- Once registered, ask Claude Code (or whichever tool you fixed the bug with)
35
- to call `submit_bug_resolution` with the bug report's id (a UUID — from
36
- whoever asked you to fix it, or the report's admin-panel URL) and a Markdown
37
- writeup. It sets the report's resolution notes to that writeup and moves its
38
- status straight to `resolved`.
80
+ Once registered, give Claude Code (or whichever tool) the bug report's id (a
81
+ UUID — copy it from the ID chip in the report's detail panel, or from
82
+ whoever asked you to fix it).
83
+
84
+ 1. Ask it to call `fetch_bug_context` with that id first — you'll get the
85
+ title, description, severity/status, discussion, and every screenshot
86
+ actually rendered as images, so the tool can see the bug instead of just
87
+ reading a URL to it.
88
+ 2. After fixing it, ask it to call `submit_bug_resolution` with the same id
89
+ and a Markdown writeup. It sets the report's resolution notes to that
90
+ writeup and moves its status straight to `resolved`.
39
91
 
40
92
  ## Notes
41
93
 
42
94
  - Not part of the pnpm workspace (`pnpm-workspace.yaml`) or the deployed
43
- backend image — a standalone dev tool, installed with plain `npm install`.
95
+ backend image, and published separately from this repo's release process
96
+ (`npm publish` from this directory, manually, when `index.js` changes).
97
+ - No secrets in the package itself — the API key is supplied per-installation
98
+ via the `BUG_REPORT_API_KEY` env var, never embedded.
44
99
  - The key is per-developer and individually revocable from the same API Keys
45
100
  panel — revoking one never affects any other person's key.
46
- - This server only exposes the one write it needs. It can't list, search, or
47
- read bug reports — the person/tool using it needs the report's id already.
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.
package/index.js CHANGED
@@ -1,11 +1,15 @@
1
1
  #!/usr/bin/env node
2
- // bug-resolution MCP server — a single tool wrapping
3
- // POST /api/admin/bug-reports/resolution/:id (see backend/src/features/
4
- // bug-reports/README.md) so a developer's Claude Code / Cursor session can
5
- // report back how it fixed a bug as a native tool call instead of hand-
6
- // rolling a curl command. Thin wrapper: all the actual logic (auth, the
7
- // resolutionNotes/status write) lives server-side: this just forwards
8
- // {bugReportId, markdown} as the HTTP call the README already documents.
2
+ // bug-resolution MCP server — two tools wrapping the bug-reports resolution
3
+ // API (see backend/src/features/bug-reports/README.md) so a developer's
4
+ // Claude Code / Cursor session can work a bug report end to end as native
5
+ // tool calls instead of hand-rolling curl commands:
6
+ // - fetch_bug_context: GET /api/admin/bug-reports/context/:id — pulls the
7
+ // full report (title/description/severity/status/comments) plus every
8
+ // inline screenshot as real image content.
9
+ // - submit_bug_resolution: POST /api/admin/bug-reports/resolution/:id —
10
+ // writes back the fix as a Markdown resolution and marks it resolved.
11
+ // Thin wrapper: all the actual logic (auth, scoping, the DB writes) lives
12
+ // server-side; this just forwards the HTTP calls the README documents.
9
13
  //
10
14
  // Setup: see README.md in this directory.
11
15
 
@@ -21,7 +25,26 @@ if (!API_KEY) {
21
25
  'Generate one from the company-admin panel (Bug Reports → API Keys) and set it when registering this server.');
22
26
  }
23
27
 
24
- const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.0.0' });
28
+ const server = new McpServer({ name: 'zopkit-bug-resolution', version: '1.1.0' });
29
+
30
+ const IMG_SRC = /<img\b[^>]*\bsrc="([^"]+)"[^>]*>/gi;
31
+
32
+ function extractImageUrls(html) {
33
+ const urls = [];
34
+ for (const match of html.matchAll(IMG_SRC)) {
35
+ const src = match[1];
36
+ urls.push(src.startsWith('http') ? src : `${BASE_URL}${src}`);
37
+ }
38
+ return urls;
39
+ }
40
+
41
+ async function fetchImageContentBlock(url) {
42
+ const res = await fetch(url);
43
+ if (!res.ok) return null;
44
+ const contentType = res.headers.get('content-type') || 'image/png';
45
+ const buffer = Buffer.from(await res.arrayBuffer());
46
+ return { type: 'image', data: buffer.toString('base64'), mimeType: contentType };
47
+ }
25
48
 
26
49
  server.registerTool(
27
50
  'submit_bug_resolution',
@@ -83,5 +106,88 @@ server.registerTool(
83
106
  }
84
107
  );
85
108
 
109
+ server.registerTool(
110
+ 'fetch_bug_context',
111
+ {
112
+ title: 'Fetch bug report context',
113
+ description:
114
+ 'Pull the full context for a Zopkit bug report — title, description, severity, status, ' +
115
+ 'page URL, browser info, discussion comments, and every inline screenshot as actual images — ' +
116
+ 'so an AI coding tool has everything it needs to diagnose and fix the bug without ' +
117
+ 'opening the admin panel. Screenshots come back as image content alongside the text summary.',
118
+ inputSchema: {
119
+ bugReportId: z.string().min(1).describe("The bug report's id (UUID)."),
120
+ },
121
+ },
122
+ async ({ bugReportId }) => {
123
+ if (!API_KEY) {
124
+ return {
125
+ isError: true,
126
+ content: [{ type: 'text', text: 'BUG_REPORT_API_KEY is not set on this MCP server — see README.md.' }],
127
+ };
128
+ }
129
+
130
+ let res;
131
+ try {
132
+ res = await fetch(`${BASE_URL}/api/admin/bug-reports/context/${encodeURIComponent(bugReportId)}`, {
133
+ headers: { 'X-API-Key': API_KEY },
134
+ });
135
+ } catch (err) {
136
+ return {
137
+ isError: true,
138
+ content: [{ type: 'text', text: `Could not reach ${BASE_URL}: ${err instanceof Error ? err.message : String(err)}` }],
139
+ };
140
+ }
141
+
142
+ const body = await res.json().catch(() => null);
143
+
144
+ if (!res.ok) {
145
+ const message = body?.message || body?.error || `HTTP ${res.status}`;
146
+ return { isError: true, content: [{ type: 'text', text: `Failed to fetch bug context: ${message}` }] };
147
+ }
148
+
149
+ const report = body?.data;
150
+ if (!report) {
151
+ return { isError: true, content: [{ type: 'text', text: 'Bug context response had no data.' }] };
152
+ }
153
+
154
+ const comments = report.comments || [];
155
+ const commentsText = comments.length
156
+ ? comments.map((c) => `- ${c.authorName || c.authorEmail} (${c.createdAt}): ${c.body}`).join('\n')
157
+ : '(no comments)';
158
+
159
+ const summary = [
160
+ `# ${report.title}`,
161
+ `id: ${report.id}`,
162
+ `severity: ${report.severity} | status: ${report.status}`,
163
+ report.pageUrl ? `page: ${report.pageUrl}` : null,
164
+ report.browserInfo ? `browser: ${report.browserInfo}` : null,
165
+ '',
166
+ '## Description',
167
+ report.description,
168
+ '',
169
+ '## Discussion',
170
+ commentsText,
171
+ ].filter((line) => line !== null).join('\n');
172
+
173
+ const content = [{ type: 'text', text: summary }];
174
+
175
+ // Inline screenshots live as <img> tags inside the description HTML —
176
+ // fetch each (now-public) media URL and hand it back as real image
177
+ // content so the AI tool actually sees the bug, not just a URL to it.
178
+ const imageUrls = extractImageUrls(report.description || '');
179
+ for (const url of imageUrls) {
180
+ try {
181
+ const block = await fetchImageContentBlock(url);
182
+ if (block) content.push(block);
183
+ } catch {
184
+ content.push({ type: 'text', text: `(failed to load screenshot: ${url})` });
185
+ }
186
+ }
187
+
188
+ return { content };
189
+ }
190
+ );
191
+
86
192
  const transport = new StdioServerTransport();
87
193
  await server.connect(transport);
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "zopkit-bug-resolution-mcp",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
- "description": "MCP server: submit how a bug report was fixed to Zopkit wrapper's POST /admin/bug-reports/resolution/:id. 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) and submit how a bug was fixed. 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"