sentinel-ir-mcp
Analyzes JavaScript source files (.js, .mjs, .cjs) and returns a compact structural IR covering risk level, dangerous capabilities, imports/exports, routes, environment reads, and network/filesystem/process/crypto calls.
Analyzes Python source files (.py, .pyi) and returns a compact structural IR with risk and capability facts, subject to the documented Python detection limits.
Analyzes TypeScript source files (.ts, .mts, .cts, .tsx) and returns a compact structural IR with risk, capability, import/export, and operation facts; parse failures are marked incomplete.
This server does not currently have a deployable release on Glama. Follow the repository's setup instructions to install and configure it for your MCP client, including any required credentials and local dependencies.
Sentinel-IR-MCP
MCP server for sentinel-ir:
it lets AI agents in Claude Desktop, Cursor, Windsurf or any other
Model Context Protocol client read a
compact structural IR of JavaScript, TypeScript and Python files instead of the raw source.
Library behind it: https://gitlab.com/jakubrojicek11/sentinel-ir
The IR tells the agent what a file does (risk level, dangerous
capabilities such as eval or child_process, routes, exports, imports,
environment reads, network / filesystem / process / crypto calls) in a few
hundred characters. The agent can triage a folder first and then read the raw
source only of the files that matter.
Supported: JavaScript (.js, .mjs, .cjs), TypeScript (.ts, .mts,
.cts, .tsx; since 0.2.0) and Python (.py, .pyi; since 0.3.0). The
language is picked from the file extension; analyze_code also takes target
or an explicit language (javascript | typescript | tsx | python). Every result carries language and
analysisStatus; a file that does not parse cleanly (including JSX in a .ts
file) is "incomplete", and a low risk on an incomplete result is not evidence
of safety. Other languages are not supported yet. What is detected in Python
(and what is not) is listed in the
sentinel-ir README.
0.4.5 adds to that log the SHA-256 of every file the server read, so you can check later which version of a file the agent was shown (see Audit log).
0.4.4 can keep a log of what the agent asked for (off unless you turn it on):
One question in
setup: "Keep a log of what the agent asks for (file names and results, never your code)? [y/N]". Orsetup --audit-log;--no-audit-logturns it off. Runningsetupagain keeps the setting.One command to read it:
npx -y sentinel-ir-mcp@0.4 logprints the last 50 entries as plain lines,--alleverything,--jsonfor scripts:2026-10-18 10:32:05 [cursor] file src/app.js: critical 2026-10-18 10:32:09 [cursor] REFUSED file secrets/aws.js: excluded by your settings 2026-10-18 10:33:40 [cursor] folder .: 42 file(s) (1 critical, 6 medium, 35 low), 3 excluded critical src/app.jsdoctorsays whether the log is on, where it is and how big, and proves the log can be written. Details: Audit log.
Also uses sentinel-ir 0.4.5 (Python parse errors point where Python does).
0.4.3 lets you exclude files and folders:
One question in
setup: "Files or folders the agent must not read", e.g.secrets, dist, *.min.js(Enter = none). Orsetup --exclude secrets --exclude dist.setupwrites them into the client config, so there is no JSON to edit; running it again keeps them,--no-excluderemoves them.Set by you only. The agent cannot change them, and the analyzed project cannot either: there is no
.sentinelignore, because a repository could use it to hide its own files.analyze_filerefuses an excluded path whether or not it exists;analyze_directoryonly counts excluded entries inskipped.excluded, because their names can be private too.doctorlists the exclusions and how many files and folders they skip in the project.
0.4.2 makes analyze_directory short by default:
Short entries: each file is
{path, risk, analysisStatus, language, lines, operations, riskSignals}, plusmoreRiskSignalsandparseErrorwhen they apply.riskSignalsare up to 3kind:code@linesignals that set the file's risk level (code cut to 120 characters).analyze_filestill returns the full IR;detail: "full"returns the 0.4.1 entries.folder (all files)
0.4.1
0.4.2 default
a 190-file Node project
96,550 chars (~24,100 tokens)
35,856 chars (~9,000 tokens)
MCP SDK
dist/cjs(174 files)79,945 chars (~20,000 tokens)
29,046 chars (~7,300 tokens)
Tokens estimated as chars / 4. The tool list the client loads once grows from 4,103 to 5,322 chars.
Risk explained to the agent: tool descriptions say that risk is about capabilities, not vulnerabilities; that
medium(file writes, network, crypto) is normal in backend code; and that values are not tracked, soreadFileSync(req.query.f)islow.
0.4.1 uses sentinel-ir 0.4.3:
Faster on large JavaScript: a valid 1 MiB file takes about 5 s instead of about 170 s, so
analyze_directoryno longer runs past the 60 s request timeout of MCP SDK clients on one big file.Read-time check: files are opened first and then checked, so a file swapped for a symlink between listing and reading is refused instead of read from outside the allowed folders. A file removed during a scan no longer fails the whole scan; it is listed in
skipped.unreadable. Files with more than one hard link are refused (skipped.hardlinks).Finite limits:
--max-file-bytesis capped at 2 MiB. Valid JavaScript withsentinel-ir0.4.3:size
Linux
Windows CI (Node 20/22/24)
1 MiB
4–5 s, 0.9 GB
13–23 s, 0.8 GB
2 MiB
10–14 s, 1.6 GB
29–40 s, 1.5 GB
4 MiB
25–29 s, 3 GB
process aborts
A file above 2 MiB can take longer than the 60 s request timeout on its own.
Time budget:
analyze_directorystarts no new file after 30 s (--time-budget-ms). ThentimedOutistrueand the remaining files are listed innotAnalyzed. A file already being analyzed is not interrupted.Parser errors: incomplete files carry
facts.parseErrorwith the line, column and kind of the first parser error (fromsentinel-ir0.4.3).
0.4.0 uses sentinel-ir 0.4.2:
Native code:
ctypes/cffi,process.dlopen,*.nodeaddons and FFI packages (koffi,ffi-napi,bun:ffi, ...) arecritical, withnative_codeinforbiddenandnativeCodeinfacts.operations.Long sessions: older versions never freed the WebAssembly parser memory. After roughly 87 MB of analyzed source in one server process every later result became
"incomplete"(text fallback) without an error. Withsentinel-ir-mcp0.3.0 +sentinel-ir0.3.1 over stdio,analyze_codeon an 18 KB file wentincompletefrom call 4,918 on; 0.4.0 stayscomplete(measurement below).
Tools
tool | input | returns |
|
| compact IR of the given source |
|
| compact IR of one file inside the allowed folders |
| optional | one short entry per file (path, risk, status, language, lines, operations, up to 3 risk signals; |
Every tool returns the result twice: as JSON text (for clients that only read
text) and as structuredContent. All three are annotated
readOnlyHint: true, destructiveHint: false, openWorldHint: false.
Example analyze_file result for a three-line file that calls eval and
child_process (real output, wrapped for reading):
{"analysisStatus":"complete",
"mission":{"target":"danger.js","objective":"general_optimization"},
"world":{"pressure":0.5,"confidence":0.75,"budget":0.015,"risk":"critical"},
"constraints":["preserve_api","avoid_breaking_changes"],
"forbidden":["eval","child_process","process_spawn"],
"summary":{"size":93,"lines":3,"hasCrypto":false,"hasFilesystem":false,"hasNetwork":false},
"facts":{"ast":true,"exports":["default"],"imports":["child_process"],
"operations":["dynamicEval","processSpawn","childProcess"],
"calls":{"process":["cp.exec","require(child_process)"],"dynamic":["eval"]},
"dangerous":["eval","child_process","process_spawn"],
"riskSignals":["child_process_import:require(child_process)@1","dynamic_code:eval@2","process_spawn:cp.exec@2"]}}Rely on world.risk, forbidden and facts. mission.objective is a coarse
hint from sentinel-ir and can stay general_optimization even for risky
files, as above.
Related MCP server: ast-editor
Setup
Requires Node.js 20 or newer. npx downloads and runs the server; there is
nothing to compile (the parser is WebAssembly).
Quick setup (since 0.2.2)
Run it in your project folder:
npx -y sentinel-ir-mcp@0.4 setup --client cursor # or windsurf, claude-desktop, vscode, claude-code
npx -y sentinel-ir-mcp@0.4 doctor --client cursor # after restarting the client
npx -y sentinel-ir-mcp@0.4 guide # optional: text for AGENTS.md / client rulessetup (without --client it asks which client):
shows the change first and writes only after you confirm (or with
--yes);--dry-runshows the change and writes nothing;adds or replaces only the
"sentinel-ir"entry; other servers and other fields in the file stay as they are, and running it again changes nothing;copies the old file to
<file>.bak-<timestamp>before writing and writes through a temporary file plus rename; a file with invalid JSON is never touched;gives the agent one folder (
--project, default: the current folder) and refuses your home folder or a whole disk;writes
sentinel-ir-mcp@0.4, so clients get 0.4.x fixes but no unexpected new features;asks which files or folders the agent must not read (
--exclude <name>, repeatable, skips the question; see Excluding files);for VS Code and Claude Code it writes nothing and prints their own install command (
code --add-mcp ...,claude mcp add ...) instead.
| file written by |
|
|
|
|
| macOS |
| nothing; prints the command to run |
On Windows the entry is "command": "cmd", "args": ["/c", "npx", "-y", "sentinel-ir-mcp@0.4", "<folder>"]: there npx is npx.cmd, and starting it
without a shell failed with ENOENT on a real Windows runner. For VS Code and
Claude Code, setup prints a PowerShell and a Command Prompt version of the
command. What was measured on which system: COMPATIBILITY.md.
doctor checks Node.js, the package version, the client config (valid JSON, a
"sentinel-ir" entry that starts sentinel-ir-mcp, existing folders), then
starts its own server with the same folders, performs a real MCP
handshake, expects exactly the three tools and calls analyze_directory with
max_files: 1. It lists your exclusions, how many files and folders they skip
in the project right now, and warns about one that matches nothing (a typo).
It never runs the command stored in the client config. Exit
code 0 when everything passes, 1 otherwise; --json gives a machine-readable
report.
Only setup writes files, and only the client config. The MCP server itself
reads only; the one exception is the audit log, if you turn it on.
Cursor: a server from a project .cursor/mcp.json starts switched off.
Open Cursor Settings → Customize → MCPs and turn on the switch next to
sentinel-ir (its source is the project's .cursor/mcp.json). It then shows
Connected and 3 tools. A server installed with the link below (user-level
~/.cursor/mcp.json) connected without this step.
Getting a newer 0.2.x
npx keeps the package it downloaded for sentinel-ir-mcp@0.2 and keeps
starting that copy, even when a newer 0.2.x is on npm (measured with npm
10.8.2, 10.8.3 and 10.9.9: a cached 0.2.2 kept running after 0.2.3 was
released; --prefer-online and npm cache clean --force did not change it).
To get the newest 0.2.x:
Check what runs:
npx -y sentinel-ir-mcp@0.2 doctorprintssentinel-ir-mcp <version>.Close the client app and delete the
_npxfolder in npm's cache (npm config get cacheshows where it is; usually~/.npm/_npx, on Windows%LocalAppData%\npm-cache\_npx). This removes all packages cached bynpx, not only this one; they are downloaded again when needed.Start the client again. The next start downloads the newest 0.2.x.
A folder that is literally named setup, doctor or guide must now be
passed as ./setup (or an absolute path); the bare word is the command.
Upgrading from 0.3.x or older
Configs written by older versions pin sentinel-ir-mcp@0.3 (or @0.2) and
keep running that line. 0.3.x has no native-code detection, and its parser
stops working in long sessions (see 0.4.0 below); 0.2.x also has no Python
support. Run setup again for your client (it replaces only the
"sentinel-ir" entry); doctor warns about an entry that still pins 0.3 or
older. If the client still starts an old version, see
Getting a newer version.
One-click install links
These links follow the Cursor and VS Code documentation. They were opened in
VS Code 1.143 and Cursor 3.26 (see COMPATIBILITY.md). A link cannot know your
project path, so it uses ${workspaceFolder}; if your client does not expand
it, use setup instead.
Cursor: Install in Cursor
VS Code: Install in VS Code
Some sites (including GitLab and npm) may not open cursor:// or vscode:
links; copy the link into the browser address bar or use setup.
Manual setup
Pass the folder(s) the agent may read as arguments. Use absolute paths.
Claude Desktop
Settings → Developer → Edit Config opens claude_desktop_config.json
(macOS: ~/Library/Application Support/Claude/, Windows: %APPDATA%\Claude\):
{
"mcpServers": {
"sentinel-ir": {
"command": "npx",
"args": ["-y", "sentinel-ir-mcp@0.4", "/Users/you/projects/my-app"]
}
}
}Restart Claude Desktop afterwards.
Cursor
Project .cursor/mcp.json (or ~/.cursor/mcp.json for all projects).
${workspaceFolder} is the folder that contains .cursor/mcp.json. A
project entry has to be switched on once in Cursor Settings → Customize → MCPs:
{
"mcpServers": {
"sentinel-ir": {
"command": "npx",
"args": ["-y", "sentinel-ir-mcp@0.4", "${workspaceFolder}"]
}
}
}Windsurf
Windsurf 3 (Devin Desktop) reads ~/.config/devin/mcp_config.json
(Windows: %APPDATA%\devin\mcp_config.json); older versions read
~/.codeium/windsurf/mcp_config.json. Add the same entry as for Claude Desktop
with your project's absolute path.
Other clients
Any MCP client that starts stdio servers works with
command: "npx", args: ["-y", "sentinel-ir-mcp@0.4", "<folder>"]
(on Windows: command: "cmd", args: ["/c", "npx", "-y", "sentinel-ir-mcp@0.4", "<folder>"]).
Without folder arguments the server uses the client's
MCP roots
if the client offers them; if it offers none, only analyze_code works.
Excluding files
npx -y sentinel-ir-mcp@0.4 setup --client cursor --exclude secrets --exclude "*.min.js"you write | excludes |
| every file or folder named |
| only this path, relative to the project folder |
| files by mask; |
|
|
Only additions: there is no
!to re-include something. Absolute paths,..and the characters" & | < > ^ % !are refused.Windows
\works like/. On Windows and macOS letter case does not matter (SECRETS/a.jsis excluded bysecrets), on Linux it does.The check uses the real path, so
lib/../secrets/a.js, a symlink to an excluded file and a Windows short name (SECRE~1) are refused too. It is repeated when the file is opened.analyze_codeis not affected: it analyzes text the agent already has.
The exclusions only bind this server. An agent that can read files with other tools of its client is told about them, but this server cannot stop it.
Audit log
npx -y sentinel-ir-mcp@0.4 setup --client cursor --audit-log # or answer "y" in setup
npx -y sentinel-ir-mcp@0.4 log # what the agent asked forsystem | folder |
Windows |
|
macOS |
|
Linux |
|
--audit-log=<folder> uses another folder: a full path outside the project
(the characters " & | < > ^ % ! are refused because Windows starts the
server through cmd). Then read it with log --dir <folder>.
What is written: one JSON line per tool call in
audit-YYYY-MM-DD.jsonl(local date): time, client name, tool, the path the agent asked for, the result (risk,analysisStatus, file counts, up to 20critical/highfile paths of a folder) or why it was refused (excluded,outside,not-found,too-large), and the duration. A line when the server starts.Which version of a file (0.4.5): for every file the server read, the SHA-256 of the exact bytes it analyzed:
sha256foranalyze_file; foranalyze_directorythe folder line gets acallid andhashedcount, and the hashes follow in"event": "hashes"lines (40 files each, samecallid), so no line gets long.logshows the first 12 digits for files and the risky files of a folder;log --jsonhas all of them. To check whether a file is still the one the agent saw, compare withcertutil -hashfile <file> SHA256(Windows),shasum -a 256 <file>(macOS) orsha256sum <file>(Linux). Pasted code (analyze_code) gets no hash: a very short snippet could be guessed from it. The tool answers to the agent are unchanged.What is not written: source code. For
analyze_codeonly the size in bytes, the language and the risk. Names of excluded files are not written either, only their count. Long values are cut to 300 characters.Cleanup: day files older than 30 days are deleted; a day stops 1 MB below 50 MB with one line saying so, so parallel servers stay under 50 MB too. Other files in the folder are never touched.
Set by you only, like exclusions: the agent and the project cannot turn it on, off or move it. The log is never written inside the project.
Safe to read: a path with a newline cannot add a fake line (JSON escapes it), and
logshows control and bidi characters as\u{1b}, so a path cannot recolor or rewrite your terminal.Never breaks the tools: when the log cannot be written (disk full, no permission) the tools answer as usual and the server says so once on stderr;
doctorreports it as[FAIL]. Several servers can share the folder: each line is one append, so lines do not mix.doctoradds one line,doctor checked that the log can be written, and none of its own tool calls.A day file that is a symlink, junction or hard link is not written to, on Windows too, so the log cannot be used to overwrite another file.
It records only what the agent asked this server for. Other file tools of your AI app are not in it.
Files are created readable by you only (
0600, folder0700; on Windows the folder in%LOCALAPPDATA%is private to your account).
Options
sentinel-ir-mcp [--exclude <name>]... [--audit-log[=<folder>]] [--max-file-bytes <n>] [--max-files <n>] [--time-budget-ms <n>] [folder ...]
--exclude <name> file or folder the file tools refuse (repeatable, max 100); see Excluding files
--audit-log[=<folder>] keep a log of the tool calls (never source code); see Audit log
--max-file-bytes <n> largest file to analyze (default 1048576 = 1 MiB, max 2097152 = 2 MiB)
--time-budget-ms <n> analyze_directory starts no new file after this (default 30000, max 600000)
--max-files <n> default file limit for analyze_directory (default 50, max 500)What the server can and cannot do
Reads only. The MCP server never writes project files, runs commands or opens network connections. It writes only the audit log when you turn it on (outside the project). The separate
setupcommand writes only the client config file you confirm;doctorstarts this package's own server. The only files it opens are.js/.mjs/.cjs/.ts/.mts/.cts/.tsx/.py/.pyifiles up to--max-file-bytes.Only inside the allowed folders. Every path is resolved to its real path (symlinks followed) and must lie inside a folder given on the command line, or, without folder arguments, inside one of the client's MCP roots.
../, absolute paths elsewhere and symlinks pointing outside are refused.Exclusions are yours. They come only from the command line the client starts (written by
setup), never from the agent or from files in the project. Excluded paths are refused byanalyze_fileand counted, not named, inskipped.excluded.analyze_directoryskipsnode_modules,.git, hidden folders and every symlink, and stops aftermax_filesfiles (truncated: true) or when the time budget is spent (timedOut: true, rest innotAnalyzed). Each file is checked again when it is opened: it must still be a regular file inside the allowed folders.Hard links are refused. A hard link inside the folder can point to a file outside it, and the other names of a file cannot be found. So files with more than one hard link are refused (
analyze_fileerror,skipped.hardlinksinanalyze_directory). This also refuses harmless hard links, e.g. pnpm's innode_modules.The IR is still untrusted text. Comments, string bodies and function bodies are not copied into it, so instructions hidden there do not reach the agent through the IR. It does contain identifiers and some literals from the code (route paths, module names, export names, env variable names). That reduces the prompt-injection surface; it does not remove it.
It is not a security scanner. The risk level comes from a fixed set of AST patterns in
sentinel-ir; code that hides its behaviour (e.g. building a module name at runtime) can look harmless.
Measurements
The library's own published sources (sentinel-ir@0.1.3), read through
analyze_file / analyze_directory over stdio. Tokens counted with
gpt-tokenizer@4.0.4 (o200k_base encoding); other models' tokenizers give
different numbers.
file | raw chars | raw tokens | IR chars | IR tokens |
| 39,877 | 8,439 | 450 | 120 |
| 9,123 | 2,084 | 515 | 133 |
| 4,393 | 891 | 465 | 121 |
| 600 | 150 | 461 | 120 |
all 4 via | 11,564 | 317 |
The IR has a floor of roughly 120 tokens per file, so it does not pay off for
very small files. analyze_file on src/ast-facts.js took about 160 ms per
call (average of 10, Node 24, Linux cloud VM); the first call also loads the
WebAssembly parser.
Long session (one server process over stdio, 6,000 analyze_code calls on
the same 17.8 KB JavaScript file, about 107 MB in total, Node 24):
version | calls with a different | first such call |
0.3.0 + | 1,083 | 4,918 |
0.4.0 + | 0 | — |
Dependencies
Three runtime dependencies: the official
@modelcontextprotocol/sdk,
zod (its schema peer dependency) and sentinel-ir. With their own
dependencies that is about 100 packages in node_modules; npm audit
reported 0 vulnerabilities at release time (0.4.0: after updating
body-parser to 2.4.0 in the lock file). Unlike sentinel-ir itself,
this package is not dependency-free.
Development
npm ci
npm test # starts the real server over stdio and talks to it with the MCP SDK clientTests cover the tool list and annotations, all three tools, refusals for
../, absolute paths, symlinks out of the folder, unsupported file types and
size limits, MCP roots from the client, and the command-line options.
setup/doctor tests run against temporary HOME/APPDATA folders: new
file, keeping other servers and fields, backup, idempotent second run, invalid
JSON left untouched, --dry-run, refused home/root folders, symlinked config,
Claude Desktop/Windsurf macOS/Windows paths (simulated), a real MCP
handshake, failing configs and old Node.js.
A second CI job runs on a real Windows runner (Node 20/22/24): the full test
suite, the packed package with setup/doctor against the real %APPDATA%,
starting the published package through npx, and the printed VS Code/Claude
Code commands pasted into PowerShell 5.1, PowerShell 7 and cmd.exe. See
COMPATIBILITY.md.
Release: bump version in package.json, push a protected tag vX.Y.Z;
GitLab CI tests and publishes to npm with provenance.
License
Apache-2.0. See NOTICE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
This server cannot be deployed
Maintenance
Related MCP Connectors
A comprehensive Model Context Protocol (MCP) server that enables AI assistants to interact with yo…
MCP server for Sentry - error monitoring, issue tracking, and debugging for AI assistants
MCP server for progressive tool usage at any scale (see https://klavis.ai)
Shared memory MCP server for Claude Code, Codex, Gemini CLI and Cursor. Cited, editable.
Related MCP Servers
- AlicenseAqualityDmaintenanceA Model Context Protocol (MCP) server designed to easily dump your codebase context into Large Language Models (LLMs).12 npm3Apache 2.0
- AlicenseAqualityBmaintenanceA robust, language-agnostic Model Context Protocol (MCP) server that provides AI coding agents with the ability to edit files surgically via Abstract Syntax Trees (AST) instead of relying on token-heavy, brittle search-and-replace or diff operations.2810MIT
- AlicenseNot gradedqualityBmaintenanceStandalone MCP server for code structure analysis using tree-sitter. Directory trees, symbol definitions, and call graphs without reading raw source files. Supports Rust, Python, Go, Java, TypeScript, Fortran, JavaScript, C/C++, and C#. Benchmarked up to 68% fewer tokens vs native tools.7Apache 2.0
- AlicenseNot gradedqualityCmaintenanceA local MCP server that indexes TypeScript/JavaScript projects and returns budget-aware, dependency-optimized context packs for AI coding assistants.1MIT