<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: jackymenCZ (jackymenCZ)</title>
    <description>The latest articles on DEV Community by jackymenCZ (jackymenCZ) (@jackymencz).</description>
    <link>https://dev.to/jackymencz</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4123067%2F3b6b90a9-dddd-4b7c-8869-f02fb536a680.jpg</url>
      <title>DEV Community: jackymenCZ (jackymenCZ)</title>
      <link>https://dev.to/jackymencz</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jackymencz"/>
    <language>en</language>
    <item>
      <title>Windows GUI check — published sentinel-ir-mcp 0.5.9. The No-Login Paradox 2</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sun, 15 Nov 2026 18:25:05 +0000</pubDate>
      <link>https://dev.to/jackymencz/windows-gui-check-published-sentinel-ir-mcp-059-4d15</link>
      <guid>https://dev.to/jackymencz/windows-gui-check-published-sentinel-ir-mcp-059-4d15</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ezlmqzrdos49a2u0jjl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5ezlmqzrdos49a2u0jjl.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fok0n2moxvmd9wleucftt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fok0n2moxvmd9wleucftt.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqhlep3b49bq7iv5upou.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuqhlep3b49bq7iv5upou.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Verdict
&lt;/h2&gt;

&lt;p&gt;Source (GitLab, Apache-2.0): &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir&lt;/a&gt;&lt;br&gt;
Playground (runs in your browser): &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Desktop startup is verified; desktop-originated tool calls remain untested.&lt;/strong&gt; Normally installed VS Code and its Cline extension both started the published server without signing in. Actual analysis calls passed through the &lt;strong&gt;MCP Inspector browser fallback, not a desktop client&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;VS Code also reproduced the reported &lt;code&gt;Error&lt;/code&gt;/empty-output symptom, but on its &lt;strong&gt;session-bound&lt;/strong&gt; server entry. The separate &lt;strong&gt;locally configured / Global in Code&lt;/strong&gt; entry started successfully with the same configuration. Evidence points to VS Code's session/policy surface, not a broken Sentinel executable. The exact underlying VS Code policy requirement is not established.&lt;/p&gt;
&lt;h2&gt;
  
  
  Environment and configuration
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Windows Server 2022; VS Code &lt;strong&gt;1.146.0&lt;/strong&gt;, official &lt;strong&gt;user installer&lt;/strong&gt;, not portable.&lt;/li&gt;
&lt;li&gt;Onboarding bypassed with &lt;strong&gt;Continue without Signing In&lt;/strong&gt;. No Microsoft/GitHub/Cline login.&lt;/li&gt;
&lt;li&gt;Primary runtime &lt;strong&gt;Node 22.23.5&lt;/strong&gt;, secondary &lt;strong&gt;Node 24.22.2&lt;/strong&gt;; spawned server &lt;code&gt;ExecutablePath&lt;/code&gt; checked using &lt;code&gt;Win32_Process&lt;/code&gt;, not inferred from shell PATH.&lt;/li&gt;
&lt;li&gt;Project: &lt;code&gt;C:\t\Projekt Žluťoučký kůň\app&lt;/code&gt;; files &lt;code&gt;src\index.js&lt;/code&gt;, &lt;code&gt;src\util.js&lt;/code&gt;, &lt;code&gt;src\main.rs&lt;/code&gt;, &lt;code&gt;tool.py&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Setup invoked as &lt;code&gt;npx -y sentinel-ir-mcp@0.5 setup --client vscode&lt;/code&gt;. It &lt;strong&gt;printed a command; it did not write configuration&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Registered using the printed &lt;code&gt;code --add-mcp&lt;/code&gt; form; resulting user configuration: &lt;code&gt;%APPDATA%\Code\User\mcp.json&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Actual running package: &lt;code&gt;%LOCALAPPDATA%\npm-cache\_npx\3ec5bc0582433d0b\node_modules\sentinel-ir-mcp\package.json&lt;/code&gt; → &lt;strong&gt;0.5.9&lt;/strong&gt;. Both Node versions used this published npm cache installation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;VS Code's generated entry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"sentinel-ir-mcp@stable-0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"C:\\t\\Projekt Žluťoučký kůň\\app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For Cline and Inspector, copied the Windows JSON-client form confirmed by &lt;code&gt;setup --client claude-desktop --project "&amp;lt;project&amp;gt;" --config &amp;lt;temporary-file&amp;gt; --dry-run&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"cmd"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"/c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"sentinel-ir-mcp@stable-0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"C:\\t\\Projekt Žluťoučký kůň\\app"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Verified results
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Client / surface&lt;/th&gt;
&lt;th&gt;Started?&lt;/th&gt;
&lt;th&gt;Tools / calls&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VS Code 1.146.0, local user entry, Node 22&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Running&lt;/code&gt;; &lt;code&gt;Discovered 3 tools&lt;/code&gt;; real Sentinel Node 22 process. Initial restricted workspace worked; trusting it and restarting also worked.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cline 4.1.26, Node 22, generated &lt;code&gt;cmd /c npx&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Green connected indicator; schemas for &lt;code&gt;analyze_code&lt;/code&gt;, &lt;code&gt;analyze_file&lt;/code&gt;, &lt;code&gt;analyze_directory&lt;/code&gt;. Calls &lt;strong&gt;untested&lt;/strong&gt;: no configured working model. Selected local Ollama provider to finish onboarding without an account; no model service was installed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;MCP Inspector 2.15.0 &lt;strong&gt;browser fallback&lt;/strong&gt;, Node 22, same &lt;code&gt;cmd /c npx&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;All three tools listed. Both required real calls passed; results below.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code 1.146.0, &lt;strong&gt;session-bound&lt;/strong&gt; entry after relaunch, Node 24 environment&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;No&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;Error&lt;/code&gt;, empty MCP output; renderer: &lt;code&gt;Could not list installed MCP servers. (refused: policy-context-unavailable)&lt;/code&gt;. No additional Sentinel process from this attempt. Cline's separately running Node 24 server was not mistaken for this entry.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code 1.146.0, &lt;strong&gt;local / Global in Code&lt;/strong&gt; entry, Node 24&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Same generated &lt;code&gt;npx&lt;/code&gt; configuration; &lt;code&gt;Running&lt;/code&gt; + &lt;code&gt;Discovered 3 tools&lt;/code&gt;; separate actual Node 24 server process.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;VS Code local entry, Node 24, &lt;code&gt;npx.cmd&lt;/code&gt; variant&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Yes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Changed only command from &lt;code&gt;npx&lt;/code&gt; to &lt;code&gt;npx.cmd&lt;/code&gt;; &lt;code&gt;Running&lt;/code&gt; + &lt;code&gt;Discovered 3 tools&lt;/code&gt;. Original generated configuration restored afterward.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Inspector calls:&lt;/strong&gt; entered absolute Czech/spaced paths through visible tool forms and clicked Execute Tool.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;analyze_file&lt;/code&gt; on &lt;code&gt;...\src\index.js&lt;/code&gt;: OK, 75 ms in result pane; &lt;code&gt;analysisStatus: complete&lt;/code&gt;, &lt;code&gt;language: javascript&lt;/code&gt;, &lt;code&gt;risk: medium&lt;/code&gt;, filesystem and network recognized (&lt;code&gt;fs.readFileSync&lt;/code&gt;, &lt;code&gt;https.get&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;analyze_directory&lt;/code&gt; on project root: OK, 39 ms; &lt;strong&gt;4 analyzed, 4 complete, 0 incomplete&lt;/strong&gt;, no timeout/truncation; Rust/Python critical (process spawning), index.js medium, util.js low.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Protocol monitoring panel separately displayed approximately 73 ms / 34 ms for those calls. No CLI/SDK call is being presented as GUI coverage.&lt;/p&gt;

&lt;h2&gt;
  
  
  Visual evidence
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;VS Code Node 22 — running&lt;/th&gt;
&lt;th&gt;Cline — connected and tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2F164bae5d-f201-46ca-b7bb-d110824b3aeb%2Fvscode-running.png" alt="VS Code running" width="800" height="400"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2Fd05f5d7b-d8db-44ec-8bab-e8be839a3e84%2Fcline-tools.png" alt="Cline tool schemas" width="800" height="400"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Inspector fallback — file result&lt;/th&gt;
&lt;th&gt;Inspector fallback — directory result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2Ff88d8538-203f-4d34-a07b-7a7e09749dee%2Finspector-file-result.png" alt="File result" width="800" height="400"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2F6199ea62-18cb-493d-b760-75abece6834e%2Finspector-directory-result.png" alt="Directory result" width="800" height="400"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;🔴 VS Code session entry — Error&lt;/th&gt;
&lt;th&gt;🟢 Same runtime, local entry — Running / 3 tools&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2F61f92762-a71a-467a-b50f-74c9769ec3ac%2Fvscode-node24-status.png" alt="Session error" width="800" height="400"&gt;&lt;/td&gt;
&lt;td&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fapp.devin.ai%2Fattachments%2Fb4831b2e-f8aa-4886-8455-bbc11a3caf24%2Fvscode-node24-local-output.png" alt="Local running" width="800" height="400"&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Failure evidence and limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Exact working route after relaunch: &lt;strong&gt;MCP: List Servers → Show locally configured servers… → sentinel-ir (Global in Code) → Start Server&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Session-bound failure output file was &lt;strong&gt;empty&lt;/strong&gt;. Full failure-time logs are preserved in &lt;code&gt;vscode-node24-failure-logs&lt;/code&gt;; focused &lt;code&gt;renderer.log&lt;/code&gt;, &lt;code&gt;exthost.log&lt;/code&gt; and full subsequent local MCP log are also supplied in &lt;code&gt;windows-mcp-logs.zip&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Generated &lt;code&gt;npx&lt;/code&gt; and alternate &lt;code&gt;npx.cmd&lt;/code&gt; both work locally; &lt;code&gt;cmd /c npx&lt;/code&gt; works in Cline and Inspector. The session-bound entry was &lt;strong&gt;not fully retested with every command variant&lt;/strong&gt;. Switching to the local surface resolved startup without any command change.&lt;/li&gt;
&lt;li&gt;Desktop analysis calls, account-policy root cause, and Node 24 analysis calls: &lt;strong&gt;untested&lt;/strong&gt;. Inspector demonstrates server tools, not desktop model orchestration. No claim that Copilot model usage works without sign-in.&lt;/li&gt;
&lt;li&gt;No source-code, repository, or MR changes. No unit-test results substituted for runtime coverage.&lt;/li&gt;
&lt;li&gt;Recording required recovery after an unfinalized MP4 stop. The supplied condensed video is reconstructed from the original desktop H.264 frames, with idle sections removed, two labeled portions at 2× speed, and explanatory overlays; it is not a second test run. Screenshots/logs are the primary evidence.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Facts vs interpretation:&lt;/strong&gt; successful local launches and Inspector tool responses are observed facts. Session/policy routing is the best-supported explanation for the failed entry; these tests do not establish which VS Code setting or sign-in would repair that session-bound route. No Sentinel launch failure was observed on the functioning local/client routes.&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>opensource</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>sentinel-ir 0.5.9 / sentinel-ir-mcp 0.5.9: ordinary-user test, round 3 (macOS)</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sun, 15 Nov 2026 17:53:37 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-059-sentinel-ir-mcp-059-ordinary-user-test-round-3-macos-5e88</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-059-sentinel-ir-mcp-059-ordinary-user-test-round-3-macos-5e88</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdzznspgmirey6fqxp0m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkdzznspgmirey6fqxp0m.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Machine:&lt;/strong&gt; macOS 26.6.2 (Darwin 25.6.0), Apple Silicon arm64&lt;br&gt;
&lt;strong&gt;Node:&lt;/strong&gt; 20.20.2 (npm 10.8.2), 22.23.5 (npm 10.9.9), 24.22.3 (npm 11.19.1). The desktop-client runs used Homebrew Node 24.21.0 (npm 10.8.3).&lt;/p&gt;

&lt;p&gt;Source (GitLab, Apache-2.0): &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir&lt;/a&gt;&lt;br&gt;
Playground (runs in your browser): &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Packages:&lt;/strong&gt; everything came from the npm registry (&lt;code&gt;sentinel-ir@0.5.9&lt;/code&gt;, &lt;code&gt;sentinel-ir-mcp@0.5.9&lt;/code&gt;, &lt;code&gt;stable-0.5&lt;/code&gt; = &lt;code&gt;latest&lt;/code&gt; = 0.5.9). I read the version of the copy that actually ran from its &lt;code&gt;package.json&lt;/code&gt; in &lt;code&gt;~/.npm/_npx/&amp;lt;key&amp;gt;/node_modules/...&lt;/code&gt; each time. No repo checkouts, no code changes, no MRs.&lt;br&gt;
&lt;strong&gt;MCP client:&lt;/strong&gt; a real &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt; &lt;code&gt;Client&lt;/code&gt; + &lt;code&gt;StdioClientTransport&lt;/code&gt; that starts &lt;code&gt;npx -y sentinel-ir-mcp@... &amp;lt;folder&amp;gt;&lt;/code&gt;. I also used VS Code 1.146.0, Cursor 3.30.14 and Claude Desktop 5.11492.5.&lt;/p&gt;
&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;There are &lt;strong&gt;no blockers&lt;/strong&gt;. Every 0.5.9 fix in scope works at runtime on macOS. The remaining problems are in &lt;code&gt;doctor&lt;/code&gt;'s output, a gap between the library README and its behaviour, and some smaller papercuts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Issue&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;bug&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor --client vscode&lt;/code&gt; / &lt;code&gt;claude-code&lt;/code&gt; prints &lt;code&gt;[ok] Excluded: nothing&lt;/code&gt; and &lt;code&gt;[ok] Audit log: off&lt;/code&gt; even when the real client entry has &lt;code&gt;--exclude=secrets --audit-log&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;bug&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt;'s stale-npx-cache check misses a pinned &lt;code&gt;@0.5.8&lt;/code&gt; copy, and it labels every stale copy &lt;code&gt;sentinel-ir-mcp@^0.5.8&lt;/code&gt;, whatever the user actually wrote (&lt;code&gt;@0.5&lt;/code&gt;, &lt;code&gt;@latest&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;bug (docs)&lt;/td&gt;
&lt;td&gt;Library: an explicit unsupported &lt;code&gt;language&lt;/code&gt; (&lt;code&gt;"ruby"&lt;/code&gt;) returns &lt;code&gt;language: "ruby"&lt;/code&gt; with the message under &lt;code&gt;facts.error&lt;/code&gt;. The README promises &lt;code&gt;language: "unknown"&lt;/code&gt; and &lt;code&gt;facts.reason&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;Library README "Field reference" shows &lt;code&gt;world.risk&lt;/code&gt; / &lt;code&gt;mission.objective&lt;/code&gt;, but &lt;code&gt;analyze()&lt;/code&gt; returns flat &lt;code&gt;risk&lt;/code&gt; / &lt;code&gt;objective&lt;/code&gt;, so &lt;code&gt;ir.world.risk&lt;/code&gt; throws a TypeError&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;Library README "Why send IR" size table and the "about 400 characters" claim are out of date for 0.5.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; prints &lt;code&gt;[ok] analyze_directory works ... (0 file(s) checked)&lt;/code&gt; when nothing was checked&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;Fix-it hints from &lt;code&gt;doctor&lt;/code&gt; and &lt;code&gt;setup&lt;/code&gt; leave out &lt;code&gt;--client&lt;/code&gt; (and &lt;code&gt;--global&lt;/code&gt;), so copying them fails outside a TTY or changes the wrong config&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;MCP &lt;code&gt;analyze_code.language&lt;/code&gt; is case-sensitive (&lt;code&gt;"JS"&lt;/code&gt; gives a -32602 error); the library accepts &lt;code&gt;"JS"&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;mailto:...&lt;/code&gt; / &lt;code&gt;data:...&lt;/code&gt; are treated as relative file names (&lt;code&gt;Not found&lt;/code&gt;), not refused as URLs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;URL refusal text is formatted differently from every other refusal&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;The folder summary line in &lt;code&gt;log&lt;/code&gt; leaves out the skipped-symlink and incomplete counts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;12&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;Different per-file shapes in &lt;code&gt;analyze_directory&lt;/code&gt; (&lt;code&gt;riskSignals&lt;/code&gt; without &lt;code&gt;forbidden&lt;/code&gt; for parsed files, &lt;code&gt;forbidden&lt;/code&gt; with empty &lt;code&gt;riskSignals&lt;/code&gt; for broken ones)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;13&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;Rust: idiomatic &lt;code&gt;use std::process; process::exit(0)&lt;/code&gt; in a broken file is &lt;code&gt;critical&lt;/code&gt; with &lt;code&gt;forbidden: ["child_process"]&lt;/code&gt;. A parsed &lt;code&gt;process::exit&lt;/code&gt; is &lt;code&gt;low&lt;/code&gt; but still has &lt;code&gt;forbidden: ["process.exit"]&lt;/code&gt; (a JS-style name)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;14&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;examples/basic.js&lt;/code&gt; on a missing file prints a raw Node &lt;code&gt;ENOENT&lt;/code&gt; stack trace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;15&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;Cosmetic: ragged line wrap in &lt;code&gt;guide&lt;/code&gt; step 2, uneven sub-line indentation in &lt;code&gt;doctor&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;16&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;MCP README: the audit-log reason list leaves out &lt;code&gt;not-a-folder&lt;/code&gt;; "What the server can and cannot do" leaves out the URL / UNC refusals; the minimum &lt;code&gt;sentinel-ir&lt;/code&gt; version appears only in the changelog&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;17&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;MCP README says npx range copies stay stale "incl. npm 11.21.2"; on npm 11.19.1 the &lt;code&gt;@0.5&lt;/code&gt; copy was refreshed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;18&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;Library README alias column lists &lt;code&gt;py&lt;/code&gt; / &lt;code&gt;rs&lt;/code&gt; only; &lt;code&gt;js&lt;/code&gt; / &lt;code&gt;ts&lt;/code&gt; (and upper case) also work&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h2&gt;
  
  
  Results of the 8 regression checks
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;UNC / remote &lt;code&gt;file://&lt;/code&gt; refused before any FS/network access&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS (macOS semantics).&lt;/strong&gt; &lt;code&gt;//server/share/x.js&lt;/code&gt; → &lt;code&gt;Access denied: ... is outside the allowed folders&lt;/code&gt;. &lt;code&gt;file://server/share/x.js&lt;/code&gt;, &lt;code&gt;file://192.168.1.50/share/x.js&lt;/code&gt; and &lt;code&gt;smb://192.168.1.50/s/x.js&lt;/code&gt; → &lt;code&gt;path must be a file path, not a URL&lt;/code&gt;. &lt;code&gt;file:////192.168.1.50/s/x.js&lt;/code&gt; → outside. &lt;code&gt;\\server\share\x.js&lt;/code&gt; → &lt;code&gt;Not found&lt;/code&gt;, which is correct on POSIX because &lt;code&gt;\&lt;/code&gt; is an ordinary file-name character. &lt;code&gt;tcpdump -i any&lt;/code&gt; on ports 445/139/137/53/5353 during the calls: &lt;strong&gt;zero SMB packets&lt;/strong&gt; and no DNS lookup for &lt;code&gt;server&lt;/code&gt; or the IP (only unrelated Apple DNS).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;file:src/x.js&lt;/code&gt;, &lt;code&gt;http(s)://&lt;/code&gt;, &lt;code&gt;ftp://&lt;/code&gt; refused as URLs; local &lt;code&gt;file:///&lt;/code&gt; works&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS.&lt;/strong&gt; &lt;code&gt;file:src/x.js&lt;/code&gt;, &lt;code&gt;file:src/app.js&lt;/code&gt; (the file exists), &lt;code&gt;http://&lt;/code&gt;, &lt;code&gt;https://&lt;/code&gt;, &lt;code&gt;HTTP://&lt;/code&gt; and &lt;code&gt;ftp://&lt;/code&gt; all give &lt;code&gt;path must be a file path, not a URL&lt;/code&gt;. &lt;code&gt;file:///&amp;lt;root&amp;gt;/src/app.js&lt;/code&gt; (raw and percent-encoded, with spaces and &lt;code&gt;ž&lt;/code&gt;) and &lt;code&gt;file://localhost/...&lt;/code&gt; analyse fine. On another volume the local &lt;code&gt;file://&lt;/code&gt; URL also works. Side note: &lt;code&gt;mailto:&lt;/code&gt; / &lt;code&gt;data:&lt;/code&gt; are not covered (#9).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Broken Python/Rust is &lt;code&gt;critical&lt;/code&gt; only when &lt;code&gt;forbidden&lt;/code&gt; names the capability&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS.&lt;/strong&gt; Broken files with &lt;code&gt;subprocess&lt;/code&gt;, &lt;code&gt;os.system&lt;/code&gt;, &lt;code&gt;runpy&lt;/code&gt;, &lt;code&gt;spec_from_file_location(...).loader.exec_module&lt;/code&gt;, &lt;code&gt;from ctypes import *&lt;/code&gt; and &lt;code&gt;std::process::Command&lt;/code&gt; (short and full path) → &lt;code&gt;critical&lt;/code&gt;, with the capability in &lt;code&gt;forbidden&lt;/code&gt;. Harmless ones (&lt;code&gt;importlib.reload&lt;/code&gt;, &lt;code&gt;from ctypes import c_int&lt;/code&gt;, &lt;code&gt;std::process::exit&lt;/code&gt;, &lt;code&gt;std::process::id&lt;/code&gt;, &lt;code&gt;std::io::stdin&lt;/code&gt;, plain code) → &lt;code&gt;low&lt;/code&gt;, &lt;code&gt;forbidden: []&lt;/code&gt;. Same results on Node 20/22/24 and through MCP. Bare &lt;code&gt;import pickle&lt;/code&gt; / &lt;code&gt;import ctypes&lt;/code&gt; / &lt;code&gt;use std::process&lt;/code&gt; → &lt;code&gt;critical&lt;/code&gt; as documented (see #13).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analyze_directory&lt;/code&gt; summaries list &lt;code&gt;forbidden&lt;/code&gt; for dangerous broken files&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS.&lt;/strong&gt; In a folder of 11 dangerous and 7 harmless broken files, the 11 are &lt;code&gt;critical&lt;/code&gt; with &lt;code&gt;forbidden&lt;/code&gt; and the 7 are &lt;code&gt;low&lt;/code&gt; without &lt;code&gt;forbidden&lt;/code&gt;.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analyze_code.language&lt;/code&gt; accepts &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;ts&lt;/code&gt;, &lt;code&gt;py&lt;/code&gt;, &lt;code&gt;rs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS.&lt;/strong&gt; All four work, and the enum is in the tool schema. Upper-case &lt;code&gt;"JS"&lt;/code&gt; is rejected (#8).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;log&lt;/code&gt; wording&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS.&lt;/strong&gt; &lt;code&gt;x.rb&lt;/code&gt; → "incomplete: unsupported language". Remote URL → "not a local file path". Outside → "outside the allowed folders". Hard link → "file has several hard links". In a folder, "3 not read (several hard links)" and "1 not read (too large or unreadable)" are separate. "not a folder" / "not a file" are correct. Symlink and incomplete counts are missing from the folder line (#11).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt;: unmatched exclusion warning; stale-cache wording for range vs tag&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PARTIAL.&lt;/strong&gt; The unmatched-exclusion warning works (&lt;code&gt;[warn] ... matches nothing&lt;/code&gt;). The stale-cache warning appears, but it always names the npx-internal range &lt;code&gt;^0.5.8&lt;/code&gt; instead of the user's spec (&lt;code&gt;@0.5&lt;/code&gt;, &lt;code&gt;@latest&lt;/code&gt;) and misses a pinned &lt;code&gt;@0.5.8&lt;/code&gt; copy (#2).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;README documents &lt;code&gt;language&lt;/code&gt; + aliases, every refusal reason, minimum &lt;code&gt;sentinel-ir&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PARTIAL.&lt;/strong&gt; The MCP README Tools table documents &lt;code&gt;language&lt;/code&gt; and its aliases. The refusal reasons are spread across the changelog and the security section, and &lt;code&gt;not-a-folder&lt;/code&gt; is missing from the audit-log list. "Needs sentinel-ir 0.5.9" appears only in the changelog (#16).&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;0.5.8 fixes rechecked, all still OK: the same &lt;code&gt;Access denied&lt;/code&gt; for existing and missing outside paths (including &lt;code&gt;..&lt;/code&gt;, &lt;code&gt;src/../..&lt;/code&gt; and a dangling symlink); &lt;code&gt;.rb&lt;/code&gt; / &lt;code&gt;.go&lt;/code&gt; → &lt;code&gt;incomplete&lt;/code&gt; / &lt;code&gt;unknown&lt;/code&gt;; &lt;code&gt;doctor --client&lt;/code&gt; works for each of cursor, windsurf, claude-desktop, vscode and claude-code; &lt;code&gt;log | head -1&lt;/code&gt; exits cleanly with no EPIPE; strict TypeScript declarations; Rust/TSX parse positions; &lt;code&gt;.jsx&lt;/code&gt; accepted.&lt;/p&gt;
&lt;h2&gt;
  
  
  Issues
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. bug: &lt;code&gt;doctor --client vscode&lt;/code&gt; reports exclusions and audit log that contradict the real config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS 26.6.2 arm64, Node 24.21.0 (Homebrew). The same code path runs on 20/22/24.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;&lt;code&gt;cd "/Users/devin/Projekty/Můj projekt ž"&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 setup --client vscode --exclude secrets --audit-log&lt;/code&gt;, then run the printed &lt;code&gt;code --add-mcp '{...}'&lt;/code&gt; command. VS Code writes &lt;code&gt;~/Library/Application Support/Code/User/mcp.json&lt;/code&gt; containing &lt;code&gt;"--exclude=secrets", "--audit-log"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor --client vscode&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; Because doctor cannot read VS Code's list, it should not say anything about exclusions or the audit log, or it should say "unknown, check the VS Code entry". It should not report &lt;code&gt;[ok] Excluded: nothing&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt; It says, as &lt;code&gt;[ok]&lt;/code&gt;, that nothing is excluded and the audit log is off, which is the opposite of the real setup. A user who checked can come away thinking their &lt;code&gt;secrets&lt;/code&gt; exclusion is gone, or that it is not needed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full output:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ok]   Node.js v24.21.0
[ok]   sentinel-ir-mcp 0.5.9, sentinel-ir 0.5.9
[ok]   sentinel-ir-mcp 0.5.9 is the newest stable-0.5 on npm
[ok]   npx cache (/Users/devin/.npm/_npx): no copy with sentinel-ir older than 0.5.9
[skip] VS Code keeps its own server list; check it there (VS Code: "MCP: List Servers", Claude Code: "claude mcp list").
[ok]   Excluded: nothing (the agent may analyze every source file in the folder)
[ok]   Audit log: off (turn it on: npx -y sentinel-ir-mcp@stable-0.5 setup --audit-log)
[ok]   MCP handshake with sentinel-ir-mcp 0.5.9
[ok]   Tools: analyze_code, analyze_directory, analyze_file
[ok]   analyze_directory works on /Users/devin/Projekty/Můj projekt ž (1 file(s) checked)

All checks passed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The actual &lt;code&gt;mcp.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"sentinel-ir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"stdio"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"command"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"npx"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"args"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"-y"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"sentinel-ir-mcp@stable-0.5"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"--exclude=secrets"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"--audit-log"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"/Users/devin/Projekty/Můj projekt ž"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nl"&gt;"inputs"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The audit-log hint has the same problem: &lt;code&gt;setup --audit-log&lt;/code&gt; without &lt;code&gt;--client vscode&lt;/code&gt; would not change this entry (see #7). I did not run &lt;code&gt;claude-code&lt;/code&gt; separately; the &lt;code&gt;[skip]&lt;/code&gt; line shows it takes the same branch.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. bug: the stale-npx-cache warning misses a pinned copy and names a range the user never wrote
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS 26.6.2 arm64, Node 20.20.2 / npm 10.8.2. Isolated &lt;code&gt;HOME=/Users/devin/r3/hup20&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro (pinned copy missed):&lt;/strong&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;npx -y sentinel-ir-mcp@0.5.8 --version&lt;/code&gt; → &lt;code&gt;0.5.8&lt;/code&gt;. The cache key &lt;code&gt;e90bfea4b29781f8&lt;/code&gt; = sha512("&lt;a href="mailto:sentinel-ir-mcp@0.5.8"&gt;sentinel-ir-mcp@0.5.8&lt;/a&gt;"), and its &lt;code&gt;package.json&lt;/code&gt; has 0.5.8 / sentinel-ir 0.5.8.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npx --before=2026-11-15T16:00:00Z -y sentinel-ir-mcp@latest --version&lt;/code&gt; → &lt;code&gt;0.5.8&lt;/code&gt; (key &lt;code&gt;44517f9a0f1686df&lt;/code&gt; = &lt;code&gt;@latest&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor --client cursor&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt; Doctor reports only the &lt;code&gt;@latest&lt;/code&gt; copy:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[warn] The npx cache still holds sentinel-ir-mcp@^0.5.8 (sentinel-ir-mcp 0.5.8, sentinel-ir 0.5.8) in /Users/devin/r3/hup20/.npm/_npx/44517f9a0f1686df; a client entry with that version or range keeps starting it, and sentinel-ir older than 0.5.9 misses security fixes
       -&amp;gt; Close the client, delete that folder (npm 11: npm cache npx rm 44517f9a0f1686df), then run: npx -y sentinel-ir-mcp@stable-0.5 setup
...
All checks passed, 1 warning above.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then move &lt;code&gt;44517f9a0f1686df&lt;/code&gt; away and run doctor again. It prints &lt;code&gt;All checks passed.&lt;/code&gt; with no warning, even though &lt;code&gt;e90bfea4b29781f8&lt;/code&gt; (a pinned &lt;code&gt;@0.5.8&lt;/code&gt;, the one copy that really does keep starting 0.5.8 forever) is still in the cache with sentinel-ir 0.5.8.&lt;br&gt;
  The &lt;code&gt;@latest&lt;/code&gt; copy that &lt;em&gt;was&lt;/em&gt; flagged gets refreshed by npm 10 itself: &lt;code&gt;npx -y sentinel-ir-mcp@latest --version&lt;/code&gt; → &lt;code&gt;0.5.9&lt;/code&gt;, and its &lt;code&gt;package.json&lt;/code&gt; → 0.5.9.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (wording):&lt;/strong&gt; seed &lt;code&gt;@0.5&lt;/code&gt; the same way (&lt;code&gt;npx --before=... -y sentinel-ir-mcp@0.5 --version&lt;/code&gt;), then run doctor. The warning names &lt;code&gt;sentinel-ir-mcp@^0.5.8&lt;/code&gt; for key &lt;code&gt;46f9bf218df4163a&lt;/code&gt; (= &lt;code&gt;sentinel-ir-mcp@0.5&lt;/code&gt;). That is the range npx writes into its private &lt;code&gt;package.json&lt;/code&gt;; the user's config says &lt;code&gt;sentinel-ir-mcp@0.5&lt;/code&gt;. For the &lt;code&gt;@latest&lt;/code&gt; key it also says &lt;code&gt;^0.5.8&lt;/code&gt;, so the message reads the same for a range and a tag.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; Every cached copy with sentinel-ir &amp;lt; 0.5.9 is listed, labelled with the spec a client entry would contain (&lt;code&gt;sentinel-ir-mcp@0.5&lt;/code&gt;, &lt;code&gt;@latest&lt;/code&gt;, &lt;code&gt;@0.5.8&lt;/code&gt;). The text says which of them npx refreshes by itself (tags) and which stay stale (pinned versions; ranges on npm 10).&lt;/li&gt;
&lt;li&gt;Note: the hint shows &lt;code&gt;npm 11: npm cache npx rm ...&lt;/code&gt; on npm 10, which is labelled and fine.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. bug (docs vs behaviour): an explicit unsupported &lt;code&gt;language&lt;/code&gt; is not reported as &lt;code&gt;unknown&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS 26.6.2 arm64, Node 20/22/24 (same result).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; &lt;code&gt;npm install sentinel-ir&lt;/code&gt;, then
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compress&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sentinel-ir"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"x = 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"ruby"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nb"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;analysisStatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;facts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected (README §Languages):&lt;/strong&gt; "An explicit unsupported &lt;code&gt;language&lt;/code&gt; (e.g. &lt;code&gt;"ruby"&lt;/code&gt;) ... is reported as &lt;code&gt;analysisStatus: "incomplete"&lt;/code&gt; with &lt;code&gt;language: "unknown"&lt;/code&gt; and &lt;code&gt;facts.reason: "unsupported file type: .rb (…)"&lt;/code&gt;".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="n"&gt;incomplete&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;unsupported&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;supported&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rust&lt;/span&gt;&lt;span class="err"&gt;)'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="err"&gt;'&lt;/span&gt;&lt;span class="n"&gt;unsupported&lt;/span&gt; &lt;span class="n"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ruby&lt;/span&gt; &lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="n"&gt;supported&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;javascript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;typescript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tsx&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;python&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;rust&lt;/span&gt;&lt;span class="err"&gt;)'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;language&lt;/code&gt; is &lt;code&gt;"ruby"&lt;/code&gt;, not &lt;code&gt;"unknown"&lt;/code&gt;. The raw IR puts the message in &lt;code&gt;facts.error&lt;/code&gt;, not &lt;code&gt;facts.reason&lt;/code&gt; (only &lt;code&gt;compress()&lt;/code&gt; uses &lt;code&gt;reason&lt;/code&gt;). A target with an unknown extension (&lt;code&gt;x.rb&lt;/code&gt;) does give &lt;code&gt;unknown&lt;/code&gt; + &lt;code&gt;facts.reason&lt;/code&gt;, so the two paths are inconsistent. Code that checks &lt;code&gt;ir.language === "unknown"&lt;/code&gt; misses this case. Also, &lt;code&gt;analyze("eval(1)", {language:"ruby"})&lt;/code&gt; comes back &lt;code&gt;incomplete&lt;/code&gt; + &lt;code&gt;critical&lt;/code&gt; from the text heuristics. That is fine, but the README does not say it.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. docs: the library README field reference does not match the shape &lt;code&gt;analyze()&lt;/code&gt; returns
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS, Node 20/22/24.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; following the README, &lt;code&gt;const ir = analyze(src, { target: "app.js" }); console.log(ir.world.risk);&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; prints the risk, since the README "Field reference" documents &lt;code&gt;world.risk&lt;/code&gt;, &lt;code&gt;mission.objective&lt;/code&gt; and so on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;TypeError: Cannot read properties of undefined (reading 'risk')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The raw IR is flat (&lt;code&gt;ir.risk&lt;/code&gt;, &lt;code&gt;ir.target&lt;/code&gt;, &lt;code&gt;ir.objective&lt;/code&gt;, &lt;code&gt;ir.forbidden&lt;/code&gt;, &lt;code&gt;ir.analysisStatus&lt;/code&gt;, &lt;code&gt;ir.facts&lt;/code&gt;). Only &lt;code&gt;compress(ir)&lt;/code&gt; has &lt;code&gt;world&lt;/code&gt; / &lt;code&gt;mission&lt;/code&gt;. The shipped &lt;code&gt;.d.ts&lt;/code&gt; is correct, so TypeScript users find out at compile time and JS users at runtime. The README should say which shape each section describes.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. docs: the library "Why send IR" table and the "about 400 characters" claim are out of date
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS, Node 22. &lt;code&gt;node node_modules/sentinel-ir/examples/basic.js &amp;lt;file&amp;gt;&lt;/code&gt; on the package's own files.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;README:&lt;/strong&gt; &lt;code&gt;ast-facts.js&lt;/code&gt; 21,823 → 449; &lt;code&gt;sentinel-ir.js&lt;/code&gt; 9,123 → 515; &lt;code&gt;tree-parser.js&lt;/code&gt; 4,393 → 465; &lt;code&gt;basic.js&lt;/code&gt; 600 → 461; "about 400 characters plus the facts".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual 0.5.9:&lt;/strong&gt; &lt;code&gt;ast-facts.js&lt;/code&gt; 96,821 → 511; &lt;code&gt;sentinel-ir.js&lt;/code&gt; 20,413 → 621; &lt;code&gt;tree-parser.js&lt;/code&gt; 13,175 → 803; &lt;code&gt;basic.js&lt;/code&gt; 600 → 510/532. The ratio claim still holds, but the numbers no longer match.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. papercut: &lt;code&gt;doctor&lt;/code&gt; says &lt;code&gt;[ok] analyze_directory works&lt;/code&gt; after checking 0 files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS, Node 22.23.5.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; &lt;code&gt;mkdir "p q" &amp;amp;&amp;amp; cd "p q" &amp;amp;&amp;amp; echo 'eval(1)' &amp;gt; a.js &amp;amp;&amp;amp; ln -f a.js ../a_link.js&lt;/code&gt; (so the only file has 2 links), then &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 setup --client cursor --no-exclude --no-audit-log --yes&lt;/code&gt; and &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor --client cursor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; &lt;code&gt;[warn]&lt;/code&gt; "no file could be analysed (1 skipped: several hard links)", or at least no "works" claim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ok]   analyze_directory works on /Users/devin/r3/hl/p q (0 file(s) checked)

All checks passed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I saw the same on the second volume when the first candidate file was a hard link.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. papercut: copy-paste hints leave out &lt;code&gt;--client&lt;/code&gt; / &lt;code&gt;--global&lt;/code&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS, Node 20/22/24.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro A:&lt;/strong&gt; the stale-cache hint in #2 ends with &lt;code&gt;then run: npx -y sentinel-ir-mcp@stable-0.5 setup&lt;/code&gt;. Run from a script, CI or a non-TTY terminal:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sentinel-ir-mcp setup: Choose a client with --client (cursor, windsurf, claude-desktop, vscode, claude-code).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro B:&lt;/strong&gt; &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 setup --client cursor --global --dry-run&lt;/code&gt; prints &lt;code&gt;Audit log: off (turn it on with: npx -y sentinel-ir-mcp@stable-0.5 setup --audit-log)&lt;/code&gt;. The hint drops &lt;code&gt;--client cursor --global&lt;/code&gt;, so in a TTY it asks again, and it may target the project config instead of the global one. Doctor for vscode prints the same hint (#1).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; the hints repeat the &lt;code&gt;--client&lt;/code&gt; (and &lt;code&gt;--global&lt;/code&gt;) that was used. Related: the README says "without &lt;code&gt;--client&lt;/code&gt; it asks", but it only asks on a TTY. With stdin piped it exits with the message above. That is reasonable behaviour, but the README should mention it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8. papercut: MCP &lt;code&gt;analyze_code.language&lt;/code&gt; is case-sensitive while the library is not
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;OS / Node:&lt;/strong&gt; macOS, Node 20/22/24, via the MCP SDK client.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; &lt;code&gt;callTool("analyze_code", { code: "eval(1)", target: "a.js", language: "JS" })&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MCP error -32602: Input validation error: Invalid arguments for tool analyze_code: Invalid option: expected one of "javascript"|"typescript"|"tsx"|"python"|"rust"|"js"|"ts"|"py"|"rs" at language
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library &lt;code&gt;analyze("eval(1)", { language: "JS" })&lt;/code&gt; → &lt;code&gt;language: "javascript"&lt;/code&gt;, &lt;code&gt;complete&lt;/code&gt;, &lt;code&gt;critical&lt;/code&gt;. The error message itself is clear; this is a consistency issue only.&lt;/p&gt;

&lt;h3&gt;
  
  
  9. papercut: &lt;code&gt;mailto:&lt;/code&gt; / &lt;code&gt;data:&lt;/code&gt; paths are treated as relative file names
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (MCP, Node 24):&lt;/strong&gt; &lt;code&gt;analyze_file { path: "mailto:a@b.js" }&lt;/code&gt; and &lt;code&gt;analyze_file { path: "data:text/javascript,eval(1)" }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt; &lt;code&gt;Not found: mailto:a@b.js&lt;/code&gt;, &lt;code&gt;Not found: data:text/javascript,eval(1)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; the same &lt;code&gt;path must be a file path, not a URL&lt;/code&gt; as &lt;code&gt;file:src/x.js&lt;/code&gt; / &lt;code&gt;http://&lt;/code&gt;, which the 0.5.9 changelog describes as covering "other URLs". There is no security impact (they resolve inside the root and do not exist), but the wording is inconsistent.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. papercut: the URL refusal is formatted differently from the other refusals
&lt;/h3&gt;

&lt;p&gt;Other errors start with a capitalised label and name the path: &lt;code&gt;Access denied: X is outside the allowed folders&lt;/code&gt;, &lt;code&gt;Not found: X&lt;/code&gt;, &lt;code&gt;File too large: big.js is 1800000 bytes, limit is 1048576&lt;/code&gt;, &lt;code&gt;Unsupported file type: ...&lt;/code&gt;, &lt;code&gt;Not a file: ...&lt;/code&gt;. The URL refusal is a lowercase sentence that does not name the path: &lt;code&gt;path must be a file path, not a URL&lt;/code&gt;. Suggested wording: &lt;code&gt;Not a file path: &amp;lt;value&amp;gt; is a URL&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  11. papercut: the folder summary in &lt;code&gt;log&lt;/code&gt; leaves out the symlink and incomplete counts
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; &lt;code&gt;analyze_directory&lt;/code&gt; on a folder with 3 symlinks, 3 hard links, 1 oversized file and broken files, then &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 log&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt; the folder line lists the analysed files, "3 not read (several hard links)" and "1 not read (too large or unreadable)". The 3 skipped symlinks and the number of &lt;code&gt;incomplete&lt;/code&gt; files, both in the tool result, do not appear in the log.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; the same counts the agent saw, since the log is the user's record of what the agent got.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  12. papercut: inconsistent per-file entries in &lt;code&gt;analyze_directory&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Parsed files have &lt;code&gt;riskSignals&lt;/code&gt; (e.g. &lt;code&gt;["dynamic_code:eval@4"]&lt;/code&gt;) and no &lt;code&gt;forbidden&lt;/code&gt; key. Broken files have &lt;code&gt;forbidden&lt;/code&gt; and &lt;code&gt;riskSignals: []&lt;/code&gt;, even when they are &lt;code&gt;critical&lt;/code&gt;. A consumer has to read both fields to find out why a file is risky. The guide tells agents to look at &lt;code&gt;riskSignals&lt;/code&gt;, so for a broken critical file they see an empty list.&lt;/p&gt;

&lt;h3&gt;
  
  
  13. papercut: Rust &lt;code&gt;process::exit&lt;/code&gt; naming in broken and parsed files
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (library, Node 22):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;A broken file containing &lt;code&gt;use std::process;&lt;/code&gt; … &lt;code&gt;process::exit(0)&lt;/code&gt; (Rust Book ch. 12 style) plus a syntax error → &lt;code&gt;critical&lt;/code&gt;, &lt;code&gt;forbidden: ["child_process"]&lt;/code&gt;. The same file without the syntax error → &lt;code&gt;low&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;A parsed file with &lt;code&gt;std::process::exit(1)&lt;/code&gt; → &lt;code&gt;risk: "low"&lt;/code&gt; but &lt;code&gt;forbidden: ["process.exit"]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The bare &lt;code&gt;use std::process&lt;/code&gt; rule is documented, but a "child_process" label on Rust code that only exits looks wrong to a newcomer. A non-empty &lt;code&gt;forbidden&lt;/code&gt; with a JS name at &lt;code&gt;low&lt;/code&gt; risk is confusing.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  14. papercut: &lt;code&gt;examples/basic.js&lt;/code&gt; prints a stack trace for a missing file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (Node 22):&lt;/strong&gt; &lt;code&gt;node node_modules/sentinel-ir/examples/basic.js nope.js&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;node:fs:440
    return binding.readFileUtf8(path, stringToFlags(options.flag));
                   ^

Error: ENOENT: no such file or directory, open 'nope.js'
    at Object.readFileSync (node:fs:440:20)
    at Object.&amp;lt;anonymous&amp;gt; (/Users/devin/r3/lib22/node_modules/sentinel-ir/examples/basic.js:7:17)
    at Module._compile (node:internal/modules/cjs/loader:1781:14)
    ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; a one-line &lt;code&gt;basic.js: cannot read nope.js&lt;/code&gt; plus usage. Also, &lt;code&gt;npx sentinel-ir file.js&lt;/code&gt; gives &lt;code&gt;npm error could not determine executable to run&lt;/code&gt;. The README does not promise a CLI, but a newcomer will try it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  15. papercut (cosmetic): text layout
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;guide&lt;/code&gt; step 2 has a ragged wrap:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2. Look at "incomplete", "skipped.unreadable" and "skipped.hardlinks" before
   anything else. Those
   files were not fully analyzed: ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;In &lt;code&gt;doctor&lt;/code&gt;, the sub-lines under &lt;code&gt;[ok]&lt;/code&gt; and &lt;code&gt;[warn]&lt;/code&gt; items are indented differently (&lt;code&gt;[ok]     in ...&lt;/code&gt; vs &lt;code&gt;[warn]   matches nothing&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  16. docs: gaps in the MCP README
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The audit-log section lists why a request was refused (&lt;code&gt;excluded&lt;/code&gt;, &lt;code&gt;outside&lt;/code&gt;, ...) but leaves out &lt;code&gt;not-a-folder&lt;/code&gt;, which &lt;code&gt;log&lt;/code&gt; does print.&lt;/li&gt;
&lt;li&gt;"What the server can and cannot do" (the section a cautious user reads) does not mention that URLs, &lt;code&gt;file:&lt;/code&gt; without &lt;code&gt;//&lt;/code&gt;, remote &lt;code&gt;file://host/&lt;/code&gt; and UNC paths are refused. That is only in the 0.5.9 changelog.&lt;/li&gt;
&lt;li&gt;The minimum &lt;code&gt;sentinel-ir&lt;/code&gt; version (0.5.9) is stated only in the changelog, not in "Dependencies" or "Setup".&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  17. docs: npx range refresh depends on the npm version
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; seed &lt;code&gt;npx --before=2026-11-15T16:00:00Z -y sentinel-ir-mcp@0.5 --version&lt;/code&gt; (→ 0.5.8, cache &lt;code&gt;package.json&lt;/code&gt; 0.5.8 / 0.5.8), then &lt;code&gt;npx -y sentinel-ir-mcp@0.5 --version&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm 10.8.2 (Node 20):&lt;/strong&gt; stays &lt;code&gt;0.5.8&lt;/code&gt;, as the README says.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;npm 11.19.1 (Node 24):&lt;/strong&gt; &lt;code&gt;0.5.9&lt;/code&gt;, and the cache &lt;code&gt;46f9bf218df4163a&lt;/code&gt; → 0.5.9 / 0.5.9.&lt;/li&gt;
&lt;li&gt;The README says range copies stay stale and that this was "measured incl. npm 11.21.2". The advice (use &lt;code&gt;@stable-0.5&lt;/code&gt;) is still right, but the sentence should say the behaviour depends on the npm version.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  18. docs: the library alias list is incomplete
&lt;/h3&gt;

&lt;p&gt;The library README table shows &lt;code&gt;python (alias py)&lt;/code&gt; and &lt;code&gt;rust (alias rs)&lt;/code&gt; only. &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;ts&lt;/code&gt; and upper-case names (&lt;code&gt;JS&lt;/code&gt;) also work, and the MCP README lists &lt;code&gt;js&lt;/code&gt; / &lt;code&gt;ts&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I verified works
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;sentinel-ir (Node 20, 22, 24)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm install sentinel-ir&lt;/code&gt; in a fresh project. npm 11 prints the &lt;code&gt;install-scripts ... allowScripts&lt;/code&gt; notice for &lt;code&gt;tree-sitter*&lt;/code&gt;, which the README mentions; analysis works regardless (native prebuild on darwin-arm64, WASM for the others).&lt;/li&gt;
&lt;li&gt;The README Usage example output matches the README exactly. &lt;code&gt;examples/basic.js&lt;/code&gt; works on JS, JSX, TS, &lt;code&gt;.d.ts&lt;/code&gt;, TSX, Python and Rust.&lt;/li&gt;
&lt;li&gt;Files that do not parse fall back to heuristics with &lt;code&gt;facts.ast: false&lt;/code&gt; and correct positions: &lt;code&gt;broken.js&lt;/code&gt; 1:11 unclosed &lt;code&gt;(&lt;/code&gt;, &lt;code&gt;broken.tsx&lt;/code&gt; 1:29 unclosed &lt;code&gt;{&lt;/code&gt;, &lt;code&gt;broken.rs&lt;/code&gt; 1:11 unclosed &lt;code&gt;{&lt;/code&gt;, Python 2:7 missing &lt;code&gt;)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.rb&lt;/code&gt; / &lt;code&gt;.go&lt;/code&gt; targets → &lt;code&gt;incomplete&lt;/code&gt;, &lt;code&gt;unknown&lt;/code&gt;, &lt;code&gt;unsupported file type&lt;/code&gt; reason. Sync Python without init → &lt;code&gt;incomplete&lt;/code&gt; with a clear reason.&lt;/li&gt;
&lt;li&gt;Output is identical on all three Node versions.&lt;/li&gt;
&lt;li&gt;Strict TypeScript (&lt;code&gt;module&lt;/code&gt; = &lt;code&gt;nodenext&lt;/code&gt;, &lt;code&gt;bundler&lt;/code&gt;, &lt;code&gt;preserve&lt;/code&gt;): the shipped declarations type-check, aliases are typed, ESM import works, and wrong argument types are caught.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;sentinel-ir-mcp (Node 20, 22, 24)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npx -y sentinel-ir-mcp@0.5 --version&lt;/code&gt; / &lt;code&gt;-v&lt;/code&gt;, &lt;code&gt;--help&lt;/code&gt;, &lt;code&gt;guide&lt;/code&gt;, &lt;code&gt;log&lt;/code&gt;; &lt;code&gt;log | head -1&lt;/code&gt; exits without EPIPE.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setup&lt;/code&gt;: interactive on a TTY (cursor, with exclusions + audit log), &lt;code&gt;--yes&lt;/code&gt; is idempotent, &lt;code&gt;--dry-run&lt;/code&gt;, &lt;code&gt;--global&lt;/code&gt;, &lt;code&gt;--project &amp;lt;dir&amp;gt;&lt;/code&gt;. Windsurf and Claude Desktop JSON is valid. VS Code / Claude Code print a working command. Re-running preserves keys that Claude Desktop added (&lt;code&gt;preferences&lt;/code&gt;) and makes a backup.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doctor&lt;/code&gt; per client, a real MCP handshake, the unmatched-exclusion warning, the warning for a pinned or old entry ("run setup"), and the 0.5.8 self-check ("This is sentinel-ir-mcp 0.5.8, npm has 0.5.9 ... runs from the npx cache (...)").&lt;/li&gt;
&lt;li&gt;MCP stdio with the SDK client: &lt;code&gt;analyze_code&lt;/code&gt;, &lt;code&gt;analyze_file&lt;/code&gt; and &lt;code&gt;analyze_directory&lt;/code&gt; all work, including paths with spaces and &lt;code&gt;ž ü&lt;/code&gt; and a &lt;code&gt;..cache/&lt;/code&gt; folder name. Refusals: outside existing/missing/&lt;code&gt;..&lt;/code&gt;/&lt;code&gt;src/../..&lt;/code&gt;/dangling symlink → the same &lt;code&gt;Access denied&lt;/code&gt;; symlink out → outside; hard link → &lt;code&gt;has 2 hard links; files with more than one are refused&lt;/code&gt;; &lt;code&gt;big.js&lt;/code&gt; → &lt;code&gt;File too large: big.js is 1800000 bytes, limit is 1048576&lt;/code&gt;; &lt;code&gt;.md&lt;/code&gt; / &lt;code&gt;Makefile&lt;/code&gt; / &lt;code&gt;.rb&lt;/code&gt; → &lt;code&gt;Unsupported file type ... (only ...)&lt;/code&gt;; excluded (existing and missing) → the same refusal; &lt;code&gt;Not a file&lt;/code&gt; / &lt;code&gt;Not a folder&lt;/code&gt;; &lt;code&gt;file:///etc/passwd&lt;/code&gt; → outside; empty path → schema error.&lt;/li&gt;
&lt;li&gt;The audit log records every case above with distinct wording (check 6).&lt;/li&gt;
&lt;li&gt;First connect on a fresh cache took about 1.8 s; npm's "npm 12" notice goes to stderr and does not break stdio.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Upgrade from 0.5.8 (README flow, npm 10.8.2)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Seeded &lt;code&gt;@0.5&lt;/code&gt; and &lt;code&gt;@stable-0.5&lt;/code&gt; with &lt;code&gt;--before&lt;/code&gt; → cache &lt;code&gt;package.json&lt;/code&gt; 0.5.8 / 0.5.8. Then &lt;code&gt;@stable-0.5&lt;/code&gt; → 0.5.9 (cache &lt;code&gt;3ec5bc0582433d0b&lt;/code&gt; 0.5.9 / 0.5.9), while &lt;code&gt;@0.5&lt;/code&gt; stays 0.5.8, as documented. Doctor warns (with the wording problem in #2). Following the README (close the client, delete the folder, &lt;code&gt;setup --client cursor --yes&lt;/code&gt;) rewrites the entry to &lt;code&gt;@stable-0.5&lt;/code&gt;. The command in the config then runs 0.5.9, confirmed from the cache &lt;code&gt;package.json&lt;/code&gt;, and doctor is all &lt;code&gt;[ok]&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Another volume&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;APFS disk image mounted at &lt;code&gt;/Volumes/Druhý disk&lt;/code&gt;, project &lt;code&gt;Moje projekty/app ž&lt;/code&gt; (Node 22). Relative and absolute paths and &lt;code&gt;file://&lt;/code&gt; URLs work. A symlink back to the boot volume is refused as outside, and hard links are refused. &lt;code&gt;analyze_directory&lt;/code&gt; works. &lt;code&gt;setup --client claude-desktop --project "&amp;lt;vol path&amp;gt;" --audit-log --yes&lt;/code&gt; works, doctor is OK, and the exact command from the generated config starts the server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Desktop clients (no login)&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;VS Code 1.146.0:&lt;/strong&gt; the printed &lt;code&gt;code --add-mcp&lt;/code&gt; command → "Added MCP servers: sentinel-ir"; &lt;code&gt;mcp.json&lt;/code&gt; is correct, including the non-ASCII path. "Continue without Signing In" → &lt;code&gt;MCP: List Servers&lt;/code&gt; → Start → &lt;code&gt;Running&lt;/code&gt;, "Discovered 3 tools". &lt;code&gt;ps&lt;/code&gt; showed &lt;code&gt;node ~/.npm/_npx/3ec5bc0582433d0b/node_modules/.bin/sentinel-ir-mcp ...&lt;/code&gt;, whose cache &lt;code&gt;package.json&lt;/code&gt; is 0.5.9 / 0.5.9. &lt;strong&gt;Works end to end.&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cursor 3.30.14:&lt;/strong&gt; accepted the generated &lt;code&gt;.cursor/mcp.json&lt;/code&gt;. Its log shows &lt;code&gt;mcp.installed {name: sentinel-ir, command: npx -y sentinel-ir-mcp@stable-0.5}&lt;/code&gt; and &lt;code&gt;State transition: project-0-Můj projekt ž-sentinel-ir none → disconnected&lt;/code&gt;. It then shows a Log In / Sign Up wall, and no server process starts without login. This is a client limitation, not a package bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Claude Desktop 5.11492.5:&lt;/strong&gt; accepted the config and kept our entry when it added its own &lt;code&gt;preferences&lt;/code&gt;. The "Get started" login wall blocks MCP; &lt;code&gt;mcp.log&lt;/code&gt; stays empty and no process starts. This is a client limitation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Network:&lt;/strong&gt; across all URL and UNC calls, no SMB traffic and no lookups of the named hosts (check 1).&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>agents</category>
      <category>opensource</category>
      <category>architecture</category>
      <category>devops</category>
    </item>
    <item>
      <title>sentinel-ir 0.5.9 / sentinel-ir-mcp 0.5.9: Windows user test (round 3)</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sun, 15 Nov 2026 17:50:46 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-059-sentinel-ir-mcp-059-windows-user-test-round-3-3j87</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-059-sentinel-ir-mcp-059-windows-user-test-round-3-3j87</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnst1e7657t48habgorgk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnst1e7657t48habgorgk.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; 2026-11-15&lt;br&gt;
&lt;strong&gt;OS:&lt;/strong&gt; Windows Server 2022 (x64). I used Git Bash for most steps and PowerShell for pktmon, links, the VHD and the desktop apps.&lt;/p&gt;

&lt;p&gt;Source (GitLab, Apache-2.0): &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir&lt;/a&gt;&lt;br&gt;
npm: &lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir&lt;/a&gt;&lt;br&gt;
Playground (runs in your browser): &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Node:&lt;/strong&gt; 20.20.2 (npm 10.8.2), 22.23.5 (npm 10.9.9), 24.22.2 (npm 11.19.1)&lt;br&gt;
&lt;strong&gt;Other tools:&lt;/strong&gt; MCP client &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt; 1.32.2 (&lt;code&gt;Client&lt;/code&gt; + &lt;code&gt;StdioClientTransport&lt;/code&gt;), TypeScript 5.9.3 and 7.0.2, VS Code (portable zip, latest), Cursor (latest installer), Claude Desktop 0.14.10.&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; only the published npm packages, installed or run through &lt;code&gt;npx&lt;/code&gt;. No repo checkout, no code changes, no MR.&lt;br&gt;
&lt;strong&gt;Not tested:&lt;/strong&gt; macOS and Linux. This session ran only on the Windows machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;There are no blockers. All 8 regression checks for 0.5.9 pass on Windows on Node 20, 22 and 24. Check 8 (README) has small gaps: the aliases are missing from one paragraph and &lt;code&gt;not-a-folder&lt;/code&gt; is missing from the audit-log reasons. The MCP results were identical on all three Node versions. A diff of the normalized output for every call showed no differences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Not verified:&lt;/strong&gt; whether a desktop client actually starts the server from its config.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;VS Code accepted the entry. Starting it from &lt;em&gt;MCP: List Servers&lt;/em&gt; turned the status to &lt;code&gt;Error&lt;/code&gt; with an empty server log, and no &lt;code&gt;sentinel-ir-mcp&lt;/code&gt; process appeared. VS Code was unauthenticated and still in onboarding, so I can't blame the package (see U1).&lt;/li&gt;
&lt;li&gt;Cursor and Claude Desktop stop at their login screens.&lt;/li&gt;
&lt;li&gt;The same command line started fine through the real SDK client.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Issues found
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Severity&lt;/th&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;bug (docs/behaviour mismatch)&lt;/td&gt;
&lt;td&gt;sentinel-ir&lt;/td&gt;
&lt;td&gt;An explicit &lt;code&gt;language: "ruby"&lt;/code&gt; returns &lt;code&gt;language: "ruby"&lt;/code&gt; and reason &lt;code&gt;unsupported language: ruby&lt;/code&gt;, but the README says &lt;code&gt;"unknown"&lt;/code&gt; / &lt;code&gt;unsupported file type: .rb&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analyze_code&lt;/code&gt; rejects &lt;code&gt;language: "JS"&lt;/code&gt; (-32602); the library accepts any case&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; prints &lt;code&gt;[ok] analyze_directory works ... (0 file(s) checked)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; stale-cache warning names the copy &lt;code&gt;sentinel-ir-mcp@^0.5.8&lt;/code&gt; under npm 10 but &lt;code&gt;sentinel-ir-mcp@0.5&lt;/code&gt; under npm 11&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;The README upgrade text ("npx keeps starting the copy it downloaded first for a range such as &lt;code&gt;@0.5&lt;/code&gt;") is true for npm 10 but not for npm 11.19.1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;sentinel-ir&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;examples/basic.js&lt;/code&gt; (the README's CLI route) prints a raw Node stack trace for a missing file&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;sentinel-ir&lt;/td&gt;
&lt;td&gt;The "Fewer tokens" table is stale (raw sizes are 2-4x smaller than the shipped files)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;docs&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;The "Supported" paragraph lists &lt;code&gt;language&lt;/code&gt; values without the aliases, and the audit-log refusal reasons don't include &lt;code&gt;not-a-folder&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;9&lt;/td&gt;
&lt;td&gt;papercut&lt;/td&gt;
&lt;td&gt;sentinel-ir&lt;/td&gt;
&lt;td&gt;Rust: &lt;code&gt;use std::process; process::exit(0)&lt;/code&gt; in a broken file is &lt;code&gt;critical&lt;/code&gt; with &lt;code&gt;child_process&lt;/code&gt;; a valid file with &lt;code&gt;std::process::exit&lt;/code&gt; is &lt;code&gt;low&lt;/code&gt; but &lt;code&gt;forbidden: ["process.exit"]&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;papercut (transient)&lt;/td&gt;
&lt;td&gt;sentinel-ir-mcp&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; once printed &lt;code&gt;[skip] No usable answer from the npm registry ...&lt;/code&gt; while the network was busy&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Issues in detail
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. bug: explicit unsupported &lt;code&gt;language&lt;/code&gt; doesn't match the README (sentinel-ir)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node:&lt;/strong&gt; 20/22/24 (all identical)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  mkdir lib &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; lib &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm init -y &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm install sentinel-ir@0.5.9
  node -e &lt;span class="s1"&gt;'const s=require("sentinel-ir");(async()=&amp;gt;{await s.initializeWasm();
   for (const o of [{language:"ruby"},{target:"x.rb"}]) {
     const r=s.compress(s.analyze("puts 1",o));
     console.log(JSON.stringify(o), r.analysisStatus, r.language, r.facts.reason) }})()'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected (README, "Supported languages"):&lt;/strong&gt; "An explicit unsupported &lt;code&gt;language&lt;/code&gt; (e.g. &lt;code&gt;"ruby"&lt;/code&gt;) or, since 0.5.8, a &lt;code&gt;target&lt;/code&gt; with any other extension ... is reported as &lt;code&gt;analysisStatus: "incomplete"&lt;/code&gt; with &lt;code&gt;language: "unknown"&lt;/code&gt; and &lt;code&gt;facts.reason: "unsupported file type: .rb (…)"&lt;/code&gt;."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  {"language":"ruby"} incomplete ruby unsupported language: ruby (supported: javascript, typescript, tsx, python, rust)
  {"target":"x.rb"} incomplete unknown unsupported file type: .rb (supported: .js .mjs .cjs .jsx .ts .mts .cts .tsx .py .pyi .rs)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The status is correctly &lt;code&gt;incomplete&lt;/code&gt;, but &lt;code&gt;language&lt;/code&gt; echoes the caller's string. Code that switches on &lt;code&gt;language === "unknown"&lt;/code&gt; misses this case. Either always return &lt;code&gt;"unknown"&lt;/code&gt; or change the README sentence.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. papercut: MCP &lt;code&gt;analyze_code&lt;/code&gt; &lt;code&gt;language&lt;/code&gt; is case-sensitive, the library isn't
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node:&lt;/strong&gt; 20/22/24, MCP client SDK 1.32.2, server &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 &amp;lt;folder&amp;gt;&lt;/code&gt; (reports 0.5.9)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; call &lt;code&gt;analyze_code&lt;/code&gt; with &lt;code&gt;{ "code": "eval(x)", "language": "JS" }&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; the same result as &lt;code&gt;js&lt;/code&gt;, because the library accepts &lt;code&gt;JS&lt;/code&gt; and &lt;code&gt;Python&lt;/code&gt;. The 0.5.9 changelog says "&lt;code&gt;analyze_code&lt;/code&gt; takes the &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;ts&lt;/code&gt;, &lt;code&gt;py&lt;/code&gt; and &lt;code&gt;rs&lt;/code&gt; aliases like sentinel-ir".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  MCP error -32602: Input validation error: Invalid option: expected one of "javascript"|"typescript"|"tsx"|"python"|"rust"|"js"|"ts"|"py"|"rs" at language
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Note:&lt;/strong&gt; the error is clear and lists the valid values, so this is minor. The lowercase aliases &lt;code&gt;js&lt;/code&gt;/&lt;code&gt;ts&lt;/code&gt;/&lt;code&gt;py&lt;/code&gt;/&lt;code&gt;rs&lt;/code&gt; all work.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. papercut: &lt;code&gt;doctor&lt;/code&gt; says &lt;code&gt;[ok]&lt;/code&gt; when 0 files were checked
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node:&lt;/strong&gt; 24.22.2&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; use a project folder where the first file the walk reaches is skipped. My fixture had a 1.6 MB &lt;code&gt;big/huge.js&lt;/code&gt;, symlinks and a hard link before &lt;code&gt;src/&lt;/code&gt;; a folder where exclusions leave nothing analyzable also works. Then:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npx -y sentinel-ir-mcp@stable-0.5 setup --client cursor --yes --exclude nomatch-folder --exclude &lt;span class="s2"&gt;"*.rb"&lt;/span&gt; --audit-log
  npx -y sentinel-ir-mcp@stable-0.5 doctor --client cursor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; a &lt;code&gt;[warn]&lt;/code&gt; or &lt;code&gt;[skip]&lt;/code&gt;, for example "nothing analyzable found in the first N files", or the check keeps walking until it analyzes one file.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  [ok]   analyze_directory works on C:\t59\Projekt Žluťoučký kůň\myapp (0 file(s) checked)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The same folder without the Cursor config gives &lt;code&gt;(1 file(s) checked)&lt;/code&gt;. "Works" next to "0 files checked" reads as a contradiction.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. papercut: the stale-npx-cache warning labels the copy differently on npm 10 and npm 11
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (npm 10, Node 20.20.2, &lt;code&gt;npm_config_cache=C:\t59\c20&lt;/code&gt;):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  npx -y sentinel-ir-mcp@0.5.8 --version       &lt;span class="c"&gt;# puts 0.5.8 in the cache (key 46f9bf218df4163a)&lt;/span&gt;
  npx -y sentinel-ir-mcp@0.5 doctor            &lt;span class="c"&gt;# runs 0.5.8 (README behaviour)&lt;/span&gt;
  npx -y sentinel-ir-mcp@stable-0.5 doctor     &lt;span class="c"&gt;# runs 0.5.9&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Actual (npm 10):&lt;/strong&gt; the warning names the cached copy &lt;code&gt;sentinel-ir-mcp@^0.5.8&lt;/code&gt;. On npm 11.19.1 the same situation reads:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  [warn] The npx cache still holds sentinel-ir-mcp@0.5 (sentinel-ir-mcp 0.5.8, sentinel-ir 0.5.8) in C:\Users\Administrator\AppData\Local\npm-cache\_npx\46f9bf218df4163a; a client entry with that version or range keeps starting it, and sentinel-ir older than 0.5.9 misses security fixes
         -&amp;gt; Close the client, delete that folder (npm 11: npm cache npx rm 46f9bf218df4163a), then run: npx -y sentinel-ir-mcp@stable-0.5 setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cause (from the cache files):&lt;/strong&gt; npm 10's &lt;code&gt;_npx/&amp;lt;hash&amp;gt;/package.json&lt;/code&gt; is only &lt;code&gt;{"dependencies":{"sentinel-ir-mcp":"^0.5.8"}}&lt;/code&gt;. npm 11 adds &lt;code&gt;"_npx":{"packages":["sentinel-ir-mcp@0.5"]}&lt;/code&gt;. &lt;code&gt;doctor&lt;/code&gt; falls back to the dependency range, which the user never typed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; a label the user recognises on both npm versions. When &lt;code&gt;_npx.packages&lt;/code&gt; is missing, "a cached copy of sentinel-ir-mcp 0.5.8" would be enough. Apart from the label, both warnings are correct and actionable, and the folder hash is right.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. docs: the README upgrade claim depends on the npm version
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repro (Node 24.22.2 / npm 11.19.1):&lt;/strong&gt; put 0.5.8 in the cache under both &lt;code&gt;@0.5&lt;/code&gt; and &lt;code&gt;@stable-0.5&lt;/code&gt;, then run &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor&lt;/code&gt; and then &lt;code&gt;npx -y sentinel-ir-mcp@0.5 doctor&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual:&lt;/strong&gt; the second command also ran &lt;strong&gt;0.5.9&lt;/strong&gt;. Its cache folder &lt;code&gt;_npx/46f9bf218df4163a/package.json&lt;/code&gt; now reads &lt;code&gt;{"dependencies":{"sentinel-ir-mcp":"^0.5.9"},"_npx":{"packages":["sentinel-ir-mcp@0.5"]}}&lt;/code&gt;. On npm 10.8.2 the &lt;code&gt;@0.5&lt;/code&gt; copy stayed on 0.5.8, as the README says.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Caveat:&lt;/strong&gt; I made the 0.5.8 cache entry with &lt;code&gt;--before&lt;/code&gt;. A cache from a real earlier download may behave differently.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Suggestion:&lt;/strong&gt; say "npm 10 keeps starting ...; npm 11 may re-resolve". The advice to use &lt;code&gt;@stable-0.5&lt;/code&gt; is right either way.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  6. papercut: &lt;code&gt;examples/basic.js&lt;/code&gt; prints a raw stack trace for a missing file
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node:&lt;/strong&gt; 24.22.2 (same on 20/22)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Repro:&lt;/strong&gt; &lt;code&gt;node node_modules/sentinel-ir/examples/basic.js does/not/exist.js&lt;/code&gt;. The README gives this as the command-line route, and the package has no &lt;code&gt;bin&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Actual (exit 1):&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  node:fs:483
      return binding.readFileUtf8(path, stringToFlags(options.flag));
                     ^

  Error: ENOENT: no such file or directory, open 'C:\t59\Projekt Žluťoučký kůň\lib20\does\not\exist.js'
      at Object.readFileSync (node:fs:483:20)
      at Object.&amp;lt;anonymous&amp;gt; (C:\t59\Projekt Žluťoučký kůň\lib20\node_modules\sentinel-ir\examples\basic.js:7:17)
      at Module._compile (node:internal/modules/cjs/loader:1929:14)
      at Object..js (node:internal/modules/cjs/loader:2060:10)
      at Module.load (node:internal/modules/cjs/loader:1651:32)
      at Module._load (node:internal/modules/cjs/loader:1443:12)
      at wrapModuleLoad (node:internal/modules/cjs/loader:261:19)
      at Module.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:154:5)
      at node:internal/main/run_main_module:33:47 {
    errno: -4058,
    code: 'ENOENT',
    syscall: 'open',
    path: 'C:\\t59\\Projekt Žluťoučký kůň\\lib20\\does\\not\\exist.js'
  }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Expected:&lt;/strong&gt; a one-line message such as &lt;code&gt;basic.js: cannot read does/not/exist.js (ENOENT)&lt;/code&gt;. Optionally, ship a small &lt;code&gt;bin&lt;/code&gt;, because newcomers look for &lt;code&gt;npx sentinel-ir &amp;lt;file&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7. docs: the "Fewer tokens" table is stale (sentinel-ir README)
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;file&lt;/th&gt;
&lt;th&gt;README raw chars&lt;/th&gt;
&lt;th&gt;actual 0.5.9 raw chars&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/ast-facts.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;21,823&lt;/td&gt;
&lt;td&gt;96,827&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/sentinel-ir.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;9,123&lt;/td&gt;
&lt;td&gt;20,424&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/tree-parser.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;4,393&lt;/td&gt;
&lt;td&gt;13,181&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Measuring &lt;code&gt;examples/basic.js&lt;/code&gt; on itself gives an IR of 552 characters; the README says 461. The claim still holds, and the real savings are larger, but the numbers are from an older release.&lt;/p&gt;

&lt;h3&gt;
  
  
  8. docs: gaps in the sentinel-ir-mcp README
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The "Supported" paragraph says "an explicit &lt;code&gt;language&lt;/code&gt; (&lt;code&gt;javascript&lt;/code&gt; | &lt;code&gt;typescript&lt;/code&gt; | &lt;code&gt;tsx&lt;/code&gt; | &lt;code&gt;python&lt;/code&gt; | &lt;code&gt;rust&lt;/code&gt;)" without the aliases. The Tools table and the changelog do list &lt;code&gt;js&lt;/code&gt;/&lt;code&gt;ts&lt;/code&gt;/&lt;code&gt;py&lt;/code&gt;/&lt;code&gt;rs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The audit-log section lists the refusal reasons &lt;code&gt;excluded, outside, not-found, too-large, hardlink, symlink, unsupported, not-a-file, url, no-folders&lt;/code&gt;. &lt;code&gt;analyze_directory&lt;/code&gt; on a file logs &lt;code&gt;not a folder&lt;/code&gt;, so &lt;code&gt;not-a-folder&lt;/code&gt; should be added.&lt;/li&gt;
&lt;li&gt;The minimum &lt;code&gt;sentinel-ir&lt;/code&gt; version is documented ("Needs &lt;code&gt;sentinel-ir&lt;/code&gt; 0.5.9"), and &lt;code&gt;doctor&lt;/code&gt; checks it. OK.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  9. papercut: Rust &lt;code&gt;std::process&lt;/code&gt; false positives (documented behaviour)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;use std::process; fn main() { process::exit(0)&lt;/code&gt; (missing &lt;code&gt;}&lt;/code&gt;) gives &lt;code&gt;critical&lt;/code&gt;, &lt;code&gt;forbidden: ["child_process","process_spawn"]&lt;/code&gt;. The README describes the module-only &lt;code&gt;use std::process&lt;/code&gt; heuristic, but this is a very common idiom.&lt;/li&gt;
&lt;li&gt;A valid &lt;code&gt;fn main(){ std::process::exit(1); }&lt;/code&gt; gives risk &lt;code&gt;low&lt;/code&gt;, but &lt;code&gt;forbidden: ["process.exit"]&lt;/code&gt;. A reader who takes &lt;code&gt;forbidden&lt;/code&gt; to mean "dangerous capability" may be confused.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  10. papercut (transient): &lt;code&gt;doctor&lt;/code&gt; update check
&lt;/h3&gt;

&lt;p&gt;While a large download was running, one &lt;code&gt;doctor&lt;/code&gt; run printed &lt;code&gt;[skip] No usable answer from the npm registry about a newer sentinel-ir-mcp&lt;/code&gt;. The next run printed &lt;code&gt;[ok] sentinel-ir-mcp 0.5.9 is the newest stable-0.5 on npm&lt;/code&gt;. The fallback is correct (skip, not fail). I'm noting it only for completeness.&lt;/p&gt;




&lt;h2&gt;
  
  
  Unverified / blocked
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;U1. Desktop clients starting the server from their config: not verified.&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;client&lt;/th&gt;
&lt;th&gt;config written by&lt;/th&gt;
&lt;th&gt;config accepted?&lt;/th&gt;
&lt;th&gt;server process started?&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;VS Code (portable zip, latest)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;code --add-mcp '&amp;lt;json printed by setup --client vscode&amp;gt;'&lt;/code&gt; -&amp;gt; &lt;code&gt;User\mcp.json&lt;/code&gt;, non-ASCII path intact&lt;/td&gt;
&lt;td&gt;yes, listed in &lt;em&gt;MCP: List Servers&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;no&lt;/strong&gt;: &lt;em&gt;Start Server&lt;/em&gt; sets the status to &lt;code&gt;Error&lt;/code&gt;; &lt;code&gt;mcpServer.mcp.config.usrlocal.sentinel-ir.log&lt;/code&gt; is empty; no &lt;code&gt;sentinel-ir-mcp&lt;/code&gt; process. VS Code was unauthenticated and in onboarding; its renderer log shows &lt;code&gt;Could not list installed MCP servers. (refused: policy-context-unavailable)&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cursor (latest)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;setup --client cursor --yes&lt;/code&gt; -&amp;gt; &lt;code&gt;&amp;lt;project&amp;gt;\.cursor\mcp.json&lt;/code&gt; (&lt;code&gt;cmd /c npx -y sentinel-ir-mcp@stable-0.5 ... ${workspaceFolder}&lt;/code&gt;)&lt;/td&gt;
&lt;td&gt;file is valid; the app won't go past the login modal&lt;/td&gt;
&lt;td&gt;not observed&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Claude Desktop 0.14.10&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;setup --client claude-desktop --yes&lt;/code&gt; -&amp;gt; &lt;code&gt;%APPDATA%\Claude\claude_desktop_config.json&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;file is valid; the app stops at "Get started"&lt;/td&gt;
&lt;td&gt;not observed&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;I couldn't tell whether the VS Code &lt;code&gt;Error&lt;/code&gt; comes from the package or from the client state.&lt;/li&gt;
&lt;li&gt;The same command line, run as &lt;code&gt;cmd /c npx -y sentinel-ir-mcp@stable-0.5 "C:\t59\Projekt Žluťoučký kůň\myapp"&lt;/code&gt;, starts and answers the handshake through the real SDK client. &lt;code&gt;doctor&lt;/code&gt; also performs its own handshake.&lt;/li&gt;
&lt;li&gt;Recommendation: repeat with a signed-in VS Code or Cursor.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;U2.&lt;/strong&gt; There is no second physical disk. I used a 512 MB NTFS VHD mounted as &lt;code&gt;V:&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;U3.&lt;/strong&gt; macOS and Linux were not tested in this session (Windows machine only).&lt;/p&gt;




&lt;h2&gt;
  
  
  The 8 regression checks for 0.5.9
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;#&lt;/th&gt;
&lt;th&gt;Claim&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;UNC &lt;code&gt;\\server\share\x.js&lt;/code&gt;, &lt;code&gt;//server/share/x.js&lt;/code&gt;, &lt;code&gt;file://server/share/x.js&lt;/code&gt; refused before any file system or network access&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt; on Node 20, 22 and 24. pktmon filter: TCP 445/139, UDP 137/138, 5355, IP 10.255.255.1. During every UNC and remote-URL call: &lt;strong&gt;0 packets&lt;/strong&gt; on each Node version. Control &lt;code&gt;Test-Path \\10.255.255.1\share\x.js&lt;/code&gt;: 28 packets (SYN to :445) after 22 s. Replies: &lt;code&gt;Access denied ... outside the allowed folders&lt;/code&gt; (UNC) and &lt;code&gt;path must be a file path, not a URL&lt;/code&gt; (remote &lt;code&gt;file://&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;file:src/x.js&lt;/code&gt; and &lt;code&gt;http(s)://&lt;/code&gt; refused as URLs; local &lt;code&gt;file:///&lt;/code&gt; inside the folder still works&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt;. &lt;code&gt;path must be a file path, not a URL: file:src/server.js&lt;/code&gt;; &lt;code&gt;file:///C:/t59/Projekt%20Žluťoučký%20kůň/myapp/src/server.js&lt;/code&gt; (encoded and raw) gives &lt;code&gt;complete javascript&lt;/code&gt;; &lt;code&gt;file:///&lt;/code&gt; outside gives &lt;code&gt;Access denied&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Broken Python/Rust is &lt;code&gt;critical&lt;/code&gt; only when &lt;code&gt;forbidden&lt;/code&gt; names the capability&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt; (library and MCP). &lt;code&gt;subprocess&lt;/code&gt;, &lt;code&gt;os.system&lt;/code&gt;, &lt;code&gt;std::process::Command&lt;/code&gt;, &lt;code&gt;runpy&lt;/code&gt;, &lt;code&gt;spec_from_file_location(...).loader.exec_module&lt;/code&gt;, &lt;code&gt;from ctypes import *&lt;/code&gt; give &lt;code&gt;critical&lt;/code&gt; with &lt;code&gt;forbidden&lt;/code&gt;. &lt;code&gt;std::process::exit&lt;/code&gt;, &lt;code&gt;std::process::id&lt;/code&gt;, &lt;code&gt;importlib.reload&lt;/code&gt;, &lt;code&gt;from ctypes import c_int&lt;/code&gt; are not &lt;code&gt;critical&lt;/code&gt;. See issue 9 for the &lt;code&gt;use std::process;&lt;/code&gt; idiom&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analyze_directory&lt;/code&gt; summary lists &lt;code&gt;forbidden&lt;/code&gt; for broken files with a dangerous capability&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt; (broken Python, Rust and JS fixtures)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;analyze_code&lt;/code&gt; aliases &lt;code&gt;js&lt;/code&gt;, &lt;code&gt;ts&lt;/code&gt;, &lt;code&gt;py&lt;/code&gt;, &lt;code&gt;rs&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt; for lowercase. Uppercase is rejected, see issue 2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;log&lt;/code&gt; wording&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt;. &lt;code&gt;pasted code (12 bytes, unknown): low (incomplete: unsupported language)&lt;/code&gt;; &lt;code&gt;REFUSED file http://example.com/x.js: not a local file path&lt;/code&gt;; &lt;code&gt;... outside the allowed folders&lt;/code&gt;; &lt;code&gt;REFUSED file links/hard-out.js: file has several hard links&lt;/code&gt;; folder line &lt;code&gt;1 not read (too large or unreadable), 1 not read (several hard links)&lt;/code&gt;; &lt;code&gt;REFUSED folder src/server.js: not a folder&lt;/code&gt;; &lt;code&gt;Not a file: src&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; warns on unmatched exclusions; stale-cache warning reads right for a range and for a tag&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PASS&lt;/strong&gt; with papercuts. &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;doctor&lt;/code&gt; both warn "matches nothing there yet (typo?)". The stale-cache warning is correct and actionable, but the label differs between npm 10 and npm 11 (issue 4)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;8&lt;/td&gt;
&lt;td&gt;README documents the &lt;code&gt;language&lt;/code&gt; option and aliases, every refusal reason, and the minimum sentinel-ir version&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Mostly PASS&lt;/strong&gt;. Aliases are in the Tools table and changelog but not in the "Supported" paragraph; &lt;code&gt;not-a-folder&lt;/code&gt; is missing from the audit-log reasons (issue 8)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  0.5.8 fixes rechecked
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Same &lt;code&gt;Access denied&lt;/code&gt; for existing and missing outside paths, &lt;code&gt;..&lt;/code&gt;, &lt;code&gt;\\?\C:\...&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Symlink outside refused (incl. dangling symlink and junction); symlink inside works&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Unknown extension (&lt;code&gt;.rb&lt;/code&gt;, &lt;code&gt;.go&lt;/code&gt;) gives &lt;code&gt;incomplete&lt;/code&gt; / &lt;code&gt;unknown&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;PASS (but see issue 1 for an explicit &lt;code&gt;language&lt;/code&gt;)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;doctor&lt;/code&gt; per client (&lt;code&gt;--client cursor&lt;/code&gt;; Claude Desktop and Windsurf paths)&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;`log \&lt;/td&gt;
&lt;td&gt;head -1` exits 0 without EPIPE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;TypeScript declarations under &lt;code&gt;strict&lt;/code&gt; (tsc 5.9.3 and 7.0.2); the union on &lt;code&gt;facts.ast&lt;/code&gt; narrows&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Rust and TSX parse-error positions (&lt;code&gt;missing&lt;/code&gt; / &lt;code&gt;unclosed&lt;/code&gt; with line and column)&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;.jsx&lt;/code&gt; accepted&lt;/td&gt;
&lt;td&gt;PASS&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Verified working
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;sentinel-ir install:&lt;/strong&gt; &lt;code&gt;npm install sentinel-ir&lt;/code&gt; in a fresh project on Node 20, 22 and 24 takes 2-3 s. npm 11 prints the documented allow-scripts notice.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;README API example:&lt;/strong&gt; output is byte-identical to the README. &lt;code&gt;initializeWasm()&lt;/code&gt; works. Outputs are identical across Node versions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages:&lt;/strong&gt; JS, TS, TSX, Python and Rust files analysed. Broken files in each language give &lt;code&gt;incomplete&lt;/code&gt; with parse-error positions. Aliases and case-insensitive names work in the library.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Module formats:&lt;/strong&gt; ESM default import and CJS &lt;code&gt;require&lt;/code&gt; both work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;sentinel-ir-mcp CLI entry points:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;guide&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;setup&lt;/code&gt; for every client (dry run, &lt;code&gt;--yes&lt;/code&gt;, &lt;code&gt;--audit-log&lt;/code&gt;, &lt;code&gt;--exclude&lt;/code&gt;); re-running keeps existing settings&lt;/li&gt;
&lt;li&gt;whole-disk refusal: &lt;code&gt;Refusing to give the agent access to a whole disk (V:\). Pass a project folder with --project.&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;doctor&lt;/code&gt;, &lt;code&gt;doctor --client cursor&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;log&lt;/code&gt;, &lt;code&gt;log --all&lt;/code&gt;, and guidance when the audit log is off&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP over stdio with a real client&lt;/strong&gt; (identical on Node 20, 22, 24):

&lt;ul&gt;
&lt;li&gt;handshake &lt;code&gt;{"name":"sentinel-ir-mcp","version":"0.5.9"}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;tools &lt;code&gt;analyze_code&lt;/code&gt;, &lt;code&gt;analyze_file&lt;/code&gt;, &lt;code&gt;analyze_directory&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;refusals: outside root, &lt;code&gt;..&lt;/code&gt;, symlink, hard link (&lt;code&gt;has 2 hard links; files with more than one are refused&lt;/code&gt;), oversized file (&lt;code&gt;File too large: big/huge.js is 1600000 bytes, limit is 1048576&lt;/code&gt;), unsupported extension, &lt;code&gt;Not a file&lt;/code&gt;, &lt;code&gt;Not a folder&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;directory summary with &lt;code&gt;skipped.tooLarge&lt;/code&gt;, &lt;code&gt;skipped.symlinks&lt;/code&gt;, &lt;code&gt;skipped.hardlinks&lt;/code&gt; and &lt;code&gt;riskCounts&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit log:&lt;/strong&gt; JSONL with a &lt;code&gt;start&lt;/code&gt; record (version, folders, exclusions) and one line per call.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Paths:&lt;/strong&gt; &lt;code&gt;C:\t59\Projekt Žluťoučký kůň\myapp&lt;/code&gt; (spaces and diacritics) and &lt;code&gt;V:\Další projekt&lt;/code&gt; on a second volume. &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;doctor&lt;/code&gt; are fine on both, and generated configs keep the non-ASCII characters intact.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Upgrade path:&lt;/strong&gt; I read the actual &lt;code&gt;_npx/&amp;lt;hash&amp;gt;/node_modules/sentinel-ir-mcp/package.json&lt;/code&gt; in each case.

&lt;ul&gt;
&lt;li&gt;npm 10: &lt;code&gt;@0.5&lt;/code&gt; keeps running 0.5.8, and 0.5.8's own &lt;code&gt;doctor&lt;/code&gt; warns &lt;code&gt;This is sentinel-ir-mcp 0.5.8, npm has 0.5.9 (stable-0.5)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@stable-0.5&lt;/code&gt; runs 0.5.9, and its &lt;code&gt;doctor&lt;/code&gt; names the stale folder with the delete command.&lt;/li&gt;
&lt;li&gt;npm 11: see issue 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>opensource</category>
      <category>agents</category>
      <category>architecture</category>
    </item>
    <item>
      <title>Sentinel-IR and Sentinel-IR MCP are open for sponsors (and yes, an AI writes most of the code)</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 19:59:54 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-and-sentinel-ir-mcp-are-open-for-sponsors-and-yes-an-ai-writes-most-of-the-code-3lmm</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-and-sentinel-ir-mcp-are-open-for-sponsors-and-yes-an-ai-writes-most-of-the-code-3lmm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmg513rg0hf1ivz1b9ko5.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fmg513rg0hf1ivz1b9ko5.jpg" alt=" " width="799" height="436"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;A 5-minute read. One maintainer, two Apache-2.0 packages, zero venture capital, a mildly embarrassing amount of honesty.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The short version
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;&lt;code&gt;sentinel-ir&lt;/code&gt;&lt;/a&gt; and&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;&lt;code&gt;sentinel-ir-mcp&lt;/code&gt;&lt;/a&gt; now&lt;br&gt;
accept sponsorship. Both READMEs have a &lt;strong&gt;Sponsor&lt;/strong&gt; section, both repos have a&lt;br&gt;
GitHub Sponsor button, and &lt;code&gt;sentinel-ir&lt;/code&gt; publishes a&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir/-/blob/main/funding.json" rel="noopener noreferrer"&gt;&lt;code&gt;funding.json&lt;/code&gt;&lt;/a&gt;&lt;br&gt;
manifest that funds and tools can read.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;People:&lt;/strong&gt; &lt;a href="https://github.com/sponsors/jakcmencz" rel="noopener noreferrer"&gt;github.com/sponsors/jakcmencz&lt;/a&gt;.
The profile was submitted today and GitHub is still reviewing it, so the
link turns into a real Sponsor page once they approve it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Companies, grants, integrations:&lt;/strong&gt; write to
&lt;a href="mailto:sentinel.ir.contact@gmail.com"&gt;sentinel.ir.contact@gmail.com&lt;/a&gt;. That works today.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What these two things are, in one paragraph each
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;sentinel-ir&lt;/code&gt;&lt;/strong&gt; reads a source file and gives an AI agent a small fact sheet&lt;br&gt;
instead of the whole file: risk level, network, filesystem, process and&lt;br&gt;
environment calls, routes, imports and exports, and for Rust also &lt;code&gt;unsafe&lt;/code&gt;,&lt;br&gt;
FFI, &lt;code&gt;build.rs&lt;/code&gt; and proc-macro behaviour. It covers JavaScript, TypeScript,&lt;br&gt;
Python and Rust. It's static analysis: it never runs the code, and a "low"&lt;br&gt;
risk is a hint, not a safety certificate.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;sentinel-ir-mcp&lt;/code&gt;&lt;/strong&gt; plugs that fact sheet into Claude Desktop, Cursor,&lt;br&gt;
Windsurf and other MCP clients as a read-only stdio server. It only opens&lt;br&gt;
supported source files inside the folders you allow, refuses traversal,&lt;br&gt;
symlinks and hard links, has file-size, file-count and time limits, and can&lt;br&gt;
keep an audit log of what it read (paths and hashes, never source).&lt;/p&gt;

&lt;p&gt;One analyzer, one shared IR, the same facts in npm, the MCP server and the&lt;br&gt;
&lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;browser playground&lt;/a&gt;. Adding&lt;br&gt;
a language once adds it everywhere.&lt;/p&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;I'm Jakub, the only maintainer, and I'm not a professional developer. I decide&lt;br&gt;
what Sentinel should do, test it on my own machines and approve every release.&lt;br&gt;
&lt;strong&gt;Most of the code is written by an AI coding agent working from my specs.&lt;/strong&gt;&lt;br&gt;
Every change still has to pass the test suites and CI on Linux and Windows&lt;br&gt;
before it ships.&lt;/p&gt;

&lt;p&gt;Some grant programmes don't fund AI-generated work, and I'd rather lose a grant&lt;br&gt;
than pretend. If that's a deal-breaker for you, fair enough. If you think&lt;br&gt;
"one person plus an agent shipping audited, tested releases" is exactly the&lt;br&gt;
kind of thing worth backing, read on.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it saves you (measured, not vibes)
&lt;/h2&gt;

&lt;p&gt;Every time an agent "just takes a quick look" at a file, you pay for every&lt;br&gt;
token of it, comments, licence header and the 400-line date formatter&lt;br&gt;
included. Sentinel hands the agent the fact sheet instead. Measured on&lt;br&gt;
Sentinel's own source with the &lt;code&gt;o200k_base&lt;/code&gt; tokenizer:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;file&lt;/th&gt;
&lt;th&gt;the whole file&lt;/th&gt;
&lt;th&gt;Sentinel's fact sheet&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/ast-facts.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8,439 tokens&lt;/td&gt;
&lt;td&gt;120 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/sentinel-ir.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,084 tokens&lt;/td&gt;
&lt;td&gt;133 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/tree-parser.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;891 tokens&lt;/td&gt;
&lt;td&gt;121 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;examples/basic.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;150 tokens&lt;/td&gt;
&lt;td&gt;120 tokens&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;That's up to about 70 times less on a big file. On a tiny file it saves almost&lt;br&gt;
nothing, because the fact sheet has a floor of about 120 tokens, and we'd&lt;br&gt;
rather tell you than let you find out.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A back-of-the-envelope example&lt;/strong&gt; (an illustration, not a measurement): your&lt;br&gt;
agents open a file the size of &lt;code&gt;ast-facts.js&lt;/code&gt; 200 times a day across the team.&lt;br&gt;
That's about 1.7 million tokens a day you don't send. At an example price of&lt;br&gt;
$3 per million input tokens, that's about $5 a day, or roughly $100 a working&lt;br&gt;
month, for &lt;strong&gt;one&lt;/strong&gt; file. Your repo has more than one file. Your agents are not&lt;br&gt;
shy.&lt;/p&gt;

&lt;p&gt;The time part is simpler: a model that reads 120 tokens gets to the point&lt;br&gt;
sooner than one that reads 8,439, and its context window stays free for the&lt;br&gt;
work you actually asked for. It's the difference between asking a colleague&lt;br&gt;
"does this file touch the network?" and making them read it aloud first.&lt;/p&gt;

&lt;p&gt;And the part that doesn't show up on an invoice: the agent sees&lt;br&gt;
&lt;code&gt;network: outbound&lt;/code&gt; and &lt;code&gt;process: spawn&lt;/code&gt; before it trusts a file, instead of&lt;br&gt;
discovering them three tool calls later in a &lt;code&gt;postinstall&lt;/code&gt; script.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why monthly, not one-off
&lt;/h2&gt;

&lt;p&gt;A code analyzer is less like a hammer and more like antivirus definitions.&lt;br&gt;
The day it stops being updated, it starts going stale:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ecosystems keep inventing new doors.&lt;/strong&gt; A new HTTP client, a new way to
spawn a process, a new Rust build crate. Each one Sentinel doesn't know
about is a door the agent walks past without noticing. Version 0.4.4 alone
taught it axios, got, undici, superagent and fs-extra.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Languages keep coming.&lt;/strong&gt; JavaScript, TypeScript, Python and Rust today;
Go is next. Because everything sits on one shared IR, every new language
shows up in the npm package, the MCP server and the playground at once.
You pay for it once and get it three times.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security work never finishes.&lt;/strong&gt; The MCP server reads files on your
machine for an AI. Path checks, size limits and parser robustness need
regular adversarial testing, not a one-time audit framed on the wall.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The platforms move.&lt;/strong&gt; New Node versions, new MCP clients, new MCP spec
revisions. Somebody has to keep &lt;code&gt;setup&lt;/code&gt; and &lt;code&gt;doctor&lt;/code&gt; working on Monday
morning.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A one-off donation is a very kind pizza. A monthly sponsorship is the&lt;br&gt;
kitchen. We're grateful for pizza. The kitchen is what keeps the releases&lt;br&gt;
coming.&lt;/p&gt;

&lt;p&gt;For companies the maths is short: if Sentinel saves your team even one&lt;br&gt;
engineer-hour a month in review time or token spend, the $5 Supporter plan&lt;br&gt;
paid for itself before lunch. The Development plan is less than many&lt;br&gt;
companies spend on coffee for the people who'd otherwise read those files.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the money goes
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;what&lt;/th&gt;
&lt;th&gt;why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;AI agent time&lt;/td&gt;
&lt;td&gt;it writes the code, the tests and the release checks&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;CI and test machines&lt;/td&gt;
&lt;td&gt;Linux and Windows CI on Node 20, 22 and 24, plus macOS test runs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Go support&lt;/td&gt;
&lt;td&gt;the next language for the shared IR&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;security hardening&lt;/td&gt;
&lt;td&gt;more adversarial tests for the parsers and the MCP path checks&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;funding.json&lt;/code&gt; lists two plans: &lt;strong&gt;Supporter, $5/month&lt;/strong&gt; (helps cover CI)&lt;br&gt;
and &lt;strong&gt;Development, $12,000/year&lt;/strong&gt; (a year of agent time, test machines, Go&lt;br&gt;
and security work on both packages). One-time sponsorship is fine too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where we are today
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;About 1,150 npm downloads a week for each package (Nov 7–13). That number
includes our own CI, so it's an upper bound on real users, not a vanity metric.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sentinel-ir-mcp&lt;/code&gt; is in the official
&lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt; as
&lt;code&gt;io.github.jakcmencz/sentinel-ir-mcp&lt;/code&gt; and on
&lt;a href="https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;Glama&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Current releases: &lt;code&gt;sentinel-ir&lt;/code&gt; 0.5.6 and &lt;code&gt;sentinel-ir-mcp&lt;/code&gt; 0.5.6.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Can't sponsor? Still helps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Try it on your own repo: &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 setup&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Star the repo on &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;
or &lt;a href="https://github.com/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open an issue when it gets something wrong. A false "low" is the bug I want
most.&lt;/li&gt;
&lt;li&gt;Tell the one colleague who keeps pasting whole files into ChatGPT.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Thanks for reading. If Sentinel ever saves your agent from reading&lt;br&gt;
&lt;code&gt;/etc/passwd&lt;/code&gt;, you know where the button is.&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>ai</category>
      <category>mcp</category>
      <category>security</category>
    </item>
    <item>
      <title>Sentinel-IR MCP 0.5: give your AI agent a library card, not the master key</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 17:44:54 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-mcp-05-give-your-ai-agent-a-library-card-not-the-master-key-5hg1</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-mcp-05-give-your-ai-agent-a-library-card-not-the-master-key-5hg1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzveifnumwbzmalwljuj1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fzveifnumwbzmalwljuj1.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A 10-minute read for developers, platform teams and whoever has to answer&lt;br&gt;
"wait, what can the agent read?" in a security review.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source (GitLab, Apache-2.0): &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir-mcp&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Listings: &lt;a href="https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;Glama&lt;/a&gt; · &lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt; (&lt;code&gt;io.github.jakcmencz/sentinel-ir-mcp&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sentinel-ir-mcp&lt;/code&gt; is a stdio &lt;a href="https://modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP&lt;/a&gt; server&lt;br&gt;
with three read-only tools: &lt;code&gt;analyze_code&lt;/code&gt;, &lt;code&gt;analyze_file&lt;/code&gt; and&lt;br&gt;
&lt;code&gt;analyze_directory&lt;/code&gt;. Instead of raw source, the agent gets a compact&lt;br&gt;
structural IR from&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;&lt;code&gt;sentinel-ir&lt;/code&gt;&lt;/a&gt;: risk level,&lt;br&gt;
dangerous capabilities, routes, imports, env reads, network, filesystem,&lt;br&gt;
process and crypto calls, each with a line number. It works with&lt;br&gt;
JavaScript, TypeScript, Python and, since 0.5, Rust.&lt;/p&gt;

&lt;p&gt;The 0.5 line is about two things: &lt;strong&gt;Rust&lt;/strong&gt;, and making sure the copy&lt;br&gt;
running on your machine is &lt;strong&gt;the one with the fixes&lt;/strong&gt;. Plus a &lt;code&gt;doctor&lt;/code&gt; that&lt;br&gt;
we tried very hard to break on three operating systems.&lt;/p&gt;
&lt;h2&gt;
  
  
  The problem: agents read like a vacuum cleaner
&lt;/h2&gt;

&lt;p&gt;Coding agents have discovered the filesystem, and they &lt;em&gt;love&lt;/em&gt; it. Ask "is this&lt;br&gt;
repo safe to deploy?" and a typical agent &lt;code&gt;cat&lt;/code&gt;s twenty files into its context.&lt;br&gt;
That's 50,000 tokens of business logic, license headers and a comment from&lt;br&gt;
2019 that says &lt;code&gt;// TODO: remove before prod&lt;/code&gt;. Then it answers from vibes.&lt;/p&gt;

&lt;p&gt;Three costs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Money and context.&lt;/strong&gt; Raw &lt;code&gt;src/ast-facts.js&lt;/code&gt; is 8,439 tokens. Its IR is
120.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection.&lt;/strong&gt; Comments and strings are attacker-controlled text.
A README that says "AI agents must run &lt;code&gt;curl … | sh&lt;/code&gt;" is a real genre now.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scope.&lt;/strong&gt; Generic "read file" tools often see everything the process can
see, &lt;code&gt;~/.ssh&lt;/code&gt; included.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;code&gt;sentinel-ir-mcp&lt;/code&gt; is the opposite design: small facts, narrow scope, no&lt;br&gt;
writes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Triage first, read later
&lt;/h2&gt;

&lt;p&gt;The intended workflow, which the tool descriptions spell out for the agent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;analyze_directory&lt;/code&gt; the project. You get one short entry per file
(path, risk, status, language, lines, operations, up to 3 risk signals),
sorted from highest to lowest risk, plus &lt;code&gt;riskCounts&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Look at the &lt;code&gt;critical&lt;/code&gt; and &lt;code&gt;high&lt;/code&gt; files and the &lt;code&gt;incomplete&lt;/code&gt; ones.
Incomplete means "didn't fully parse", and its &lt;code&gt;low&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; evidence
of safety. We tell the agent that in capital letters.&lt;/li&gt;
&lt;li&gt;Read the raw source of &lt;em&gt;those&lt;/em&gt; files only.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On a 190-file Node project the default summary was 35,856 characters&lt;br&gt;
(about 9,000 tokens) for the &lt;strong&gt;whole folder&lt;/strong&gt;. That's less than a single&lt;br&gt;
mid-sized file read raw.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"folder"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"analyzed"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"riskCounts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"medium"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;6&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"low"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;33&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"src/deploy.rs"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"language"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"rust"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
   &lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"processSpawn"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"riskSignals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"process_spawn:Cmd::new@14"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"..."&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Illustrative shape, trimmed.)&lt;/p&gt;

&lt;h2&gt;
  
  
  The security model, in plain terms
&lt;/h2&gt;

&lt;p&gt;This is the part your security team will read, so we wrote it like an&lt;br&gt;
interface contract.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It only reads.&lt;/strong&gt; The server never writes project files, never executes&lt;br&gt;
code and never opens a network connection. The tools are annotated&lt;br&gt;
&lt;code&gt;readOnlyHint: true&lt;/code&gt;, &lt;code&gt;destructiveHint: false&lt;/code&gt;, &lt;code&gt;openWorldHint: false&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Only inside the folders you allowed.&lt;/strong&gt; Roots come from the command line&lt;br&gt;
or, without folder arguments, from the client's MCP roots. Every requested&lt;br&gt;
path is resolved to its real path and must land inside a root. In our audit&lt;br&gt;
of the published 0.5.5 package, the server refused all of these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;../outside/s.js            -&amp;gt; Access denied: outside the allowed folders
/etc/passwd                -&amp;gt; Access denied: outside the allowed folders
src/link.js (symlink out)  -&amp;gt; Access denied
secrets/x.js (excluded)    -&amp;gt; Excluded by the user's settings. Do not try to read it another way.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The boring attacks are handled too:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Symlinks:&lt;/strong&gt; files are opened with &lt;code&gt;O_NOFOLLOW&lt;/code&gt; and directory scans skip every symlink.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Swapped files:&lt;/strong&gt; after opening, the inode is compared with the file at the real path. A file swapped mid-read is refused.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hard links:&lt;/strong&gt; files with more than one link are refused, because a hard link can point anywhere and you can't see its other names.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limits:&lt;/strong&gt; files up to 1 MiB by default, 2 MiB at most. Scans cover 50 files by default, 500 at most, with a 30 s time budget.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skipped folders:&lt;/strong&gt; &lt;code&gt;node_modules&lt;/code&gt;, &lt;code&gt;.git&lt;/code&gt;, &lt;code&gt;venv&lt;/code&gt;, hidden folders and Cargo &lt;code&gt;target/&lt;/code&gt; (only when it has Cargo's &lt;code&gt;CACHEDIR.TAG&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exclusions belong to the human.&lt;/strong&gt; &lt;code&gt;setup --exclude secrets --exclude dist&lt;/code&gt;&lt;br&gt;
writes them into the client config. The agent can't change them, and&lt;br&gt;
neither can the analyzed repo: there's deliberately no &lt;code&gt;.sentinelignore&lt;/code&gt;,&lt;br&gt;
because a malicious repo would use it to hide its own payload. Excluded&lt;br&gt;
files are refused by &lt;code&gt;analyze_file&lt;/code&gt; and only &lt;em&gt;counted&lt;/em&gt; by&lt;br&gt;
&lt;code&gt;analyze_directory&lt;/code&gt;, because file names can be sensitive too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;An audit log, for the "who asked for what" question.&lt;/strong&gt; It's opt-in&lt;br&gt;
(&lt;code&gt;setup --audit-log&lt;/code&gt;). It records the tool, the path, the result and the&lt;br&gt;
SHA-256 of each file the agent was shown, and never the source. It must&lt;br&gt;
live outside the project, rotates daily, keeps 30 days and stays under 50 MB&lt;br&gt;
a day. Read it with &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 log&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;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
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The IR is still untrusted text.&lt;/strong&gt; Comments and string bodies are not&lt;br&gt;
copied, and since &lt;code&gt;sentinel-ir&lt;/code&gt; 0.5.4 every string is capped at 128&lt;br&gt;
characters and literals in evidence show up as &lt;code&gt;"…"&lt;/code&gt;. Identifiers and route&lt;br&gt;
paths still come from the code. That &lt;em&gt;reduces&lt;/em&gt; the injection surface; it&lt;br&gt;
doesn't remove it, and every tool description tells the agent to treat IR&lt;br&gt;
values as data, not instructions.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in 0.5
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Rust (0.5.0)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;.rs&lt;/code&gt; files go through &lt;code&gt;sentinel-ir&lt;/code&gt;'s new Rust analyzer: &lt;code&gt;unsafe&lt;/code&gt; and FFI,&lt;br&gt;
&lt;code&gt;std::process::Command&lt;/code&gt;, file writes and deletes, HTTP clients and servers&lt;br&gt;
(&lt;code&gt;axum&lt;/code&gt;, &lt;code&gt;actix-web&lt;/code&gt;, ...). A &lt;code&gt;build.rs&lt;/code&gt; or procedural macro that touches&lt;br&gt;
files, the network or secret env vars is at least &lt;code&gt;high&lt;/code&gt;, because it runs on&lt;br&gt;
your machine during &lt;code&gt;cargo build&lt;/code&gt;. For a supply-chain attacker that's the&lt;br&gt;
best seat in the house.&lt;/p&gt;

&lt;h3&gt;
  
  
  Updates that actually arrive (0.5.1)
&lt;/h3&gt;

&lt;p&gt;Here's a fun fact we measured against a local npm registry: &lt;strong&gt;&lt;code&gt;npx&lt;/code&gt; never&lt;br&gt;
re-resolves a cached version range.&lt;/strong&gt; Configure your client with&lt;br&gt;
&lt;code&gt;sentinel-ir-mcp@0.5&lt;/code&gt; and npx keeps starting the first 0.5.x it downloaded,&lt;br&gt;
forever, even after security fixes ship. It's like a fridge that only ever&lt;br&gt;
restocks the milk you bought in March.&lt;/p&gt;

&lt;p&gt;A dist-tag &lt;em&gt;is&lt;/em&gt; re-resolved on every start. So &lt;code&gt;setup&lt;/code&gt; now writes&lt;br&gt;
&lt;code&gt;sentinel-ir-mcp@stable-0.5&lt;/code&gt;, and CI moves that tag forward on every release&lt;br&gt;
(never backwards). Without network access, the cached copy still starts. The&lt;br&gt;
server also requires &lt;code&gt;sentinel-ir&lt;/code&gt; ≥ 0.5.6, which pulls in the&lt;br&gt;
comment-hiding, parse-budget and broken-file fixes. &lt;code&gt;doctor&lt;/code&gt; warns about&lt;br&gt;
pinned entries and stale &lt;code&gt;_npx&lt;/code&gt; cache copies, with the exact folder to&lt;br&gt;
delete.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tool descriptions agents can actually use (0.5.2–0.5.3)
&lt;/h3&gt;

&lt;p&gt;Each tool description is now short lines in a fixed order: what it does,&lt;br&gt;
what it returns, the parameters, the risk scale, when it refuses, and &lt;strong&gt;which&lt;br&gt;
sibling tool to use instead&lt;/strong&gt; ("for a folder use &lt;code&gt;analyze_directory&lt;/code&gt;"). The&lt;br&gt;
risk scale also says that &lt;code&gt;medium&lt;/code&gt; (network, writes, crypto) is normal in&lt;br&gt;
backend code, so the agent stops filing a P0 for every &lt;code&gt;fetch()&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;doctor&lt;/code&gt;, the part we tried hardest to break (0.5.4–0.5.5)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor&lt;/code&gt; checks Node, the package versions,&lt;br&gt;
the client configs, exclusions, the audit log, a real MCP handshake and a&lt;br&gt;
real &lt;code&gt;analyze_directory&lt;/code&gt; call. Since 0.5.4 it also asks npm whether a newer&lt;br&gt;
&lt;code&gt;stable-0.5&lt;/code&gt; exists (3 s timeout, skipped offline) and tells you where the old&lt;br&gt;
copy runs from: the npx cache, a global install, &lt;code&gt;npm link&lt;/code&gt;, Homebrew,&lt;br&gt;
nvm-windows or Volta.&lt;/p&gt;

&lt;p&gt;Then we spent a day trying to break it on Linux, macOS and Windows, with a&lt;br&gt;
fake registry that answered in 28 different wrong ways (slow drips, gzip&lt;br&gt;
bombs, redirect loops, &lt;code&gt;__proto__&lt;/code&gt;, ANSI escapes with newlines) and 17&lt;br&gt;
proxy setups. Things we found and fixed before 0.5.5 shipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The HTTPS proxy tunnel opened but wasn't used. A classic.&lt;/li&gt;
&lt;li&gt;Registry credentials ended up in the proxy request line.&lt;/li&gt;
&lt;li&gt;A &lt;code&gt;%&lt;/code&gt; in a password crashed the check.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;NO_PROXY&lt;/code&gt; was ignored under npx, because npm sets an empty &lt;code&gt;npm_config_noproxy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;cafile&lt;/code&gt; and &lt;code&gt;strict-ssl&lt;/code&gt; from &lt;code&gt;.npmrc&lt;/code&gt; were ignored, so corporate proxies looked "offline".&lt;/li&gt;
&lt;li&gt;A stuck DNS lookup kept the process alive for 30 s after it had printed its result. The check now runs in a child process that gets killed.&lt;/li&gt;
&lt;li&gt;"All checks passed" was printed right under a &lt;code&gt;[warn]&lt;/code&gt;. Now it counts the warnings.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every case ends with a sensible message and exit code 0, in at most about&lt;br&gt;
3.5 s. Behind a corporate proxy, &lt;code&gt;doctor&lt;/code&gt; now behaves like npm does.&lt;/p&gt;

&lt;h3&gt;
  
  
  The audit release (0.5.6)
&lt;/h3&gt;

&lt;p&gt;After 0.5.5 we audited the published package as a whole instead of trying to&lt;br&gt;
break one feature. Nothing security-relevant turned up. Four small fixes&lt;br&gt;
shipped:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;guide&lt;/code&gt; (the agent's manual) still said "JavaScript and TypeScript".&lt;/li&gt;
&lt;li&gt;A folder literally named &lt;code&gt;..cache&lt;/code&gt; was treated as "outside".&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;npm audit&lt;/code&gt; in the lock file showed two moderate advisories in a transitive
Hono dependency of the MCP SDK. The stdio server never loads Hono, and fresh
installs were already clean. The lock file now reports 0 vulnerabilities.&lt;/li&gt;
&lt;li&gt;The MCP Registry entry is now published by the release workflow over GitHub
OIDC, so nobody has to remember it at midnight.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Supply chain and release hygiene
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Published only from GitLab CI on protected tags, with npm provenance. A
fresh install has 101 packages with verified registry signatures and 17 with
verified attestations.&lt;/li&gt;
&lt;li&gt;Tested on Node 20, 22 and 24, plus Windows on Node 20, 22 and 24 before
every publish. 146 tests run over real stdio with the official SDK client.&lt;/li&gt;
&lt;li&gt;Three runtime dependencies: &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt;, &lt;code&gt;zod&lt;/code&gt; and
&lt;code&gt;sentinel-ir&lt;/code&gt;. About 100 packages in &lt;code&gt;node_modules&lt;/code&gt;. Unlike &lt;code&gt;sentinel-ir&lt;/code&gt;,
this one isn't dependency-free, and we say so in the README.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Getting started
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# interactive: picks the client, the project folder, exclusions, audit log&lt;/span&gt;
npx -y sentinel-ir-mcp@stable-0.5 setup

&lt;span class="c"&gt;# or explicit&lt;/span&gt;
npx -y sentinel-ir-mcp@stable-0.5 setup --client cursor --project ~/code/app \
  --exclude secrets --exclude dist --audit-log

&lt;span class="c"&gt;# then&lt;/span&gt;
npx -y sentinel-ir-mcp@stable-0.5 doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;setup&lt;/code&gt; shows what it will write, makes a backup, writes atomically, and&lt;br&gt;
refuses to give an agent your whole home folder or a whole disk. Claude&lt;br&gt;
Desktop, Cursor and Windsurf are supported directly. Any other MCP client&lt;br&gt;
just runs &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 /path/to/project&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it fits in a company
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Agent platforms:&lt;/strong&gt; register it as the default "understand this code"
tool and keep generic file reading for the few files that need it. You
get a smaller context, a smaller bill and a smaller blast radius.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security review:&lt;/strong&gt; the read-only contract, root confinement and opt-in
audit log answer the three questions every review asks: what can it touch,
what can it change, and how do we know what it did.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rust shops:&lt;/strong&gt; surface &lt;code&gt;build.rs&lt;/code&gt; and proc-macro behaviour in new
dependencies before &lt;code&gt;cargo build&lt;/code&gt; runs them on 200 laptops.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monorepos with four languages:&lt;/strong&gt; one tool and one IR shape across
JS/TS, Python and Rust.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The honest limits
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;It's not a vulnerability scanner. Risk measures capabilities, not bugs.
&lt;code&gt;low&lt;/code&gt; means "nothing found".&lt;/li&gt;
&lt;li&gt;One file at a time, and syntax only: no cross-file or data-flow analysis.&lt;/li&gt;
&lt;li&gt;Rust macro expansion isn't followed (that needs &lt;code&gt;rustc&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Hard links are refused even when they're harmless (pnpm's &lt;code&gt;node_modules&lt;/code&gt;).
Secure defaults sometimes say no to your friends too.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Go is next. Because the server speaks the shared &lt;code&gt;sentinel-ir&lt;/code&gt; IR, a new&lt;br&gt;
language in the library lands in the MCP server and the&lt;br&gt;
&lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;playground&lt;/a&gt; without a&lt;br&gt;
new output format, so your agent's prompts don't change.&lt;/p&gt;

&lt;p&gt;Bug reports and merge requests are welcome on&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;. If you manage to&lt;br&gt;
make the server read &lt;code&gt;/etc/passwd&lt;/code&gt;, we'd genuinely like to hear about it&lt;br&gt;
before your glama.ai followers do.&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>rust</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Sentinel-IR 0.5: your LLM doesn't need the whole file, it needs the gossip</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 17:39:35 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-05-your-llm-doesnt-need-the-whole-file-it-needs-the-gossip-4k64</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-05-your-llm-doesnt-need-the-whole-file-it-needs-the-gossip-4k64</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsfos5mnmi6epdv7l6vtc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsfos5mnmi6epdv7l6vtc.jpg" alt=" " width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A 10-minute read for developers, tech leads and the person at your company&lt;br&gt;
who signs the AI invoice.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Source (GitLab, Apache-2.0): &lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm: &lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Playground (runs in your browser): &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  TL;DR for people who scroll
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;sentinel-ir&lt;/code&gt; turns one source file into a small JSON fact sheet built from a&lt;br&gt;
real syntax tree: imports, exports, routes, env reads, network, filesystem,&lt;br&gt;
process and crypto calls, dangerous syntax, a risk level and the line that&lt;br&gt;
caused it. You send &lt;em&gt;that&lt;/em&gt; to the model instead of the raw file.&lt;/p&gt;

&lt;p&gt;0.5 adds &lt;strong&gt;Rust&lt;/strong&gt;, &lt;strong&gt;compile-time risk&lt;/strong&gt; for &lt;code&gt;build.rs&lt;/code&gt; and procedural macros,&lt;br&gt;
&lt;strong&gt;hardening&lt;/strong&gt; against comments and hostile input, and is &lt;strong&gt;~28% faster&lt;/strong&gt; on a&lt;br&gt;
35,000-file corpus. Same IR shape for every language, so the tools built on&lt;br&gt;
top (an MCP server and a browser playground) didn't need a single new field.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why bother: the "senior dev at standup" problem
&lt;/h2&gt;

&lt;p&gt;Ask a senior developer what a file does and you won't get a recital of 1,200&lt;br&gt;
lines. You'll get: "It's an Express handler, reads &lt;code&gt;STRIPE_KEY&lt;/code&gt;, writes to&lt;br&gt;
&lt;code&gt;/tmp&lt;/code&gt;, and shells out to ImageMagick, which is the part that'll bite us."&lt;/p&gt;

&lt;p&gt;That's the IR. An LLM reading the raw file is the intern reading all 1,200&lt;br&gt;
lines out loud. The tokens cost money, the comments can contain "ignore&lt;br&gt;
previous instructions", and the important line is somewhere around 940.&lt;/p&gt;

&lt;p&gt;Measured on the library's own source through the MCP server (tokenizer&lt;br&gt;
&lt;code&gt;gpt-tokenizer&lt;/code&gt;, &lt;code&gt;o200k_base&lt;/code&gt;):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;file&lt;/th&gt;
&lt;th&gt;raw tokens&lt;/th&gt;
&lt;th&gt;IR tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/ast-facts.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;8,439&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/sentinel-ir.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,084&lt;/td&gt;
&lt;td&gt;133&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;src/tree-parser.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;891&lt;/td&gt;
&lt;td&gt;121&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;examples/basic.js&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;150&lt;/td&gt;
&lt;td&gt;120&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The IR has a floor of about 120 tokens. On a tiny file it doesn't pay off,&lt;br&gt;
and we say so. On anything you'd actually want reviewed, it's one or two&lt;br&gt;
orders of magnitude.&lt;/p&gt;
&lt;h2&gt;
  
  
  What the output looks like
&lt;/h2&gt;

&lt;p&gt;Three lines of JavaScript that call &lt;code&gt;eval&lt;/code&gt; and &lt;code&gt;child_process&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"analysisStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"complete"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nl"&gt;"world"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"risk"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"critical"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nl"&gt;"forbidden"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eval"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"child_process"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"process_spawn"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
 &lt;span class="nl"&gt;"facts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"ast"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"child_process"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"operations"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"dynamicEval"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"processSpawn"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"childProcess"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nl"&gt;"riskSignals"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"child_process_import:require(child_process)@1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                 &lt;span class="s2"&gt;"dynamic_code:eval@2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="s2"&gt;"process_spawn:cp.exec@2"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Trimmed. The full IR also has &lt;code&gt;mission&lt;/code&gt;, &lt;code&gt;constraints&lt;/code&gt;, &lt;code&gt;summary&lt;/code&gt; and the&lt;br&gt;
call lists.) Every signal is &lt;code&gt;kind:evidence@line&lt;/code&gt;, so a reviewer, human or&lt;br&gt;
model, can jump straight to the proof. &lt;code&gt;facts.ast: true&lt;/code&gt; means the file&lt;br&gt;
parsed completely, so a &lt;em&gt;missing&lt;/em&gt; category is a proven absence, not a shrug.&lt;/p&gt;

&lt;p&gt;The rules are structural, not vibes: a route is a call on&lt;br&gt;
&lt;code&gt;app&lt;/code&gt;/&lt;code&gt;router&lt;/code&gt;/&lt;code&gt;server&lt;/code&gt;/&lt;code&gt;api&lt;/code&gt; with a string path, &lt;code&gt;exec&lt;/code&gt; is only process&lt;br&gt;
spawning when it's bound to a spawning module, and a function called&lt;br&gt;
&lt;code&gt;evaluate&lt;/code&gt; is not &lt;code&gt;eval&lt;/code&gt;. Regex-based scanners have been confusing those two&lt;br&gt;
since before some of you were born.&lt;/p&gt;
&lt;h2&gt;
  
  
  New in 0.5: Rust
&lt;/h2&gt;

&lt;p&gt;Rust is the language you pick when you'd rather argue with a compiler than&lt;br&gt;
with production. It's also where a lot of new infrastructure, browsers and&lt;br&gt;
crypto lives, so it's a good place to know what code can &lt;em&gt;do&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The Rust analyzer produces the same IR and fact categories as JS/TS/Python.&lt;br&gt;
Paths are resolved through the file's own &lt;code&gt;use&lt;/code&gt; declarations, including&lt;br&gt;
aliases, &lt;code&gt;{...}&lt;/code&gt; lists, &lt;code&gt;self&lt;/code&gt;, &lt;code&gt;*&lt;/code&gt; globs and &lt;code&gt;extern crate&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nc"&gt;Command&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;Cmd&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;deploy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nc"&gt;Cmd&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sh"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"-c"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;arg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;script&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// -&amp;gt; processSpawn, critical, process_spawn@2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;crate::&lt;/code&gt;, &lt;code&gt;self::&lt;/code&gt;, &lt;code&gt;super::&lt;/code&gt; and a local &lt;code&gt;mod fs { ... }&lt;/code&gt; are not mistaken&lt;br&gt;
for the standard library. A few of the things it recognizes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Processes:&lt;/strong&gt; &lt;code&gt;std::process&lt;/code&gt;, &lt;code&gt;tokio::process&lt;/code&gt;, &lt;code&gt;nix&lt;/code&gt; exec and fork, &lt;code&gt;libc::system&lt;/code&gt;, &lt;code&gt;duct&lt;/code&gt;, &lt;code&gt;xshell&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Native code (critical):&lt;/strong&gt; &lt;code&gt;unsafe&lt;/code&gt;, &lt;code&gt;extern&lt;/code&gt; blocks, &lt;code&gt;#[no_mangle]&lt;/code&gt;, &lt;code&gt;asm!&lt;/code&gt;, &lt;code&gt;libloading&lt;/code&gt;, Windows &lt;code&gt;LoadLibrary&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Embedded interpreters:&lt;/strong&gt; &lt;code&gt;rhai&lt;/code&gt;, &lt;code&gt;mlua&lt;/code&gt;, &lt;code&gt;deno_core&lt;/code&gt;, &lt;code&gt;v8&lt;/code&gt;, &lt;code&gt;pyo3&lt;/code&gt; &lt;code&gt;run&lt;/code&gt;/&lt;code&gt;eval&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Filesystem, network and env:&lt;/strong&gt; &lt;code&gt;std::fs&lt;/code&gt;, &lt;code&gt;tokio::fs&lt;/code&gt;, &lt;code&gt;reqwest&lt;/code&gt;, &lt;code&gt;hyper&lt;/code&gt;, &lt;code&gt;TcpStream&lt;/code&gt;, &lt;code&gt;std::env::var&lt;/code&gt;, &lt;code&gt;dotenvy&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Routes:&lt;/strong&gt; axum &lt;code&gt;.route("/p", get(h))&lt;/code&gt;, actix attributes, rocket, warp, poem, salvo.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Does it actually parse real Rust?&lt;/strong&gt; The stock tree-sitter-rust 0.24.2&lt;br&gt;
grammar rejects a surprising amount of real code: all of &lt;code&gt;web-sys&lt;/code&gt;/&lt;code&gt;js-sys&lt;/code&gt;&lt;br&gt;
(wasm-bindgen &lt;code&gt;pub type X;&lt;/code&gt;), &lt;code&gt;safe fn&lt;/code&gt;, &lt;code&gt;const impl&lt;/code&gt;, trait aliases, &lt;code&gt;macro&lt;/code&gt;&lt;br&gt;
2.0, &lt;code&gt;f16&lt;/code&gt;/&lt;code&gt;f128&lt;/code&gt;, and more. We ship a patched grammar that rebuilds byte&lt;br&gt;
for byte from a script. Results:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;corpus&lt;/th&gt;
&lt;th&gt;stock grammar&lt;/th&gt;
&lt;th&gt;patched&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;15,238 files, 424 crate versions (tokio, reqwest, axum, nix, libc, ring, web-sys…)&lt;/td&gt;
&lt;td&gt;13,693 parse&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;15,238&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2,874 files of the Rust standard library&lt;/td&gt;
&lt;td&gt;2,724 parse&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,874&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Is &lt;code&gt;unsafe&lt;/code&gt; detection any good?&lt;/strong&gt; We compared it with&lt;br&gt;
&lt;a href="https://crates.io/crates/syn" rel="noopener noreferrer"&gt;syn&lt;/a&gt;, the parser behind &lt;code&gt;cargo-geiger&lt;/code&gt;, on the&lt;br&gt;
15,237 files syn parses:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;construct&lt;/th&gt;
&lt;th&gt;syn&lt;/th&gt;
&lt;th&gt;sentinel-ir&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unsafe impl&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;2,839&lt;/td&gt;
&lt;td&gt;2,839&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unsafe trait&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;td&gt;160&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;extern&lt;/code&gt; blocks&lt;/td&gt;
&lt;td&gt;6,266&lt;/td&gt;
&lt;td&gt;6,266&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;unsafe fn&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;10,137&lt;/td&gt;
&lt;td&gt;10,149 (12 inside wasm-bindgen &lt;code&gt;extern&lt;/code&gt; blocks syn skips)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;unsafe&lt;/code&gt; blocks&lt;/td&gt;
&lt;td&gt;35,428&lt;/td&gt;
&lt;td&gt;37,167 (the extras are in macro bodies syn doesn't look into)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;files with any &lt;code&gt;unsafe&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;4,857&lt;/td&gt;
&lt;td&gt;all 4,857 flagged&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Plus 49 labeled snippets, and 2,950 deliberately broken real files that never&lt;br&gt;
crash the analyzer and never report a dangerous operation from a half-parsed&lt;br&gt;
file.&lt;/p&gt;
&lt;h2&gt;
  
  
  New in 0.5: the code that runs before your code runs
&lt;/h2&gt;

&lt;p&gt;Here's the part that should make a security team sit up. In Rust, two kinds&lt;br&gt;
of code execute &lt;strong&gt;on the developer's machine during &lt;code&gt;cargo build&lt;/code&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;build.rs&lt;/code&gt; build scripts.&lt;/li&gt;
&lt;li&gt;Procedural macros (&lt;code&gt;#[proc_macro]&lt;/code&gt;, &lt;code&gt;#[proc_macro_derive]&lt;/code&gt;, &lt;code&gt;#[proc_macro_attribute]&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is the supply-chain attack path: you never ran the dependency, you just&lt;br&gt;
compiled it. That's like being pickpocketed by the IKEA manual.&lt;/p&gt;

&lt;p&gt;Since 0.5.1, in such a file, reading or writing files at runtime, using the&lt;br&gt;
network or touching a secret-like env variable raises the risk to at least&lt;br&gt;
&lt;code&gt;high&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cp"&gt;#[proc_macro_attribute]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;m&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;TokenStream&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;fs&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"/etc/passwd"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// low -&amp;gt; high&lt;/span&gt;
    &lt;span class="n"&gt;i&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What the compiler reads on purpose (&lt;code&gt;include!&lt;/code&gt;, &lt;code&gt;include_str!&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;#[path]&lt;/code&gt;) and Cargo's own variables (&lt;code&gt;OUT_DIR&lt;/code&gt;, &lt;code&gt;CARGO_CFG_*&lt;/code&gt;) do not raise&lt;br&gt;
it, so normal build scripts don't light up like a Christmas tree. Building or&lt;br&gt;
linking C at build time (&lt;code&gt;cc::Build&lt;/code&gt;, &lt;code&gt;cmake&lt;/code&gt;, &lt;code&gt;pkg_config&lt;/code&gt;, &lt;code&gt;vcpkg&lt;/code&gt;,&lt;br&gt;
&lt;code&gt;cargo:rustc-link-lib&lt;/code&gt;) is &lt;code&gt;nativeCode&lt;/code&gt;, i.e. critical.&lt;/p&gt;

&lt;p&gt;On 18,112 real files (crates.io and std) 0.5.1 changed 19 risk levels, all of&lt;br&gt;
them build scripts or C builds, and 17 of those went to &lt;code&gt;critical&lt;/code&gt;. Zero&lt;br&gt;
&lt;code&gt;analysisStatus&lt;/code&gt; changes.&lt;/p&gt;
&lt;h2&gt;
  
  
  New in 0.5: harder to fool, harder to stall
&lt;/h2&gt;

&lt;p&gt;An analyzer that feeds an LLM is itself an attack surface. The person who&lt;br&gt;
wrote the file you're scanning gets to pick its contents. So in 0.5.4 and&lt;br&gt;
0.5.5:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comments can't hide calls.&lt;/strong&gt; &lt;code&gt;require(/*x*/"child_process").exec(c)&lt;/code&gt;
was &lt;code&gt;low&lt;/code&gt; in 0.5.3. Ouch. Every extractor now skips comment nodes, in
every language. A fuzz run inserting comments into 2,910 files: 0 fact
differences.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Text from the code is capped and redacted.&lt;/strong&gt; Every string that reaches
the IR (routes, imports, env names, evidence) is one line and at most 128
characters. String literals in evidence become &lt;code&gt;"…"&lt;/code&gt;, so
&lt;code&gt;token == os.environ["API_TOKEN"]&lt;/code&gt; shows up as
&lt;code&gt;token == os.environ["…"]&lt;/code&gt;. Less attacker-controlled text reaches your
prompt. It's &lt;em&gt;less&lt;/em&gt;, not &lt;em&gt;none&lt;/em&gt;, and the README says so.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Broken files keep their risk.&lt;/strong&gt; When a Python or Rust file doesn't parse
(one syntax error, absurd nesting), the text fallback now knows each
language's dangerous names (&lt;code&gt;os.system&lt;/code&gt;, &lt;code&gt;subprocess&lt;/code&gt;, &lt;code&gt;Command::new&lt;/code&gt;,
&lt;code&gt;unsafe&lt;/code&gt;, &lt;code&gt;std::fs::write&lt;/code&gt;…). Breaking your own syntax used to be a way to
look harmless. Now it just makes you look broken.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;5-second parse budget.&lt;/strong&gt; tree-sitter's error recovery is superlinear on
some inputs: 16 KB of &lt;code&gt;-&amp;gt;&lt;/code&gt; took about 43 s in JS. Now a parse stops at
5 s and the result is &lt;code&gt;incomplete&lt;/code&gt; with the reason. The slowest real file
in a 35,000-file corpus takes 0.2 s, so only hostile input ever hits this.
4,000 mutated files: 0 crashes, 0 risk levels lowered.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  New in 0.5: faster, for free
&lt;/h2&gt;

&lt;p&gt;0.5.3 stopped building a JS object for every syntax node and walking the tree&lt;br&gt;
a second time for metrics nobody read. The IR stayed byte-identical:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;measurement&lt;/th&gt;
&lt;th&gt;0.5.2&lt;/th&gt;
&lt;th&gt;0.5.3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;35,000 files (Rust, JS, TS, Python)&lt;/td&gt;
&lt;td&gt;210 s&lt;/td&gt;
&lt;td&gt;151 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;memory, 910 KB Rust file&lt;/td&gt;
&lt;td&gt;381 MB&lt;/td&gt;
&lt;td&gt;222 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;memory, 744 KB Python file&lt;/td&gt;
&lt;td&gt;188 MB&lt;/td&gt;
&lt;td&gt;123 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;IR differences&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The best optimization is deleting code. Here, nobody was even reading it.&lt;/p&gt;
&lt;h2&gt;
  
  
  Supply chain: we eat our own cooking
&lt;/h2&gt;

&lt;p&gt;A security-flavored package shipped from someone's laptop at 2 a.m. is a&lt;br&gt;
genre of horror movie. &lt;code&gt;sentinel-ir&lt;/code&gt; is published only from CI on protected&lt;br&gt;
tags, with npm provenance. Since 0.5.2 the release &lt;strong&gt;fails&lt;/strong&gt; if the&lt;br&gt;
provenance attestation is missing, and CI checks &lt;code&gt;npm audit signatures&lt;/code&gt; on&lt;br&gt;
npm 10 and 11 after publishing. Tests run on Node 20, 22 and 24.&lt;/p&gt;
&lt;h2&gt;
  
  
  How companies can use it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cheaper code review bots.&lt;/strong&gt; Triage a pull request by IR first, then send
the raw source of only the files whose risk changed. The model reads 120
tokens instead of 8,000 for every file that just formats dates.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dependency gates.&lt;/strong&gt; Flag new or updated crates whose &lt;code&gt;build.rs&lt;/code&gt; or
proc macro suddenly touches the network. That's exactly the moment you
want a human to look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Agent guardrails.&lt;/strong&gt; Give agents the IR through the
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;MCP server&lt;/a&gt; so they
reason about what code &lt;em&gt;does&lt;/em&gt; without pasting it into the conversation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One shape for four languages.&lt;/strong&gt; JS/TS, Python and Rust come out in the
same JSON. Your pipeline handles one format, not four parsers with four
opinions.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;compress&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"sentinel-ir"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"build.rs"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"critical"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;world&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;risk&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s2"&gt;"high"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;flagForReview&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;sendToModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;compress&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ir&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  The honest part
&lt;/h2&gt;

&lt;p&gt;We'd rather you trust the numbers than the marketing, so here's what it&lt;br&gt;
doesn't do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One file at a time.&lt;/strong&gt; Capabilities split across files are not combined.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Syntax, not data flow.&lt;/strong&gt; &lt;code&gt;readFileSync(req.query.f)&lt;/code&gt; is a file read, not
"path traversal". Risk is about &lt;em&gt;capabilities&lt;/em&gt;, not vulnerabilities, and
&lt;code&gt;medium&lt;/code&gt; (network, writes, crypto) is normal backend code.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Macros are token trees.&lt;/strong&gt; Calls inside a macro's arguments are found.
What a macro &lt;em&gt;expands&lt;/em&gt; to is not, because that needs &lt;code&gt;rustc&lt;/code&gt; and
&lt;code&gt;cargo expand&lt;/code&gt;, which a source-only analyzer doesn't run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Not a scanner replacement.&lt;/strong&gt; Code that builds module names at runtime
can look harmless. &lt;code&gt;low&lt;/code&gt; means "nothing found", not "safe". Use it next to
your SAST and your reviewers, not instead of them.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;Go is next on the list. Same IR, same categories, a fifth language. The goal&lt;br&gt;
hasn't changed: one analyzer, one shared shape, reused everywhere (the npm&lt;br&gt;
library, the MCP server and the playground), so a new language lands in&lt;br&gt;
all three at once.&lt;/p&gt;

&lt;p&gt;Try it without installing anything in the&lt;br&gt;
&lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;playground&lt;/a&gt;, or:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm install sentinel-ir
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Issues and merge requests are welcome on&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt;. Bug reports that&lt;br&gt;
start with "I tried to break it and…" are especially welcome.&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
    <item>
      <title>Sentinel-IR MCP 0.5: a bouncer for your AI assistant</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 17:35:09 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-mcp-05-a-bouncer-for-your-ai-assistant-1m37</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-mcp-05-a-bouncer-for-your-ai-assistant-1m37</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frk5r53hgf1pgxq83mean.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Frk5r53hgf1pgxq83mean.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A 2-minute read. No programming needed. Contains one (1) nightclub metaphor.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What it is
&lt;/h2&gt;

&lt;p&gt;AI coding assistants such as Claude Desktop, Cursor and Windsurf can now read&lt;br&gt;
the files on your computer by themselves. That's handy. It's also a bit like&lt;br&gt;
giving a very fast, very eager intern the keys to the whole office.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentinel-IR MCP is the bouncer at the door.&lt;/strong&gt; MCP is the standard plug that&lt;br&gt;
lets AI assistants use outside tools. Plug Sentinel-IR MCP in, and your&lt;br&gt;
assistant gets a short X-ray of each code file (what it touches, how risky it&lt;br&gt;
is, which line does it) instead of the full text, but only for the folder you&lt;br&gt;
picked:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Only your project.&lt;/strong&gt; Shortcuts and sneaky paths that lead out of the
folder are refused. So is &lt;code&gt;/etc/passwd&lt;/code&gt;. (We checked. Twice.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Only reading.&lt;/strong&gt; It never changes a file, never runs code, never sends
your code anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Your no-go list.&lt;/strong&gt; Say "never look at &lt;code&gt;secrets&lt;/code&gt;", and the assistant can't
change that, and neither can a file in the project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A diary, if you want one.&lt;/strong&gt; Turn on the log and you can see later what
the assistant asked for: file names and results, never your code.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  What's new in 0.5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;It reads Rust.&lt;/strong&gt; JavaScript, TypeScript, Python and now Rust. It also skips&lt;br&gt;
Rust's giant build folder, so your assistant doesn't spend an afternoon&lt;br&gt;
reading machine-generated leftovers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It updates itself.&lt;/strong&gt; Before, &lt;code&gt;npx&lt;/code&gt; (the tool that starts the server) kept&lt;br&gt;
starting the first copy it ever downloaded, a bit like a phone that hides&lt;br&gt;
system updates in a drawer. Now setup points it at a label called&lt;br&gt;
&lt;code&gt;stable-0.5&lt;/code&gt;, which npx checks again on every start, so fixes reach you&lt;br&gt;
automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It has a doctor.&lt;/strong&gt; Run &lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 doctor&lt;/code&gt; and it&lt;br&gt;
checks everything: your setup, your no-go list, the diary, whether the server&lt;br&gt;
starts, and whether a newer version exists. It now works behind company&lt;br&gt;
proxies too, and it no longer says "all good" when it just printed a warning.&lt;br&gt;
(Yes, it used to. We're not proud.)&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tested on all three.&lt;/strong&gt; Linux, macOS and Windows. We tried hard to break it&lt;br&gt;
before release and fixed everything we found.&lt;/p&gt;
&lt;h2&gt;
  
  
  Get it
&lt;/h2&gt;

&lt;p&gt;One command sets up your assistant (it asks before writing anything):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npx -y sentinel-ir-mcp@stable-0.5 setup
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Listed on &lt;a href="https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;Glama&lt;/a&gt; and&lt;br&gt;
the official &lt;a href="https://registry.modelcontextprotocol.io" rel="noopener noreferrer"&gt;MCP Registry&lt;/a&gt;. Free and&lt;br&gt;
open source:&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; ·&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/sentinel-ir-mcp" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want to see what the assistant sees first? Try the&lt;br&gt;
&lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;playground&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>rust</category>
    </item>
    <item>
      <title>Sentinel-IR 0.5: an X-ray for code, now with a Rust filter</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 17:33:04 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-05-an-x-ray-for-code-now-with-a-rust-filter-kel</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-05-an-x-ray-for-code-now-with-a-rust-filter-kel</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjruxmaw2270gye83vq53.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjruxmaw2270gye83vq53.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A 2-minute read. No programming needed. Mild sarcasm included at no extra cost.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The problem, in one picture
&lt;/h2&gt;

&lt;p&gt;When you ask an AI assistant "is this code safe?", most tools paste the whole&lt;br&gt;
file into the chat. That's like asking your doctor about a sore knee and&lt;br&gt;
handing over every selfie you've ever taken. Technically it's all you. Mostly&lt;br&gt;
it's noise, and you get billed for the noise, because AI is paid by the word.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentinel-IR hands over the X-ray instead.&lt;/strong&gt; It reads a source file and&lt;br&gt;
writes a short note: what the code touches (files, the internet, passwords,&lt;br&gt;
other programs), how risky that is (&lt;code&gt;low&lt;/code&gt;, &lt;code&gt;medium&lt;/code&gt;, &lt;code&gt;high&lt;/code&gt;, &lt;code&gt;critical&lt;/code&gt;) and&lt;br&gt;
the exact line where it does it. A 40,000-character file becomes a note of&lt;br&gt;
about 450 characters. The AI reads the note, and you pay for the note.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's new in 0.5
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. It reads Rust now.&lt;/strong&gt; Rust is the language people pick when they want&lt;br&gt;
software that doesn't crash, which is why it ends up in browsers, operating&lt;br&gt;
systems and crypto wallets. Sentinel-IR already spoke JavaScript, TypeScript&lt;br&gt;
and Python. Rust is number four, and it isn't a beginner's phrasebook: we ran&lt;br&gt;
it on more than 18,000 real Rust files, including Rust's own standard library,&lt;br&gt;
and every one of them was read completely.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It watches the build, not just the program.&lt;/strong&gt; Rust has a sneaky feature:&lt;br&gt;
some code runs &lt;em&gt;while the program is being built&lt;/em&gt;, on the developer's&lt;br&gt;
laptop, before anyone has pressed "run". Think of a flat-pack wardrobe that&lt;br&gt;
rearranges your living room while you're still reading the instructions.&lt;br&gt;
Sentinel-IR now marks that kind of code as at least &lt;code&gt;high&lt;/code&gt; risk when it&lt;br&gt;
touches files, the internet or secrets.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It's harder to fool.&lt;/strong&gt; A tiny comment wedged into the middle of a line&lt;br&gt;
used to be enough to hide a dangerous call. Now it isn't. A file built to&lt;br&gt;
make the analyzer think forever gets 5 seconds and then a stamp saying "this&lt;br&gt;
one didn't finish, don't trust the low score".&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. It's faster and lighter.&lt;/strong&gt; The same 35,000 real files now take 151&lt;br&gt;
seconds instead of 210, and a large Rust file needs about 40% less memory.&lt;br&gt;
Your laptop fan says thanks.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it doesn't do
&lt;/h2&gt;

&lt;p&gt;It doesn't run your code, doesn't upload it anywhere, and doesn't pretend to&lt;br&gt;
be a security guard. "Low risk" means "nothing scary found", not "certified&lt;br&gt;
safe". It's a smoke detector, not the fire brigade.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;p&gt;Open the &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;playground&lt;/a&gt;, paste&lt;br&gt;
some code (or open a file from your phone) and see exactly what an AI agent&lt;br&gt;
would get. Everything runs inside your browser and nothing is uploaded.&lt;/p&gt;

&lt;p&gt;Free and open source (Apache-2.0):&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir" rel="noopener noreferrer"&gt;GitLab&lt;/a&gt; ·&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;npm&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>opensource</category>
      <category>rust</category>
    </item>
    <item>
      <title>Sentinel-IR 0.5 in 30 seconds</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 17:30:38 +0000</pubDate>
      <link>https://dev.to/jackymencz/sentinel-ir-05-in-30-seconds-3oh</link>
      <guid>https://dev.to/jackymencz/sentinel-ir-05-in-30-seconds-3oh</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fktn4x8q9suejbwe9t7vm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fktn4x8q9suejbwe9t7vm.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Your AI agent used to read code the way a tourist reads a menu in a language&lt;br&gt;
they don't speak: slowly, expensively, and with a lot of guessing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentinel-IR 0.5&lt;/strong&gt; speaks four languages now: JavaScript, TypeScript, Python&lt;br&gt;
and &lt;strong&gt;Rust&lt;/strong&gt;. It catches the Rust tricks that run while you're still waiting&lt;br&gt;
for &lt;code&gt;cargo build&lt;/code&gt;. A comment hidden in the middle of a line can't sneak a&lt;br&gt;
dangerous call past it anymore, and a file built to make it hang gets five&lt;br&gt;
seconds before it's cut off.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentinel-IR MCP 0.5&lt;/strong&gt; plugs all of that into Claude Desktop, Cursor and&lt;br&gt;
Windsurf. It updates itself through a &lt;code&gt;stable-0.5&lt;/code&gt; tag, and its &lt;code&gt;doctor&lt;/code&gt;&lt;br&gt;
tells you when you're running an old copy.&lt;/p&gt;

&lt;p&gt;Same rule as always: it reads your code, never runs it, and never sends it&lt;br&gt;
anywhere.&lt;/p&gt;

&lt;p&gt;Try it in your browser: &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>Why Your AI Coding Agent Needs a Doctor’s Filing Cabinet. Sentinel Ir</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Sat, 14 Nov 2026 15:32:05 +0000</pubDate>
      <link>https://dev.to/jackymencz/why-your-ai-coding-agent-needs-a-doctors-filing-cabinet-34ib</link>
      <guid>https://dev.to/jackymencz/why-your-ai-coding-agent-needs-a-doctors-filing-cabinet-34ib</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbejlh7klz82i64mwcay.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgbejlh7klz82i64mwcay.jpg" alt=" " width="799" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine walking into a doctor’s office. You hand over your ID, and the nurse walks over to a massive metal cabinet. Inside, thousands of patient records are thrown together in one giant, unsorted pile. No folders. No alphabetical dividers. No insurance codes.&lt;br&gt;
To find your file, she has to pull out every single sheet of paper, read it from top to bottom, and put it back—until she finally stumbles upon your name.&lt;br&gt;
In the real world, no doctor works like that. A real clinic organizes records by insurance provider code, then alphabetically by last name. When Mr. Novak walks in with insurance code 205, the nurse pulls his folder in three seconds flat.&lt;br&gt;
It’s boring. It’s obvious. And it saves hundreds of hours.&lt;br&gt;
Yet, when we give AI coding agents access to our software repositories today, we still hand them an unsorted pile of paper.&lt;/p&gt;

&lt;p&gt;Links&lt;br&gt;
Playground: &lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;&lt;br&gt;
GitHub: sentinel-ir · sentinel-ir-mcp · sentinel-ir-playground&lt;br&gt;
GitLab (main): sentinel-ir · sentinel-ir-mcp · sentinel-ir-playground&lt;br&gt;
Glama: &lt;a href="https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp&lt;/a&gt;&lt;br&gt;
MCP Registry: io.github.jakcmencz/sentinel-ir-mcp&lt;br&gt;
npm: sentinel-ir · sentinel-ir-mcp&lt;br&gt;
Issues are welcome on GitHub or GitLab. Bug reports with a file that fools&lt;br&gt;
the analyzer are the best kind.&lt;/p&gt;

&lt;p&gt;The Hidden Cost of AI-Generated Code&lt;/p&gt;

&lt;p&gt;We are entering an era where AI agents generate billions of lines of code every day. Repositories are growing faster than humans—or even the agents themselves—can comfortably read.&lt;br&gt;
When an AI agent (in Claude Desktop, Cursor, or Windsurf) tries to understand a project today, its default behavior is brute force:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;List the directory.&lt;/li&gt;
&lt;li&gt;Open massive source files.&lt;/li&gt;
&lt;li&gt;Read thousands of lines of implementation details, comments, and boilerplate just to find where a single function is defined or how two modules connect.
Every time it does this, two things happen:
• Context bloat: The model’s attention gets diluted by noise, leading to hallucinations and missed edge cases.
• Token &amp;amp; latency waste: You pay for tens of thousands of tokens just for the agent to "look around" the room.
As codebases scale, raw file reading stops working. You don't want your agent reading every single sheet of paper in the cabinet just to find Mr. Novak.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Building the "Boring" Infrastructure: Sentinel IR&lt;/p&gt;

&lt;p&gt;When I started building sentinel-ir-mcp, I wasn't trying to invent a flashy AI wrapper. I wanted to build the doctor’s filing cabinet for codebases.&lt;br&gt;
Instead of forcing an LLM to parse raw JavaScript, TypeScript, and Python files over and over again, Sentinel IR uses a fast, local Rust engine to extract a compact Structural Intermediate Representation (IR)—a deterministic map of symbols, signatures, exports, and relationships.&lt;br&gt;
Think of it as the index card on the front of the drawer:&lt;br&gt;
• Code 205 → Novak: The agent queries the structural IR first, sees the exact shape of the codebase in milliseconds, and only opens the raw source file when it actually needs to read or edit the implementation.&lt;br&gt;
• Local-first &amp;amp; deterministic: Powered by Rust and running locally over stdio via the Model Context Protocol (MCP), it doesn't guess, it doesn't upload your code to a third-party cloud, and it works offline.&lt;/p&gt;

&lt;p&gt;Why "Boring" Reliability Matters More Than Hype&lt;/p&gt;

&lt;p&gt;A filing cabinet is only useful if the drawer doesn't jam when you pull it.&lt;br&gt;
In real-world engineering, developers don't work on pristine laptops with open internet. They work behind corporate proxies, custom TLS certificates, weird Windows paths with non-ASCII characters, and restricted environments. If an MCP server crashes the moment it hits a proxy or a self-signed certificate, it becomes another piece of fragile hype.&lt;br&gt;
That’s why most of the work behind sentinel-ir-mcp went into the unglamorous parts: cross-platform determinism, strict environment diagnostics (sentinel-ir-mcp doctor), and adversarial testing across macOS, Windows, and Linux.&lt;/p&gt;

&lt;p&gt;Looking Ahead&lt;/p&gt;

&lt;p&gt;Ten years from now, when AI agents are maintaining codebases orders of magnitude larger than today's monorepos, nobody will want agents brute-forcing raw text files. The winners in AI tooling won't just be the smartest models—they will be the quiet, reliable indexing layers underneath them.&lt;br&gt;
Sometimes, the best way to move forward is to organize the cabinet.&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>mcp</category>
      <category>devops</category>
      <category>agents</category>
    </item>
    <item>
      <title>Running Claude Code and a Local MCP Server (sentinel-ir-mcp) on Android via Termux</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Thu, 12 Nov 2026 18:13:54 +0000</pubDate>
      <link>https://dev.to/jackymencz/running-claude-code-and-a-local-mcp-server-sentinel-ir-mcp-on-android-via-termux-5c16</link>
      <guid>https://dev.to/jackymencz/running-claude-code-and-a-local-mcp-server-sentinel-ir-mcp-on-android-via-termux-5c16</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs6w3rdrpxyd2cnec7euk.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fs6w3rdrpxyd2cnec7euk.jpg" alt=" " width="800" height="1071"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want a full-featured AI coding agent right in your pocket on Android, connected to your own local Model Context Protocol (MCP) server? While Anthropic's official Claude Code CLI doesn't natively support Android out of the box, you can get the entire stack running smoothly inside Termux in just a few minutes.&lt;/p&gt;

&lt;p&gt;In this guide, we will walk through the complete setup step-by-step—including how to bypass the exact installation roadblocks you hit on Android and how to wire up sentinel-ir-mcp so Claude can inspect your code safely without burning tokens.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1: Set Up Your Project and Verify the MCP Server in Termux
&lt;/h2&gt;

&lt;p&gt;Before installing the AI client, create a dedicated workspace and verify that the MCP server (sentinel-ir-mcp) runs properly in your Termux Node.js environment.&lt;/p&gt;

&lt;p&gt;Run the built-in health check:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx -y sentinel-ir-mcp@0.4 doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As long as you have Node.js 20+ installed, doctor will verify the handshake and confirm that all three tools (analyze_code, analyze_directory, and analyze_file) work properly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security Safeguard Note:&lt;/strong&gt; If you run &lt;code&gt;npx -y sentinel-ir-mcp@0.4 setup&lt;/code&gt; directly inside your Termux home folder (&lt;code&gt;~&lt;/code&gt;), the tool will intentionally block you with: &lt;code&gt;Refusing to give the agent access to your whole home folder.&lt;/code&gt; By design, it prevents AI agents from scanning personal configs or sensitive files outside a specific project directory.&lt;/p&gt;

&lt;p&gt;Create a dedicated project folder and generate the setup command inside it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mkdir ~/my-project
&lt;span class="nb"&gt;cd&lt;/span&gt; ~/my-project
npx -y sentinel-ir-mcp@0.4 setup --client claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The setup wizard will output the exact registration command you will need for Claude Code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 2: Why Direct Claude Code Installation Fails in Termux
&lt;/h2&gt;

&lt;p&gt;Installing Claude Code directly via &lt;code&gt;npm install -g @anthropic-ai/claude-code&lt;/code&gt; in Termux fails due to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Blocked lifecycle scripts:&lt;/strong&gt; Newer &lt;code&gt;npm&lt;/code&gt; versions block &lt;code&gt;postinstall&lt;/code&gt; scripts by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing Android binaries:&lt;/strong&gt; The installer lacks builds for Android's &lt;code&gt;bionic&lt;/code&gt; libc, supporting only standard Linux (&lt;code&gt;linux-arm64&lt;/code&gt;) and other desktop/server platforms.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Step 3: The Fix — Running Ubuntu in Termux (No Root Required)
&lt;/h2&gt;

&lt;p&gt;You can bypass this by running Ubuntu via &lt;code&gt;proot-distro&lt;/code&gt; inside Termux:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pkg update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pkg install -y proot-distro
proot-distro install ubuntu
proot-distro login ubuntu
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once inside the Ubuntu environment, install Node.js and Claude Code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt install -y curl
curl -fsSL https://nodesource.com &lt;span class="o"&gt;|&lt;/span&gt; bash -
apt install -y nodejs
npm install -g @anthropic-ai/claude-code
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4: Create a Sample File and Connect sentinel-ir-mcp
&lt;/h2&gt;

&lt;p&gt;Navigate to your Termux project folder (accessible directly from Ubuntu via its full path), create a sample JavaScript file with risky capabilities to test the analyzer, and register your MCP server.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /data/data/com.termux/files/home/my-project

cat &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="s1"&gt;'EOF'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; test.js
const fs &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'fs'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
const &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'child_process'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;function&lt;/span&gt; runCommand&lt;span class="o"&gt;(&lt;/span&gt;cmd&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="nb"&gt;exec&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;cmd&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
module.exports &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; runCommand &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
EOF
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now register sentinel-ir-mcp with Claude Code using the command generated in Step 1:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude mcp add --transport stdio sentinel-ir -- npx -y sentinel-ir-mcp@0.4 /data/data/com.termux/files/home/my-project
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 5: Launch Claude Code and Authenticate
&lt;/h2&gt;

&lt;p&gt;Everything is wired up. Start the Claude Code CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;claude
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be greeted by Claude Code's pixel mascot ("Clawd") and guided through a quick one-time onboarding:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Choose your preferred terminal theme (e.g., Dark mode).&lt;/li&gt;
&lt;li&gt;Select your authentication method—either a standard Claude.ai subscription (Pro / Max) or an Anthropic Console API account with usage credits.&lt;/li&gt;
&lt;li&gt;Open the authentication link in your phone's browser, approve access, and paste the verification code back into Termux.&lt;/li&gt;
&lt;li&gt;Trust the &lt;code&gt;/data/data/com.termux/files/home/my-project&lt;/code&gt; workspace when prompted.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Putting It to the Test
&lt;/h2&gt;

&lt;p&gt;Once inside the interactive prompt, ask Claude to inspect your workspace:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Use sentinel-ir (analyze_directory) to scan this project and summarize any capability risks."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Under the hood, Claude calls your local sentinel-ir-mcp server over stdio. Instead of ingesting raw source code, it receives a compact structural Intermediate Representation (IR) and immediately flags the critical risk from child_process (cp.exec)—giving you a fast, token-efficient, and sandboxed AI development workflow right on your phone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foojppc4n9ayenhwmlm4p.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foojppc4n9ayenhwmlm4p.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff2u1dq04so1ufsrdm49w.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff2u1dq04so1ufsrdm49w.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07h3ul9ng0p1fra55kuc.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F07h3ul9ng0p1fra55kuc.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk53u8g03hq9jxs8qo89n.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk53u8g03hq9jxs8qo89n.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F95zsvmu8em6a9p25w0m8.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F95zsvmu8em6a9p25w0m8.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3xq7y3mfabku3vegpgf.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ff3xq7y3mfabku3vegpgf.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Independent Engineering Projects
&lt;/h2&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Is Your Code Used to Train AI? The Security-First Guide to Claude Code + sentinel-ir-mcp&lt;/p&gt;

&lt;p&gt;With everyone talking about Claude Code right now, it is completely fair for security-conscious developers to ask: Does my proprietary code end up training Anthropic’s models, and can the AI snoop through my private files?&lt;/p&gt;

&lt;p&gt;The short answer is no, not when configured properly. By pairing Claude Code with a local "sentinel-ir-mcp" server, you can introduce an additional security layer between your local codebase and your AI tooling.&lt;/p&gt;

&lt;p&gt;Think of "sentinel-ir-mcp" as a strict security gate standing between your private files and the AI agent.&lt;/p&gt;

&lt;p&gt;Here is the security breakdown, including the relevant privacy policies, technical limitations, and commands to configure your environment.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Your Code Is NOT Used to Train Models&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;How Anthropic handles your data depends on your account type and settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;API Keys, Team &amp;amp; Enterprise Plans: Under Anthropic's Commercial Terms, customer inputs and outputs are not used for model training by default. Standard retention and Zero Data Retention options depend on account eligibility and configuration.&lt;/li&gt;
&lt;li&gt;Personal Accounts (Claude Pro / Max): These fall under Consumer Terms. Users can manage model-training permissions through "Anthropic's privacy settings" (&lt;a href="https://claude.ai/settings/data-privacy-controls" rel="noopener noreferrer"&gt;https://claude.ai/settings/data-privacy-controls&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;The "/feedback" Warning: Avoid submitting sensitive code through "/feedback", bug reports, or sharing features. Explicit feedback submissions may be retained separately for service improvement.&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Your Raw Code and Secrets Need Protection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The local "sentinel-ir-mcp" server provides an additional inspection layer before source information is made available through its MCP tools.&lt;/p&gt;

&lt;p&gt;Its security-oriented features include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local Execution: Source analysis runs locally rather than requiring your project files to be uploaded to an external analysis service.&lt;/li&gt;
&lt;li&gt;Intermediate Representation (IR): Instead of relying exclusively on raw source code, the server can provide compact structural information, including risk signals, forbidden operations, and relevant code locations.&lt;/li&gt;
&lt;li&gt;File Exclusions: Configure restricted paths and file patterns to prevent the MCP server from processing sensitive files.&lt;/li&gt;
&lt;li&gt;Symlink Protection: Symbolic links are skipped during directory scanning.&lt;/li&gt;
&lt;li&gt;SHA-256 Audit Trail: Local audit records can provide integrity fingerprints for analyzed content.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Important distinction: A local MCP server does not automatically control everything Claude Code can access. Claude Code has its own permissions, tools, and filesystem access. The MCP server's restrictions apply to its own operations, not necessarily to the entire AI agent.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;How to Configure Your Environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The following setup adds restrictions to the local MCP server and reduces non-essential Claude Code traffic.&lt;/p&gt;

&lt;p&gt;Step 1: Configure sentinel-ir-mcp exclusions&lt;/p&gt;

&lt;p&gt;Run these commands inside your project directory:&lt;/p&gt;

&lt;p&gt;cd your-project-folder&lt;/p&gt;

&lt;p&gt;npx -y &lt;a href="mailto:sentinel-ir-mcp@0.4"&gt;sentinel-ir-mcp@0.4&lt;/a&gt; setup \&lt;br&gt;
  --client claude-code \&lt;br&gt;
  --exclude secrets \&lt;br&gt;
  --exclude ".env*" \&lt;br&gt;
  --exclude "*.pem"&lt;/p&gt;

&lt;p&gt;These exclusions configure the MCP server's scanning scope. They should not be interpreted as a global filesystem restriction for Claude Code.&lt;/p&gt;

&lt;p&gt;Step 2: Disable non-essential traffic&lt;/p&gt;

&lt;p&gt;Add the following environment variables to your shell profile ("~/.bashrc" or "~/.zshrc"):&lt;/p&gt;

&lt;p&gt;export CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1&lt;br&gt;
export DISABLE_TELEMETRY=1&lt;br&gt;
export DISABLE_ERROR_REPORTING=1&lt;br&gt;
export DISABLE_FEEDBACK_COMMAND=1&lt;/p&gt;

&lt;p&gt;These settings should be checked against the current Claude Code documentation because supported environment variables and their behavior can change between releases.&lt;/p&gt;

&lt;p&gt;Step 3: Reduce local session caching&lt;/p&gt;

&lt;p&gt;Edit:&lt;/p&gt;

&lt;p&gt;"~/.claude/settings.json"&lt;/p&gt;

&lt;p&gt;Set the cleanup period to one day:&lt;/p&gt;

&lt;p&gt;{&lt;br&gt;
  "cleanupPeriodDays": 1&lt;br&gt;
}&lt;/p&gt;

&lt;p&gt;This controls local session cleanup. It does not change Anthropic's server-side data retention policies.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;What This Setup Actually Protects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;It is important to distinguish between three separate security boundaries:&lt;/p&gt;

&lt;p&gt;Security layer| What it controls&lt;br&gt;
Anthropic privacy settings| Whether submitted data can be used for model training&lt;br&gt;
Claude Code permissions| What the AI agent can access and execute&lt;br&gt;
sentinel-ir-mcp| What the MCP server itself can inspect and expose&lt;/p&gt;

&lt;p&gt;No single configuration replaces the others.&lt;/p&gt;

&lt;p&gt;A local MCP server is an additional security component, not a complete sandbox or a guarantee that proprietary source code never reaches a cloud service.&lt;/p&gt;

&lt;p&gt;Bottom Line: Security Before Convenience&lt;/p&gt;

&lt;p&gt;You do not have to abandon advanced AI development tools to take source-code privacy seriously.&lt;/p&gt;

&lt;p&gt;Combining commercial data-protection terms, carefully configured Claude Code permissions, and a local "sentinel-ir-mcp" analysis layer provides a more controlled development environment.&lt;/p&gt;

&lt;p&gt;The important thing is understanding exactly where each security boundary begins and ends.&lt;/p&gt;

&lt;p&gt;Experiment first. Headline second. Security claims should always match what the software actually enforces.&lt;/p&gt;

&lt;h3&gt;
  
  
  Explore the projects
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Sentinel Ecosystem:&lt;/strong&gt; AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Agentix Lite:&lt;/strong&gt; Independent security and system-observation project.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Find the projects:&lt;/strong&gt; GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Support the work:&lt;/strong&gt; ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Connect:&lt;/strong&gt; Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>agents</category>
      <category>mcp</category>
      <category>claude</category>
    </item>
    <item>
      <title>90 Minutes for One Merge Gitlab? Windows Developers Can't Afford to Wait.</title>
      <dc:creator>jackymenCZ (jackymenCZ)</dc:creator>
      <pubDate>Wed, 11 Nov 2026 15:26:44 +0000</pubDate>
      <link>https://dev.to/jackymencz/90-minutes-for-one-merge-windows-developers-cant-afford-to-wait-38j6</link>
      <guid>https://dev.to/jackymencz/90-minutes-for-one-merge-windows-developers-cant-afford-to-wait-38j6</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpvagl6n4n1zhbc42ntpt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fpvagl6n4n1zhbc42ntpt.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp/-/blob/09b61d9dc1b90f3ebc7a37be0493d951249453cf/.github/workflows/windows.yml" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir-mcp/-/blob/09b61d9dc1b90f3ebc7a37be0493d951249453cf/.github/workflows/windows.yml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;description: "Slow CI is only the beginning. Shell differences, executable resolution, special characters in paths, and MCP client configurations create a hidden compatibility tax for developers building AI tools for Windows."&lt;/p&gt;

&lt;p&gt;series: "The Windows Development Challenge"&lt;/p&gt;

&lt;p&gt;The Windows Tax: Why Building AI Developer Tools for Windows Is Harder Than It Looks 🪟&lt;/p&gt;

&lt;p&gt;In "Part 1, we reduced our Windows CI/CD pipeline from 13.6 minutes to 2 minutes and 44 seconds" (&lt;a href="https://dev.to/jackymencz/how-we-slashed-our-windows-cicd-pipeline-from-14-minutes-to-under-3-2id0"&gt;https://dev.to/jackymencz/how-we-slashed-our-windows-cicd-pipeline-from-14-minutes-to-under-3-2id0&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;That solved one problem: waiting for tests.&lt;/p&gt;

&lt;p&gt;But it exposed another problem that developers working on AI infrastructure know very well.&lt;/p&gt;

&lt;p&gt;Getting your software to actually work on Windows is an entirely different engineering challenge.&lt;/p&gt;

&lt;p&gt;Writing an MCP server is one thing. Making sure it installs, launches, connects, and behaves correctly across different Windows environments is another.&lt;/p&gt;

&lt;p&gt;And sometimes, the problem is not your application code at all.&lt;/p&gt;

&lt;p&gt;It's the operating system standing between your code and the user.&lt;/p&gt;

&lt;p&gt;🛑 Problem No. 1: "npx" Is Not Always "npx"&lt;/p&gt;

&lt;p&gt;Let's start with something deceptively simple.&lt;/p&gt;

&lt;p&gt;An MCP server needs to launch through Node.js. On Linux, developers often use a straightforward process invocation:&lt;/p&gt;

&lt;p&gt;"spawn("npx", args)"&lt;/p&gt;

&lt;p&gt;Easy enough.&lt;/p&gt;

&lt;p&gt;Except Windows has a different executable resolution model for command-line tools.&lt;/p&gt;

&lt;p&gt;The npm executable on Windows is typically provided through "npx.cmd", not a regular executable that every process launcher can start directly.&lt;/p&gt;

&lt;p&gt;During our Windows compatibility testing, a direct "spawn("npx")" failed with "ENOENT" across Node.js 20, 22, and 24.&lt;/p&gt;

&lt;p&gt;We tested alternative launch strategies.&lt;/p&gt;

&lt;p&gt;The solution was to use the Windows command interpreter:&lt;/p&gt;

&lt;p&gt;"cmd /c npx"&lt;/p&gt;

&lt;p&gt;Our recorded compatibility tests confirmed that this approach connected successfully across all three Node.js versions.&lt;/p&gt;

&lt;p&gt;One small launcher difference. Three Node.js versions. An entirely different execution path.&lt;/p&gt;

&lt;p&gt;This is the kind of problem that can make an otherwise perfectly functional AI tool completely unusable.&lt;/p&gt;

&lt;p&gt;🧩 Problem No. 2: Windows Paths Are a Minefield&lt;/p&gt;

&lt;p&gt;You might think handling a project path is trivial.&lt;/p&gt;

&lt;p&gt;Let's try this one:&lt;/p&gt;

&lt;p&gt;"C:\sentinel-test\it's $HOME &amp;amp; 100% ^caret! (x)"&lt;/p&gt;

&lt;p&gt;This is a legitimate Windows path.&lt;/p&gt;

&lt;p&gt;Now imagine passing it through multiple layers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Node.js argument handling.&lt;/li&gt;
&lt;li&gt;CMD.&lt;/li&gt;
&lt;li&gt;PowerShell 5.1.&lt;/li&gt;
&lt;li&gt;PowerShell 7.&lt;/li&gt;
&lt;li&gt;An MCP client.&lt;/li&gt;
&lt;li&gt;A process launcher.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every layer can interpret certain characters differently.&lt;/p&gt;

&lt;p&gt;During our tests, unescaped "&amp;amp;" broke VS Code commands. A path containing "%OS%" could also be interpreted as an environment-variable reference when passed through "cmd /c".&lt;/p&gt;

&lt;p&gt;PowerShell introduced another layer of complexity because ".ps1", ".cmd", and ".exe" commands do not necessarily behave identically.&lt;/p&gt;

&lt;p&gt;The result?&lt;/p&gt;

&lt;p&gt;You cannot simply quote everything and assume the problem is solved.&lt;/p&gt;

&lt;p&gt;We implemented explicit argument handling and escaping, then tested the actual values received by the target programs.&lt;/p&gt;

&lt;p&gt;Our recorded Windows compatibility work includes a quoting probe with 45 successful checks across PowerShell 5.1, PowerShell 7.6.7, CMD, multiple executable types, and difficult project paths.&lt;/p&gt;

&lt;p&gt;That's not glamorous software engineering.&lt;/p&gt;

&lt;p&gt;But it is necessary software engineering.&lt;/p&gt;

&lt;p&gt;⚙️ Problem No. 3: Passing CI Does Not Mean Your MCP Server Works&lt;/p&gt;

&lt;p&gt;Here is another misconception.&lt;/p&gt;

&lt;p&gt;A green CI badge does not automatically mean your MCP server works in a real desktop application.&lt;/p&gt;

&lt;p&gt;There are several distinct stages:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The package installs.&lt;/li&gt;
&lt;li&gt;The launcher starts the correct executable.&lt;/li&gt;
&lt;li&gt;The server initializes.&lt;/li&gt;
&lt;li&gt;The MCP client establishes a connection.&lt;/li&gt;
&lt;li&gt;The client discovers the available tools.&lt;/li&gt;
&lt;li&gt;An actual request reaches the server and returns the expected result.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A test that verifies only the first three stages does not prove the entire integration works.&lt;/p&gt;

&lt;p&gt;We therefore added compatibility checks that go beyond simply running the test suite.&lt;/p&gt;

&lt;p&gt;Our "doctor" command checks the Node.js environment, package configuration, project folders, starts the server independently, performs a real MCP handshake, and checks the available tools.&lt;/p&gt;

&lt;p&gt;This does not prove that every desktop client's UI or agent works correctly. That distinction matters.&lt;/p&gt;

&lt;p&gt;But it provides considerably stronger evidence than checking whether a configuration file exists.&lt;/p&gt;

&lt;p&gt;🧪 Problem No. 4: Cross-Platform Tests Need Different Expectations&lt;/p&gt;

&lt;p&gt;Windows and Linux do not implement every filesystem behavior identically.&lt;/p&gt;

&lt;p&gt;For example, a Linux test using:&lt;/p&gt;

&lt;p&gt;"chmod 000"&lt;/p&gt;

&lt;p&gt;cannot simply be copied into a Windows test suite and expected to mean the same thing.&lt;/p&gt;

&lt;p&gt;Windows uses a different permission model.&lt;/p&gt;

&lt;p&gt;Our compatibility records explicitly identify a permission-related test as skipped on Windows rather than pretending that the same behavior was verified.&lt;/p&gt;

&lt;p&gt;That is an important distinction.&lt;/p&gt;

&lt;p&gt;Cross-platform testing is not about forcing every operating system to behave identically.&lt;/p&gt;

&lt;p&gt;It is about testing equivalent guarantees using the mechanisms each platform actually supports.&lt;/p&gt;

&lt;p&gt;🔬 What We Learned Building Sentinel-IR-MCP&lt;/p&gt;

&lt;p&gt;This is not theoretical research.&lt;/p&gt;

&lt;p&gt;We encountered these problems while developing our own MCP server and testing its compatibility.&lt;/p&gt;

&lt;p&gt;The project has separate components:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Sentinel-IR-MCP on GitLab" (&lt;a href="https://gitlab.com/jakubrojicek11/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://gitlab.com/jakubrojicek11/sentinel-ir-mcp&lt;/a&gt;) – the MCP server.&lt;/li&gt;
&lt;li&gt;"sentinel-ir-mcp on npm" (&lt;a href="https://www.npmjs.com/package/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir-mcp&lt;/a&gt;) – the published Node.js package.&lt;/li&gt;
&lt;li&gt;"sentinel-ir on npm" (&lt;a href="https://www.npmjs.com/package/sentinel-ir" rel="noopener noreferrer"&gt;https://www.npmjs.com/package/sentinel-ir&lt;/a&gt;) – the underlying code analysis engine.&lt;/li&gt;
&lt;li&gt;"Sentinel-IR-MCP on Glama" (&lt;a href="https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp" rel="noopener noreferrer"&gt;https://glama.ai/mcp/servers/jakcmencz/sentinel-ir-mcp&lt;/a&gt;) – the MCP directory listing.&lt;/li&gt;
&lt;li&gt;"Sentinel-IR Playground" (&lt;a href="https://sentinel-ir-playground-dc30c8.gitlab.io/" rel="noopener noreferrer"&gt;https://sentinel-ir-playground-dc30c8.gitlab.io/&lt;/a&gt;) – the browser-based analysis playground.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The implementation includes platform-aware launch configuration, compatibility checks, Windows-specific argument handling, and a separate diagnostic command.&lt;/p&gt;

&lt;p&gt;We also maintain a dedicated "Windows compatibility test record" (&lt;a href="https://github.com/jakcmencz/sentinel-ir-mcp/blob/main/COMPATIBILITY.md" rel="noopener noreferrer"&gt;https://github.com/jakcmencz/sentinel-ir-mcp/blob/main/COMPATIBILITY.md&lt;/a&gt;), documenting what was actually tested and what remains unverified.&lt;/p&gt;

&lt;p&gt;That last part is important.&lt;/p&gt;

&lt;p&gt;We don't consider an untested environment supported just because the code looks portable.&lt;/p&gt;

&lt;p&gt;💸 The Hidden Windows Tax&lt;/p&gt;

&lt;p&gt;Every platform-specific failure has a cost.&lt;/p&gt;

&lt;p&gt;A developer loses time reproducing it.&lt;/p&gt;

&lt;p&gt;A maintainer needs another compatibility fix.&lt;/p&gt;

&lt;p&gt;A CI pipeline needs another test.&lt;/p&gt;

&lt;p&gt;A user encounters another installation problem.&lt;/p&gt;

&lt;p&gt;And when the software is designed for AI agents, the consequences multiply.&lt;/p&gt;

&lt;p&gt;An MCP server that cannot launch is not merely inconvenient.&lt;/p&gt;

&lt;p&gt;The agent loses access to the functionality it was supposed to provide.&lt;/p&gt;

&lt;p&gt;You can have a sophisticated analyzer, a carefully designed security model, and a fast execution engine.&lt;/p&gt;

&lt;p&gt;But none of those capabilities matter if the process never starts.&lt;/p&gt;

&lt;p&gt;🚀 The Next Bottleneck Is Not Intelligence&lt;/p&gt;

&lt;p&gt;The AI industry is moving rapidly toward autonomous development environments, persistent agents, and increasingly sophisticated MCP integrations.&lt;/p&gt;

&lt;p&gt;Yet developers still have to solve problems that have existed for decades:&lt;/p&gt;

&lt;p&gt;Process execution.&lt;/p&gt;

&lt;p&gt;Shell compatibility.&lt;/p&gt;

&lt;p&gt;Filesystem semantics.&lt;/p&gt;

&lt;p&gt;Environment variables.&lt;/p&gt;

&lt;p&gt;Executable resolution.&lt;/p&gt;

&lt;p&gt;Cross-platform packaging.&lt;/p&gt;

&lt;p&gt;These are not exciting problems to advertise.&lt;/p&gt;

&lt;p&gt;They are the problems that determine whether someone can actually use your software.&lt;/p&gt;

&lt;p&gt;Part 1 was about removing unnecessary waiting from Windows CI/CD.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgk9zlz44soyk48ldnu39.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fgk9zlz44soyk48ldnu39.jpg" alt=" " width="800" height="1778"&gt;&lt;/a&gt;&lt;br&gt;
Part 2 is about removing unnecessary friction from Windows compatibility.&lt;/p&gt;

&lt;p&gt;Because building an AI tool that works on your machine is not the same as building one that works for everyone.&lt;/p&gt;

&lt;p&gt;And Windows developers deserve better than being an afterthought.&lt;/p&gt;

&lt;p&gt;💨One more critical thing that people don't realize about slow CI/CD for AI agents:&lt;br&gt;
Why the hell would you wait an hour and a half for a single merge with fixes when you can have everything—the bug fixed, tests run, and code merged—completely done in under 9 minutes?&lt;br&gt;
While I was stuck waiting 90 minutes for one single merge on GitLab, we could have built and deployed 10 consecutive patches on this new GitHub setup.&lt;br&gt;
When your infrastructure is slow, you aren't just losing time—you are paralyzing the agent's velocity. An AI agent doesn't need coffee breaks; it needs instant feedback loops to learn, adapt, and push the next iteration. Slow CI kills that rhythm. Fast CI unlocks pure exponential development.🤔😃&lt;/p&gt;




&lt;p&gt;Independent Engineering Projects&lt;/p&gt;

&lt;p&gt;Built as independent engineering experiments across AI, software analysis, security, and autonomous systems.&lt;/p&gt;

&lt;p&gt;Evaluated by reproducible evidence, not by the number of layers.&lt;/p&gt;

&lt;p&gt;Explore the projects:&lt;/p&gt;

&lt;p&gt;Sentinel Ecosystem: AutoDoc-Sentinel, Sentinel-IR, Sentinel-IR-MCP and related tools.&lt;br&gt;
Agentix Lite: Independent security and system-observation project.&lt;br&gt;
Find the projects: GitLab repositories | NPM packages&lt;/p&gt;

&lt;p&gt;Support the work: ⭐ Star the repositories on GitLab.&lt;/p&gt;

&lt;p&gt;Connect: Follow me here on DEV Community or explore the Playground.&lt;/p&gt;

&lt;p&gt;Each project is distributed under its own license. Check the corresponding repository or NPM package for licensing terms.&lt;/p&gt;

&lt;p&gt;Copyright © 2026 jackymenCZ. All rights reserved. Written for the community.&lt;/p&gt;

</description>
      <category>devops</category>
      <category>cicd</category>
      <category>ai</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
