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