toolstile 0.4.2 → 0.5.1
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 +19 -3
- package/dist/agents.js +3 -1
- package/dist/cli.js +2 -2
- package/dist/fix.js +3 -1
- package/dist/report.js +1 -1
- package/dist/sources.d.ts +2 -0
- package/dist/sources.js +9 -1
- package/dist/types.d.ts +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -7,7 +7,7 @@ npx toolstile audit
|
|
|
7
7
|
```
|
|
8
8
|
|
|
9
9
|
It reads the configs of Claude Code, Claude Desktop, Cursor, Codex, VS Code,
|
|
10
|
-
Windsurf, Gemini CLI and Cline and grades them for:
|
|
10
|
+
Windsurf, Devin Desktop, Gemini CLI and Cline and grades them for:
|
|
11
11
|
|
|
12
12
|
| Check | Severity |
|
|
13
13
|
| --- | --- |
|
|
@@ -73,7 +73,7 @@ you pick which to apply (space toggles, enter applies):
|
|
|
73
73
|
| Rule that approves a whole server, or `enableAllProjectMcpServers` | Removes it. These start unselected, since the agent then asks before each tool. |
|
|
74
74
|
|
|
75
75
|
Everything else (secrets in arguments or URLs, Claude Desktop and Cline, which
|
|
76
|
-
cannot read variables, Windows, `http://` servers, Docker images, broad
|
|
76
|
+
cannot read variables, Devin Desktop, Windows, `http://` servers, Docker images, broad
|
|
77
77
|
filesystem roots and auto-approve lists) is listed with what to change by hand.
|
|
78
78
|
|
|
79
79
|
Each edited file is checked before it is written: it must parse to exactly the
|
|
@@ -160,6 +160,7 @@ values) and prints a ready-to-post link. To post the text report instead, add
|
|
|
160
160
|
| Codex | `$CODEX_HOME/config.toml` (default `~/.codex`) | `.codex/config.toml` |
|
|
161
161
|
| VS Code | user `settings.json` and `mcp.json` | `.vscode/mcp.json` |
|
|
162
162
|
| Windsurf | `~/.codeium/windsurf/mcp_config.json` | |
|
|
163
|
+
| Devin Desktop | `~/.config/devin/mcp_config.json` (`%APPDATA%\devin` on Windows) | `.devin/mcp_config.json`, `.devin/mcp_config.local.json` |
|
|
163
164
|
| Gemini CLI | `~/.gemini/settings.json` | `.gemini/settings.json` |
|
|
164
165
|
| Cline | `cline_mcp_settings.json` in VS Code or Cursor storage | |
|
|
165
166
|
|
|
@@ -182,7 +183,7 @@ reach the machine you are signing in, so it works over SSH, in containers and on
|
|
|
182
183
|
servers without a browser.
|
|
183
184
|
|
|
184
185
|
`mcp setup` lists the agents it finds (Claude Code, Claude Desktop, Cursor,
|
|
185
|
-
Codex, VS Code, Windsurf, Gemini CLI, Cline) and lets you turn each one on or
|
|
186
|
+
Codex, VS Code, Windsurf, Devin Desktop, Gemini CLI, Cline) and lets you turn each one on or
|
|
186
187
|
off. Run it again any time to change the selection. Each enabled agent appears
|
|
187
188
|
under this machine in the Toolstile dashboard with its own access, so you can
|
|
188
189
|
grant, limit or revoke it there; then choose which MCP servers it can use.
|
|
@@ -243,6 +244,21 @@ works on its own and does not require a Toolstile account.
|
|
|
243
244
|
|
|
244
245
|
Questions and reports: support@toolstile.com.
|
|
245
246
|
|
|
247
|
+
## Development
|
|
248
|
+
|
|
249
|
+
Development uses [Vite+](https://viteplus.dev). Its `vp env` shims pick Node.js
|
|
250
|
+
from `.node-version` and pnpm from `packageManager`, so install Vite+, then run:
|
|
251
|
+
|
|
252
|
+
```sh
|
|
253
|
+
pnpm install
|
|
254
|
+
pnpm typecheck
|
|
255
|
+
pnpm test # builds dist, then runs the tests with vp test
|
|
256
|
+
pnpm build
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
Development needs Node.js 22.18+, 24.11+ or 26+. The published CLI still runs on
|
|
260
|
+
Node.js 18 and later.
|
|
261
|
+
|
|
246
262
|
## Releases
|
|
247
263
|
|
|
248
264
|
The npm package is published by [GitHub Actions](.github/workflows/publish.yml)
|
package/dist/agents.js
CHANGED
|
@@ -4,7 +4,7 @@ import { chmodSync, existsSync, mkdirSync, readFileSync, realpathSync, renameSyn
|
|
|
4
4
|
import { dirname, join } from "node:path";
|
|
5
5
|
import { parse as parseToml } from "smol-toml";
|
|
6
6
|
import { parseJsonc } from "./parse.js";
|
|
7
|
-
import { appData, CLIENT_NAMES } from "./sources.js";
|
|
7
|
+
import { appData, CLIENT_NAMES, devinDir } from "./sources.js";
|
|
8
8
|
/** The server name `toolstile mcp setup` adds to each agent's config. */
|
|
9
9
|
export const ENTRY = "toolstile";
|
|
10
10
|
export function targets(opts) {
|
|
@@ -21,6 +21,8 @@ export function targets(opts) {
|
|
|
21
21
|
["codex", join(codexHome, "config.toml"), "codex", codexHome],
|
|
22
22
|
["vscode", join(code, "mcp.json"), "vscode", code],
|
|
23
23
|
["windsurf", join(home, ".codeium", "windsurf", "mcp_config.json"), "mcpServers", join(home, ".codeium", "windsurf")],
|
|
24
|
+
// Devin Desktop, which replaced the Windsurf editor, reads its own user config.
|
|
25
|
+
["devin", join(devinDir(opts), "mcp_config.json"), "mcpServers", devinDir(opts)],
|
|
24
26
|
["gemini", join(home, ".gemini", "settings.json"), "mcpServers", join(home, ".gemini")],
|
|
25
27
|
["cline", join(cline, "settings", "cline_mcp_settings.json"), "mcpServers", cline],
|
|
26
28
|
];
|
package/dist/cli.js
CHANGED
|
@@ -27,7 +27,7 @@ Usage
|
|
|
27
27
|
npx toolstile audit [options]
|
|
28
28
|
|
|
29
29
|
Checks Claude Code, Claude Desktop, Cursor, Codex, VS Code, Windsurf,
|
|
30
|
-
Gemini CLI and Cline configs for plaintext secrets, credentials copied
|
|
30
|
+
Devin Desktop, Gemini CLI and Cline configs for plaintext secrets, credentials copied
|
|
31
31
|
between clients, unpinned packages, auto-approved tools, broad
|
|
32
32
|
filesystem access and unencrypted remote servers.
|
|
33
33
|
|
|
@@ -89,7 +89,7 @@ Options for setup
|
|
|
89
89
|
--workspace <slug> Workspace the agents use (default: the last one, or personal)
|
|
90
90
|
--device-name <name> Name for this machine (default: its hostname)
|
|
91
91
|
|
|
92
|
-
Agents: claude-code, claude-desktop, cursor, codex, vscode, windsurf, gemini, cline
|
|
92
|
+
Agents: claude-code, claude-desktop, cursor, codex, vscode, windsurf, devin, gemini, cline
|
|
93
93
|
`;
|
|
94
94
|
function version() {
|
|
95
95
|
const pkg = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
package/dist/fix.js
CHANGED
|
@@ -156,7 +156,9 @@ function moveSecret(sec, names) {
|
|
|
156
156
|
}
|
|
157
157
|
const ref = REFERENCE[s.client];
|
|
158
158
|
if (!ref)
|
|
159
|
-
return
|
|
159
|
+
return s.client === "devin"
|
|
160
|
+
? "--fix does not move Devin Desktop secrets yet; replace the value with an ${env:NAME} reference by hand"
|
|
161
|
+
: `${CLIENT_NAMES[s.client]} does not read environment variables in its config`;
|
|
160
162
|
if (where === "env" && key) {
|
|
161
163
|
const name = names.pick([key, `${server}_${key}`], sec.value);
|
|
162
164
|
if (!name)
|
package/dist/report.js
CHANGED
|
@@ -120,7 +120,7 @@ export function renderReport(r, home, s, redacted = false, columns = 80) {
|
|
|
120
120
|
const lines = [];
|
|
121
121
|
lines.push("", `${s.bold("toolstile audit")} ${s.dim("· local MCP config check · nothing leaves this machine")}`, "");
|
|
122
122
|
if (r.files.length === 0 && r.unreadable.length === 0) {
|
|
123
|
-
lines.push("No MCP client configs found.", s.dim("Looked for Claude Code, Claude Desktop, Cursor, Codex, VS Code, Windsurf, Gemini CLI and Cline."), "");
|
|
123
|
+
lines.push("No MCP client configs found.", s.dim("Looked for Claude Code, Claude Desktop, Cursor, Codex, VS Code, Windsurf, Devin Desktop, Gemini CLI and Cline."), "");
|
|
124
124
|
return lines.join("\n");
|
|
125
125
|
}
|
|
126
126
|
if (r.grade !== null) {
|
package/dist/sources.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface ScanOptions {
|
|
|
7
7
|
env: Record<string, string | undefined>;
|
|
8
8
|
}
|
|
9
9
|
export declare const CLIENT_NAMES: Record<ClientId, string>;
|
|
10
|
+
/** Devin Desktop's user config folder: `%APPDATA%\\devin` on Windows, `~/.config/devin` elsewhere. */
|
|
11
|
+
export declare function devinDir({ home, platform, env }: Omit<ScanOptions, "cwd">): string;
|
|
10
12
|
export declare function appData({ home, platform, env }: Omit<ScanOptions, "cwd">): string;
|
|
11
13
|
/** Appends a key to a pointer, quoting keys that are not plain identifiers. */
|
|
12
14
|
export declare function pointerJoin(base: string, key: string): string;
|
package/dist/sources.js
CHANGED
|
@@ -8,9 +8,16 @@ export const CLIENT_NAMES = {
|
|
|
8
8
|
codex: "Codex",
|
|
9
9
|
vscode: "VS Code",
|
|
10
10
|
windsurf: "Windsurf",
|
|
11
|
+
devin: "Devin Desktop",
|
|
11
12
|
gemini: "Gemini CLI",
|
|
12
13
|
cline: "Cline",
|
|
13
14
|
};
|
|
15
|
+
/** Devin Desktop's user config folder: `%APPDATA%\\devin` on Windows, `~/.config/devin` elsewhere. */
|
|
16
|
+
export function devinDir({ home, platform, env }) {
|
|
17
|
+
if (platform === "win32")
|
|
18
|
+
return join(env.APPDATA ?? join(home, "AppData", "Roaming"), "devin");
|
|
19
|
+
return join(home, ".config", "devin");
|
|
20
|
+
}
|
|
14
21
|
export function appData({ home, platform, env }) {
|
|
15
22
|
if (platform === "darwin")
|
|
16
23
|
return join(home, "Library", "Application Support");
|
|
@@ -30,6 +37,7 @@ function candidates(opts) {
|
|
|
30
37
|
{ client: "cursor", path: join(home, ".cursor", "mcp.json"), kind: "mcp" },
|
|
31
38
|
{ client: "codex", path: join(codexHome, "config.toml"), kind: "codex" },
|
|
32
39
|
{ client: "windsurf", path: join(home, ".codeium", "windsurf", "mcp_config.json"), kind: "mcp" },
|
|
40
|
+
{ client: "devin", path: join(devinDir(opts), "mcp_config.json"), kind: "mcp" },
|
|
33
41
|
{ client: "gemini", path: join(home, ".gemini", "settings.json"), kind: "mcp" },
|
|
34
42
|
];
|
|
35
43
|
for (const edition of ["Code", "Code - Insiders"]) {
|
|
@@ -38,7 +46,7 @@ function candidates(opts) {
|
|
|
38
46
|
list.push({ client: "cline", path: join(data, "Cursor", "User", ...clineSettings), kind: "mcp" });
|
|
39
47
|
if (cwd) {
|
|
40
48
|
const dir = resolve(cwd);
|
|
41
|
-
list.push({ client: "claude-code", path: join(dir, ".mcp.json"), kind: "mcp" }, { client: "claude-code", path: join(dir, ".claude", "settings.json"), kind: "claude-settings" }, { client: "claude-code", path: join(dir, ".claude", "settings.local.json"), kind: "claude-settings" }, { client: "cursor", path: join(dir, ".cursor", "mcp.json"), kind: "mcp" }, { client: "vscode", path: join(dir, ".vscode", "mcp.json"), kind: "vscode-mcp" }, { client: "gemini", path: join(dir, ".gemini", "settings.json"), kind: "mcp" }, { client: "codex", path: join(dir, ".codex", "config.toml"), kind: "codex" });
|
|
49
|
+
list.push({ client: "claude-code", path: join(dir, ".mcp.json"), kind: "mcp" }, { client: "claude-code", path: join(dir, ".claude", "settings.json"), kind: "claude-settings" }, { client: "claude-code", path: join(dir, ".claude", "settings.local.json"), kind: "claude-settings" }, { client: "cursor", path: join(dir, ".cursor", "mcp.json"), kind: "mcp" }, { client: "vscode", path: join(dir, ".vscode", "mcp.json"), kind: "vscode-mcp" }, { client: "gemini", path: join(dir, ".gemini", "settings.json"), kind: "mcp" }, { client: "codex", path: join(dir, ".codex", "config.toml"), kind: "codex" }, { client: "devin", path: join(dir, ".devin", "mcp_config.json"), kind: "mcp" }, { client: "devin", path: join(dir, ".devin", "mcp_config.local.json"), kind: "mcp" });
|
|
42
50
|
}
|
|
43
51
|
const seen = new Set();
|
|
44
52
|
return list.filter((c) => (seen.has(c.path) ? false : (seen.add(c.path), true)));
|
package/dist/types.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/** `info` findings are context only and do not affect the score. */
|
|
2
2
|
export type Severity = "high" | "medium" | "low" | "info";
|
|
3
|
-
export type ClientId = "claude-code" | "claude-desktop" | "cursor" | "codex" | "vscode" | "windsurf" | "gemini" | "cline";
|
|
3
|
+
export type ClientId = "claude-code" | "claude-desktop" | "cursor" | "codex" | "vscode" | "windsurf" | "devin" | "gemini" | "cline";
|
|
4
4
|
export interface Location {
|
|
5
5
|
/** Absolute path of the config file. */
|
|
6
6
|
file: string;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "toolstile",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Audit the MCP server configs your AI agents use for plaintext secrets, unpinned packages, auto-approved tools and duplicated setups, and connect those agents to Toolstile without putting tokens in their configs.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"mcp",
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"scripts": {
|
|
40
40
|
"build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
|
-
"test": "pnpm build &&
|
|
42
|
+
"test": "pnpm build && vp test run",
|
|
43
43
|
"prepublishOnly": "pnpm typecheck && pnpm test"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"devDependencies": {
|
|
50
50
|
"@types/node": "^22.19.0",
|
|
51
51
|
"typescript": "^5.9.3",
|
|
52
|
-
"
|
|
52
|
+
"vite-plus": "1.5.1"
|
|
53
53
|
},
|
|
54
54
|
"packageManager": "pnpm@11.20.0",
|
|
55
55
|
"optionalDependencies": {
|