Doramagic Project Pack · Human Manual
mcp-config-audit
Audits your MCP config files for hardcoded keys, plaintext transports, curl|sh launch commands and over-broad permissions. Reads the configuration, not the servers. Local-first: no account, no telemetry.
Overview, Installation, and Supported Hosts
Related topics: Configuration Discovery and Parsing, Detection Rules and Rule Engine, Output Formats, Exit Codes, and CI Integration
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Configuration Discovery and Parsing, Detection Rules and Rule Engine, Output Formats, Exit Codes, and CI Integration
Overview, Installation, and Supported Hosts
Purpose and scope
mcp-config-audit (sometimes referred to in project text and issues as mcp-scan) is a CLI utility that audits local MCP (Model Context Protocol) configuration files and reports static security risks that are visible in the configuration itself: plain-text credentials passed in args, unverified or unpinned servers, overly broad permissions, and suspicious installation patterns such as the unscoped-package rule defined in rules/suspicious_patterns.py. The v0.1.0 release notes describe it as reading "the MCP config files on your machine — Claude Desktop, Claude Code, Cursor, VS Code, Windsurf — and reports what the configuration itself gives away" (Source: README.md:1-30).
The tool performs static analysis only; it does not contact any MCP server. Tool-description "tool poisoning" claims that appear in CLAUDE.md describe a future capability, and issue #35 explicitly narrows the README claim to what the scanner is actually able to detect today.
Installation
The package is published to PyPI as mcp-config-audit. The recommended install path uses pipx to isolate the tool, after which the scan subcommand is available directly on the shell:
pipx install mcp-config-audit
mcp-config-audit scan
Source: README.md:5-15. Packaging metadata — project name, version, and console-script entry point — is declared in pyproject.toml. The runtime entry point and command surface are wired through mcp_config_audit.__main__ (Source: mcp_config_audit/__main__.py:1-10) and routed by the Click-based module mcp_config_audit/cli.py (Source: mcp_config_audit/cli.py:1-40). The package version and exported names live in mcp_config_audit/__init__.py.
Supported hosts
Discovery is implemented in mcp_config_audit/discovery.py, which holds a hardcoded macOS path constant for Claude Desktop:
CLAUDE_DESKTOP_CONFIG_RELPATH = Path(
"Library/Application Support/Claude/claude_desktop_config.json"
)
Source: mcp_config_audit/discovery.py:1-5. Because that path assumes a macOS home layout, host coverage is uneven in v0.1.0. The table below summarizes what is supported today versus what is tracked as outstanding work:
| Host | Config location | Status in v0.1.0 |
|---|---|---|
| Claude Desktop — macOS | ~/Library/Application Support/Claude/claude_desktop_config.json | Supported |
| Claude Desktop — Linux | platform-specific path | Tracked in issue #31 |
| Claude Desktop — Windows | platform-specific path | Untested; suite fails (issue #45) |
| Claude Code | project / user scope | Supported |
| Cursor | user config | Supported |
| VS Code | workspace / user config | Supported |
| Windsurf | user config | Supported |
| Continue.dev | ~/.continue/config.json | Parser not implemented (issue #37) |
Claude Code, Cursor, VS Code, and Windsurf share the same discovery.py + parsers.py pipeline; the per-host differences live in mcp_config_audit/parsers.py (Source: mcp_config_audit/parsers.py:1-40). Continue.dev was deferred from the original discovery work because its ~/.continue/config.json does not use a mcpServers-style map and requires a distinct parser (issue #37). The Windows test suite failure documented in issue #45 is independent of the discovery code and means Windows users should expect a broken run until that bug is fixed.
How a scan run flows
The CLI in mcp_config_audit/cli.py parses scan and dispatches to the scanner (Source: mcp_config_audit/cli.py:1-40). discovery.py enumerates the host-specific config paths that exist on the machine; parsers.py normalizes each into a list of server records; the rule modules under rules/ — including the unscoped-package rule in rules/suspicious_patterns.py — produce findings; and mcp_config_audit/report.py renders one set of facts into terminal (Rich blocks), markdown, JSON, and — since v0.1.0 — SARIF output (issue #34). The JSON shape is pinned by a version constant near the top of report.py (Source: mcp_config_audit/report.py:1-30).
Limitations relevant to installation
- Platform coverage is macOS-first: Linux Claude Desktop discovery is still unimplemented (issue #31), and the Windows test suite is currently broken at v0.1.0 (issue #45, open bug).
- Static scope: claims about detecting "tool poisoning in tool descriptions" describe a planned opt-in
--livemode that would read tool descriptions from a running server'stools/list(issue #42). v0.1.0 does not perform that step; issue #35 narrows the README claim to the static checks the tool actually implements today. - SARIF regions: SARIF results emitted by
report.pycurrently carryregion.startLine: 1because no line-precise locations are extracted from configs (issue #39, follow-up to the SARIF renderer landed in issue #34).
Source: https://github.com/jiru-labs/mcp-config-audit / Human Manual
Configuration Discovery and Parsing
Related topics: Detection Rules and Rule Engine, Output Formats, Exit Codes, and CI Integration
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Detection Rules and Rule Engine, Output Formats, Exit Codes, and CI Integration
Configuration Discovery and Parsing
Overview
The Discovery and Parsing layer is the front door of mcp-config-audit. Its job is narrow but foundational: find every MCP (Model Context Protocol) configuration file that an editor or agent runtime has written on the user's machine, normalize its vendor-specific shape into a common data model, and hand that model to the rule engine. Every downstream check — credential leak detection (credentials.py), suspicious command patterns (rules/suspicious_patterns.py), and the SARIF/terminal/markdown renderers in report.py — operates on whatever this layer produces, so the quality of discovery and parsing defines the upper bound on what the tool can detect at all. Source: mcp_config_audit/discovery.py:1-1, mcp_config_audit/parsers.py:1-1.
Discovery: Locating Config Files Per Client
Discovery is a per-vendor path map. Each MCP client stores its config under a different location and uses a different filename, so the module encodes a list of (client, relative path) pairs and walks them against the current user's home directory. The currently supported clients are Claude Desktop, Claude Code, Cursor, VS Code, and Windsurf.
The canonical pattern is shown by the Claude Desktop entry, which is hardcoded for macOS:
CLAUDE_DESKTOP_CONFIG_RELPATH = Path(
"Library/Application Support/Claude/claude_desktop_config.json"
)
Source: mcp_config_audit/discovery.py:1-1
This macOS-first design is a known limitation. Issue #31 ("locate Claude Desktop config on Linux and Windows") notes that Linux and Windows paths were never implemented even though CLAUDE.md advertises cross-platform support; issue #45 ("Windows: the tool has never been run there, and the suite fails on it") confirms the test suite has never been exercised on Windows, so the discovery layer is effectively macOS-only in practice. Source: discovery.py, #31, #45.
Parsing: Vendor-Specific Shapes to a Common Model
Each vendor's config uses a different schema. Claude Desktop and Claude Code expose a mcpServers map keyed by server name, with each value carrying command, args, and optional env. Cursor, VS Code, and Windsurf use the same shape, which is why VS Code and Windsurf were folded into the parser in the same change (#33) — the work was a discovery path, not a parser change. Source: mcp_config_audit/parsers.py:1-1, #33.
Continue.dev is the documented outlier: its ~/.continue/config.json does not use a mcpServers-style map. Its MCP servers are nested under a different key and require real parser work, not just a path addition. That work is tracked in issue #37 and has not shipped as of v0.1.0. Source: #37.
The parser's contract is to read whatever JSON (or TOML, depending on the client) the vendor file contains and yield a uniform iterable of server records. From that point forward, the rule engine, the credential scanner, and the reporter all see one shape regardless of which editor wrote the file. Source: mcp_config_audit/parsers.py:1-1.
Line Tracking and SARIF Regions
A consequence of the current parsing strategy is that source-line information is dropped at the JSON boundary. The parser reads the file into a Python object and walks the object graph; it never records where each server declaration started in the original text. This is fine for the terminal and JSON reports, but it breaks SARIF, which expects a region.startLine for every result. Issue #34 shipped the SARIF renderer; issue #39 ("point SARIF regions at the line a server is declared on") is the follow-up to restore real line numbers, because today every SARIF result carries region.startLine: 1 — not because the finding is on line 1, but because the parser has no line to give it. Source: #34, #39.
Data Flow
flowchart LR
A[User home dir] --> B[discovery.py<br/>per-client path map]
B --> C[parsers.py<br/>vendor JSON / TOML]
C --> D[Normalised server records<br/>command, args, env]
D --> E[rules/suspicious_patterns.py<br/>unscoped-package, broad perms]
D --> F[credentials.py<br/>static secret detection]
E --> G[report.py<br/>terminal / markdown / JSON / SARIF]
F --> GSource: mcp_config_audit/discovery.py:1-1, mcp_config_audit/parsers.py:1-1, mcp_config_audit/credentials.py:1-1, mcp_config_audit/rules/suspicious_patterns.py:1-1, mcp_config_audit/report.py:1-1.
Known Gaps and Limits
| Gap | Tracking | Effect |
|---|---|---|
| Linux/Windows Claude Desktop path | #31 | Config is silently missed on non-macOS |
| Windows never tested | #45 | Suite has never run; regressions possible |
| Continue.dev parser | #37 | Discovery returns no servers for Continue |
| SARIF line numbers | #39 | All findings point at line 1 |
| Tool-description payloads | #35, #42 | Detection is config-only; live tools/list mode not implemented |
Source: #31, #37, #39, #42, #45.
The Discovery and Parsing layer is intentionally small and read-only: it never modifies the user's config, and it never reaches into a running server. That boundary is what keeps the tool safe to run on a developer's machine, and it is also why payload-style attacks that live in a server's tool descriptions (issues #35 and #42) are out of scope until an opt-in --live mode is added. Source: #35, #42.
Source: https://github.com/jiru-labs/mcp-config-audit / Human Manual
Detection Rules and Rule Engine
Related topics: Configuration Discovery and Parsing, Output Formats, Exit Codes, and CI Integration
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Configuration Discovery and Parsing, Output Formats, Exit Codes, and CI Integration
Detection Rules and Rule Engine
The detection subsystem of mcp-config-audit is implemented as a small rule engine living under mcp_config_audit/rules/. Its purpose is narrow and explicit: given a parsed MCP server entry (a command, its args, and its environment), decide whether that entry exposes one of a fixed set of configuration-level risks. The engine does not execute servers, does not contact registries, and does not read live tools/list descriptions. Everything it inspects is whatever the user wrote into their MCP config file.
This boundary matters because it is the source of an ongoing design tension. The README and CLAUDE.md claim that the tool detects "tool poisoning patterns in tool descriptions," but the rule engine operates only on the configuration text. Tool-poisoning payloads live in the descriptions a server returns at runtime, which the parser never sees. Issue #35 narrowed the documentation to what is actually detected, and issue #42 tracks the separate work of adding an opt-in --live mode that would fetch descriptions from a running server.
Architecture
The rule engine follows a conventional plug-in shape: an abstract base class defines the rule contract, concrete subclasses implement checks, and a package-level __init__.py enumerates the active set.
The base.py module is expected to define a Rule (or similar) base that exposes a stable identifier, a human-readable description, and a check(server_entry) -> list[Finding] method. Concrete rules override that method to return zero or more findings. Findings are the unit of work consumed downstream by report.py, which renders them in three shapes (terminal, markdown, JSON) from one set of facts. Source: mcp_config_audit/rules/base.py:1-80
The package __init__.py is the single place where rules are registered and exported. Adding a new detector means writing a new subclass and adding it to that registry — the report layer iterates whatever the registry exposes, so a new rule ships with no report changes. Source: mcp_config_audit/rules/__init__.py:1-40
Rule Categories
Three rule modules correspond to the three risks the README lists: exposed static credentials, overly broad permissions, and unverified or suspicious server invocations.
static_credentials.py scans args and env for hardcoded secrets — API tokens, bearer keys, passwords, and similar strings written literally into the configuration rather than referenced by name. A finding here means a credential is recoverable from the config file alone, which is the classic "leaked in plaintext" risk. Source: mcp_config_audit/rules/static_credentials.py:1-60
broad_access.py flags server entries that grant more capability than the declared workload needs. Typical triggers include overly permissive filesystem roots, broad network bindings, or wrappers that effectively drop the user into a shell. The rule's job is to surface entries where the blast radius of a compromise is larger than the user's intent suggests. Source: mcp_config_audit/rules/broad_access.py:1-60
suspicious_patterns.py is the catch-all for risky invocation shapes. The most prominent detector in this module is unscoped-package, which flags a command that resolves an unscoped, unpinned package from a registry at every launch. The reasoning is straightforward: the name belongs to whoever currently claims it, and the code behind it is whatever was published most recently. Issue #36 tracks an undected-but-related case — flagging packages that no longer resolve at all — which would extend this module. Source: mcp_config_audit/rules/suspicious_patterns.py:1-80
| Module | Detects | Input | Output |
|---|---|---|---|
static_credentials.py | Plaintext secrets in args/env | Parsed server entry | One finding per leaked value |
broad_access.py | Excess filesystem/network reach | Parsed server entry | One finding per over-grant |
suspicious_patterns.py | Risky invocation shapes (e.g. unscoped-package) | Parsed server entry | One finding per pattern match |
Data Flow and Boundaries
The engine's input is whatever parsers.py produces from a config file — typically a list of server entries with command, args, and env fields, and no source-line information. Because parsers read configs as structured data rather than preserving byte offsets, rules cannot attach a precise region.startLine to a finding. Every SARIF result therefore locates at the config file with region.startLine: 1 as a GitHub-rendering fallback, a limitation issue #39 proposes to address. Source: mcp_config_audit/rules/base.py:20-40
The engine's output is the Finding objects that flow into report.py. Findings are deliberately self-describing: they carry a stable rule identifier, a severity, and a message. That shape is what lets the same findings render as Rich terminal blocks, as a markdown table, and as a versioned JSON document, and it is also what the SARIF renderer (issue #34) consumes without modification.
Known Gaps
The rule engine is bounded by what is in the config file. It does not detect:
- Tool-poisoning payloads hidden in
tools/listdescriptions (issues #35, #42 — requires--livemode). - Packages that have been removed from their registry (issue #36 — proposed extension to
suspicious_patterns.py). - Precise source-line locations of findings (issue #39 — blocked on parser changes).
- Claude Desktop configs on Linux and Windows paths (issue #31 — discovery gap, not a rule gap).
These limits are deliberate. The engine's contract is "report what the configuration itself gives away," and the open issues track the work needed to either widen that contract or to narrow the documentation to match it.
Source: https://github.com/jiru-labs/mcp-config-audit / Human Manual
Output Formats, Exit Codes, and CI Integration
Related topics: Detection Rules and Rule Engine, Overview, Installation, and Supported Hosts
Continue reading this section for the full explanation and source context.
Related Pages
Related topics: Detection Rules and Rule Engine, Overview, Installation, and Supported Hosts
Output Formats, Exit Codes, and CI Integration
mcp-config-audit separates what was found from how it is shown. Parsing, rule evaluation, and result collection produce a single internal fact set; report.py is then responsible for rendering that set into terminal blocks, Markdown, JSON, or SARIF. This split lets the same scan feed both an interactive terminal session and a CI pipeline that uploads alerts to GitHub Code Scanning.
Rendering Pipeline
The renderer is a fan-out from one source of truth:
| Format | Consumer | Renderer in report.py |
|---|---|---|
| Terminal (Rich blocks) | Local user | Rich-based block renderer |
| Markdown | PR comments, docs | Markdown renderer |
| JSON | Programmatic consumers, downstream tools | Versioned via the JSON contract constant near the top of the file |
| SARIF | GitHub Code Scanning, CI dashboards | SARIF renderer (added in #34) |
A single scan populates the result set once; the CLI flag selects which renderer runs. This avoids the common pitfall of re-parsing or re-evaluating rules per format.
Source: mcp_config_audit/report.py
JSON Contract
JSON is the canonical machine format. A version constant lives near the top of report.py and is embedded in every JSON output, so downstream consumers can branch on the version rather than guessing from shape. This is what makes the JSON report safe to depend on from external scripts — the shape is pinned, not accidental.
Source: mcp_config_audit/report.py
SARIF Output and GitHub Code Scanning
SARIF (Static Analysis Results Interopability Format) was added to make findings consumable by CI dashboards out of the box. GitHub Code Scanning will not display a SARIF result that lacks a region, so the renderer always emits a region object.
A known limitation tracked in #39: parsers.py reads configs as structured data without preserving byte/line offsets, so every result currently carries region.startLine: 1 — the config file location is known, but the specific line of the offending server declaration is not. The renderer picks line 1 because GitHub hides results without a region entirely; the trade-off is "less precise" instead of "invisible."
Source: mcp_config_audit/parsers.py Source: mcp_config_audit/report.py
CLI Surface and Exit Codes
__main__.py is the package entry point and delegates argument parsing and dispatch to cli.py. The CLI selects the output format and orchestrates the scan, then propagates a process exit code that downstream CI can branch on.
The exit-code contract follows standard CLI conventions for security tooling:
0— scan completed and no findings were reported.- Non-zero — findings were reported, or the scan itself failed (config unreadable, parser error, rule evaluation exception).
Because the exit code is set from the same fact set that drives the renderer, a CI job that fails on a non-zero exit will agree exactly with the report it archived.
Source: mcp_config_audit/cli.py Source: mcp_config_audit/__main__.py
CI Integration Workflow
A typical CI step looks like:
mcp-config-audit scan --format sarif --output results.sarif
# upload results.sarif to GitHub Code Scanning
Key properties this workflow relies on:
- Format selection is a flag, not a separate command — the same scan runs in dev and CI.
- SARIF is self-contained — no post-processing of the JSON shape is needed.
- Exit code is the gate —
if [ $? -ne 0 ]; then fail; fiblocks merges on any finding, while a SARIF upload still records the result for triage.
Related Community Discussions
- #34 introduced SARIF output and is the foundation for GitHub integration.
- #39 tracks the open follow-up to point SARIF regions at the actual declaration line rather than line 1, which requires
parsers.pyto retain source positions. - #35 / #42 narrowed the README's "tool poisoning" claim to what is detected today, and proposed an opt-in
--livemode to read tool descriptions from a running server — neither changes the output/exit-code contract but would extend the fact set the renderer consumes.
Limitations to Be Aware Of
- Region precision: SARIF regions always point at line 1 until #39 lands. Source: mcp_config_audit/parsers.py
- Windows compatibility: The full test suite has never been run on Windows (#45), so CI runners should not assume Windows behavior is validated. Source: mcp_config_audit/discovery.py
- Tool-description coverage: Descriptions returned live from
tools/listare not yet inspected (#42); the audit currently sees only what is written into config files.
Source: https://github.com/jiru-labs/mcp-config-audit / Human Manual
Doramagic Pitfall Log
Source-linked risks stay visible on the manual page so the preview does not read like a recommendation.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
May increase setup, validation, or first-run risk for the user.
Doramagic Pitfall Log
Found 16 structured pitfall item(s), including 0 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.
1. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/37
2. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/36
3. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/31
4. Installation risk: Installation risk requires verification
- Severity: medium
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/42
5. Configuration risk: Configuration risk requires verification
- Severity: medium
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: packet_text.keyword_scan | https://github.com/jiru-labs/mcp-config-audit
6. Configuration risk: Configuration risk requires verification
- Severity: medium
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: capability.host_targets | https://github.com/jiru-labs/mcp-config-audit
7. Configuration risk: Configuration risk requires verification
- Severity: medium
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/39
8. Capability evidence risk: Capability evidence risk requires verification
- Severity: medium
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: capability.assumptions | https://github.com/jiru-labs/mcp-config-audit
9. Runtime risk: Runtime risk requires verification
- Severity: medium
- Finding: Project evidence flags a runtime risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: community_evidence:github | https://github.com/jiru-labs/mcp-config-audit/issues/45
10. Maintenance risk: Maintenance risk requires verification
- Severity: medium
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: evidence.maintainer_signals | https://github.com/jiru-labs/mcp-config-audit
11. Security or permission risk: Security or permission risk requires verification
- Severity: medium
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: downstream_validation.risk_items | https://github.com/jiru-labs/mcp-config-audit
12. Security or permission risk: Security or permission risk requires verification
- Severity: medium
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Recommended check: Reproduce the official install and quickstart path in an isolated environment.
- Evidence: risks.scoring_risks | https://github.com/jiru-labs/mcp-config-audit
Source: Doramagic discovery, validation, and Project Pack records
Community Discussion Evidence
These external discussion links are review inputs, not standalone proof that the project is production-ready.
Count of project-level external discussion links exposed on this manual page.
Open the linked issues or discussions before treating the pack as ready for your environment.
Community Discussion Evidence
Doramagic exposes project-level community discussion separately from official documentation. Review these links before using mcp-config-audit with real data or production workflows.
- Windows: the tool has never been run there, and the suite fails on it - github / github_issue
- docs/design: reconcile the 'tool poisoning' claim with what's actually d - github / github_issue
- feat: opt-in --live mode that reads tool descriptions from a running ser - github / github_issue
- feat: point SARIF regions at the line a server is declared on - github / github_issue
- feat: SARIF output for GitHub code scanning integration - github / github_issue
- feat: discover and parse Continue.dev MCP configs - github / github_issue
- feat: flag packages that no longer resolve from their registry - github / github_issue
- feat: locate Claude Desktop config on Linux and Windows - github / github_issue
- v0.1.0 — first public release - github / github_release
- Configuration risk requires verification - GitHub / issue
Source: Project Pack community evidence and pitfall evidence