@debugai/mcp 1.0.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 +48 -8
- package/dist/backend.d.ts +28 -0
- package/dist/backend.js +12 -4
- package/dist/config.d.ts +13 -0
- package/dist/config.js +48 -0
- package/dist/index.js +16 -8
- 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
|
@@ -14,6 +14,19 @@ This package is the same server, standalone. No VS Code required.
|
|
|
14
14
|
2. Copy your API key (`dbg_...`) from [debugai.io/dashboard](https://debugai.io/dashboard).
|
|
15
15
|
3. Add the server to your MCP client (snippets below). Node 18+ required.
|
|
16
16
|
|
|
17
|
+
### Set the key once for every client (optional)
|
|
18
|
+
|
|
19
|
+
Instead of repeating the key in each client's `env` block, write it to
|
|
20
|
+
`~/.debugai/config.json`:
|
|
21
|
+
|
|
22
|
+
```json
|
|
23
|
+
{ "api_key": "dbg_your_key_here" }
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Every MCP client launching `npx -y @debugai/mcp` picks it up — you can then
|
|
27
|
+
drop the `env` block from the snippets below entirely. An explicit
|
|
28
|
+
`DEBUGAI_API_KEY` env var still wins over the file.
|
|
29
|
+
|
|
17
30
|
### Claude Code
|
|
18
31
|
|
|
19
32
|
```bash
|
|
@@ -93,7 +106,7 @@ You don't need this package. The
|
|
|
93
106
|
registers the MCP server automatically (VS Code 1.101+) and adds one-click
|
|
94
107
|
fix apply, proactive scan, and codebase indexing on top.
|
|
95
108
|
|
|
96
|
-
## The
|
|
109
|
+
## The tools
|
|
97
110
|
|
|
98
111
|
### `debug_error`
|
|
99
112
|
|
|
@@ -106,10 +119,35 @@ Give it an error, get an analysis.
|
|
|
106
119
|
| `codeSnippet` | no | Code around the failing line, if the agent has it. |
|
|
107
120
|
| `filePath` | no | Path to the file that threw. |
|
|
108
121
|
|
|
109
|
-
Returns the root cause, up to 3 fixes ranked by confidence
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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.
|
|
113
151
|
|
|
114
152
|
Example, in Claude Code:
|
|
115
153
|
|
|
@@ -123,9 +161,10 @@ Example, in Claude Code:
|
|
|
123
161
|
|
|
124
162
|
| Variable | Default | Description |
|
|
125
163
|
|----------|---------|-------------|
|
|
126
|
-
| `DEBUGAI_API_KEY` | (none) | Your API key.
|
|
127
|
-
| `DEBUGAI_API_BASE` | DebugAI production | Override for self-hosted or staging setups. |
|
|
164
|
+
| `DEBUGAI_API_KEY` | (none) | Your API key. Falls back to `api_key` in the config file. |
|
|
165
|
+
| `DEBUGAI_API_BASE` | DebugAI production | Override for self-hosted or staging setups. Falls back to `api_base` in the config file. |
|
|
128
166
|
| `DEBUGAI_TIMEOUT_MS` | `150000` | Per-request deadline. Deep analyses can take 30-90s. |
|
|
167
|
+
| `DEBUGAI_CONFIG_PATH` | `~/.debugai/config.json` | Alternate config file location. Rarely needed. |
|
|
129
168
|
|
|
130
169
|
## Limits and honesty
|
|
131
170
|
|
|
@@ -140,7 +179,8 @@ Example, in Claude Code:
|
|
|
140
179
|
## Troubleshooting
|
|
141
180
|
|
|
142
181
|
- **"authentication failed"**: key missing or wrong. Check the `env` block in
|
|
143
|
-
your client config
|
|
182
|
+
your client config or `~/.debugai/config.json`, restart the client. Keys
|
|
183
|
+
start with `dbg_`.
|
|
144
184
|
- **Nothing happens on `npx @debugai/mcp`**: correct. It's a stdio server that
|
|
145
185
|
waits for an MCP client to speak first. Run `npx @debugai/mcp --help` to
|
|
146
186
|
verify the install.
|
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/config.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface FileConfig {
|
|
2
|
+
apiKey?: string;
|
|
3
|
+
apiBase?: string;
|
|
4
|
+
}
|
|
5
|
+
export interface ResolvedSettings {
|
|
6
|
+
apiKey: string;
|
|
7
|
+
apiBase: string;
|
|
8
|
+
/** Where the key came from — used only for the startup log line. */
|
|
9
|
+
keySource: 'env' | 'file' | 'none';
|
|
10
|
+
}
|
|
11
|
+
export declare function configPath(env?: NodeJS.ProcessEnv): string;
|
|
12
|
+
export declare function loadFileConfig(env?: NodeJS.ProcessEnv, warn?: (msg: string) => void): FileConfig;
|
|
13
|
+
export declare function resolveSettings(defaultApiBase: string, env?: NodeJS.ProcessEnv, warn?: (msg: string) => void): ResolvedSettings;
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Optional on-disk config so a user can set their key once instead of
|
|
2
|
+
// repeating it in every MCP client's env block:
|
|
3
|
+
//
|
|
4
|
+
// ~/.debugai/config.json { "api_key": "dbg_...", "api_base": "..." }
|
|
5
|
+
//
|
|
6
|
+
// Environment variables always win over the file. DEBUGAI_CONFIG_PATH
|
|
7
|
+
// overrides the file location (tests point it at a temp dir; users normally
|
|
8
|
+
// never set it).
|
|
9
|
+
import { readFileSync } from 'node:fs';
|
|
10
|
+
import { homedir } from 'node:os';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
export function configPath(env = process.env) {
|
|
13
|
+
const override = (env.DEBUGAI_CONFIG_PATH ?? '').trim();
|
|
14
|
+
return override || join(homedir(), '.debugai', 'config.json');
|
|
15
|
+
}
|
|
16
|
+
export function loadFileConfig(env = process.env, warn = (msg) => console.error(msg)) {
|
|
17
|
+
const path = configPath(env);
|
|
18
|
+
let raw;
|
|
19
|
+
try {
|
|
20
|
+
raw = readFileSync(path, 'utf8');
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return {}; // no config file is the normal case — stay silent
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const parsed = JSON.parse(raw);
|
|
27
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
28
|
+
warn(`[debugai-mcp] ignoring ${path}: expected a JSON object`);
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
const record = parsed;
|
|
32
|
+
const str = (v) => typeof v === 'string' && v.trim() !== '' ? v.trim() : undefined;
|
|
33
|
+
return { apiKey: str(record.api_key), apiBase: str(record.api_base) };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
warn(`[debugai-mcp] ignoring ${path}: malformed JSON`);
|
|
37
|
+
return {};
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function resolveSettings(defaultApiBase, env = process.env, warn = (msg) => console.error(msg)) {
|
|
41
|
+
const file = loadFileConfig(env, warn);
|
|
42
|
+
const envKey = (env.DEBUGAI_API_KEY ?? '').trim();
|
|
43
|
+
const apiKey = envKey || file.apiKey || '';
|
|
44
|
+
const keySource = envKey ? 'env' : file.apiKey ? 'file' : 'none';
|
|
45
|
+
const envBase = (env.DEBUGAI_API_BASE ?? '').trim();
|
|
46
|
+
const apiBase = (envBase || file.apiBase || defaultApiBase).replace(/\/+$/, '');
|
|
47
|
+
return { apiKey, apiBase, keySource };
|
|
48
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { dirname, join } from 'node:path';
|
|
|
5
5
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
6
6
|
import { createServer } from './server.js';
|
|
7
7
|
import { DEFAULT_TIMEOUT_MS } from './backend.js';
|
|
8
|
+
import { configPath, resolveSettings } from './config.js';
|
|
8
9
|
const DEFAULT_API_BASE = 'https://debugai-mvp-production.up.railway.app/api';
|
|
9
10
|
function packageVersion() {
|
|
10
11
|
try {
|
|
@@ -18,8 +19,11 @@ function packageVersion() {
|
|
|
18
19
|
const VERSION = packageVersion();
|
|
19
20
|
const HELP = `debugai-mcp v${VERSION} — DebugAI MCP server (stdio)
|
|
20
21
|
|
|
21
|
-
Exposes
|
|
22
|
-
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
|
|
23
27
|
|
|
24
28
|
Usage:
|
|
25
29
|
npx @debugai/mcp # start the server (stdio transport)
|
|
@@ -27,10 +31,14 @@ Usage:
|
|
|
27
31
|
npx @debugai/mcp --help
|
|
28
32
|
|
|
29
33
|
Environment:
|
|
30
|
-
DEBUGAI_API_KEY
|
|
34
|
+
DEBUGAI_API_KEY your API key (dbg_...) from https://debugai.io/dashboard
|
|
31
35
|
DEBUGAI_API_BASE optional — API base URL (default: DebugAI production)
|
|
32
36
|
DEBUGAI_TIMEOUT_MS optional — per-request deadline in ms (default: ${DEFAULT_TIMEOUT_MS})
|
|
33
37
|
|
|
38
|
+
Config file (set the key once, every MCP client picks it up):
|
|
39
|
+
~/.debugai/config.json {"api_key": "dbg_..."}
|
|
40
|
+
Env vars win over the file. api_base is also accepted.
|
|
41
|
+
|
|
34
42
|
This is a stdio MCP server: it is meant to be launched BY an MCP client
|
|
35
43
|
(Claude Desktop, Claude Code, Cursor, Zed, ...), not run interactively.
|
|
36
44
|
Config snippets: https://www.npmjs.com/package/@debugai/mcp
|
|
@@ -51,13 +59,13 @@ function main() {
|
|
|
51
59
|
process.exitCode = 1;
|
|
52
60
|
return;
|
|
53
61
|
}
|
|
54
|
-
const apiKey
|
|
55
|
-
const apiBase = (process.env.DEBUGAI_API_BASE ?? DEFAULT_API_BASE).trim().replace(/\/+$/, '');
|
|
62
|
+
const { apiKey, apiBase, keySource } = resolveSettings(DEFAULT_API_BASE);
|
|
56
63
|
const rawTimeout = Number(process.env.DEBUGAI_TIMEOUT_MS);
|
|
57
64
|
const timeoutMs = Number.isFinite(rawTimeout) && rawTimeout > 0 ? rawTimeout : DEFAULT_TIMEOUT_MS;
|
|
58
65
|
if (!apiKey) {
|
|
59
|
-
console.error('[debugai-mcp]
|
|
60
|
-
'Get a key at https://debugai.io/dashboard
|
|
66
|
+
console.error('[debugai-mcp] no API key found — tools will return auth errors. ' +
|
|
67
|
+
'Get a key at https://debugai.io/dashboard, then either set DEBUGAI_API_KEY in your ' +
|
|
68
|
+
`MCP client config or write it once to ${configPath()} as {"api_key": "dbg_..."}.`);
|
|
61
69
|
}
|
|
62
70
|
else if (!apiKey.startsWith('dbg_')) {
|
|
63
71
|
console.error('[debugai-mcp] warning: DEBUGAI_API_KEY does not look like a DebugAI key (expected dbg_ prefix).');
|
|
@@ -70,7 +78,7 @@ function main() {
|
|
|
70
78
|
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
71
79
|
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
72
80
|
const transport = new StdioServerTransport();
|
|
73
|
-
server.connect(transport).then(() => console.error(`[debugai-mcp] v${VERSION} connected on stdio (api: ${apiBase})`), (err) => {
|
|
81
|
+
server.connect(transport).then(() => console.error(`[debugai-mcp] v${VERSION} connected on stdio (api: ${apiBase}, key: ${keySource})`), (err) => {
|
|
74
82
|
console.error('[debugai-mcp] fatal:', err);
|
|
75
83
|
process.exit(1);
|
|
76
84
|
});
|
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",
|