sentinel-ir-mcp 0.4.6 → 0.5.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 +34 -18
- package/bin/sentinel-ir-mcp.js +1 -1
- package/package.json +4 -3
- package/src/analyze.js +26 -4
- package/src/clients.js +1 -1
- package/src/doctor.js +2 -2
- package/src/paths.js +2 -1
- package/src/server.js +15 -15
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
MCP server for [`sentinel-ir`](https://www.npmjs.com/package/sentinel-ir):
|
|
4
4
|
it lets AI agents in Claude Desktop, Cursor, Windsurf or any other
|
|
5
5
|
[Model Context Protocol](https://modelcontextprotocol.io) client read a
|
|
6
|
-
compact **structural IR** of JavaScript, TypeScript and
|
|
6
|
+
compact **structural IR** of JavaScript, TypeScript, Python and Rust files instead of the raw source.
|
|
7
7
|
|
|
8
8
|
- npm: <https://www.npmjs.com/package/sentinel-ir-mcp>
|
|
9
9
|
- GitLab (main repository): <https://gitlab.com/jakubrojicek11/sentinel-ir-mcp>
|
|
@@ -18,15 +18,28 @@ hundred characters. The agent can triage a folder first and then read the raw
|
|
|
18
18
|
source only of the files that matter.
|
|
19
19
|
|
|
20
20
|
**Supported:** JavaScript (`.js`, `.mjs`, `.cjs`), TypeScript (`.ts`, `.mts`,
|
|
21
|
-
`.cts`, `.tsx`; since 0.2.0)
|
|
21
|
+
`.cts`, `.tsx`; since 0.2.0), Python (`.py`, `.pyi`; since 0.3.0) and Rust
|
|
22
|
+
(`.rs`; since 0.5.0). The
|
|
22
23
|
language is picked from the file extension; `analyze_code` also takes `target`
|
|
23
|
-
or an explicit `language` (`javascript` | `typescript` | `tsx` | `python`). Every result carries `language` and
|
|
24
|
+
or an explicit `language` (`javascript` | `typescript` | `tsx` | `python` | `rust`). Every result carries `language` and
|
|
24
25
|
`analysisStatus`; a file that does not parse cleanly (including JSX in a `.ts`
|
|
25
26
|
file) is `"incomplete"`, and a low risk on an incomplete result is not evidence
|
|
26
27
|
of safety. Other languages are not supported yet. What is detected in Python
|
|
27
|
-
(and what is not) is listed in the
|
|
28
|
+
and Rust (and what is not) is listed in the
|
|
28
29
|
[sentinel-ir README](https://gitlab.com/jakubrojicek11/sentinel-ir#python).
|
|
29
30
|
|
|
31
|
+
**0.5.0** reads Rust (`.rs`) with `sentinel-ir` 0.5.2: `unsafe` and FFI,
|
|
32
|
+
`std::process::Command`, file deletes and writes, HTTP clients and routes
|
|
33
|
+
(`axum`, `actix-web`, ...). A `build.rs` or procedural macro that touches
|
|
34
|
+
files, the network or secret environment variables is at least `high`,
|
|
35
|
+
because it runs on your machine during `cargo build`.
|
|
36
|
+
|
|
37
|
+
- `analyze_directory` skips Cargo's build folder: a `target` folder with
|
|
38
|
+
Cargo's `CACHEDIR.TAG` file, listed in `skipped.cargoTarget`. A plain folder
|
|
39
|
+
named `target` (e.g. in a JS project) is still scanned.
|
|
40
|
+
- `setup` writes `sentinel-ir-mcp@0.5`; `doctor` asks entries pinned to 0.4
|
|
41
|
+
or older to run `setup` again.
|
|
42
|
+
|
|
30
43
|
**0.4.6** is listed in the official
|
|
31
44
|
[MCP Registry](https://registry.modelcontextprotocol.io) as
|
|
32
45
|
`io.github.jakcmencz/sentinel-ir-mcp`, so clients that browse the registry
|
|
@@ -41,7 +54,7 @@ can check later which version of a file the agent was shown (see
|
|
|
41
54
|
- **One question in `setup`:** *"Keep a log of what the agent asks for (file
|
|
42
55
|
names and results, never your code)? [y/N]"*. Or `setup --audit-log`;
|
|
43
56
|
`--no-audit-log` turns it off. Running `setup` again keeps the setting.
|
|
44
|
-
- **One command to read it:** `npx -y sentinel-ir-mcp@0.
|
|
57
|
+
- **One command to read it:** `npx -y sentinel-ir-mcp@0.5 log` prints the last
|
|
45
58
|
50 entries as plain lines, `--all` everything, `--json` for scripts:
|
|
46
59
|
|
|
47
60
|
```text
|
|
@@ -170,9 +183,9 @@ nothing to compile (the parser is WebAssembly).
|
|
|
170
183
|
Run it in your project folder:
|
|
171
184
|
|
|
172
185
|
```bash
|
|
173
|
-
npx -y sentinel-ir-mcp@0.
|
|
174
|
-
npx -y sentinel-ir-mcp@0.
|
|
175
|
-
npx -y sentinel-ir-mcp@0.
|
|
186
|
+
npx -y sentinel-ir-mcp@0.5 setup --client cursor # or windsurf, claude-desktop, vscode, claude-code
|
|
187
|
+
npx -y sentinel-ir-mcp@0.5 doctor --client cursor # after restarting the client
|
|
188
|
+
npx -y sentinel-ir-mcp@0.5 guide # optional: text for AGENTS.md / client rules
|
|
176
189
|
```
|
|
177
190
|
|
|
178
191
|
`setup` (without `--client` it asks which client):
|
|
@@ -186,7 +199,7 @@ npx -y sentinel-ir-mcp@0.4 guide # optional: text for AGENTS
|
|
|
186
199
|
touched;
|
|
187
200
|
- gives the agent one folder (`--project`, default: the current folder) and
|
|
188
201
|
refuses your home folder or a whole disk;
|
|
189
|
-
- writes `sentinel-ir-mcp@0.
|
|
202
|
+
- writes `sentinel-ir-mcp@0.5`, so clients get 0.5.x fixes but no unexpected
|
|
190
203
|
new features;
|
|
191
204
|
- asks which files or folders the agent must not read (`--exclude <name>`,
|
|
192
205
|
repeatable, skips the question; see [Excluding files](#excluding-files));
|
|
@@ -201,7 +214,7 @@ npx -y sentinel-ir-mcp@0.4 guide # optional: text for AGENTS
|
|
|
201
214
|
| `vscode`, `claude-code` | nothing; prints the command to run |
|
|
202
215
|
|
|
203
216
|
On Windows the entry is `"command": "cmd", "args": ["/c", "npx", "-y",
|
|
204
|
-
"sentinel-ir-mcp@0.
|
|
217
|
+
"sentinel-ir-mcp@0.5", "<folder>"]`: there `npx` is `npx.cmd`, and starting it
|
|
205
218
|
without a shell failed with `ENOENT` on a real Windows runner. For VS Code and
|
|
206
219
|
Claude Code, `setup` prints a PowerShell and a Command Prompt version of the
|
|
207
220
|
command. What was measured on which system: [COMPATIBILITY.md](COMPATIBILITY.md).
|
|
@@ -281,7 +294,7 @@ Settings → Developer → Edit Config opens `claude_desktop_config.json`
|
|
|
281
294
|
"mcpServers": {
|
|
282
295
|
"sentinel-ir": {
|
|
283
296
|
"command": "npx",
|
|
284
|
-
"args": ["-y", "sentinel-ir-mcp@0.
|
|
297
|
+
"args": ["-y", "sentinel-ir-mcp@0.5", "/Users/you/projects/my-app"]
|
|
285
298
|
}
|
|
286
299
|
}
|
|
287
300
|
}
|
|
@@ -300,7 +313,7 @@ project entry has to be switched on once in Cursor Settings → Customize → MC
|
|
|
300
313
|
"mcpServers": {
|
|
301
314
|
"sentinel-ir": {
|
|
302
315
|
"command": "npx",
|
|
303
|
-
"args": ["-y", "sentinel-ir-mcp@0.
|
|
316
|
+
"args": ["-y", "sentinel-ir-mcp@0.5", "${workspaceFolder}"]
|
|
304
317
|
}
|
|
305
318
|
}
|
|
306
319
|
}
|
|
@@ -316,8 +329,8 @@ with your project's absolute path.
|
|
|
316
329
|
#### Other clients
|
|
317
330
|
|
|
318
331
|
Any MCP client that starts stdio servers works with
|
|
319
|
-
`command: "npx"`, `args: ["-y", "sentinel-ir-mcp@0.
|
|
320
|
-
(on Windows: `command: "cmd"`, `args: ["/c", "npx", "-y", "sentinel-ir-mcp@0.
|
|
332
|
+
`command: "npx"`, `args: ["-y", "sentinel-ir-mcp@0.5", "<folder>"]`
|
|
333
|
+
(on Windows: `command: "cmd"`, `args: ["/c", "npx", "-y", "sentinel-ir-mcp@0.5", "<folder>"]`).
|
|
321
334
|
Without folder arguments the server uses the client's
|
|
322
335
|
[MCP roots](https://modelcontextprotocol.io/specification/latest/client/roots)
|
|
323
336
|
if the client offers them; if it offers none, only `analyze_code` works.
|
|
@@ -325,7 +338,7 @@ if the client offers them; if it offers none, only `analyze_code` works.
|
|
|
325
338
|
### Excluding files
|
|
326
339
|
|
|
327
340
|
```bash
|
|
328
|
-
npx -y sentinel-ir-mcp@0.
|
|
341
|
+
npx -y sentinel-ir-mcp@0.5 setup --client cursor --exclude secrets --exclude "*.min.js"
|
|
329
342
|
```
|
|
330
343
|
|
|
331
344
|
| you write | excludes |
|
|
@@ -350,8 +363,8 @@ other tools of its client is told about them, but this server cannot stop it.
|
|
|
350
363
|
### Audit log
|
|
351
364
|
|
|
352
365
|
```bash
|
|
353
|
-
npx -y sentinel-ir-mcp@0.
|
|
354
|
-
npx -y sentinel-ir-mcp@0.
|
|
366
|
+
npx -y sentinel-ir-mcp@0.5 setup --client cursor --audit-log # or answer "y" in setup
|
|
367
|
+
npx -y sentinel-ir-mcp@0.5 log # what the agent asked for
|
|
355
368
|
```
|
|
356
369
|
|
|
357
370
|
| system | folder |
|
|
@@ -508,7 +521,10 @@ A second CI job runs on a real Windows runner (Node 20/22/24): the full test
|
|
|
508
521
|
suite, the packed package with `setup`/`doctor` against the real `%APPDATA%`,
|
|
509
522
|
starting the published package through `npx`, and the printed VS Code/Claude
|
|
510
523
|
Code commands pasted into PowerShell 5.1, PowerShell 7 and cmd.exe. See
|
|
511
|
-
[COMPATIBILITY.md](COMPATIBILITY.md).
|
|
524
|
+
[COMPATIBILITY.md](COMPATIBILITY.md). On every push it runs on GitHub Actions
|
|
525
|
+
in the mirror (`.github/workflows/windows.yml`); on GitLab it runs on release
|
|
526
|
+
tags before `publish`, and by hand on other branches. The informational
|
|
527
|
+
large-file size probe runs on release tags only.
|
|
512
528
|
|
|
513
529
|
Release: bump `version` in `package.json` and both `version` fields in
|
|
514
530
|
`server.json` (a test checks they match), push a protected tag `vX.Y.Z`;
|
package/bin/sentinel-ir-mcp.js
CHANGED
|
@@ -12,7 +12,7 @@ const { normalizePattern, ExcludeError, MAX_PATTERNS } = require("../src/exclude
|
|
|
12
12
|
const { AuditLogError, defaultAuditDir, normalizeAuditDir, assertOutsideProjects } = require("../src/auditLog");
|
|
13
13
|
const { version } = require("../package.json");
|
|
14
14
|
|
|
15
|
-
const USAGE = `sentinel-ir-mcp ${version} — MCP server for sentinel-ir (JavaScript, TypeScript and
|
|
15
|
+
const USAGE = `sentinel-ir-mcp ${version} — MCP server for sentinel-ir (JavaScript, TypeScript, Python and Rust)
|
|
16
16
|
|
|
17
17
|
Usage: sentinel-ir-mcp [options] [folder ...]
|
|
18
18
|
sentinel-ir-mcp setup | doctor | guide | log [--help]
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sentinel-ir-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"mcpName": "io.github.jakcmencz/sentinel-ir-mcp",
|
|
5
|
-
"description": "MCP server for sentinel-ir: lets AI agents (Claude Desktop, Cursor, Windsurf, ...) read a compact structural IR of JavaScript, TypeScript and
|
|
5
|
+
"description": "MCP server for sentinel-ir: lets AI agents (Claude Desktop, Cursor, Windsurf, ...) read a compact structural IR of JavaScript, TypeScript, Python and Rust files instead of the raw source. Read-only, sandboxed to allowed folders.",
|
|
6
6
|
"main": "src/server.js",
|
|
7
7
|
"bin": {
|
|
8
8
|
"sentinel-ir-mcp": "bin/sentinel-ir-mcp.js"
|
|
@@ -20,11 +20,12 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
23
|
-
"sentinel-ir": "^0.
|
|
23
|
+
"sentinel-ir": "^0.5.2",
|
|
24
24
|
"zod": "^4.1.0"
|
|
25
25
|
},
|
|
26
26
|
"keywords": [
|
|
27
27
|
"python",
|
|
28
|
+
"rust",
|
|
28
29
|
"mcp",
|
|
29
30
|
"model-context-protocol",
|
|
30
31
|
"mcp-server",
|
package/src/analyze.js
CHANGED
|
@@ -87,6 +87,25 @@ function realNative(p) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const SKIP_DIRS = new Set(["node_modules", ".git", "__pycache__", "venv", "site-packages"]);
|
|
90
|
+
// Cargo marks its build folder with a CACHEDIR.TAG (cache directory tagging
|
|
91
|
+
// spec); a plain folder named "target" in a JS project is still scanned.
|
|
92
|
+
const CACHEDIR_SIGNATURE = "Signature: 8a477f597d28d172789f06886806bc55";
|
|
93
|
+
async function isCargoTarget(dir) {
|
|
94
|
+
let handle;
|
|
95
|
+
try {
|
|
96
|
+
const tag = path.join(dir, "CACHEDIR.TAG");
|
|
97
|
+
if (!(await fs.lstat(tag)).isFile()) return false;
|
|
98
|
+
handle = await fs.open(tag, OPEN_FLAGS);
|
|
99
|
+
const buffer = Buffer.alloc(CACHEDIR_SIGNATURE.length);
|
|
100
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, 0);
|
|
101
|
+
return buffer.toString("latin1", 0, bytesRead) === CACHEDIR_SIGNATURE;
|
|
102
|
+
} catch {
|
|
103
|
+
return false;
|
|
104
|
+
} finally {
|
|
105
|
+
await handle?.close();
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
90
109
|
const RISK_ORDER = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
91
110
|
|
|
92
111
|
async function analyzeCode(code, target, language) {
|
|
@@ -102,7 +121,7 @@ async function analyzeFile(requested, roots, { maxFileBytes, excluder = NONE, ha
|
|
|
102
121
|
const stat = await fs.stat(real);
|
|
103
122
|
if (!stat.isFile()) throw new AccessError(`Not a file: ${requested}`);
|
|
104
123
|
if (!isSourceFile(real)) {
|
|
105
|
-
throw new AccessError(`Unsupported file type: ${requested} (only .js, .mjs, .cjs, .ts, .mts, .cts, .tsx, .py and .
|
|
124
|
+
throw new AccessError(`Unsupported file type: ${requested} (only .js, .mjs, .cjs, .ts, .mts, .cts, .tsx, .py, .pyi and .rs)`);
|
|
106
125
|
}
|
|
107
126
|
if (stat.size > maxFileBytes) {
|
|
108
127
|
throw new AccessError(`File too large: ${requested} is ${stat.size} bytes, limit is ${maxFileBytes}`);
|
|
@@ -112,11 +131,12 @@ async function analyzeFile(requested, roots, { maxFileBytes, excluder = NONE, ha
|
|
|
112
131
|
}
|
|
113
132
|
|
|
114
133
|
// Breadth-first, sorted walk. Symlinks are never followed; node_modules, .git,
|
|
115
|
-
// __pycache__, venv, site-packages
|
|
134
|
+
// __pycache__, venv, site-packages, hidden folders and Cargo build folders
|
|
135
|
+
// (target/ with a CACHEDIR.TAG, listed in skipped.cargoTarget) are skipped. Excluded
|
|
116
136
|
// files and folders are only counted: their names can be private too.
|
|
117
137
|
async function collectFiles(dir, root, { maxFiles, maxFileBytes, recursive, excluder = NONE }) {
|
|
118
138
|
const files = [];
|
|
119
|
-
const skipped = { excluded: 0, tooLarge: [], symlinks: 0, unreadable: [], hardlinks: [] };
|
|
139
|
+
const skipped = { excluded: 0, tooLarge: [], symlinks: 0, unreadable: [], hardlinks: [], cargoTarget: [] };
|
|
120
140
|
// Exclusions match the real names: on Windows dir may be spelled with
|
|
121
141
|
// short names (SRC~1) that the patterns do not know.
|
|
122
142
|
const prefix = excluder.active ? path.relative(realNative(root), realNative(dir)) : "";
|
|
@@ -142,7 +162,9 @@ async function collectFiles(dir, root, { maxFiles, maxFileBytes, recursive, excl
|
|
|
142
162
|
} else if (entry.isSymbolicLink()) {
|
|
143
163
|
skipped.symlinks++;
|
|
144
164
|
} else if (entry.isDirectory()) {
|
|
145
|
-
if (recursive
|
|
165
|
+
if (!recursive || SKIP_DIRS.has(entry.name) || entry.name.startsWith(".")) continue;
|
|
166
|
+
if (entry.name === "target" && await isCargoTarget(full)) skipped.cargoTarget.push(rel);
|
|
167
|
+
else queue.push(full);
|
|
146
168
|
} else if (entry.isFile() && isSourceFile(entry.name)) {
|
|
147
169
|
let size;
|
|
148
170
|
try {
|
package/src/clients.js
CHANGED
package/src/doctor.js
CHANGED
|
@@ -84,8 +84,8 @@ function checkConfig(client, file, explicit, project, add, platform, env) {
|
|
|
84
84
|
const runs = `${label}: "${SERVER_NAME}" runs ${entry.command} ${args.join(" ")}`;
|
|
85
85
|
if (entry.command === "npx" && platform === "win32") {
|
|
86
86
|
add("warn", `${client}.entry`, runs, `On Windows a client may not find "npx" without a shell. Run: npx -y ${PACKAGE_SPEC} setup --client ${client}`);
|
|
87
|
-
} else if (args.some(a => /^sentinel-ir-mcp@0\.[0-
|
|
88
|
-
add("warn", `${client}.entry`, runs, `This entry stays on an older version without
|
|
87
|
+
} else if (args.some(a => /^sentinel-ir-mcp@0\.[0-4](\.|$)/.test(a))) {
|
|
88
|
+
add("warn", `${client}.entry`, runs, `This entry stays on an older version without Rust support. Run: npx -y ${PACKAGE_SPEC} setup --client ${client}`);
|
|
89
89
|
} else if (entry.command === "npx" || isWindowsNpxWrapper(entry)) {
|
|
90
90
|
add("ok", `${client}.entry`, runs);
|
|
91
91
|
} else {
|
package/src/paths.js
CHANGED
|
@@ -7,7 +7,8 @@ const { fileURLToPath } = require("url");
|
|
|
7
7
|
const JS_EXTENSIONS = new Set([".js", ".mjs", ".cjs"]);
|
|
8
8
|
const TS_EXTENSIONS = new Set([".ts", ".mts", ".cts", ".tsx"]);
|
|
9
9
|
const PY_EXTENSIONS = new Set([".py", ".pyi"]);
|
|
10
|
-
const
|
|
10
|
+
const RS_EXTENSIONS = new Set([".rs"]);
|
|
11
|
+
const SOURCE_EXTENSIONS = new Set([...JS_EXTENSIONS, ...TS_EXTENSIONS, ...PY_EXTENSIONS, ...RS_EXTENSIONS]);
|
|
11
12
|
|
|
12
13
|
class AccessError extends Error {}
|
|
13
14
|
|
package/src/server.js
CHANGED
|
@@ -39,10 +39,10 @@ const RISK_NOTE =
|
|
|
39
39
|
"is unknown: e.g. readFileSync(req.query.f) is low. Start with critical files, and report a problem only after reading the code.";
|
|
40
40
|
|
|
41
41
|
const INSTRUCTIONS =
|
|
42
|
-
"sentinel-ir-mcp turns JavaScript, TypeScript and
|
|
42
|
+
"sentinel-ir-mcp turns JavaScript, TypeScript, Python and Rust source into a compact structural IR (risk level, dangerous " +
|
|
43
43
|
"capabilities, routes, exports, imports, env reads, network/filesystem/process/crypto calls). " +
|
|
44
44
|
"Prefer analyze_file or analyze_directory over reading raw source when you only need structure " +
|
|
45
|
-
"or risk, then read the raw source of the files that matter. Supported: JavaScript (.js, .mjs, .cjs), TypeScript (.ts, .mts, .cts, .tsx)
|
|
45
|
+
"or risk, then read the raw source of the files that matter. Supported: JavaScript (.js, .mjs, .cjs), TypeScript (.ts, .mts, .cts, .tsx), Python (.py, .pyi) and Rust (.rs); the language is picked from the file extension. " +
|
|
46
46
|
"Rely on analysisStatus, world.risk, forbidden and facts; analysisStatus \"incomplete\" means the file did not parse cleanly. " +
|
|
47
47
|
"analyze_directory lists such files separately in 'incomplete'; their low risk is not evidence of safety. " +
|
|
48
48
|
"Suggested order: analyze_directory first, check 'incomplete', 'skipped.unreadable' and 'notAnalyzed' (set when the time budget ran out), pick the relevant files, " +
|
|
@@ -131,45 +131,45 @@ function createServer(options = {}) {
|
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
server.registerTool("analyze_code", {
|
|
134
|
-
title: "Analyze JavaScript, TypeScript or
|
|
134
|
+
title: "Analyze JavaScript, TypeScript, Python or Rust source",
|
|
135
135
|
description:
|
|
136
|
-
"Analyze JavaScript, TypeScript or
|
|
136
|
+
"Analyze JavaScript, TypeScript, Python or Rust source text and return its compact structural IR: " +
|
|
137
137
|
"{analysisStatus, language, mission, world.risk, constraints, forbidden, summary, facts}. " +
|
|
138
138
|
"Use analyze_file instead when the code is on disk, so the source does not have to pass " +
|
|
139
139
|
"through the conversation. " + UNTRUSTED_NOTE,
|
|
140
140
|
inputSchema: {
|
|
141
|
-
code: z.string().max(limits.maxFileBytes).describe("JavaScript, TypeScript or
|
|
142
|
-
target: z.string().max(1024).optional().describe("Optional file name shown as mission.target; its extension (.ts, .tsx, .py, ...) selects the language"),
|
|
143
|
-
language: z.enum(["javascript", "typescript", "tsx", "python"]).optional().describe("Overrides the language picked from target (default javascript)")
|
|
141
|
+
code: z.string().max(limits.maxFileBytes).describe("JavaScript, TypeScript, Python or Rust source"),
|
|
142
|
+
target: z.string().max(1024).optional().describe("Optional file name shown as mission.target; its extension (.ts, .tsx, .py, .rs, ...) selects the language"),
|
|
143
|
+
language: z.enum(["javascript", "typescript", "tsx", "python", "rust"]).optional().describe("Overrides the language picked from target (default javascript)")
|
|
144
144
|
},
|
|
145
|
-
annotations: { title: "Analyze JavaScript, TypeScript or
|
|
145
|
+
annotations: { title: "Analyze JavaScript, TypeScript, Python or Rust source", ...READ_ONLY }
|
|
146
146
|
}, ({ code, target, language }) => guarded(() => analyzeCode(code, target, language), auditor("analyze_code", { code, target, language })));
|
|
147
147
|
|
|
148
148
|
server.registerTool("analyze_file", {
|
|
149
|
-
title: "Analyze a JavaScript, TypeScript or
|
|
149
|
+
title: "Analyze a JavaScript, TypeScript, Python or Rust file",
|
|
150
150
|
description:
|
|
151
|
-
"Read one .js/.mjs/.cjs/.ts/.mts/.cts/.tsx/.py/.pyi file inside the allowed folders and return its compact structural IR. " +
|
|
151
|
+
"Read one .js/.mjs/.cjs/.ts/.mts/.cts/.tsx/.py/.pyi/.rs file inside the allowed folders and return its compact structural IR. " +
|
|
152
152
|
`Relative paths are resolved against the first allowed folder. Files over ${limits.maxFileBytes} bytes are refused. ` +
|
|
153
153
|
UNTRUSTED_NOTE + excludeNote,
|
|
154
154
|
inputSchema: {
|
|
155
155
|
path: z.string().min(1).describe("File path, absolute or relative to the first allowed folder")
|
|
156
156
|
},
|
|
157
|
-
annotations: { title: "Analyze a JavaScript, TypeScript or
|
|
157
|
+
annotations: { title: "Analyze a JavaScript, TypeScript, Python or Rust file", ...READ_ONLY }
|
|
158
158
|
}, async ({ path }) => {
|
|
159
159
|
const hashes = auditLog.active ? new Map() : null;
|
|
160
160
|
return guarded(async () => analyzeFile(path, await allowedRoots(), { ...limits, excluder, hashes }), auditor("analyze_file", { path, hashes }));
|
|
161
161
|
});
|
|
162
162
|
|
|
163
163
|
server.registerTool("analyze_directory", {
|
|
164
|
-
title: "Analyze a folder of JavaScript, TypeScript and
|
|
164
|
+
title: "Analyze a folder of JavaScript, TypeScript, Python and Rust files",
|
|
165
165
|
description:
|
|
166
|
-
"Scan a folder inside the allowed folders and return one short entry per JavaScript, TypeScript or
|
|
166
|
+
"Scan a folder inside the allowed folders and return one short entry per JavaScript, TypeScript, Python or Rust file, " +
|
|
167
167
|
"sorted from highest to lowest risk, plus risk counts. By default (detail \"summary\") each entry is " +
|
|
168
168
|
"{path, risk, analysisStatus, language, lines, operations, riskSignals, moreRiskSignals?, parseError?}: " +
|
|
169
169
|
"riskSignals are up to 3 'kind:code@line' signals (code cut to 120 characters) that set the file's risk level (empty when the risk comes from " +
|
|
170
170
|
"the text fallback of an incomplete file or from a path containing 'crypto'), moreRiskSignals counts the rest. " +
|
|
171
171
|
"detail \"full\" adds forbidden, objective and all facts per file instead (much larger). " + RISK_NOTE + " " +
|
|
172
|
-
"Skips node_modules, .git, __pycache__, venv, site-packages, hidden folders " +
|
|
172
|
+
"Skips node_modules, .git, __pycache__, venv, site-packages, hidden folders, Cargo build folders (target/ with a CACHEDIR.TAG, listed in skipped.cargoTarget) " +
|
|
173
173
|
`and symlinks. Analyzes at most max_files files (default ${limits.maxFiles}); 'truncated' tells ` +
|
|
174
174
|
"you when there were more. 'incomplete' lists files whose analysis did not finish (e.g. parse errors); " +
|
|
175
175
|
"their risk is not reliable, check them separately. statusCounts counts complete vs incomplete. " +
|
|
@@ -186,7 +186,7 @@ function createServer(options = {}) {
|
|
|
186
186
|
detail: z.enum(["summary", "full"]).optional()
|
|
187
187
|
.describe("\"summary\" (default): short entries; \"full\": all facts per file, much larger")
|
|
188
188
|
},
|
|
189
|
-
annotations: { title: "Analyze a folder of JavaScript, TypeScript and
|
|
189
|
+
annotations: { title: "Analyze a folder of JavaScript, TypeScript, Python and Rust files", ...READ_ONLY }
|
|
190
190
|
}, async ({ path, max_files, recursive, detail }) => {
|
|
191
191
|
const hashes = auditLog.active ? new Map() : null;
|
|
192
192
|
return guarded(async () => analyzeDirectory(path ?? ".", await allowedRoots(), {
|