@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 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 tool
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 (with code
123
- patches), the detected framework, and whether the answer came from cache.
124
- Read-only: it never touches your files. Applying a fix is your agent's
125
- (and your) call.
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
- export async function callDebugBackend(req, config) {
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}/debug`, {
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(req),
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 the debug_error tool to any MCP client: hand it an error or stack
23
- trace, get root cause + ranked fixes from DebugAI.
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;
@@ -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 patches.',
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,3 @@
1
+ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
+ import type { BackendConfig } from '../backend.js';
3
+ export declare function registerReportOutcome(server: McpServer, config: BackendConfig): void;
@@ -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": "1.1.0",
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",