agent-mcp-guard 0.4.7 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -19,9 +19,30 @@ Live demo PR: [mcp-guard-demo#1](https://github.com/ChaoYue0307/mcp-guard-demo/p
19
19
  <a href="https://github.com/marketplace/actions/mcp-guard-mcp-security-scanner"><img alt="GitHub Marketplace" src="https://img.shields.io/badge/Marketplace-mcp--guard-0f766e?logo=github"></a>
20
20
  <a href="https://github.com/ChaoYue0307/mcp-guard/actions"><img alt="CI" src="https://github.com/ChaoYue0307/mcp-guard/actions/workflows/ci.yml/badge.svg"></a>
21
21
  <a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-111827"></a>
22
- <a href="https://github.com/ChaoYue0307/mcp-guard/releases/tag/v0.4.7"><img alt="Release" src="https://img.shields.io/github/v/release/ChaoYue0307/mcp-guard?color=7c2d12"></a>
22
+ <a href="https://github.com/ChaoYue0307/mcp-guard/releases/tag/v0.4.9"><img alt="Release" src="https://img.shields.io/github/v/release/ChaoYue0307/mcp-guard?color=7c2d12"></a>
23
23
  </p>
24
24
 
25
+ ## MCP Basics
26
+
27
+ MCP, the Model Context Protocol, is a common way for AI apps and agents to connect to external tools, files, and services.
28
+
29
+ In practice, most MCP risk lives in the config file that tells an AI client what servers to start or call.
30
+
31
+ | Concept | Plain-English meaning | Why security teams should care |
32
+ | --- | --- | --- |
33
+ | MCP client | The AI app or agent runtime, such as Claude Desktop, Cursor, Codex, or an internal agent. | It decides which MCP servers are available to the model. |
34
+ | MCP server | A local process or remote endpoint that exposes tools, resources, or prompts. | It may read files, run commands, call SaaS APIs, or send data outside the machine. |
35
+ | MCP config | JSON or project config that lists server commands, URLs, args, env vars, working directories, and headers. | A small config change can introduce shell execution, broad filesystem access, or long-lived credentials. |
36
+ | Review point | The moment before a server is added locally or merged into a repo. | This is where `mcp-guard` scans for risky commands, unpinned packages, leaked secrets, broad paths, and remote endpoints. |
37
+
38
+ `mcp-guard` does not try to replace MCP. It gives teams a quick way to review MCP tool access before agents can use it.
39
+
40
+ To see the exact risk checks in the current release:
41
+
42
+ ```bash
43
+ mcp-guard rules --format markdown
44
+ ```
45
+
25
46
  ## Install
26
47
 
27
48
  ```bash
@@ -65,10 +86,18 @@ Generate SARIF for GitHub code scanning:
65
86
  mcp-guard scan --format sarif --output mcp-guard.sarif
66
87
  ```
67
88
 
89
+ Inspect the rule catalog:
90
+
91
+ ```bash
92
+ mcp-guard rules
93
+ mcp-guard rules --format json
94
+ ```
95
+
68
96
  Generate a review-ready audit pack:
69
97
 
70
98
  ```bash
71
99
  mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --output-dir mcp-guard-audit
100
+ mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
72
101
  ```
73
102
 
74
103
  Use in CI:
@@ -93,7 +122,7 @@ mcp-guard scan --config .mcp.json --baseline .mcp-guard-baseline.json --fail-on
93
122
  Use the GitHub Action:
94
123
 
95
124
  ```yaml
96
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
125
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
97
126
  with:
98
127
  config: .mcp.json
99
128
  # policy: .mcp-guard-policy.json
@@ -117,8 +146,9 @@ Use the transparent example to evaluate what the scanner actually does:
117
146
  - generated audit summary: [site/e2e/audit/mcp-guard-executive-summary.md](site/e2e/audit/mcp-guard-executive-summary.md)
118
147
  - generated remediation plan: [site/e2e/audit/mcp-guard-remediation.md](site/e2e/audit/mcp-guard-remediation.md)
119
148
  - generated remediation checklist: [site/e2e/audit/mcp-guard-remediation-checklist.md](site/e2e/audit/mcp-guard-remediation-checklist.md)
149
+ - generated audit manifest: [site/e2e/audit/mcp-guard-audit-manifest.json](site/e2e/audit/mcp-guard-audit-manifest.json)
120
150
 
121
- The example scans 3 MCP servers and reports 9 active findings with a risk score of 98. It is synthetic, but fully reproducible from committed files.
151
+ The example scans 3 MCP servers and reports 9 active findings with a risk score of 98. It is synthetic, but fully reproducible from committed files. The audit manifest can be verified with `mcp-guard verify-audit` to confirm the generated reports still match their recorded SHA-256 hashes.
122
152
 
123
153
  For the GitHub Action workflow, inspect the public demo repository: [ChaoYue0307/mcp-guard-demo](https://github.com/ChaoYue0307/mcp-guard-demo).
124
154
 
@@ -152,7 +182,7 @@ For paid setup or internal review handoff, `mcp-guard audit` writes a complete e
152
182
  - remediation plan grouped by MCP server;
153
183
  - remediation checklist for PR or handoff tracking;
154
184
  - Markdown, HTML, JSON, and SARIF reports;
155
- - machine-readable audit manifest with artifact hashes.
185
+ - machine-readable audit manifest with artifact hashes and a CLI verifier.
156
186
 
157
187
  For stricter governance, commit `.mcp-guard-policy.json` and define the commands, remote packages, filesystem roots, and remote MCP endpoints the team has approved. See [Policy files](docs/policy.md).
158
188
 
package/docs/audit.md CHANGED
@@ -26,6 +26,14 @@ Use `--fail-on` in CI when the audit should write artifacts and then fail on act
26
26
  mcp-guard audit --config .mcp.json --fail-on high
27
27
  ```
28
28
 
29
+ Verify the audit pack before handoff or after downloading a CI artifact:
30
+
31
+ ```bash
32
+ mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
33
+ ```
34
+
35
+ `verify-audit` recalculates every recorded artifact size and SHA-256 hash. It exits `0` when the pack still matches the manifest and exits `2` when a report is missing or changed.
36
+
29
37
  ## Generated Files
30
38
 
31
39
  | File | Purpose |
@@ -46,7 +54,7 @@ mcp-guard audit --config .mcp.json --fail-on high
46
54
  3. Work through `mcp-guard-remediation.md` with the engineering team.
47
55
  4. Track concrete work in `mcp-guard-remediation-checklist.md`.
48
56
  5. Use `mcp-guard-report.html` for readable evidence and `mcp-guard-report.json` or `mcp-guard.sarif` for automation.
49
- 6. Use the `integrity.artifacts` section in `mcp-guard-audit-manifest.json` when you need to prove an audit artifact has not changed.
57
+ 6. Run `mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json` when you need to prove an audit artifact has not changed.
50
58
  7. Commit a reviewed policy and baseline only after the team has decided what risk is intentionally accepted.
51
59
 
52
60
  ## Privacy
package/docs/baseline.md CHANGED
@@ -30,7 +30,7 @@ If the scan finds only baseline-accepted findings, the exit code is `0`. If a ne
30
30
  ## GitHub Action
31
31
 
32
32
  ```yaml
33
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
33
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
34
34
  with:
35
35
  config: .mcp.json
36
36
  baseline: .mcp-guard-baseline.json
@@ -45,7 +45,7 @@ The generated Markdown, HTML, JSON, and PR comment separate active findings from
45
45
  {
46
46
  "version": 1,
47
47
  "generatedAt": "2026-05-10T00:00:00.000Z",
48
- "toolVersion": "0.4.7",
48
+ "toolVersion": "0.4.9",
49
49
  "findings": [
50
50
  {
51
51
  "fingerprint": "mcpg_a009b2c2",
@@ -17,7 +17,7 @@ Deliverables:
17
17
  - install the CLI and GitHub Action;
18
18
  - run `mcp-guard init` or generate an equivalent workflow manually;
19
19
  - generate Markdown, HTML, JSON, and SARIF reports;
20
- - generate a customer handoff audit pack with executive summary, remediation plan, remediation checklist, reports, and hashed manifest;
20
+ - generate a customer handoff audit pack with executive summary, remediation plan, remediation checklist, reports, hashed manifest, and verifier output;
21
21
  - define an initial `.mcp-guard-policy.json` for approved commands, packages, directories, and remote URLs;
22
22
  - create an initial baseline for accepted known findings;
23
23
  - enable PR comments and optional SARIF upload;
@@ -24,6 +24,7 @@ node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --form
24
24
  node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --format json --output site/e2e/report.json
25
25
  node ./bin/mcp-guard.js scan --config site/e2e/claude_desktop_config.json --format sarif --output site/e2e/report.sarif
26
26
  node ./bin/mcp-guard.js audit --config site/e2e/claude_desktop_config.json --output-dir site/e2e/audit
27
+ node ./bin/mcp-guard.js verify-audit --manifest site/e2e/audit/mcp-guard-audit-manifest.json
27
28
  ```
28
29
 
29
30
  ## Expected Result
@@ -63,3 +64,4 @@ Important findings include:
63
64
  - Secret-like values are redacted in reports.
64
65
  - Findings include rule IDs, severity, evidence, and remediation guidance.
65
66
  - The same scan can feed a human-readable HTML report, automation JSON, GitHub code scanning SARIF, and a review-ready audit handoff package with a remediation checklist.
67
+ - The audit handoff can be verified later because the manifest records SHA-256 hashes and byte sizes for each generated report.
@@ -6,6 +6,8 @@ The action runs the CLI from the pinned GitHub Action tag, generates an audit pa
6
6
 
7
7
  It can also use a committed baseline to accept known findings, enforce a committed policy file, and optionally post a pull request comment with only the active findings.
8
8
 
9
+ After downloading the artifact, run `mcp-guard verify-audit --manifest mcp-guard-report/mcp-guard-audit-manifest.json` to confirm the generated reports still match the manifest hashes.
10
+
9
11
  If `.mcp-guard-policy.json` is committed at the repository root, the CLI auto-loads it. Use the `policy` input when the policy file lives elsewhere.
10
12
 
11
13
  Marketplace/action repository: <https://github.com/ChaoYue0307/mcp-guard-action>
@@ -37,7 +39,7 @@ jobs:
37
39
  runs-on: ubuntu-latest
38
40
  steps:
39
41
  - uses: actions/checkout@v6
40
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
42
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
41
43
  with:
42
44
  config: .mcp.json
43
45
  fail-on: high
@@ -65,7 +67,7 @@ jobs:
65
67
  runs-on: ubuntu-latest
66
68
  steps:
67
69
  - uses: actions/checkout@v6
68
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
70
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
69
71
  with:
70
72
  config: .mcp.json
71
73
  fail-on: high
@@ -77,7 +79,7 @@ jobs:
77
79
  Use `fail-on: none` when you want artifacts and summaries without blocking a pull request.
78
80
 
79
81
  ```yaml
80
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
82
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
81
83
  with:
82
84
  fail-on: none
83
85
  ```
@@ -93,7 +95,7 @@ mcp-guard scan --config .mcp.json --write-baseline .mcp-guard-baseline.json
93
95
  Commit `.mcp-guard-baseline.json`, then reference it from the action:
94
96
 
95
97
  ```yaml
96
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
98
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
97
99
  with:
98
100
  config: .mcp.json
99
101
  baseline: .mcp-guard-baseline.json
@@ -107,7 +109,7 @@ Reports will show active findings separately from findings accepted by the basel
107
109
  Use a policy when you want CI to enforce approved commands, packages, directories, and remote URLs.
108
110
 
109
111
  ```yaml
110
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
112
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
111
113
  with:
112
114
  config: .mcp.json
113
115
  policy: .mcp-guard-policy.json
@@ -32,12 +32,13 @@ mcp-guard scan --config .mcp.json --fail-on high
32
32
 
33
33
  ```bash
34
34
  mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --output-dir mcp-guard-audit
35
+ mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
35
36
  ```
36
37
 
37
38
  ## GitHub Action Setup
38
39
 
39
40
  ```yaml
40
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
41
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
41
42
  with:
42
43
  config: .mcp.json
43
44
  baseline: .mcp-guard-baseline.json
@@ -23,7 +23,7 @@ jobs:
23
23
  runs-on: ubuntu-latest
24
24
  steps:
25
25
  - uses: actions/checkout@v6
26
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
26
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
27
27
  with:
28
28
  config: .mcp.json
29
29
  fail-on: high
@@ -42,7 +42,7 @@ jobs:
42
42
  runs-on: ubuntu-latest
43
43
  steps:
44
44
  - uses: actions/checkout@v6
45
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
45
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
46
46
  with:
47
47
  config: .mcp.json
48
48
  fail-on: high
@@ -88,7 +88,7 @@ The action generates an audit pack:
88
88
  - SARIF 2.1.0 for GitHub code scanning.
89
89
  - Remediation Markdown for server-by-server handoff.
90
90
  - Remediation checklist for PR and setup tracking.
91
- - Audit manifest JSON with SHA-256 hashes and byte sizes for downstream automation.
91
+ - Audit manifest JSON with SHA-256 hashes, byte sizes, and `mcp-guard verify-audit` support for downloaded artifacts.
92
92
 
93
93
  Secret-like values are redacted before reports are written.
94
94
 
@@ -103,7 +103,7 @@ mcp-guard scan --config .mcp.json --write-baseline .mcp-guard-baseline.json
103
103
  Then enforce only new findings:
104
104
 
105
105
  ```yaml
106
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
106
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
107
107
  with:
108
108
  config: .mcp.json
109
109
  baseline: .mcp-guard-baseline.json
@@ -115,7 +115,7 @@ Then enforce only new findings:
115
115
  Commit `.mcp-guard-policy.json` or pass `policy` to enforce approved commands, remote packages, directories, and remote MCP URLs.
116
116
 
117
117
  ```yaml
118
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
118
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
119
119
  with:
120
120
  config: .mcp.json
121
121
  policy: .mcp-guard-policy.json
@@ -82,17 +82,18 @@ Code quality
82
82
  Current release title:
83
83
 
84
84
  ```text
85
- v0.4.7
85
+ v0.4.9
86
86
  ```
87
87
 
88
88
  Release notes:
89
89
 
90
90
  ```text
91
- Audit integrity release.
91
+ Rule catalog and MCP education release.
92
92
 
93
- - Adds SHA-256 checksums and byte sizes for generated audit artifacts in `mcp-guard-audit-manifest.json`.
94
- - Keeps remediation checklist, first remediation steps in PR comments, executive summary, remediation plan, Markdown, HTML, JSON, SARIF, policy, baseline, artifacts, and SARIF upload support.
95
- - Improves paid setup and internal review handoff by making generated evidence easier to verify after delivery.
93
+ - Adds `mcp-guard rules` so users can inspect every scanner rule from the CLI in text, Markdown, or JSON.
94
+ - Adds MCP basics to the README and website so new readers can understand clients, servers, configs, and review points.
95
+ - Keeps audit pack verification, remediation checklist, first remediation steps in PR comments, executive summary, remediation plan, Markdown, HTML, JSON, SARIF, policy, baseline, artifacts, and SARIF upload support.
96
+ - Improves product transparency by making both the documented E2E example and the scanner rule catalog visible before adoption.
96
97
  ```
97
98
 
98
99
  ## Manual Publishing Steps
@@ -105,7 +106,7 @@ Completed:
105
106
  - README, docs, and website examples now use:
106
107
 
107
108
  ```yaml
108
- - uses: ChaoYue0307/mcp-guard-action@v0.4.7
109
+ - uses: ChaoYue0307/mcp-guard-action@v0.4.9
109
110
  ```
110
111
 
111
112
  Remaining Marketplace web step:
@@ -13,7 +13,7 @@ It is not currently advertised as an active consulting service. Keep public webs
13
13
  ## Deliverables
14
14
 
15
15
  - MCP and agent tool inventory.
16
- - `mcp-guard audit` evidence pack with executive summary, remediation plan, remediation checklist, reports, and hashed manifest.
16
+ - `mcp-guard audit` evidence pack with executive summary, remediation plan, remediation checklist, reports, hashed manifest, and `verify-audit` output.
17
17
  - Risk report covering shell access, package execution, filesystem scope, secrets, remote servers, and dangerous commands.
18
18
  - Practical remediation checklist.
19
19
  - Optional PR with safer config and policy changes.
package/docs/roadmap.md CHANGED
@@ -13,7 +13,7 @@
13
13
  - Optional GitHub pull request comments from the Marketplace Action.
14
14
  - `mcp-guard init` for bootstrapping a GitHub Action workflow and optional baseline.
15
15
  - Policy file enforcement for approved commands, packages, directories, and remote URLs.
16
- - `mcp-guard audit` for review-ready executive summaries, remediation plans, remediation checklists, reports, and hashed manifests.
16
+ - `mcp-guard audit` and `mcp-guard verify-audit` for review-ready summaries, remediation plans, checklists, reports, and verifiable manifests.
17
17
  - npm Trusted Publishing workflow prepared for tokenless release publishing.
18
18
 
19
19
  ## Next
package/docs/rules.md CHANGED
@@ -2,27 +2,35 @@
2
2
 
3
3
  `mcp-guard` uses practical heuristics for the first public version. It is designed to surface risky MCP configuration quickly, not to prove a system is fully secure.
4
4
 
5
- | Rule | Severity | What it detects |
6
- | --- | --- | --- |
7
- | MCP000 | Low | No MCP config files found in common locations. |
8
- | MCP001 | High | Server has neither `command` nor `url`. |
9
- | MCP002 | Medium | Config file has no `mcpServers` or `servers` object. |
10
- | MCP003 | High | Config file cannot be parsed as JSON. |
11
- | MCP010 | High/Critical | MCP server runs through a shell, especially with inline `-c`. |
12
- | MCP011 | High | Interpreter eval mode such as `node -e` or `python -c`. |
13
- | MCP020 | Medium | Remote package runner such as `npx`, `uvx`, `bunx`, or `pnpm dlx`. |
14
- | MCP021 | High | Remote package runner without exact package version pinning. |
15
- | MCP030 | High | Secret-like environment variable exposed to the MCP server. |
16
- | MCP040 | Medium/High | Broad working directory such as home, root, Desktop, Documents, or Downloads. |
17
- | MCP041 | Medium/High | Broad filesystem path passed in server arguments. |
18
- | MCP050 | Critical | Dangerous command pattern such as `rm -rf`, `sudo`, `chmod 777`, or curl pipe to shell. |
19
- | MCP060 | Medium | Remote MCP server URL configured. |
20
- | MCP061 | High | Secret-like header configured for a remote MCP server. |
21
- | MCP070 | High | MCP server command is outside `allowedCommands` policy. |
22
- | MCP071 | High | Remote MCP package is outside `allowedPackages` policy. |
23
- | MCP072 | High | MCP server working directory is outside `allowedDirectories` policy. |
24
- | MCP073 | High | Filesystem argument is outside `allowedDirectories` policy. |
25
- | MCP074 | High | Remote MCP URL is outside `allowedRemoteUrls` policy. |
5
+ The same catalog is available from the CLI:
6
+
7
+ ```bash
8
+ mcp-guard rules
9
+ mcp-guard rules --format markdown
10
+ mcp-guard rules --format json
11
+ ```
12
+
13
+ | Rule | Severity | Title | What it detects | Recommended response |
14
+ | --- | --- | --- | --- | --- |
15
+ | MCP000 | Low | No MCP config files found | No MCP config files were found in common project or user locations. | Pass `--config` when the MCP config lives outside default discovery paths. |
16
+ | MCP001 | High | Server has no command or URL | An MCP server entry has neither `command` nor `url`. | Remove the server or define an explicit command or URL with reviewed settings. |
17
+ | MCP002 | Medium | Config has no MCP servers object | A config file does not contain an `mcpServers` or `servers` object. | Check that the file is the intended MCP config before relying on the scan. |
18
+ | MCP003 | High | Config cannot be parsed as JSON | A config file could not be parsed as JSON. | Fix the JSON syntax so the scanner and MCP client can read the same config. |
19
+ | MCP010 | High/Critical | MCP server runs through a shell | Shell wrappers such as `sh`, `bash`, `zsh`, `fish`, PowerShell, or `cmd`, especially inline scripts. | Use a direct, pinned executable instead of a shell wrapper. |
20
+ | MCP011 | High | Interpreter eval mode is enabled | Interpreter eval flags such as `node -e`, `python -c`, `ruby -e`, or similar inline code execution. | Replace inline code with a reviewed package or checked-in script. |
21
+ | MCP020 | Medium | Remote package runner is used | Remote package runners such as `npx`, `uvx`, `bunx`, `pipx`, or package-manager `dlx` commands. | Pin the package version and prefer a reviewed lockfile or vendored executable for sensitive tools. |
22
+ | MCP021 | High | Remote MCP package is not version pinned | Remote package execution without an exact package version. | Pin the package to an exact version such as `package@1.2.3` and review updates before changing it. |
23
+ | MCP030 | High | Secret-like environment variable is exposed | Secret-like environment variable names or values passed into an MCP server. | Use least-privilege, short-lived credentials and dedicated service accounts. |
24
+ | MCP040 | Medium/High | MCP server has a broad working directory | Broad working directories such as home, root, Desktop, Documents, or Downloads. | Run the server in a narrow project directory or sandbox with only the files it needs. |
25
+ | MCP041 | Medium/High | MCP server argument grants broad filesystem access | Filesystem arguments that grant broad access to home, root, or sensitive user folders. | Replace broad filesystem paths with a dedicated project folder or read-only sandbox path. |
26
+ | MCP050 | Critical | Command includes a dangerous operation | Dangerous command patterns such as `rm -rf`, `sudo`, `chmod 777`, force push, or curl-pipe-shell. | Remove dangerous startup operations and run setup steps manually after review. |
27
+ | MCP060 | Medium | Remote MCP server URL is configured | Remote HTTP or HTTPS MCP server URLs. | Verify the provider, document data sent to the server, and keep an allowlist of approved endpoints. |
28
+ | MCP061 | High | Secret-like header is configured | Secret-like remote MCP headers such as authorization or API key headers. | Use scoped, short-lived credentials and avoid long-lived secrets in MCP config files. |
29
+ | MCP070 | High | Command is outside policy | An MCP server command that is not listed in `allowedCommands`. | Use an approved command or update policy only after review. |
30
+ | MCP071 | High | Remote package is outside policy | A remote MCP package that is not listed in `allowedPackages`. | Use an approved package or update policy only after package review. |
31
+ | MCP072 | High | Working directory is outside policy | A server working directory outside `allowedDirectories`. | Move the server into an approved workspace or update policy only after review. |
32
+ | MCP073 | High | Filesystem argument is outside policy | A filesystem argument outside `allowedDirectories`. | Limit filesystem arguments to approved directories or update policy only after review. |
33
+ | MCP074 | High | Remote MCP URL is outside policy | A remote MCP endpoint outside `allowedRemoteUrls`. | Use an approved endpoint or update policy only after remote provider review. |
26
34
 
27
35
  ## Severity Model
28
36
 
@@ -36,7 +36,7 @@ Configure this once on npmjs.com:
36
36
  After this is saved, run the workflow from GitHub Actions with the release tag, for example:
37
37
 
38
38
  ```text
39
- v0.4.7
39
+ v0.4.9
40
40
  ```
41
41
 
42
42
  ## Release Flow After Setup
@@ -44,7 +44,7 @@ v0.4.7
44
44
  1. Update `package.json` and `src/cli.js`.
45
45
  2. Run `npm test` and `npm run release:check`.
46
46
  3. Commit and push to `main`.
47
- 4. Create a GitHub release tag such as `v0.4.7`.
47
+ 4. Create a GitHub release tag such as `v0.4.9`.
48
48
  5. Run the `Publish npm` workflow with the same tag.
49
49
  6. Verify npm:
50
50
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agent-mcp-guard",
3
- "version": "0.4.7",
3
+ "version": "0.4.9",
4
4
  "description": "Open-source CLI scanner for risky MCP server and AI agent tool configuration.",
5
5
  "type": "module",
6
6
  "homepage": "https://chaoyue0307.github.io/mcp-guard/",
@@ -2,9 +2,9 @@
2
2
  "version": 1,
3
3
  "tool": {
4
4
  "name": "mcp-guard",
5
- "version": "0.4.7"
5
+ "version": "0.4.9"
6
6
  },
7
- "generatedAt": "2026-05-10T20:08:00.622Z",
7
+ "generatedAt": "2026-05-10T20:51:28.472Z",
8
8
  "status": "needs_review",
9
9
  "failOn": "none",
10
10
  "outputDir": "site/e2e/audit",
@@ -44,43 +44,43 @@
44
44
  "key": "executiveSummary",
45
45
  "path": "site/e2e/audit/mcp-guard-executive-summary.md",
46
46
  "bytes": 1764,
47
- "sha256": "d4679b3353a25ed4f88479ae2594fa6a7e2b1b61a9455b56a61e10a70131c87e"
47
+ "sha256": "6418987f5621711309d1a0af3edf4b1c86f1b069f90e88e7bf0b62748d11bdbe"
48
48
  },
49
49
  {
50
50
  "key": "remediation",
51
51
  "path": "site/e2e/audit/mcp-guard-remediation.md",
52
52
  "bytes": 2752,
53
- "sha256": "1a7a83531f9fea7f0dc8a074a378151f1f7e10a713abc93136e0200bb1dbd35a"
53
+ "sha256": "208468feb8bc3f7071654ad0e3901cd48312c97dd94f48389aac59ea10012103"
54
54
  },
55
55
  {
56
56
  "key": "remediationChecklist",
57
57
  "path": "site/e2e/audit/mcp-guard-remediation-checklist.md",
58
58
  "bytes": 2056,
59
- "sha256": "d75a40e9c2f243cf9193e50d6d9fa8c56e2d50950deb2f1cc843e230c002e548"
59
+ "sha256": "e51752f15bd31384b5a170504f38861db503ac95850d720031b8ab858c9d7205"
60
60
  },
61
61
  {
62
62
  "key": "markdownReport",
63
63
  "path": "site/e2e/audit/mcp-guard-report.md",
64
64
  "bytes": 3297,
65
- "sha256": "11a09f58c19ebb836ff4da6f75d050b508fc989dd41ea1c31a02dbafc3367317"
65
+ "sha256": "c493c4af9ddbcafd8f0538647662068a55206a0c670ccff2ff8ce038b0a6baa3"
66
66
  },
67
67
  {
68
68
  "key": "htmlReport",
69
69
  "path": "site/e2e/audit/mcp-guard-report.html",
70
70
  "bytes": 12988,
71
- "sha256": "3f597e3e1423159f2665b5f5c3b754c2eda4df5645cc2707fa872ddb3c9b7a3c"
71
+ "sha256": "0c0908853c1f375ce425f5996d2239feb3cae9910139b6c594fe888f9ba22612"
72
72
  },
73
73
  {
74
74
  "key": "jsonReport",
75
75
  "path": "site/e2e/audit/mcp-guard-report.json",
76
76
  "bytes": 5938,
77
- "sha256": "a7b2f086928c1d697c4402b37e84b055a7e12ed2bef55654a19733a80d082947"
77
+ "sha256": "16feaffcd491dd09864dcd548570c4a1adaea7f3ef401fce4f7fa4151af75796"
78
78
  },
79
79
  {
80
80
  "key": "sarifReport",
81
81
  "path": "site/e2e/audit/mcp-guard.sarif",
82
82
  "bytes": 20856,
83
- "sha256": "994c3bd89e616a76c53cd3797f6323001cc11dcb1afd96ddaeeb9d53c6432ee8"
83
+ "sha256": "5a00e73219117d9f8e193519a9ddb174b2640c30ade4e963d0829ac94608fa62"
84
84
  }
85
85
  ]
86
86
  }
@@ -1,6 +1,6 @@
1
1
  # mcp-guard Executive Summary
2
2
 
3
- Generated: 2026-05-10T20:08:00.622Z
3
+ Generated: 2026-05-10T20:51:28.472Z
4
4
  Status: **Needs review**
5
5
  Risk score: **98**
6
6
  Fail threshold: **none**
@@ -1,6 +1,6 @@
1
1
  # mcp-guard Remediation Checklist
2
2
 
3
- Generated: 2026-05-10T20:08:00.622Z
3
+ Generated: 2026-05-10T20:51:28.472Z
4
4
  Risk score: **98**
5
5
  Active findings: **9**
6
6
 
@@ -1,6 +1,6 @@
1
1
  # mcp-guard Remediation Plan
2
2
 
3
- Generated: 2026-05-10T20:08:00.622Z
3
+ Generated: 2026-05-10T20:51:28.472Z
4
4
 
5
5
  ## Priority
6
6
 
@@ -297,7 +297,7 @@
297
297
  <div class="metric"><strong>1</strong><span>Scanned files</span></div>
298
298
  <div class="metric"><strong>3</strong><span>MCP servers</span></div>
299
299
  <div class="metric"><strong>9</strong><span>Active findings</span></div>
300
- <div class="metric"><strong>2026-05-10 20:08 UTC</strong><span>Generated</span></div>
300
+ <div class="metric"><strong>2026-05-10 20:51 UTC</strong><span>Generated</span></div>
301
301
  </div>
302
302
  </div>
303
303
  <aside class="scorecard" aria-label="Risk score">
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "metadata": {
3
- "generatedAt": "2026-05-10T20:08:00.622Z",
3
+ "generatedAt": "2026-05-10T20:51:28.472Z",
4
4
  "cwd": ".",
5
5
  "home": "~",
6
6
  "policyPath": "",
7
7
  "policyEnabled": false,
8
- "toolVersion": "0.4.7"
8
+ "toolVersion": "0.4.9"
9
9
  },
10
10
  "policy": null,
11
11
  "scannedFiles": [
@@ -1,6 +1,6 @@
1
1
  # mcp-guard Scan Report
2
2
 
3
- Generated: 2026-05-10T20:08:00.622Z
3
+ Generated: 2026-05-10T20:51:28.472Z
4
4
 
5
5
  ## Summary
6
6
 
@@ -7,7 +7,7 @@
7
7
  "driver": {
8
8
  "name": "mcp-guard",
9
9
  "informationUri": "https://github.com/ChaoYue0307/mcp-guard",
10
- "semanticVersion": "0.4.7",
10
+ "semanticVersion": "0.4.9",
11
11
  "rules": [
12
12
  {
13
13
  "id": "MCP010",
@@ -297,7 +297,7 @@
297
297
  <div class="metric"><strong>1</strong><span>Scanned files</span></div>
298
298
  <div class="metric"><strong>3</strong><span>MCP servers</span></div>
299
299
  <div class="metric"><strong>9</strong><span>Active findings</span></div>
300
- <div class="metric"><strong>2026-05-10 20:07 UTC</strong><span>Generated</span></div>
300
+ <div class="metric"><strong>2026-05-10 20:51 UTC</strong><span>Generated</span></div>
301
301
  </div>
302
302
  </div>
303
303
  <aside class="scorecard" aria-label="Risk score">
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "metadata": {
3
- "generatedAt": "2026-05-10T20:07:57.237Z",
3
+ "generatedAt": "2026-05-10T20:51:23.431Z",
4
4
  "cwd": ".",
5
5
  "home": "~",
6
6
  "policyPath": "",
7
7
  "policyEnabled": false,
8
- "toolVersion": "0.4.7"
8
+ "toolVersion": "0.4.9"
9
9
  },
10
10
  "policy": null,
11
11
  "scannedFiles": [
@@ -1,6 +1,6 @@
1
1
  # mcp-guard Scan Report
2
2
 
3
- Generated: 2026-05-10T20:07:52.252Z
3
+ Generated: 2026-05-10T20:51:23.405Z
4
4
 
5
5
  ## Summary
6
6
 
@@ -7,7 +7,7 @@
7
7
  "driver": {
8
8
  "name": "mcp-guard",
9
9
  "informationUri": "https://github.com/ChaoYue0307/mcp-guard",
10
- "semanticVersion": "0.4.7",
10
+ "semanticVersion": "0.4.9",
11
11
  "rules": [
12
12
  {
13
13
  "id": "MCP010",
package/src/audit.js CHANGED
@@ -72,6 +72,76 @@ export async function writeAuditPack({
72
72
  };
73
73
  }
74
74
 
75
+ export async function verifyAuditPack({ cwd, manifestPath = "" }) {
76
+ const resolvedManifestPath = path.resolve(cwd, manifestPath || path.join("mcp-guard-audit", AUDIT_FILENAMES.manifest));
77
+ const manifestDir = path.dirname(resolvedManifestPath);
78
+ const manifest = JSON.parse(await fs.readFile(resolvedManifestPath, "utf8"));
79
+ const algorithm = manifest.integrity?.algorithm;
80
+ const artifacts = Array.isArray(manifest.integrity?.artifacts) ? manifest.integrity.artifacts : [];
81
+ const results = [];
82
+
83
+ if (algorithm !== "sha256") {
84
+ results.push({
85
+ key: "manifest",
86
+ path: displayPath(resolvedManifestPath, cwd),
87
+ status: "failed",
88
+ reason: `Unsupported or missing integrity algorithm: ${algorithm || "none"}`
89
+ });
90
+ }
91
+
92
+ if (artifacts.length === 0) {
93
+ results.push({
94
+ key: "manifest",
95
+ path: displayPath(resolvedManifestPath, cwd),
96
+ status: "failed",
97
+ reason: "No integrity artifacts found in manifest."
98
+ });
99
+ }
100
+
101
+ for (const artifact of artifacts) {
102
+ results.push(await verifyAuditArtifact(artifact, { cwd, manifestDir }));
103
+ }
104
+
105
+ const failedCount = results.filter((result) => result.status !== "passed").length;
106
+ const checkedCount = artifacts.length;
107
+ return {
108
+ manifestPath: resolvedManifestPath,
109
+ status: failedCount === 0 ? "passed" : "failed",
110
+ checkedCount,
111
+ passedCount: results.filter((result) => result.status === "passed").length,
112
+ failedCount,
113
+ results
114
+ };
115
+ }
116
+
117
+ export function generateAuditVerificationSummary(verification, cwd) {
118
+ const lines = [];
119
+ lines.push("mcp-guard audit verification");
120
+ lines.push(`Manifest: ${displayPath(verification.manifestPath, cwd)}`);
121
+ lines.push(`Status: ${verification.status}`);
122
+ lines.push(`Artifacts checked: ${verification.checkedCount}`);
123
+ lines.push(`Passed: ${verification.passedCount}`);
124
+ lines.push(`Failed: ${verification.failedCount}`);
125
+ lines.push("");
126
+
127
+ for (const result of verification.results) {
128
+ lines.push(`- [${result.status === "passed" ? "ok" : "failed"}] ${result.key}: ${result.path}`);
129
+ if (result.status !== "passed") {
130
+ lines.push(` Reason: ${result.reason}`);
131
+ if (result.expectedSha256 || result.actualSha256) {
132
+ lines.push(` Expected sha256: ${result.expectedSha256 || "-"}`);
133
+ lines.push(` Actual sha256: ${result.actualSha256 || "-"}`);
134
+ }
135
+ if (Number.isInteger(result.expectedBytes) || Number.isInteger(result.actualBytes)) {
136
+ lines.push(` Expected bytes: ${result.expectedBytes ?? "-"}`);
137
+ lines.push(` Actual bytes: ${result.actualBytes ?? "-"}`);
138
+ }
139
+ }
140
+ }
141
+
142
+ return `${lines.join("\n")}\n`;
143
+ }
144
+
75
145
  export function auditFilePaths(outputDir) {
76
146
  return Object.fromEntries(
77
147
  Object.entries(AUDIT_FILENAMES).map(([key, filename]) => [key, path.join(outputDir, filename)])
@@ -294,6 +364,69 @@ async function auditArtifacts(files, cwd) {
294
364
  return artifacts;
295
365
  }
296
366
 
367
+ async function verifyAuditArtifact(artifact, { cwd, manifestDir }) {
368
+ if (!artifact?.key || !artifact?.path) {
369
+ return {
370
+ key: artifact?.key || "<unknown>",
371
+ path: artifact?.path || "<missing>",
372
+ status: "failed",
373
+ reason: "Artifact entry is missing key or path."
374
+ };
375
+ }
376
+
377
+ const resolvedPath = await resolveAuditArtifactPath(artifact.path, { cwd, manifestDir });
378
+ let content;
379
+ try {
380
+ content = await fs.readFile(resolvedPath);
381
+ } catch {
382
+ return {
383
+ key: artifact.key,
384
+ path: artifact.path,
385
+ resolvedPath: displayPath(resolvedPath, cwd),
386
+ status: "failed",
387
+ reason: "Artifact file is missing.",
388
+ expectedBytes: artifact.bytes,
389
+ expectedSha256: artifact.sha256
390
+ };
391
+ }
392
+
393
+ const actualBytes = content.byteLength;
394
+ const actualSha256 = crypto.createHash("sha256").update(content).digest("hex");
395
+ const bytesMatch = actualBytes === artifact.bytes;
396
+ const hashMatch = actualSha256 === artifact.sha256;
397
+ return {
398
+ key: artifact.key,
399
+ path: artifact.path,
400
+ resolvedPath: displayPath(resolvedPath, cwd),
401
+ status: bytesMatch && hashMatch ? "passed" : "failed",
402
+ reason: bytesMatch && hashMatch ? "" : "Artifact content does not match manifest integrity metadata.",
403
+ expectedBytes: artifact.bytes,
404
+ actualBytes,
405
+ expectedSha256: artifact.sha256,
406
+ actualSha256
407
+ };
408
+ }
409
+
410
+ async function resolveAuditArtifactPath(artifactPath, { cwd, manifestDir }) {
411
+ if (path.isAbsolute(artifactPath)) return artifactPath;
412
+
413
+ const candidates = [
414
+ path.join(manifestDir, path.basename(artifactPath)),
415
+ path.resolve(cwd, artifactPath)
416
+ ];
417
+
418
+ for (const candidate of candidates) {
419
+ try {
420
+ await fs.access(candidate);
421
+ return candidate;
422
+ } catch {
423
+ // Try the next candidate.
424
+ }
425
+ }
426
+
427
+ return candidates[0];
428
+ }
429
+
297
430
  function decisionGuidance(result) {
298
431
  if (result.findings.length === 0) {
299
432
  return ["No active findings were detected. Continue reviewing new MCP servers before adding them."];
package/src/cli.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { generateAuditSummary, writeAuditPack } from "./audit.js";
3
+ import { generateAuditSummary, generateAuditVerificationSummary, verifyAuditPack, writeAuditPack } from "./audit.js";
4
4
  import { applyBaseline, loadBaselineFile, writeBaselineFile } from "./baseline.js";
5
5
  import { initProject, renderInitSummary } from "./init.js";
6
6
  import { scan } from "./scan.js";
7
7
  import { generateHtmlReport, generateJsonReport, generateMarkdownReport, generateSarifReport, generateTextReport } from "./report.js";
8
+ import { generateRulesJson, generateRulesMarkdown, generateRulesText } from "./rule-catalog.js";
8
9
  import { compareSeverity, severityRank } from "./severity.js";
9
10
 
10
- const VERSION = "0.4.7";
11
+ const VERSION = "0.4.9";
11
12
 
12
13
  export async function runCli(argv, io) {
13
14
  const args = argv.slice(2);
@@ -23,6 +24,17 @@ export async function runCli(argv, io) {
23
24
  return 0;
24
25
  }
25
26
 
27
+ if (command === "rules") {
28
+ if (args.includes("--help") || args.includes("-h")) {
29
+ io.stdout.write(helpText());
30
+ return 0;
31
+ }
32
+
33
+ const options = parseRulesArgs(args.slice(1));
34
+ io.stdout.write(renderRules(options.format));
35
+ return 0;
36
+ }
37
+
26
38
  if (command === "init") {
27
39
  if (args.includes("--help") || args.includes("-h")) {
28
40
  io.stdout.write(helpText());
@@ -84,6 +96,25 @@ export async function runCli(argv, io) {
84
96
  return 0;
85
97
  }
86
98
 
99
+ if (command === "verify-audit") {
100
+ if (args.includes("--help") || args.includes("-h")) {
101
+ io.stdout.write(helpText());
102
+ return 0;
103
+ }
104
+
105
+ const options = parseVerifyAuditArgs(args.slice(1), io.cwd);
106
+ const verification = await verifyAuditPack({
107
+ cwd: options.cwd,
108
+ manifestPath: options.manifestPath
109
+ });
110
+ io.stdout.write(generateAuditVerificationSummary(verification, options.cwd));
111
+ if (verification.status !== "passed") {
112
+ process.exitCode = 2;
113
+ return 2;
114
+ }
115
+ return 0;
116
+ }
117
+
87
118
  if (command !== "scan") {
88
119
  io.stderr.write(`Unknown command: ${command}\n\n`);
89
120
  io.stderr.write(helpText());
@@ -329,6 +360,51 @@ function parseAuditArgs(args, defaultCwd) {
329
360
  return options;
330
361
  }
331
362
 
363
+ function parseVerifyAuditArgs(args, defaultCwd) {
364
+ const options = {
365
+ cwd: defaultCwd,
366
+ manifestPath: ""
367
+ };
368
+ let manifestValue = "";
369
+
370
+ for (let index = 0; index < args.length; index += 1) {
371
+ const arg = args[index];
372
+ if (arg === "--manifest" || arg === "-m") {
373
+ manifestValue = readValue(args, index, arg);
374
+ index += 1;
375
+ } else if (arg === "--cwd") {
376
+ options.cwd = path.resolve(readValue(args, index, arg));
377
+ index += 1;
378
+ } else {
379
+ throw new Error(`Unknown verify-audit option: ${arg}`);
380
+ }
381
+ }
382
+
383
+ options.manifestPath = manifestValue ? resolveInputPath(manifestValue, options.cwd) : "";
384
+ return options;
385
+ }
386
+
387
+ function parseRulesArgs(args) {
388
+ const options = {
389
+ format: "text"
390
+ };
391
+
392
+ for (let index = 0; index < args.length; index += 1) {
393
+ const arg = args[index];
394
+ if (arg === "--format" || arg === "-f") {
395
+ options.format = readValue(args, index, arg);
396
+ index += 1;
397
+ if (!["text", "markdown", "json"].includes(options.format)) {
398
+ throw new Error("--format must be one of: text, markdown, json");
399
+ }
400
+ } else {
401
+ throw new Error(`Unknown rules option: ${arg}`);
402
+ }
403
+ }
404
+
405
+ return options;
406
+ }
407
+
332
408
  function readValue(args, index, optionName) {
333
409
  const value = args[index + 1];
334
410
  if (!value || value.startsWith("--")) {
@@ -357,6 +433,16 @@ function renderReport(result, format) {
357
433
  return generateTextReport(result);
358
434
  }
359
435
 
436
+ function renderRules(format) {
437
+ if (format === "json") {
438
+ return generateRulesJson();
439
+ }
440
+ if (format === "markdown") {
441
+ return generateRulesMarkdown();
442
+ }
443
+ return generateRulesText();
444
+ }
445
+
360
446
  function shouldFail(result, failOn) {
361
447
  const threshold = severityRank(failOn);
362
448
  return result.findings.some((finding) => compareSeverity(finding.severity, threshold) >= 0);
@@ -370,6 +456,8 @@ Open-source scanner for risky MCP server and AI agent tool configuration.
370
456
  Usage:
371
457
  mcp-guard scan [options]
372
458
  mcp-guard audit [options]
459
+ mcp-guard verify-audit [options]
460
+ mcp-guard rules [options]
373
461
  mcp-guard init [options]
374
462
  mcp-guard version
375
463
  mcp-guard help
@@ -420,11 +508,21 @@ Audit options:
420
508
  --no-policy Do not auto-load .mcp-guard-policy.json.
421
509
  --no-defaults Only scan paths passed with --config.
422
510
 
511
+ Verify audit options:
512
+ -m, --manifest <path> Audit manifest to verify.
513
+ Default: mcp-guard-audit/mcp-guard-audit-manifest.json.
514
+ --cwd <path> Working directory for resolving relative artifact paths.
515
+
516
+ Rules options:
517
+ -f, --format <format> text, markdown, or json. Default: text.
518
+
423
519
  Examples:
424
520
  mcp-guard init
425
521
  mcp-guard init --write-baseline --upload-sarif
426
522
  mcp-guard scan
523
+ mcp-guard rules --format markdown
427
524
  mcp-guard audit --config .mcp.json --output-dir mcp-guard-audit
525
+ mcp-guard verify-audit --manifest mcp-guard-audit/mcp-guard-audit-manifest.json
428
526
  mcp-guard audit --config .mcp.json --policy .mcp-guard-policy.json --fail-on high
429
527
  mcp-guard scan --format markdown --output mcp-guard-report.md
430
528
  mcp-guard scan --format html --output mcp-guard-report.html
@@ -0,0 +1,169 @@
1
+ export const RULE_CATALOG = [
2
+ {
3
+ id: "MCP000",
4
+ severity: "low",
5
+ title: "No MCP config files found",
6
+ detects: "No MCP config files were found in common project or user locations.",
7
+ recommendation: "Pass --config when the MCP config lives outside default discovery paths."
8
+ },
9
+ {
10
+ id: "MCP001",
11
+ severity: "high",
12
+ title: "Server has no command or URL",
13
+ detects: "An MCP server entry has neither command nor url.",
14
+ recommendation: "Remove the server or define an explicit command or URL with reviewed settings."
15
+ },
16
+ {
17
+ id: "MCP002",
18
+ severity: "medium",
19
+ title: "Config has no MCP servers object",
20
+ detects: "A config file does not contain an mcpServers or servers object.",
21
+ recommendation: "Check that the file is the intended MCP config before relying on the scan."
22
+ },
23
+ {
24
+ id: "MCP003",
25
+ severity: "high",
26
+ title: "Config cannot be parsed as JSON",
27
+ detects: "A config file could not be parsed as JSON.",
28
+ recommendation: "Fix the JSON syntax so the scanner and MCP client can read the same config."
29
+ },
30
+ {
31
+ id: "MCP010",
32
+ severity: "high/critical",
33
+ title: "MCP server runs through a shell",
34
+ detects: "Shell wrappers such as sh, bash, zsh, fish, PowerShell, or cmd, especially inline scripts.",
35
+ recommendation: "Use a direct, pinned executable instead of a shell wrapper."
36
+ },
37
+ {
38
+ id: "MCP011",
39
+ severity: "high",
40
+ title: "Interpreter eval mode is enabled",
41
+ detects: "Interpreter eval flags such as node -e, python -c, ruby -e, or similar inline code execution.",
42
+ recommendation: "Replace inline code with a reviewed package or checked-in script."
43
+ },
44
+ {
45
+ id: "MCP020",
46
+ severity: "medium",
47
+ title: "Remote package runner is used",
48
+ detects: "Remote package runners such as npx, uvx, bunx, pipx, or package-manager dlx commands.",
49
+ recommendation: "Pin the package version and prefer a reviewed lockfile or vendored executable for sensitive tools."
50
+ },
51
+ {
52
+ id: "MCP021",
53
+ severity: "high",
54
+ title: "Remote MCP package is not version pinned",
55
+ detects: "Remote package execution without an exact package version.",
56
+ recommendation: "Pin the package to an exact version such as package@1.2.3 and review updates before changing it."
57
+ },
58
+ {
59
+ id: "MCP030",
60
+ severity: "high",
61
+ title: "Secret-like environment variable is exposed",
62
+ detects: "Secret-like environment variable names or values passed into an MCP server.",
63
+ recommendation: "Use least-privilege, short-lived credentials and dedicated service accounts."
64
+ },
65
+ {
66
+ id: "MCP040",
67
+ severity: "medium/high",
68
+ title: "MCP server has a broad working directory",
69
+ detects: "Broad working directories such as home, root, Desktop, Documents, or Downloads.",
70
+ recommendation: "Run the server in a narrow project directory or sandbox with only the files it needs."
71
+ },
72
+ {
73
+ id: "MCP041",
74
+ severity: "medium/high",
75
+ title: "MCP server argument grants broad filesystem access",
76
+ detects: "Filesystem arguments that grant broad access to home, root, or sensitive user folders.",
77
+ recommendation: "Replace broad filesystem paths with a dedicated project folder or read-only sandbox path."
78
+ },
79
+ {
80
+ id: "MCP050",
81
+ severity: "critical",
82
+ title: "Command includes a dangerous operation",
83
+ detects: "Dangerous command patterns such as rm -rf, sudo, chmod 777, force push, or curl-pipe-shell.",
84
+ recommendation: "Remove dangerous startup operations and run setup steps manually after review."
85
+ },
86
+ {
87
+ id: "MCP060",
88
+ severity: "medium",
89
+ title: "Remote MCP server URL is configured",
90
+ detects: "Remote HTTP or HTTPS MCP server URLs.",
91
+ recommendation: "Verify the provider, document data sent to the server, and keep an allowlist of approved endpoints."
92
+ },
93
+ {
94
+ id: "MCP061",
95
+ severity: "high",
96
+ title: "Secret-like header is configured",
97
+ detects: "Secret-like remote MCP headers such as authorization or API key headers.",
98
+ recommendation: "Use scoped, short-lived credentials and avoid long-lived secrets in MCP config files."
99
+ },
100
+ {
101
+ id: "MCP070",
102
+ severity: "high",
103
+ title: "Command is outside policy",
104
+ detects: "An MCP server command that is not listed in allowedCommands.",
105
+ recommendation: "Use an approved command or update policy only after review."
106
+ },
107
+ {
108
+ id: "MCP071",
109
+ severity: "high",
110
+ title: "Remote package is outside policy",
111
+ detects: "A remote MCP package that is not listed in allowedPackages.",
112
+ recommendation: "Use an approved package or update policy only after package review."
113
+ },
114
+ {
115
+ id: "MCP072",
116
+ severity: "high",
117
+ title: "Working directory is outside policy",
118
+ detects: "A server working directory outside allowedDirectories.",
119
+ recommendation: "Move the server into an approved workspace or update policy only after review."
120
+ },
121
+ {
122
+ id: "MCP073",
123
+ severity: "high",
124
+ title: "Filesystem argument is outside policy",
125
+ detects: "A filesystem argument outside allowedDirectories.",
126
+ recommendation: "Limit filesystem arguments to approved directories or update policy only after review."
127
+ },
128
+ {
129
+ id: "MCP074",
130
+ severity: "high",
131
+ title: "Remote MCP URL is outside policy",
132
+ detects: "A remote MCP endpoint outside allowedRemoteUrls.",
133
+ recommendation: "Use an approved endpoint or update policy only after remote provider review."
134
+ }
135
+ ];
136
+
137
+ export function generateRulesText(rules = RULE_CATALOG) {
138
+ const lines = [];
139
+ lines.push("mcp-guard rule reference");
140
+ lines.push(`Rules: ${rules.length}`);
141
+ lines.push("");
142
+ for (const rule of rules) {
143
+ lines.push(`- [${rule.severity.toUpperCase()}] ${rule.id} ${rule.title}`);
144
+ lines.push(` Detects: ${rule.detects}`);
145
+ lines.push(` Fix: ${rule.recommendation}`);
146
+ }
147
+ return `${lines.join("\n")}\n`;
148
+ }
149
+
150
+ export function generateRulesMarkdown(rules = RULE_CATALOG) {
151
+ const lines = [];
152
+ lines.push("# mcp-guard Rule Reference");
153
+ lines.push("");
154
+ lines.push("| Rule | Severity | Title | What it detects | Recommended response |");
155
+ lines.push("| --- | --- | --- | --- | --- |");
156
+ for (const rule of rules) {
157
+ lines.push(`| ${cell(rule.id)} | ${cell(rule.severity)} | ${cell(rule.title)} | ${cell(rule.detects)} | ${cell(rule.recommendation)} |`);
158
+ }
159
+ lines.push("");
160
+ return `${lines.join("\n")}`;
161
+ }
162
+
163
+ export function generateRulesJson(rules = RULE_CATALOG) {
164
+ return `${JSON.stringify({ rules }, null, 2)}\n`;
165
+ }
166
+
167
+ function cell(value) {
168
+ return String(value).replace(/\|/g, "\\|");
169
+ }