@bbearai/mcp-server 0.3.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/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "@bbearai/mcp-server",
3
+ "version": "0.3.0",
4
+ "description": "MCP server for BugBear - allows Claude Code to query bug reports",
5
+ "main": "dist/index.js",
6
+ "bin": {
7
+ "bugbear-mcp": "./dist/index.js",
8
+ "bugbear-mcp-api": "./dist/index-api.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsx watch src/index.ts",
13
+ "dev:api": "tsx watch src/index-api.ts",
14
+ "start": "node dist/index.js",
15
+ "start:api": "node dist/index-api.js"
16
+ },
17
+ "dependencies": {
18
+ "@modelcontextprotocol/sdk": "^1.25.3",
19
+ "@supabase/supabase-js": "^2.39.0"
20
+ },
21
+ "devDependencies": {
22
+ "@types/node": "^20.0.0",
23
+ "tsx": "^4.0.0",
24
+ "typescript": "^5.0.0"
25
+ },
26
+ "engines": {
27
+ "node": ">=18.0.0"
28
+ }
29
+ }
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * BugBear MCP Server (API Version)
4
+ *
5
+ * This version uses the BugBear API instead of connecting directly to Supabase.
6
+ * This is the recommended setup for new projects.
7
+ *
8
+ * Configuration:
9
+ * BUGBEAR_API_KEY - Your project's API key (bb_live_xxx)
10
+ * BUGBEAR_API_URL - API URL (optional, defaults to https://bugbear.ai/api/v1)
11
+ */
12
+
13
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
14
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
15
+ import {
16
+ CallToolRequestSchema,
17
+ ListToolsRequestSchema,
18
+ } from '@modelcontextprotocol/sdk/types.js';
19
+
20
+ // Configuration
21
+ const API_KEY = process.env.BUGBEAR_API_KEY || '';
22
+ // Default to production URL, but allow override for local development
23
+ const API_URL = process.env.BUGBEAR_API_URL || 'https://app.bugbear.ai/api/v1';
24
+
25
+ // Validate configuration
26
+ function validateConfig() {
27
+ if (!API_KEY) {
28
+ console.error('BugBear MCP Server: BUGBEAR_API_KEY environment variable is required');
29
+ console.error('Get your API key from: https://bugbear.ai/settings/projects');
30
+ process.exit(1);
31
+ }
32
+
33
+ if (!API_KEY.startsWith('bb_live_') && !API_KEY.startsWith('bb_test_')) {
34
+ console.error('BugBear MCP Server: Invalid API key format');
35
+ console.error('API keys should start with bb_live_ or bb_test_');
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ // API helper
41
+ async function apiRequest(
42
+ endpoint: string,
43
+ method: 'GET' | 'POST' | 'PATCH' = 'GET',
44
+ body?: Record<string, unknown>
45
+ ): Promise<{ data?: unknown; error?: string }> {
46
+ try {
47
+ const url = `${API_URL}${endpoint}`;
48
+ const options: RequestInit = {
49
+ method,
50
+ headers: {
51
+ 'Authorization': `Bearer ${API_KEY}`,
52
+ 'Content-Type': 'application/json',
53
+ },
54
+ };
55
+
56
+ if (body && method !== 'GET') {
57
+ options.body = JSON.stringify(body);
58
+ }
59
+
60
+ const response = await fetch(url, options);
61
+ const json = await response.json();
62
+
63
+ if (!response.ok) {
64
+ return { error: json.error || `API error: ${response.status}` };
65
+ }
66
+
67
+ return { data: json.data };
68
+ } catch (error) {
69
+ return { error: error instanceof Error ? error.message : 'API request failed' };
70
+ }
71
+ }
72
+
73
+ // Tool definitions
74
+ const tools = [
75
+ {
76
+ name: 'list_reports',
77
+ description: 'List recent bug reports for the project',
78
+ inputSchema: {
79
+ type: 'object' as const,
80
+ properties: {
81
+ limit: {
82
+ type: 'number',
83
+ description: 'Maximum reports to return (default: 10, max: 50)',
84
+ },
85
+ status: {
86
+ type: 'string',
87
+ enum: ['new', 'reviewed', 'in_progress', 'resolved', 'closed'],
88
+ description: 'Filter by status',
89
+ },
90
+ severity: {
91
+ type: 'string',
92
+ enum: ['critical', 'high', 'medium', 'low'],
93
+ description: 'Filter by severity',
94
+ },
95
+ report_type: {
96
+ type: 'string',
97
+ enum: ['bug', 'test_fail', 'feedback', 'suggestion'],
98
+ description: 'Filter by type',
99
+ },
100
+ },
101
+ },
102
+ },
103
+ {
104
+ name: 'get_report',
105
+ description: 'Get detailed information about a specific report',
106
+ inputSchema: {
107
+ type: 'object' as const,
108
+ properties: {
109
+ report_id: {
110
+ type: 'string',
111
+ description: 'The UUID of the report',
112
+ },
113
+ },
114
+ required: ['report_id'],
115
+ },
116
+ },
117
+ {
118
+ name: 'update_report_status',
119
+ description: 'Update the status of a report',
120
+ inputSchema: {
121
+ type: 'object' as const,
122
+ properties: {
123
+ report_id: {
124
+ type: 'string',
125
+ description: 'The UUID of the report',
126
+ },
127
+ status: {
128
+ type: 'string',
129
+ enum: ['new', 'reviewed', 'in_progress', 'resolved', 'closed'],
130
+ description: 'New status',
131
+ },
132
+ resolution: {
133
+ type: 'string',
134
+ description: 'Resolution notes (optional)',
135
+ },
136
+ },
137
+ required: ['report_id', 'status'],
138
+ },
139
+ },
140
+ {
141
+ name: 'create_bug_report',
142
+ description: 'Create a new bug report from Claude Code',
143
+ inputSchema: {
144
+ type: 'object' as const,
145
+ properties: {
146
+ description: {
147
+ type: 'string',
148
+ description: 'Detailed description of the bug',
149
+ },
150
+ severity: {
151
+ type: 'string',
152
+ enum: ['critical', 'high', 'medium', 'low'],
153
+ description: 'Bug severity',
154
+ },
155
+ file_path: {
156
+ type: 'string',
157
+ description: 'File path where the bug was found',
158
+ },
159
+ line_number: {
160
+ type: 'number',
161
+ description: 'Line number',
162
+ },
163
+ code_snippet: {
164
+ type: 'string',
165
+ description: 'Relevant code snippet',
166
+ },
167
+ suggested_fix: {
168
+ type: 'string',
169
+ description: 'Suggested fix',
170
+ },
171
+ },
172
+ required: ['description', 'severity'],
173
+ },
174
+ },
175
+ {
176
+ name: 'get_project_info',
177
+ description: 'Get project information and statistics',
178
+ inputSchema: {
179
+ type: 'object' as const,
180
+ properties: {},
181
+ },
182
+ },
183
+ {
184
+ name: 'list_test_cases',
185
+ description: 'List test cases for the project',
186
+ inputSchema: {
187
+ type: 'object' as const,
188
+ properties: {
189
+ limit: {
190
+ type: 'number',
191
+ description: 'Max test cases to return (default: 50)',
192
+ },
193
+ priority: {
194
+ type: 'string',
195
+ enum: ['P0', 'P1', 'P2', 'P3'],
196
+ description: 'Filter by priority',
197
+ },
198
+ },
199
+ },
200
+ },
201
+ {
202
+ name: 'create_test_case',
203
+ description: 'Create a new test case',
204
+ inputSchema: {
205
+ type: 'object' as const,
206
+ properties: {
207
+ test_key: {
208
+ type: 'string',
209
+ description: 'Unique test ID (e.g., TC-001)',
210
+ },
211
+ title: {
212
+ type: 'string',
213
+ description: 'Test title',
214
+ },
215
+ description: {
216
+ type: 'string',
217
+ description: 'Test description',
218
+ },
219
+ steps: {
220
+ type: 'array',
221
+ items: {
222
+ type: 'object',
223
+ properties: {
224
+ stepNumber: { type: 'number' },
225
+ action: { type: 'string' },
226
+ expectedResult: { type: 'string' },
227
+ },
228
+ },
229
+ description: 'Test steps',
230
+ },
231
+ expected_result: {
232
+ type: 'string',
233
+ description: 'Expected outcome',
234
+ },
235
+ priority: {
236
+ type: 'string',
237
+ enum: ['P0', 'P1', 'P2', 'P3'],
238
+ description: 'Priority level',
239
+ },
240
+ },
241
+ required: ['test_key', 'title', 'steps', 'expected_result'],
242
+ },
243
+ },
244
+ {
245
+ name: 'get_qa_tracks',
246
+ description: 'Get QA tracks for the project',
247
+ inputSchema: {
248
+ type: 'object' as const,
249
+ properties: {},
250
+ },
251
+ },
252
+ {
253
+ name: 'verify_connection',
254
+ description: 'Verify the API connection is working',
255
+ inputSchema: {
256
+ type: 'object' as const,
257
+ properties: {},
258
+ },
259
+ },
260
+ ];
261
+
262
+ // Tool handlers
263
+ async function handleTool(
264
+ name: string,
265
+ args: Record<string, unknown>
266
+ ): Promise<{ content: Array<{ type: 'text'; text: string }> }> {
267
+ let result: { data?: unknown; error?: string };
268
+
269
+ switch (name) {
270
+ case 'list_reports': {
271
+ const params = new URLSearchParams();
272
+ if (args.limit) params.set('limit', String(args.limit));
273
+ if (args.status) params.set('status', String(args.status));
274
+ if (args.severity) params.set('severity', String(args.severity));
275
+ if (args.report_type) params.set('report_type', String(args.report_type));
276
+ result = await apiRequest(`/reports?${params.toString()}`);
277
+ break;
278
+ }
279
+
280
+ case 'get_report':
281
+ result = await apiRequest(`/reports/${args.report_id}`);
282
+ break;
283
+
284
+ case 'update_report_status':
285
+ result = await apiRequest(`/reports/${args.report_id}`, 'PATCH', {
286
+ status: args.status,
287
+ resolution: args.resolution,
288
+ });
289
+ break;
290
+
291
+ case 'create_bug_report': {
292
+ // Build enhanced description with code context
293
+ let description = String(args.description);
294
+ if (args.file_path) {
295
+ description += `\n\n📁 File: ${args.file_path}`;
296
+ if (args.line_number) {
297
+ description += `:${args.line_number}`;
298
+ }
299
+ }
300
+ if (args.code_snippet) {
301
+ description += `\n\n\`\`\`\n${args.code_snippet}\n\`\`\``;
302
+ }
303
+ if (args.suggested_fix) {
304
+ description += `\n\n💡 Suggested fix: ${args.suggested_fix}`;
305
+ }
306
+
307
+ result = await apiRequest('/reports', 'POST', {
308
+ report_type: 'bug',
309
+ description,
310
+ severity: args.severity,
311
+ app_context: {
312
+ source: 'claude_code',
313
+ file_path: args.file_path,
314
+ line_number: args.line_number,
315
+ },
316
+ });
317
+ break;
318
+ }
319
+
320
+ case 'get_project_info':
321
+ result = await apiRequest('/project');
322
+ break;
323
+
324
+ case 'list_test_cases': {
325
+ const params = new URLSearchParams();
326
+ if (args.limit) params.set('limit', String(args.limit));
327
+ if (args.priority) params.set('priority', String(args.priority));
328
+ result = await apiRequest(`/test-cases?${params.toString()}`);
329
+ break;
330
+ }
331
+
332
+ case 'create_test_case':
333
+ result = await apiRequest('/test-cases', 'POST', {
334
+ test_key: args.test_key,
335
+ title: args.title,
336
+ description: args.description,
337
+ steps: args.steps,
338
+ expected_result: args.expected_result,
339
+ priority: args.priority || 'P2',
340
+ });
341
+ break;
342
+
343
+ case 'get_qa_tracks':
344
+ result = await apiRequest('/qa-tracks');
345
+ break;
346
+
347
+ case 'verify_connection':
348
+ result = await apiRequest('/verify');
349
+ break;
350
+
351
+ default:
352
+ result = { error: `Unknown tool: ${name}` };
353
+ }
354
+
355
+ if (result.error) {
356
+ return {
357
+ content: [{ type: 'text', text: `Error: ${result.error}` }],
358
+ };
359
+ }
360
+
361
+ return {
362
+ content: [{ type: 'text', text: JSON.stringify(result.data, null, 2) }],
363
+ };
364
+ }
365
+
366
+ // Main
367
+ async function main() {
368
+ validateConfig();
369
+
370
+ const server = new Server(
371
+ {
372
+ name: 'bugbear',
373
+ version: '0.2.0',
374
+ },
375
+ {
376
+ capabilities: {
377
+ tools: {},
378
+ },
379
+ }
380
+ );
381
+
382
+ // List tools
383
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
384
+ tools,
385
+ }));
386
+
387
+ // Handle tool calls
388
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
389
+ const { name, arguments: args } = request.params;
390
+ return await handleTool(name, (args || {}) as Record<string, unknown>);
391
+ });
392
+
393
+ // Connect via stdio
394
+ const transport = new StdioServerTransport();
395
+ await server.connect(transport);
396
+
397
+ console.error('BugBear MCP Server (API) connected');
398
+ }
399
+
400
+ main().catch((error) => {
401
+ console.error('Fatal error:', error);
402
+ process.exit(1);
403
+ });