@debugai/mcp 1.1.0 → 2.0.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 +30 -5
- package/dist/backend.d.ts +28 -0
- package/dist/backend.js +12 -4
- package/dist/index.js +5 -2
- package/dist/server.js +2 -0
- package/dist/tools/debugError.d.ts +1 -0
- package/dist/tools/debugError.js +31 -1
- package/dist/tools/reportOutcome.d.ts +3 -0
- package/dist/tools/reportOutcome.js +59 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -106,7 +106,7 @@ You don't need this package. The
|
|
|
106
106
|
registers the MCP server automatically (VS Code 1.101+) and adds one-click
|
|
107
107
|
fix apply, proactive scan, and codebase indexing on top.
|
|
108
108
|
|
|
109
|
-
## The
|
|
109
|
+
## The tools
|
|
110
110
|
|
|
111
111
|
### `debug_error`
|
|
112
112
|
|
|
@@ -119,10 +119,35 @@ Give it an error, get an analysis.
|
|
|
119
119
|
| `codeSnippet` | no | Code around the failing line, if the agent has it. |
|
|
120
120
|
| `filePath` | no | Path to the file that threw. |
|
|
121
121
|
|
|
122
|
-
Returns the root cause, up to 3 fixes ranked by confidence
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
122
|
+
Returns the root cause, up to 3 fixes ranked by confidence, the detected
|
|
123
|
+
framework, and whether the answer came from cache. Since 2.0 each fix also
|
|
124
|
+
carries, where derivable: `edits` (exact old/new strings your agent's edit
|
|
125
|
+
tool can apply directly), `unified_diff`, and `verify_with` (a syntax-level
|
|
126
|
+
check command to run after applying). Read-only: it never touches your
|
|
127
|
+
files. Applying a fix is your agent's (and your) call.
|
|
128
|
+
|
|
129
|
+
Every fix is labeled with its verification state, and there are three of
|
|
130
|
+
them, not two: **verified** (a mechanical check passed — currently
|
|
131
|
+
parse/import classes), **failed check** (confidence capped hard), or **not
|
|
132
|
+
verified** (the confidence number is the model's own estimate — nothing
|
|
133
|
+
checked it). We label the third case instead of hiding it.
|
|
134
|
+
|
|
135
|
+
### `report_outcome`
|
|
136
|
+
|
|
137
|
+
Tell DebugAI whether an applied fix actually worked.
|
|
138
|
+
|
|
139
|
+
| Input | Required | Description |
|
|
140
|
+
|-------|----------|-------------|
|
|
141
|
+
| `debugLogId` | yes | The `debug_log_id` from the `debug_error` response. |
|
|
142
|
+
| `result` | yes | `worked` or `failed`. |
|
|
143
|
+
| `fixRank` | no | Which ranked fix was applied (1-3). |
|
|
144
|
+
| `newError` | no | If it failed: the error you saw after applying. |
|
|
145
|
+
|
|
146
|
+
Confirmed rank-1 fixes are remembered per project (the next hit on the same
|
|
147
|
+
error starts from the confirmed fix); failed-fix follow-ups are the
|
|
148
|
+
feedback that improves future answers. Agents are asked to call this once
|
|
149
|
+
per applied fix — same pipeline human feedback flows through in the VS Code
|
|
150
|
+
extension.
|
|
126
151
|
|
|
127
152
|
Example, in Claude Code:
|
|
128
153
|
|
package/dist/backend.d.ts
CHANGED
|
@@ -6,6 +6,11 @@ export interface DebugRequest {
|
|
|
6
6
|
project_id?: string;
|
|
7
7
|
framework_hint?: string;
|
|
8
8
|
}
|
|
9
|
+
export interface DebugEdit {
|
|
10
|
+
file: string;
|
|
11
|
+
old_string: string;
|
|
12
|
+
new_string: string;
|
|
13
|
+
}
|
|
9
14
|
export interface DebugFix {
|
|
10
15
|
rank: number;
|
|
11
16
|
title: string;
|
|
@@ -13,6 +18,11 @@ export interface DebugFix {
|
|
|
13
18
|
confidence: number;
|
|
14
19
|
code?: string;
|
|
15
20
|
line_hint?: string;
|
|
21
|
+
verified?: boolean | null;
|
|
22
|
+
verification_reason?: string;
|
|
23
|
+
edits?: DebugEdit[];
|
|
24
|
+
unified_diff?: string;
|
|
25
|
+
verify_with?: string;
|
|
16
26
|
}
|
|
17
27
|
export interface DebugResponse {
|
|
18
28
|
root_cause: string;
|
|
@@ -26,6 +36,22 @@ export interface DebugResponse {
|
|
|
26
36
|
pattern_matched?: string;
|
|
27
37
|
remaining_today?: number;
|
|
28
38
|
mock?: boolean;
|
|
39
|
+
schema_version?: string;
|
|
40
|
+
debug_log_id?: string | null;
|
|
41
|
+
error_signature?: string;
|
|
42
|
+
session_id?: string;
|
|
43
|
+
memory_hit?: boolean;
|
|
44
|
+
memory_fix_confirmed?: boolean;
|
|
45
|
+
}
|
|
46
|
+
export interface OutcomeRequest {
|
|
47
|
+
debug_log_id: string;
|
|
48
|
+
result: 'worked' | 'failed';
|
|
49
|
+
fix_rank?: number;
|
|
50
|
+
new_error?: string;
|
|
51
|
+
source: 'agent';
|
|
52
|
+
}
|
|
53
|
+
export interface OutcomeResponse {
|
|
54
|
+
success: boolean;
|
|
29
55
|
}
|
|
30
56
|
export interface BackendConfig {
|
|
31
57
|
apiKey: string;
|
|
@@ -39,4 +65,6 @@ export interface BackendError extends Error {
|
|
|
39
65
|
retryAfterSeconds: number;
|
|
40
66
|
}
|
|
41
67
|
export declare const DEFAULT_TIMEOUT_MS = 150000;
|
|
68
|
+
export declare const OUTCOME_TIMEOUT_MS = 15000;
|
|
42
69
|
export declare function callDebugBackend(req: DebugRequest, config: BackendConfig): Promise<DebugResponse>;
|
|
70
|
+
export declare function callOutcomeBackend(req: OutcomeRequest, config: BackendConfig): Promise<OutcomeResponse>;
|
package/dist/backend.js
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
export const DEFAULT_TIMEOUT_MS = 150_000;
|
|
2
|
+
// Feedback writes are a fast DB insert, not an LLM call — fail fast so a
|
|
3
|
+
// stuck outcome report never holds an agent hostage for minutes.
|
|
4
|
+
export const OUTCOME_TIMEOUT_MS = 15_000;
|
|
2
5
|
function makeBackendError(message, status, retryAfterSeconds = 0) {
|
|
3
6
|
return Object.assign(new Error(message), { status, retryAfterSeconds });
|
|
4
7
|
}
|
|
5
|
-
|
|
6
|
-
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
8
|
+
async function postJson(path, payload, config, timeoutMs) {
|
|
7
9
|
const controller = new AbortController();
|
|
8
10
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
9
11
|
let res;
|
|
10
12
|
try {
|
|
11
|
-
res = await fetch(`${config.apiBase}
|
|
13
|
+
res = await fetch(`${config.apiBase}${path}`, {
|
|
12
14
|
method: 'POST',
|
|
13
15
|
headers: {
|
|
14
16
|
'content-type': 'application/json',
|
|
15
17
|
'x-api-key': config.apiKey,
|
|
16
18
|
'user-agent': `debugai-mcp/${config.version}`,
|
|
17
19
|
},
|
|
18
|
-
body: JSON.stringify(
|
|
20
|
+
body: JSON.stringify(payload),
|
|
19
21
|
signal: controller.signal,
|
|
20
22
|
});
|
|
21
23
|
}
|
|
@@ -36,3 +38,9 @@ export async function callDebugBackend(req, config) {
|
|
|
36
38
|
}
|
|
37
39
|
return res.json();
|
|
38
40
|
}
|
|
41
|
+
export async function callDebugBackend(req, config) {
|
|
42
|
+
return postJson('/debug', req, config, config.timeoutMs ?? DEFAULT_TIMEOUT_MS);
|
|
43
|
+
}
|
|
44
|
+
export async function callOutcomeBackend(req, config) {
|
|
45
|
+
return postJson('/user/debug-feedback', req, config, OUTCOME_TIMEOUT_MS);
|
|
46
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -19,8 +19,11 @@ function packageVersion() {
|
|
|
19
19
|
const VERSION = packageVersion();
|
|
20
20
|
const HELP = `debugai-mcp v${VERSION} — DebugAI MCP server (stdio)
|
|
21
21
|
|
|
22
|
-
Exposes
|
|
23
|
-
trace, get root cause + ranked
|
|
22
|
+
Exposes two tools to any MCP client:
|
|
23
|
+
debug_error hand it an error or stack trace, get root cause + ranked
|
|
24
|
+
fixes with machine-applicable edits (v2 contract)
|
|
25
|
+
report_outcome tell DebugAI whether an applied fix worked — failed-fix
|
|
26
|
+
follow-ups improve future answers for your codebase
|
|
24
27
|
|
|
25
28
|
Usage:
|
|
26
29
|
npx @debugai/mcp # start the server (stdio transport)
|
package/dist/server.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import { registerDebugError } from './tools/debugError.js';
|
|
3
|
+
import { registerReportOutcome } from './tools/reportOutcome.js';
|
|
3
4
|
export function createServer(config) {
|
|
4
5
|
const server = new McpServer({ name: 'debugai', version: config.version }, { capabilities: { tools: { listChanged: true } } });
|
|
5
6
|
registerDebugError(server, config);
|
|
7
|
+
registerReportOutcome(server, config);
|
|
6
8
|
return server;
|
|
7
9
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
2
|
import type { BackendConfig, DebugFix } from '../backend.js';
|
|
3
|
+
export declare function formatVerification(fix: DebugFix): string;
|
|
3
4
|
export declare function formatFix(fix: DebugFix, index: number): string;
|
|
4
5
|
export declare function registerDebugError(server: McpServer, config: BackendConfig): void;
|
package/dist/tools/debugError.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { callDebugBackend } from '../backend.js';
|
|
3
3
|
import { mapBackendErrorToToolResult } from '../errors.js';
|
|
4
|
+
// Tri-state verification labeling (docs/plan-v2-contract-phase1.md §1).
|
|
5
|
+
// The null case is rendered ON PURPOSE: a confidence number nothing checked
|
|
6
|
+
// must never look the same as one that was mechanically verified.
|
|
7
|
+
export function formatVerification(fix) {
|
|
8
|
+
if (fix.verified === true) {
|
|
9
|
+
return `✓ Verified — ${fix.verification_reason ?? 'mechanical check passed'}`;
|
|
10
|
+
}
|
|
11
|
+
if (fix.verified === false) {
|
|
12
|
+
return `✗ Failed mechanical check (confidence capped) — ${fix.verification_reason ?? 'check failed'}`;
|
|
13
|
+
}
|
|
14
|
+
return '· Not verified — confidence is the model\'s own estimate; nothing checked this fix.';
|
|
15
|
+
}
|
|
4
16
|
export function formatFix(fix, index) {
|
|
5
17
|
const lines = [
|
|
6
18
|
`\n**Fix ${index} (${fix.confidence}% confidence)** — ${fix.title}`,
|
|
19
|
+
formatVerification(fix),
|
|
7
20
|
fix.description,
|
|
8
21
|
];
|
|
9
22
|
if (fix.code) {
|
|
@@ -12,6 +25,15 @@ export function formatFix(fix, index) {
|
|
|
12
25
|
if (fix.line_hint) {
|
|
13
26
|
lines.push(`_Location: ${fix.line_hint}_`);
|
|
14
27
|
}
|
|
28
|
+
if (fix.edits?.length) {
|
|
29
|
+
lines.push('_Machine-applicable edit available: `edits` on this fix in structuredContent carries the exact old/new strings (apply with your Edit/replace tool)._');
|
|
30
|
+
}
|
|
31
|
+
if (fix.unified_diff) {
|
|
32
|
+
lines.push('```diff', fix.unified_diff.trimEnd(), '```');
|
|
33
|
+
}
|
|
34
|
+
if (fix.verify_with) {
|
|
35
|
+
lines.push(`_Syntax-level check after applying: \`${fix.verify_with}\`_`);
|
|
36
|
+
}
|
|
15
37
|
return lines.join('\n');
|
|
16
38
|
}
|
|
17
39
|
export function registerDebugError(server, config) {
|
|
@@ -22,7 +44,8 @@ export function registerDebugError(server, config) {
|
|
|
22
44
|
'"debug this stack trace", "fix this exception", "analyze this traceback", or shows a ' +
|
|
23
45
|
'Traceback / TypeError / ReferenceError / AttributeError. ' +
|
|
24
46
|
'Works for Python, JavaScript, TypeScript, Go, Rust. ' +
|
|
25
|
-
'Returns root cause explanation plus up to 3 ranked fixes with code
|
|
47
|
+
'Returns root cause explanation plus up to 3 ranked fixes with machine-applicable code edits. ' +
|
|
48
|
+
'After applying a fix, report whether it worked via the report_outcome tool.',
|
|
26
49
|
inputSchema: {
|
|
27
50
|
errorText: z
|
|
28
51
|
.string()
|
|
@@ -79,16 +102,23 @@ export function registerDebugError(server, config) {
|
|
|
79
102
|
if (badges.length) {
|
|
80
103
|
sections.push(`\n---\n_${badges.join(' · ')}_`);
|
|
81
104
|
}
|
|
105
|
+
if (result.debug_log_id) {
|
|
106
|
+
sections.push(`\nAfter applying a fix, call report_outcome with debugLogId "${result.debug_log_id}", ` +
|
|
107
|
+
'the fixRank you applied, and result "worked" or "failed" (include newError text if it failed).');
|
|
108
|
+
}
|
|
82
109
|
const text = sections.join('\n');
|
|
83
110
|
return {
|
|
84
111
|
content: [{ type: 'text', text }],
|
|
85
112
|
structuredContent: {
|
|
113
|
+
schema_version: result.schema_version ?? '1.0',
|
|
86
114
|
root_cause: result.root_cause,
|
|
87
115
|
fixes: result.fixes,
|
|
88
116
|
framework_detected: result.framework_detected,
|
|
89
117
|
model_used: result.model_used,
|
|
90
118
|
cached: result.cached ?? false,
|
|
91
119
|
has_project_context: result.has_project_context ?? false,
|
|
120
|
+
debug_log_id: result.debug_log_id ?? null,
|
|
121
|
+
error_signature: result.error_signature ?? null,
|
|
92
122
|
},
|
|
93
123
|
};
|
|
94
124
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { callOutcomeBackend } from '../backend.js';
|
|
3
|
+
import { mapBackendErrorToToolResult } from '../errors.js';
|
|
4
|
+
export function registerReportOutcome(server, config) {
|
|
5
|
+
server.registerTool('report_outcome', {
|
|
6
|
+
title: 'Report Fix Outcome',
|
|
7
|
+
description: 'Report whether a DebugAI fix actually worked after you applied it. ' +
|
|
8
|
+
'Call this ONCE after applying (or abandoning) a fix from debug_error, passing the ' +
|
|
9
|
+
'debug_log_id from that response. If the fix failed, include the new error text — ' +
|
|
10
|
+
'failed-fix follow-ups directly improve future answers for this codebase, and ' +
|
|
11
|
+
'confirmed rank-1 fixes are remembered for the whole team.',
|
|
12
|
+
inputSchema: {
|
|
13
|
+
debugLogId: z
|
|
14
|
+
.string()
|
|
15
|
+
.min(1)
|
|
16
|
+
.describe('The debug_log_id value from the debug_error response you are reporting on.'),
|
|
17
|
+
result: z
|
|
18
|
+
.enum(['worked', 'failed'])
|
|
19
|
+
.describe('"worked" = the fix resolved the error; "failed" = it did not (or made things worse).'),
|
|
20
|
+
fixRank: z
|
|
21
|
+
.number()
|
|
22
|
+
.int()
|
|
23
|
+
.min(1)
|
|
24
|
+
.max(3)
|
|
25
|
+
.optional()
|
|
26
|
+
.describe('Which ranked fix you applied (1-3). Rank 1 outcomes feed team error memory.'),
|
|
27
|
+
newError: z
|
|
28
|
+
.string()
|
|
29
|
+
.max(4000)
|
|
30
|
+
.optional()
|
|
31
|
+
.describe('If result is "failed": the error observed AFTER applying the fix.'),
|
|
32
|
+
},
|
|
33
|
+
annotations: {
|
|
34
|
+
title: 'Report Fix Outcome',
|
|
35
|
+
// Deliberately NO readOnlyHint — this records telemetry server-side.
|
|
36
|
+
idempotentHint: true,
|
|
37
|
+
},
|
|
38
|
+
}, async ({ debugLogId, result, fixRank, newError }) => {
|
|
39
|
+
try {
|
|
40
|
+
await callOutcomeBackend({
|
|
41
|
+
debug_log_id: debugLogId,
|
|
42
|
+
result,
|
|
43
|
+
fix_rank: fixRank,
|
|
44
|
+
new_error: newError,
|
|
45
|
+
source: 'agent',
|
|
46
|
+
}, config);
|
|
47
|
+
const ack = result === 'worked'
|
|
48
|
+
? 'Outcome recorded: fix worked. Rank-1 confirmations are remembered for this project, so the next hit on this error starts from the confirmed fix.'
|
|
49
|
+
: 'Outcome recorded: fix failed. The follow-up error was logged and feeds directly into improving future answers. If you are still stuck, call debug_error again with the NEW error text.';
|
|
50
|
+
return {
|
|
51
|
+
content: [{ type: 'text', text: ack }],
|
|
52
|
+
structuredContent: { recorded: true, result },
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
return mapBackendErrorToToolResult(err);
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@debugai/mcp",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "DebugAI MCP server — hand any error to DebugAI from Claude Desktop, Claude Code, Cursor, Zed, or any MCP client and get root cause + ranked fixes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|