@debugai/mcp 2.0.0 → 2.1.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 +114 -112
- package/dist/auth.d.ts +40 -0
- package/dist/auth.js +115 -0
- package/dist/backend.d.ts +9 -0
- package/dist/cli/clients.d.ts +36 -0
- package/dist/cli/clients.js +143 -0
- package/dist/cli/commands.d.ts +7 -0
- package/dist/cli/commands.js +360 -0
- package/dist/cli/install.d.ts +21 -0
- package/dist/cli/install.js +150 -0
- package/dist/cli/jsonc.d.ts +12 -0
- package/dist/cli/jsonc.js +115 -0
- package/dist/cli/ui.d.ts +19 -0
- package/dist/cli/ui.js +56 -0
- package/dist/config.d.ts +10 -0
- package/dist/config.js +54 -2
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +5 -0
- package/dist/deviceLink.d.ts +51 -0
- package/dist/deviceLink.js +125 -0
- package/dist/index.js +67 -23
- package/dist/server.js +29 -1
- package/dist/tools/authGate.d.ts +10 -0
- package/dist/tools/authGate.js +29 -0
- package/dist/tools/debugError.js +5 -1
- package/dist/tools/reportOutcome.js +5 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,110 +1,69 @@
|
|
|
1
1
|
# @debugai/mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
<p align="center">
|
|
4
|
+
<a href="https://www.npmjs.com/package/@debugai/mcp"><img src="https://img.shields.io/npm/v/@debugai/mcp?color=0A0A0F&label=npm" alt="npm version"></a>
|
|
5
|
+
<img src="https://img.shields.io/badge/node-%3E%3D18-0A0A0F" alt="Node 18+">
|
|
6
|
+
<img src="https://img.shields.io/badge/license-MIT-0A0A0F" alt="MIT license">
|
|
7
|
+
</p>
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
[DebugAI VS Code extension](https://marketplace.visualstudio.com/items?itemName=debugai.debugai).
|
|
9
|
-
This package is the same server, standalone. No VS Code required.
|
|
9
|
+
Give your coding agent a debugger instead of a grep loop.
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
1. Create a free account at [debugai.io](https://debugai.io) (10 debugs/day, no card).
|
|
14
|
-
2. Copy your API key (`dbg_...`) from [debugai.io/dashboard](https://debugai.io/dashboard).
|
|
15
|
-
3. Add the server to your MCP client (snippets below). Node 18+ required.
|
|
11
|
+
Your agent hands an error to `debug_error` and gets back the root cause, the exact file and line, and up to 3 ranked fixes as ready-to-apply edits. Each fix is labeled with whether a mechanical check actually passed, so the agent knows which ones were checked and which are the model's own estimate.
|
|
16
12
|
|
|
17
|
-
|
|
13
|
+
Auto-configures Claude Code, Claude Desktop, Cursor, Windsurf, Zed, Gemini CLI, and Cline. Works in any other MCP client with a manual entry. Node 18 or later.
|
|
18
14
|
|
|
19
|
-
|
|
20
|
-
`~/.debugai/config.json`:
|
|
15
|
+
## Setup
|
|
21
16
|
|
|
22
|
-
```
|
|
23
|
-
|
|
17
|
+
```bash
|
|
18
|
+
npx -y @debugai/mcp setup
|
|
24
19
|
```
|
|
25
20
|
|
|
26
|
-
|
|
27
|
-
drop the `env` block from the snippets below entirely. An explicit
|
|
28
|
-
`DEBUGAI_API_KEY` env var still wins over the file.
|
|
21
|
+
That is the whole thing. It signs you in through your browser (no key to find or copy), writes the config for every MCP client it finds on this machine, then checks that all of it actually works.
|
|
29
22
|
|
|
30
|
-
|
|
23
|
+
Restart the clients it names and your agent has the tools.
|
|
31
24
|
|
|
32
|
-
|
|
33
|
-
claude mcp add debugai --env DEBUGAI_API_KEY=dbg_your_key_here -- npx -y @debugai/mcp
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
### Claude Desktop
|
|
25
|
+
Prefer to read first? [debugai.io/start?src=npm](https://debugai.io/start?src=npm) walks the same thing per client.
|
|
37
26
|
|
|
38
|
-
|
|
27
|
+
### What that command does to your machine
|
|
39
28
|
|
|
40
|
-
|
|
41
|
-
{
|
|
42
|
-
"mcpServers": {
|
|
43
|
-
"debugai": {
|
|
44
|
-
"command": "npx",
|
|
45
|
-
"args": ["-y", "@debugai/mcp"],
|
|
46
|
-
"env": { "DEBUGAI_API_KEY": "dbg_your_key_here" }
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
```
|
|
29
|
+
Worth knowing before you run something that edits your editor config:
|
|
51
30
|
|
|
52
|
-
|
|
31
|
+
- Signs you in with a short code you confirm in the browser. Free account, 10 debugs a day, no card.
|
|
32
|
+
- Stores your key in `~/.debugai/config.json` with `0600` permissions. That is the only file that ever holds it.
|
|
33
|
+
- Adds a `debugai` entry to the config of each MCP client it detects. Every file is backed up first (`<file>.debugai-backup-<timestamp>`), every other setting in the file is preserved, and a file it cannot parse is left alone and reported instead. One caveat stated plainly: if your config contains comments, the rewrite drops them, because JSON has nowhere to put them. You get a warning before it happens and the backup still has them.
|
|
34
|
+
- Skips VS Code by default, because the [DebugAI extension](https://marketplace.visualstudio.com/items?itemName=debugai.debugai) already registers this server there and a second entry would show every tool twice. `install --client=vscode` does it anyway if you want the server without the extension.
|
|
35
|
+
- Never writes your key into a client config. Client configs get committed to repos. Keys should not.
|
|
53
36
|
|
|
54
|
-
|
|
37
|
+
Preview it without writing anything:
|
|
55
38
|
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
"mcpServers": {
|
|
59
|
-
"debugai": {
|
|
60
|
-
"command": "npx",
|
|
61
|
-
"args": ["-y", "@debugai/mcp"],
|
|
62
|
-
"env": { "DEBUGAI_API_KEY": "dbg_your_key_here" }
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
}
|
|
39
|
+
```bash
|
|
40
|
+
npx -y @debugai/mcp install --dry-run
|
|
66
41
|
```
|
|
67
42
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
`settings.json`:
|
|
43
|
+
Undo all of it:
|
|
71
44
|
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
"debugai": {
|
|
76
|
-
"command": {
|
|
77
|
-
"path": "npx",
|
|
78
|
-
"args": ["-y", "@debugai/mcp"],
|
|
79
|
-
"env": { "DEBUGAI_API_KEY": "dbg_your_key_here" }
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
}
|
|
45
|
+
```bash
|
|
46
|
+
npx -y @debugai/mcp uninstall # removes the entry from every client config
|
|
47
|
+
npx -y @debugai/mcp logout # removes the stored key
|
|
84
48
|
```
|
|
85
49
|
|
|
86
|
-
###
|
|
50
|
+
### Commands
|
|
87
51
|
|
|
88
|
-
|
|
52
|
+
| Command | What it does |
|
|
53
|
+
|---------|--------------|
|
|
54
|
+
| `setup` | `login` then `install`, then verifies. The one you want. |
|
|
55
|
+
| `login` | Browser sign-in. `--key dbg_…` to paste a key instead (CI, air-gapped boxes). `--force` to re-link. |
|
|
56
|
+
| `logout` | Removes the stored key. |
|
|
57
|
+
| `status` | Which key and account are active right now. |
|
|
58
|
+
| `install` | Writes client configs. `--list`, `--client=cursor`, `--all`, `--dry-run`, `--remove`. |
|
|
59
|
+
| `uninstall` | Removes the entry from every client config. |
|
|
60
|
+
| `doctor` | Diagnoses a broken setup: key, API reachability, per-client wiring. |
|
|
89
61
|
|
|
90
|
-
|
|
91
|
-
{
|
|
92
|
-
"mcpServers": {
|
|
93
|
-
"debugai": {
|
|
94
|
-
"command": "npx",
|
|
95
|
-
"args": ["-y", "@debugai/mcp"],
|
|
96
|
-
"env": { "DEBUGAI_API_KEY": "dbg_your_key_here" }
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
```
|
|
62
|
+
`npx -y @debugai/mcp install --list` prints every supported client, where its config lives on your OS, and whether DebugAI is already in it.
|
|
101
63
|
|
|
102
|
-
###
|
|
64
|
+
### Signing in from inside a chat
|
|
103
65
|
|
|
104
|
-
|
|
105
|
-
[DebugAI extension](https://marketplace.visualstudio.com/items?itemName=debugai.debugai)
|
|
106
|
-
registers the MCP server automatically (VS Code 1.101+) and adds one-click
|
|
107
|
-
fix apply, proactive scan, and codebase indexing on top.
|
|
66
|
+
If your agent calls a DebugAI tool before you have signed in, the tool answers with a short code and a URL instead of an error. Confirm it in the browser, tell the agent to try again, and the call goes through. No config editing, and no client restart, because the key is re-read on every call.
|
|
108
67
|
|
|
109
68
|
## The tools
|
|
110
69
|
|
|
@@ -119,18 +78,9 @@ Give it an error, get an analysis.
|
|
|
119
78
|
| `codeSnippet` | no | Code around the failing line, if the agent has it. |
|
|
120
79
|
| `filePath` | no | Path to the file that threw. |
|
|
121
80
|
|
|
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.
|
|
81
|
+
Returns the root cause, up to 3 fixes ranked by confidence, the detected framework, and whether the answer came from cache. Since 2.0 each fix also carries, where derivable: `edits` (exact old/new strings your agent's edit tool can apply directly), `unified_diff`, and `verify_with` (a syntax-level check command to run after applying). Read-only: it never touches your files. Applying a fix is your agent's call, and yours.
|
|
128
82
|
|
|
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.
|
|
83
|
+
Every fix is labeled with its verification state, and there are three of them, not two: **verified** (a mechanical check passed, currently parse and import classes), **failed check** (confidence capped hard), or **not verified** (the confidence number is the model's own estimate, nothing checked it). We label the third case instead of hiding it.
|
|
134
84
|
|
|
135
85
|
### `report_outcome`
|
|
136
86
|
|
|
@@ -143,11 +93,7 @@ Tell DebugAI whether an applied fix actually worked.
|
|
|
143
93
|
| `fixRank` | no | Which ranked fix was applied (1-3). |
|
|
144
94
|
| `newError` | no | If it failed: the error you saw after applying. |
|
|
145
95
|
|
|
146
|
-
Confirmed rank-1 fixes are remembered per project
|
|
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.
|
|
96
|
+
Confirmed rank-1 fixes are remembered per project, so the next hit on the same error starts from the confirmed fix. Failed-fix follow-ups are the feedback that improves future answers. Agents are asked to call this once per applied fix, through the same pipeline human feedback flows through in the VS Code extension.
|
|
151
97
|
|
|
152
98
|
Example, in Claude Code:
|
|
153
99
|
|
|
@@ -157,11 +103,66 @@ Example, in Claude Code:
|
|
|
157
103
|
> **Root cause:** `db.session` is used after the request context closed.
|
|
158
104
|
> **Fix 1 (94% confidence):** move the query inside the request handler...
|
|
159
105
|
|
|
106
|
+
### Making your agent reach for it
|
|
107
|
+
|
|
108
|
+
The server tells connecting agents what it is for, but a rule in your project file is the deterministic version. Add this to `CLAUDE.md`, `.cursorrules`, or whatever your agent reads:
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
On any runtime error, exception, or failing test, call the debugai
|
|
112
|
+
debug_error tool before attempting your own fix. After applying a fix,
|
|
113
|
+
call report_outcome so the project's error memory stays accurate.
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## VS Code
|
|
117
|
+
|
|
118
|
+
You do not need this package. The [DebugAI extension](https://marketplace.visualstudio.com/items?itemName=debugai.debugai) registers the MCP server automatically (VS Code 1.101+) and adds one-click fix apply, proactive scan, and codebase indexing on top. It is on [Open VSX](https://open-vsx.org/extension/debugai/debugai) too, for Cursor, Windsurf, and VSCodium.
|
|
119
|
+
|
|
120
|
+
## Manual setup
|
|
121
|
+
|
|
122
|
+
`setup` covers this, and `install --client=<id>` covers the case where a client is installed somewhere unusual. If you would still rather edit the file yourself, the entry is the same everywhere:
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"mcpServers": {
|
|
127
|
+
"debugai": {
|
|
128
|
+
"command": "npx",
|
|
129
|
+
"args": ["-y", "@debugai/mcp"]
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Where it goes:
|
|
136
|
+
|
|
137
|
+
| Client | File |
|
|
138
|
+
|--------|------|
|
|
139
|
+
| Claude Code | `~/.claude.json` (or `claude mcp add debugai -- npx -y @debugai/mcp`) |
|
|
140
|
+
| Claude Desktop | macOS `~/Library/Application Support/Claude/claude_desktop_config.json`, Windows `%APPDATA%\Claude\claude_desktop_config.json` |
|
|
141
|
+
| Cursor | `~/.cursor/mcp.json` |
|
|
142
|
+
| Windsurf | `~/.codeium/windsurf/mcp_config.json` |
|
|
143
|
+
| Gemini CLI | `~/.gemini/settings.json` |
|
|
144
|
+
| Cline | VS Code globalStorage, `saoudrizwan.claude-dev/settings/cline_mcp_settings.json` |
|
|
145
|
+
|
|
146
|
+
Zed uses a different key and a nested command:
|
|
147
|
+
|
|
148
|
+
```json
|
|
149
|
+
{
|
|
150
|
+
"context_servers": {
|
|
151
|
+
"debugai": {
|
|
152
|
+
"source": "custom",
|
|
153
|
+
"command": { "path": "npx", "args": ["-y", "@debugai/mcp"] }
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Then run `npx -y @debugai/mcp login` once to store your key. If you would rather set the key per client, `DEBUGAI_API_KEY` in that client's `env` block still works and still wins over the stored one.
|
|
160
|
+
|
|
160
161
|
## Environment variables
|
|
161
162
|
|
|
162
163
|
| Variable | Default | Description |
|
|
163
164
|
|----------|---------|-------------|
|
|
164
|
-
| `DEBUGAI_API_KEY` | (none) | Your API key.
|
|
165
|
+
| `DEBUGAI_API_KEY` | (none) | Your API key. Overrides `api_key` in the config file. |
|
|
165
166
|
| `DEBUGAI_API_BASE` | DebugAI production | Override for self-hosted or staging setups. Falls back to `api_base` in the config file. |
|
|
166
167
|
| `DEBUGAI_TIMEOUT_MS` | `150000` | Per-request deadline. Deep analyses can take 30-90s. |
|
|
167
168
|
| `DEBUGAI_CONFIG_PATH` | `~/.debugai/config.json` | Alternate config file location. Rarely needed. |
|
|
@@ -170,22 +171,23 @@ Example, in Claude Code:
|
|
|
170
171
|
|
|
171
172
|
- Free tier: 10 debugs/day. Pro ($12/mo): 1,000/mo soft cap, never hard-blocked at it.
|
|
172
173
|
- When you hit the daily cap the tool says so and stops. It will not silently retry.
|
|
173
|
-
- Simple errors route to a fast model
|
|
174
|
-
|
|
175
|
-
which one answered.
|
|
176
|
-
- Analyses run on DebugAI's servers. The error text and any snippet you pass
|
|
177
|
-
are sent there. Privacy policy: [debugai.io/privacy](https://debugai.io/privacy).
|
|
174
|
+
- Simple errors route to a fast model. Ugly cross-file ones route to a stronger one on paid tiers. The `Model:` badge in each response tells you which one answered.
|
|
175
|
+
- Analyses run on DebugAI's servers. The error text and any snippet you pass are sent there, and Claude (Anthropic) does the analysis. Privacy policy: [debugai.io/privacy](https://debugai.io/privacy?src=npm).
|
|
178
176
|
|
|
179
177
|
## Troubleshooting
|
|
180
178
|
|
|
181
|
-
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
- **
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
179
|
+
Run `npx -y @debugai/mcp doctor` first. It checks your Node version, whether a key is stored and where it came from, whether that key still authenticates against the API, the permissions on the config file, and which detected clients are missing the DebugAI entry. Most answers are in that output.
|
|
180
|
+
|
|
181
|
+
- **"authentication failed"**: the key was rotated or revoked. Run `npx -y @debugai/mcp login --force`.
|
|
182
|
+
- **Tools do not appear in the client**: the client was not restarted, or it reads a different config file. `install --list` shows which file was written.
|
|
183
|
+
- **Nothing happens on `npx @debugai/mcp`**: correct. It is a stdio server waiting for an MCP client to speak first. Use `--help` to verify the install.
|
|
184
|
+
- **Timeouts**: deep analyses can take up to 90s. If your client has its own tool timeout, raise it above that.
|
|
185
|
+
|
|
186
|
+
## Changelog
|
|
187
|
+
|
|
188
|
+
**2.1.0**: one-command setup. Browser sign-in over a device link (no key pasting), automatic client config writing with backups, `doctor` for diagnosing a broken setup, and in-conversation sign-in when an agent calls a tool before you have an account.
|
|
189
|
+
|
|
190
|
+
**2.0.0**: `report_outcome` tool, ready-to-apply `edits` per fix, and the three-state verification label.
|
|
189
191
|
|
|
190
192
|
## Development
|
|
191
193
|
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export interface AuthProviderOptions {
|
|
2
|
+
apiBase: string;
|
|
3
|
+
env?: NodeJS.ProcessEnv;
|
|
4
|
+
clientLabel?: string;
|
|
5
|
+
/** Injected in tests. */
|
|
6
|
+
now?: () => number;
|
|
7
|
+
fetchImpl?: typeof fetch;
|
|
8
|
+
}
|
|
9
|
+
export type EnsureAuth = {
|
|
10
|
+
ok: true;
|
|
11
|
+
apiKey: string;
|
|
12
|
+
} | {
|
|
13
|
+
ok: false;
|
|
14
|
+
reason: 'link_pending';
|
|
15
|
+
text: string;
|
|
16
|
+
userCode: string;
|
|
17
|
+
verificationUri: string;
|
|
18
|
+
} | {
|
|
19
|
+
ok: false;
|
|
20
|
+
reason: 'link_unavailable';
|
|
21
|
+
text: string;
|
|
22
|
+
};
|
|
23
|
+
export declare class AuthProvider {
|
|
24
|
+
private readonly opts;
|
|
25
|
+
private pending;
|
|
26
|
+
private pendingStartedAt;
|
|
27
|
+
private lastPollAt;
|
|
28
|
+
private readonly env;
|
|
29
|
+
private readonly now;
|
|
30
|
+
constructor(opts: AuthProviderOptions);
|
|
31
|
+
/** Env wins over file, re-read every call — see the header note. */
|
|
32
|
+
currentKey(): string;
|
|
33
|
+
/**
|
|
34
|
+
* Returns a usable key, or the exact words the agent should say to the user.
|
|
35
|
+
* Never throws: a failure to reach the link endpoint degrades to printed
|
|
36
|
+
* instructions, it does not take the tool call down with it.
|
|
37
|
+
*/
|
|
38
|
+
ensure(): Promise<EnsureAuth>;
|
|
39
|
+
private pendingResult;
|
|
40
|
+
}
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
// Live auth for the running server.
|
|
2
|
+
//
|
|
3
|
+
// Two problems this solves, both of which used to end the funnel:
|
|
4
|
+
//
|
|
5
|
+
// 1. The key used to be read ONCE at process start. A user who fixed their
|
|
6
|
+
// setup while the client was running still got auth errors until they
|
|
7
|
+
// restarted the whole MCP client. Now the key is re-read from env + the
|
|
8
|
+
// config file on every tool call, so `debugai-mcp login` in another
|
|
9
|
+
// terminal takes effect on the next call — no restart, no reconnect.
|
|
10
|
+
//
|
|
11
|
+
// 2. A user with no key at all used to get a dead-end error. Now the server
|
|
12
|
+
// starts a device link itself and hands the agent a short code plus a URL
|
|
13
|
+
// to read out. The human approves in a browser; the agent retries; the
|
|
14
|
+
// second call finds the key and does the real work. Signup happens inside
|
|
15
|
+
// the conversation instead of in a config file the user never opens.
|
|
16
|
+
import { loadFileConfig, writeFileConfig } from './config.js';
|
|
17
|
+
import { DeviceLinkError, pollDeviceLink, startDeviceLink, } from './deviceLink.js';
|
|
18
|
+
/** Minimum gap between in-session polls: one poll per tool call, at most. */
|
|
19
|
+
const MIN_POLL_GAP_MS = 3_000;
|
|
20
|
+
export class AuthProvider {
|
|
21
|
+
opts;
|
|
22
|
+
pending = null;
|
|
23
|
+
pendingStartedAt = 0;
|
|
24
|
+
lastPollAt = 0;
|
|
25
|
+
env;
|
|
26
|
+
now;
|
|
27
|
+
constructor(opts) {
|
|
28
|
+
this.opts = opts;
|
|
29
|
+
this.env = opts.env ?? process.env;
|
|
30
|
+
this.now = opts.now ?? Date.now;
|
|
31
|
+
}
|
|
32
|
+
/** Env wins over file, re-read every call — see the header note. */
|
|
33
|
+
currentKey() {
|
|
34
|
+
const envKey = (this.env.DEBUGAI_API_KEY ?? '').trim();
|
|
35
|
+
if (envKey)
|
|
36
|
+
return envKey;
|
|
37
|
+
return loadFileConfig(this.env, () => { }).apiKey ?? '';
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Returns a usable key, or the exact words the agent should say to the user.
|
|
41
|
+
* Never throws: a failure to reach the link endpoint degrades to printed
|
|
42
|
+
* instructions, it does not take the tool call down with it.
|
|
43
|
+
*/
|
|
44
|
+
async ensure() {
|
|
45
|
+
const existing = this.currentKey();
|
|
46
|
+
if (existing)
|
|
47
|
+
return { ok: true, apiKey: existing };
|
|
48
|
+
// A link already in flight — poll it once before minting another code, so
|
|
49
|
+
// an agent retrying in a loop doesn't spray fresh codes at the user.
|
|
50
|
+
if (this.pending && this.now() - this.pendingStartedAt < this.pending.expiresIn * 1000) {
|
|
51
|
+
if (this.now() - this.lastPollAt >= MIN_POLL_GAP_MS) {
|
|
52
|
+
this.lastPollAt = this.now();
|
|
53
|
+
try {
|
|
54
|
+
const result = await pollDeviceLink(this.pending.deviceCode, {
|
|
55
|
+
apiBase: this.opts.apiBase,
|
|
56
|
+
fetchImpl: this.opts.fetchImpl,
|
|
57
|
+
});
|
|
58
|
+
if (result.status === 'linked') {
|
|
59
|
+
writeFileConfig({ apiKey: result.apiKey }, this.env);
|
|
60
|
+
this.pending = null;
|
|
61
|
+
return { ok: true, apiKey: result.apiKey };
|
|
62
|
+
}
|
|
63
|
+
if (result.status === 'expired' || result.status === 'denied') {
|
|
64
|
+
this.pending = null; // fall through and mint a fresh code below
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
catch {
|
|
68
|
+
// Network hiccup mid-link: keep the pending code, repeat instructions.
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (this.pending)
|
|
72
|
+
return this.pendingResult(this.pending);
|
|
73
|
+
}
|
|
74
|
+
try {
|
|
75
|
+
const start = await startDeviceLink({
|
|
76
|
+
apiBase: this.opts.apiBase,
|
|
77
|
+
clientLabel: this.opts.clientLabel,
|
|
78
|
+
fetchImpl: this.opts.fetchImpl,
|
|
79
|
+
});
|
|
80
|
+
this.pending = start;
|
|
81
|
+
this.pendingStartedAt = this.now();
|
|
82
|
+
this.lastPollAt = this.now();
|
|
83
|
+
return this.pendingResult(start);
|
|
84
|
+
}
|
|
85
|
+
catch (err) {
|
|
86
|
+
const detail = err instanceof DeviceLinkError ? ` (${err.message})` : '';
|
|
87
|
+
return {
|
|
88
|
+
ok: false,
|
|
89
|
+
reason: 'link_unavailable',
|
|
90
|
+
text: 'DebugAI is not connected to an account yet, and the automatic link could not be ' +
|
|
91
|
+
`started${detail}. Tell the user to run this in a terminal, which signs them in and ` +
|
|
92
|
+
'stores the key for every MCP client at once:\n\n' +
|
|
93
|
+
' npx -y @debugai/mcp login\n\n' +
|
|
94
|
+
'It opens a browser, takes about 20 seconds, and needs no config editing. ' +
|
|
95
|
+
'Then retry this tool call. No client restart needed.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
pendingResult(start) {
|
|
100
|
+
return {
|
|
101
|
+
ok: false,
|
|
102
|
+
reason: 'link_pending',
|
|
103
|
+
userCode: start.userCode,
|
|
104
|
+
verificationUri: start.verificationUri,
|
|
105
|
+
text: 'DebugAI is not connected to an account yet. This takes about 20 seconds and needs no ' +
|
|
106
|
+
'config file editing.\n\n' +
|
|
107
|
+
'Tell the user, verbatim:\n' +
|
|
108
|
+
` 1. Open ${start.verificationUriComplete}\n` +
|
|
109
|
+
` 2. Sign in (free, 10 debugs/day, no card) and confirm the code ${start.userCode}\n\n` +
|
|
110
|
+
'Then call this tool again with the same arguments. The key is picked up automatically ' +
|
|
111
|
+
'on the next call. The MCP client does NOT need restarting. ' +
|
|
112
|
+
`The code expires in ${Math.round(start.expiresIn / 60)} minutes.`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
}
|
package/dist/backend.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AuthProvider } from './auth.js';
|
|
1
2
|
export interface DebugRequest {
|
|
2
3
|
error_message: string;
|
|
3
4
|
code_snippet?: string;
|
|
@@ -54,11 +55,19 @@ export interface OutcomeResponse {
|
|
|
54
55
|
success: boolean;
|
|
55
56
|
}
|
|
56
57
|
export interface BackendConfig {
|
|
58
|
+
/** Key resolved at process start. Present for direct/test use; live calls prefer `auth`. */
|
|
57
59
|
apiKey: string;
|
|
58
60
|
apiBase: string;
|
|
59
61
|
version: string;
|
|
60
62
|
/** Whole-request deadline in ms. Claude analysis runs 30-90s; nginx cuts at 120s. */
|
|
61
63
|
timeoutMs?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Live auth. When set, tools resolve the key through it on every call (so a
|
|
66
|
+
* key added while the client is running works without a restart) and can
|
|
67
|
+
* start a browser sign-in mid-conversation. Absent in unit tests, which pass
|
|
68
|
+
* a fixed apiKey.
|
|
69
|
+
*/
|
|
70
|
+
auth?: AuthProvider;
|
|
62
71
|
}
|
|
63
72
|
export interface BackendError extends Error {
|
|
64
73
|
status: number;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type ConfigShape = 'mcpServers' | 'context_servers' | 'vscode_servers';
|
|
2
|
+
export interface McpClient {
|
|
3
|
+
id: string;
|
|
4
|
+
label: string;
|
|
5
|
+
shape: ConfigShape;
|
|
6
|
+
/** Absolute path to the config file, or null when this OS isn't supported. */
|
|
7
|
+
configPath: string | null;
|
|
8
|
+
/** Directories that prove the app is installed even before any MCP config exists. */
|
|
9
|
+
probes: string[];
|
|
10
|
+
/** What the user must do after we write the file. */
|
|
11
|
+
afterInstall: string;
|
|
12
|
+
/**
|
|
13
|
+
* True for clients `install` skips unless named explicitly — currently only
|
|
14
|
+
* VS Code, where the DebugAI extension already registers the server and a
|
|
15
|
+
* second manual entry would show the same tools twice.
|
|
16
|
+
*/
|
|
17
|
+
optIn?: boolean;
|
|
18
|
+
note?: string;
|
|
19
|
+
}
|
|
20
|
+
export declare function knownClients(env?: NodeJS.ProcessEnv): McpClient[];
|
|
21
|
+
export declare function findClient(id: string, env?: NodeJS.ProcessEnv): McpClient | undefined;
|
|
22
|
+
/** A client counts as present when its config file OR its app directory exists. */
|
|
23
|
+
export declare function isDetected(client: McpClient): boolean;
|
|
24
|
+
export declare function detectedClients(env?: NodeJS.ProcessEnv): McpClient[];
|
|
25
|
+
/**
|
|
26
|
+
* The server entry itself. `npx -y` is deliberate: it self-updates on each
|
|
27
|
+
* launch and needs no global install, which is the only variant that works
|
|
28
|
+
* identically on a laptop, a devcontainer, and CI.
|
|
29
|
+
*
|
|
30
|
+
* No `env` block, ever — the key lives in ~/.debugai/config.json (see
|
|
31
|
+
* config.ts). Client configs get committed to repos; keys should not.
|
|
32
|
+
*/
|
|
33
|
+
export declare function serverEntry(client: McpClient): Record<string, unknown>;
|
|
34
|
+
/** Top-level key in that client's config file where servers are listed. */
|
|
35
|
+
export declare function serversKey(shape: ConfigShape): string;
|
|
36
|
+
export declare const SERVER_NAME = "debugai";
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// Where every MCP client keeps its config, and what shape it wants.
|
|
2
|
+
//
|
|
3
|
+
// This registry is the thing that replaces "copy this JSON blob into the
|
|
4
|
+
// right file" in the README. Adding a client = one entry here; the install
|
|
5
|
+
// command, doctor, and `--print` output all read from it.
|
|
6
|
+
//
|
|
7
|
+
// Two config shapes exist in the wild:
|
|
8
|
+
// mcpServers { command, args, env? } (most clients)
|
|
9
|
+
// context_servers { source, command: { path, args, env? } } (Zed)
|
|
10
|
+
// servers { type: "stdio", command, args } (VS Code native)
|
|
11
|
+
import { existsSync } from 'node:fs';
|
|
12
|
+
import { homedir, platform } from 'node:os';
|
|
13
|
+
import { join } from 'node:path';
|
|
14
|
+
function appDataDir(env) {
|
|
15
|
+
const home = homedir();
|
|
16
|
+
if (platform() === 'win32')
|
|
17
|
+
return env.APPDATA ?? join(home, 'AppData', 'Roaming');
|
|
18
|
+
if (platform() === 'darwin')
|
|
19
|
+
return join(home, 'Library', 'Application Support');
|
|
20
|
+
return env.XDG_CONFIG_HOME ?? join(home, '.config');
|
|
21
|
+
}
|
|
22
|
+
/** VS Code's per-user directory — Cline stores its MCP config under it too. */
|
|
23
|
+
function vscodeUserDir(env) {
|
|
24
|
+
return join(appDataDir(env), 'Code', 'User');
|
|
25
|
+
}
|
|
26
|
+
export function knownClients(env = process.env) {
|
|
27
|
+
const home = homedir();
|
|
28
|
+
const appData = appDataDir(env);
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
id: 'claude-code',
|
|
32
|
+
label: 'Claude Code',
|
|
33
|
+
shape: 'mcpServers',
|
|
34
|
+
configPath: join(home, '.claude.json'),
|
|
35
|
+
probes: [join(home, '.claude'), join(home, '.claude.json')],
|
|
36
|
+
afterInstall: 'Start a new Claude Code session (or run /mcp to reconnect).',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
id: 'claude-desktop',
|
|
40
|
+
label: 'Claude Desktop',
|
|
41
|
+
shape: 'mcpServers',
|
|
42
|
+
configPath: join(appData, 'Claude', 'claude_desktop_config.json'),
|
|
43
|
+
probes: [join(appData, 'Claude')],
|
|
44
|
+
afterInstall: 'Quit Claude Desktop completely and reopen it (closing the window is not enough).',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: 'cursor',
|
|
48
|
+
label: 'Cursor',
|
|
49
|
+
shape: 'mcpServers',
|
|
50
|
+
configPath: join(home, '.cursor', 'mcp.json'),
|
|
51
|
+
probes: [join(home, '.cursor')],
|
|
52
|
+
afterInstall: 'Cursor picks this up on its own — check Settings, MCP for a green dot.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
id: 'windsurf',
|
|
56
|
+
label: 'Windsurf',
|
|
57
|
+
shape: 'mcpServers',
|
|
58
|
+
configPath: join(home, '.codeium', 'windsurf', 'mcp_config.json'),
|
|
59
|
+
probes: [join(home, '.codeium', 'windsurf'), join(home, '.codeium')],
|
|
60
|
+
afterInstall: 'Open Windsurf, Settings, MCP and hit refresh.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'zed',
|
|
64
|
+
label: 'Zed',
|
|
65
|
+
shape: 'context_servers',
|
|
66
|
+
configPath: platform() === 'win32'
|
|
67
|
+
? join(appData, 'Zed', 'settings.json')
|
|
68
|
+
: join(env.XDG_CONFIG_HOME ?? join(home, '.config'), 'zed', 'settings.json'),
|
|
69
|
+
probes: [
|
|
70
|
+
join(env.XDG_CONFIG_HOME ?? join(home, '.config'), 'zed'),
|
|
71
|
+
join(appData, 'Zed'),
|
|
72
|
+
],
|
|
73
|
+
afterInstall: 'Zed reloads settings on save — the server appears in the agent panel.',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'gemini-cli',
|
|
77
|
+
label: 'Gemini CLI',
|
|
78
|
+
shape: 'mcpServers',
|
|
79
|
+
configPath: join(home, '.gemini', 'settings.json'),
|
|
80
|
+
probes: [join(home, '.gemini')],
|
|
81
|
+
afterInstall: 'Restart the Gemini CLI session.',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: 'cline',
|
|
85
|
+
label: 'Cline (VS Code)',
|
|
86
|
+
shape: 'mcpServers',
|
|
87
|
+
configPath: join(vscodeUserDir(env), 'globalStorage', 'saoudrizwan.claude-dev', 'settings', 'cline_mcp_settings.json'),
|
|
88
|
+
probes: [join(vscodeUserDir(env), 'globalStorage', 'saoudrizwan.claude-dev')],
|
|
89
|
+
afterInstall: 'Open the Cline panel, MCP Servers — it reconnects without a VS Code restart.',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'vscode',
|
|
93
|
+
label: 'VS Code (native MCP)',
|
|
94
|
+
shape: 'vscode_servers',
|
|
95
|
+
configPath: join(vscodeUserDir(env), 'mcp.json'),
|
|
96
|
+
probes: [vscodeUserDir(env)],
|
|
97
|
+
optIn: true,
|
|
98
|
+
note: 'The DebugAI VS Code extension already registers this server (VS Code 1.101+), '
|
|
99
|
+
+ 'plus one-click fix apply and proactive scan. Only install here if you do not want the extension.',
|
|
100
|
+
afterInstall: 'Run "MCP: List Servers" from the command palette to confirm.',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
}
|
|
104
|
+
export function findClient(id, env = process.env) {
|
|
105
|
+
return knownClients(env).find((c) => c.id === id.toLowerCase());
|
|
106
|
+
}
|
|
107
|
+
/** A client counts as present when its config file OR its app directory exists. */
|
|
108
|
+
export function isDetected(client) {
|
|
109
|
+
if (client.configPath && existsSync(client.configPath))
|
|
110
|
+
return true;
|
|
111
|
+
return client.probes.some((p) => existsSync(p));
|
|
112
|
+
}
|
|
113
|
+
export function detectedClients(env = process.env) {
|
|
114
|
+
return knownClients(env).filter(isDetected);
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* The server entry itself. `npx -y` is deliberate: it self-updates on each
|
|
118
|
+
* launch and needs no global install, which is the only variant that works
|
|
119
|
+
* identically on a laptop, a devcontainer, and CI.
|
|
120
|
+
*
|
|
121
|
+
* No `env` block, ever — the key lives in ~/.debugai/config.json (see
|
|
122
|
+
* config.ts). Client configs get committed to repos; keys should not.
|
|
123
|
+
*/
|
|
124
|
+
export function serverEntry(client) {
|
|
125
|
+
const command = 'npx';
|
|
126
|
+
const args = ['-y', '@debugai/mcp'];
|
|
127
|
+
if (client.shape === 'context_servers') {
|
|
128
|
+
return { source: 'custom', command: { path: command, args } };
|
|
129
|
+
}
|
|
130
|
+
if (client.shape === 'vscode_servers') {
|
|
131
|
+
return { type: 'stdio', command, args };
|
|
132
|
+
}
|
|
133
|
+
return { command, args };
|
|
134
|
+
}
|
|
135
|
+
/** Top-level key in that client's config file where servers are listed. */
|
|
136
|
+
export function serversKey(shape) {
|
|
137
|
+
if (shape === 'context_servers')
|
|
138
|
+
return 'context_servers';
|
|
139
|
+
if (shape === 'vscode_servers')
|
|
140
|
+
return 'servers';
|
|
141
|
+
return 'mcpServers';
|
|
142
|
+
}
|
|
143
|
+
export const SERVER_NAME = 'debugai';
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function cmdLogin(argv: string[], env?: NodeJS.ProcessEnv): Promise<number>;
|
|
2
|
+
export declare function cmdLogout(_argv: string[], env?: NodeJS.ProcessEnv): number;
|
|
3
|
+
export declare function cmdStatus(_argv: string[], env?: NodeJS.ProcessEnv): Promise<number>;
|
|
4
|
+
export declare function cmdInstall(argv: string[], env?: NodeJS.ProcessEnv): number;
|
|
5
|
+
export declare function cmdUninstall(argv: string[], env?: NodeJS.ProcessEnv): number;
|
|
6
|
+
export declare function cmdDoctor(_argv: string[], env?: NodeJS.ProcessEnv): Promise<number>;
|
|
7
|
+
export declare function cmdSetup(argv: string[], env?: NodeJS.ProcessEnv): Promise<number>;
|