mcp-sentinel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +290 -0
  3. package/bin/mcp-sentinel.js +2 -0
  4. package/dist/aguara.d.ts +4 -0
  5. package/dist/aguara.d.ts.map +1 -0
  6. package/dist/aguara.js +105 -0
  7. package/dist/aguara.js.map +1 -0
  8. package/dist/analyzer.d.ts +9 -0
  9. package/dist/analyzer.d.ts.map +1 -0
  10. package/dist/analyzer.js +42 -0
  11. package/dist/analyzer.js.map +1 -0
  12. package/dist/cli.d.ts +3 -0
  13. package/dist/cli.d.ts.map +1 -0
  14. package/dist/cli.js +176 -0
  15. package/dist/cli.js.map +1 -0
  16. package/dist/config.d.ts +8 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +55 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/diff.d.ts +3 -0
  21. package/dist/diff.d.ts.map +1 -0
  22. package/dist/diff.js +110 -0
  23. package/dist/diff.js.map +1 -0
  24. package/dist/formatter.d.ts +7 -0
  25. package/dist/formatter.d.ts.map +1 -0
  26. package/dist/formatter.js +184 -0
  27. package/dist/formatter.js.map +1 -0
  28. package/dist/index.d.ts +2 -0
  29. package/dist/index.d.ts.map +1 -0
  30. package/dist/index.js +165 -0
  31. package/dist/index.js.map +1 -0
  32. package/dist/markdown.d.ts +3 -0
  33. package/dist/markdown.d.ts.map +1 -0
  34. package/dist/markdown.js +106 -0
  35. package/dist/markdown.js.map +1 -0
  36. package/dist/policy.d.ts +5 -0
  37. package/dist/policy.d.ts.map +1 -0
  38. package/dist/policy.js +162 -0
  39. package/dist/policy.js.map +1 -0
  40. package/dist/scanner.d.ts +17 -0
  41. package/dist/scanner.d.ts.map +1 -0
  42. package/dist/scanner.js +147 -0
  43. package/dist/scanner.js.map +1 -0
  44. package/dist/types.d.ts +135 -0
  45. package/dist/types.d.ts.map +1 -0
  46. package/dist/types.js +2 -0
  47. package/dist/types.js.map +1 -0
  48. package/package.json +66 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gustavo Aragon (@oktsec)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,290 @@
1
+ <p align="center">
2
+ <h1 align="center">MCP Sentinel</h1>
3
+ <p align="center">
4
+ <strong>Audit and enforce security policies on MCP servers before you trust them.</strong>
5
+ </p>
6
+ <p align="center">
7
+ <a href="https://www.npmjs.com/package/mcp-sentinel"><img src="https://img.shields.io/npm/v/mcp-sentinel.svg" alt="npm version"></a>
8
+ <a href="https://github.com/oktsec/mcp-sentinel/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/mcp-sentinel.svg" alt="license"></a>
9
+ <a href="https://nodejs.org"><img src="https://img.shields.io/node/v/mcp-sentinel.svg" alt="node version"></a>
10
+ </p>
11
+ </p>
12
+
13
+ ---
14
+
15
+ MCP servers run third-party code with access to your files, credentials, and shell. MCP Sentinel connects to any server, shows you exactly what it exposes, and **enforces security policies** — blocking dangerous tools before your agent can call them.
16
+
17
+ ```bash
18
+ npx mcp-sentinel --policy .mcp-policy.yml npx @modelcontextprotocol/server-filesystem /tmp
19
+ ```
20
+
21
+ ## Real Output
22
+
23
+ Scanned against the official [MCP filesystem server](https://www.npmjs.com/package/@modelcontextprotocol/server-filesystem):
24
+
25
+ ```
26
+ 🔍 MCP Sentinel v0.1.0
27
+
28
+ 📦 Server: secure-filesystem-server v0.2.0
29
+ Capabilities: tools
30
+
31
+ 🔧 Tools (14) 11 read • 3 write • 0 admin
32
+
33
+ ✅ read_file Read the complete contents of a file as text... (3 params)
34
+ * string path
35
+ number tail
36
+ number head
37
+ ✅ read_text_file Read the complete contents of a file from... (3 params)
38
+ ✅ read_media_file Read an image or audio file... (1 params)
39
+ ✅ read_multiple_files Read multiple files simultaneously... (1 params)
40
+ ✏️ write_file Create a new file or overwrite an existing... [write] (2 params)
41
+ * string path
42
+ * string content
43
+ ✅ edit_file Make line-based edits to a text file... (3 params)
44
+ ✏️ create_directory Create a new directory... [write] (1 params)
45
+ ✅ list_directory Get a detailed listing of all files... (1 params)
46
+ ✏️ move_file Move or rename files and directories... [write] (2 params)
47
+ ✅ search_files Recursively search for files... (3 params)
48
+ ✅ get_file_info Retrieve detailed metadata... (1 params)
49
+ ✅ list_allowed_directories Returns the list of allowed directories
50
+
51
+ 🛡️ Aguara Security Analysis
52
+
53
+ 0 finding(s)
54
+
55
+ Scanned in 1706ms
56
+ ```
57
+
58
+ ### With Policy Enforcement
59
+
60
+ Using this policy:
61
+
62
+ ```yaml
63
+ # .mcp-policy.yml
64
+ deny:
65
+ categories: [admin]
66
+ tools: ["write_*", "move_*"]
67
+ require:
68
+ maxTools: 10
69
+ allow:
70
+ tools: ["write_file"] # Allow write_file despite deny pattern
71
+ ```
72
+
73
+ ```
74
+ 🛡️ Policy: .mcp-policy.yml
75
+
76
+ ❌ secure-filesystem-server: policy FAILED (2 violations)
77
+
78
+ ✖ [deny.tools] Tool 'move_file' matches denied pattern 'move_*'
79
+ ✖ [require.maxTools] Server exposes 14 tools, policy allows max 10
80
+ ```
81
+
82
+ **Exit code 2** — `write_file` was allowed by the exception, but `move_file` and the tool count violated the policy. Your CI pipeline stops here.
83
+
84
+ ## Why MCP Sentinel
85
+
86
+ Every MCP client already shows you the tool list. MCP Sentinel goes further:
87
+
88
+ | Feature | MCP Client | MCP Sentinel |
89
+ |---------|-----------|---------------|
90
+ | See tool list | Yes | Yes |
91
+ | **Security policy enforcement** | No | **Yes** |
92
+ | **CI/CD pipeline gate** | No | **Yes** |
93
+ | **Drift detection** between versions | No | **Yes** |
94
+ | **Fleet scan** all configured servers | No | **Yes** |
95
+ | Deep security analysis (aguara) | No | **Yes** |
96
+ | Exportable reports (JSON/Markdown) | No | **Yes** |
97
+
98
+ ## Policy Enforcement
99
+
100
+ Define what's allowed in `.mcp-policy.yml`:
101
+
102
+ ```yaml
103
+ deny:
104
+ categories:
105
+ - admin # No admin tools (delete, exec, shell)
106
+ tools:
107
+ - "execute_*" # Block all execution patterns
108
+ - "push_*" # Block push operations
109
+
110
+ require:
111
+ aguara: clean # Zero security findings required
112
+ maxTools: 20 # Limit attack surface
113
+
114
+ allow:
115
+ tools:
116
+ - "execute_query" # Allow specific tools even if pattern-denied
117
+ ```
118
+
119
+ ### Policy Rules
120
+
121
+ | Rule | Description |
122
+ |------|-------------|
123
+ | `deny.categories` | Block tools by category: `read`, `write`, `admin` |
124
+ | `deny.tools` | Block tools by name or glob pattern (`delete_*`, `exec*`) |
125
+ | `require.aguara` | Require aguara scan with zero findings (`clean`) |
126
+ | `require.maxTools` | Maximum number of tools a server can expose |
127
+ | `allow.tools` | Exception list — allow specific tools even if they match deny rules |
128
+
129
+ ### Example Policies
130
+
131
+ Ready-to-use policies in [`examples/policies/`](examples/policies/):
132
+
133
+ | Policy | Use case |
134
+ |--------|----------|
135
+ | [`strict.yml`](examples/policies/strict.yml) | Production — blocks admin + write, requires aguara clean |
136
+ | [`standard.yml`](examples/policies/standard.yml) | Development — blocks admin + exec patterns, allows writes |
137
+ | [`permissive.yml`](examples/policies/permissive.yml) | Local dev — only blocks destructive patterns (delete, drop, destroy) |
138
+ | [`ci-pipeline.yml`](examples/policies/ci-pipeline.yml) | CI/CD — blocks admin + deploy + push, requires aguara clean |
139
+
140
+ ### CI/CD Integration
141
+
142
+ ```yaml
143
+ # .github/workflows/mcp-sentinel.yml
144
+ name: MCP Security Audit
145
+ on: [pull_request]
146
+
147
+ jobs:
148
+ audit:
149
+ runs-on: ubuntu-latest
150
+ steps:
151
+ - uses: actions/checkout@v4
152
+ - uses: actions/setup-node@v4
153
+ with: { node-version: 20 }
154
+ - run: npx mcp-sentinel --policy .mcp-policy.yml npx ./your-mcp-server
155
+ # Exit code 2 = policy violations → build fails
156
+ ```
157
+
158
+ See full example in [`examples/github-action.yml`](examples/github-action.yml).
159
+
160
+ ## Install & Use
161
+
162
+ ```bash
163
+ # No install needed
164
+ npx mcp-sentinel <command> [args...]
165
+ ```
166
+
167
+ ### Quick Start
168
+
169
+ ```bash
170
+ # 1. Scan a server
171
+ npx mcp-sentinel npx @modelcontextprotocol/server-filesystem /tmp
172
+
173
+ # 2. Create a policy
174
+ cat > .mcp-policy.yml << 'EOF'
175
+ deny:
176
+ categories: [admin]
177
+ tools: ["execute_*", "delete_*"]
178
+ require:
179
+ aguara: clean
180
+ maxTools: 20
181
+ EOF
182
+
183
+ # 3. Enforce it
184
+ npx mcp-sentinel --policy .mcp-policy.yml npx @modelcontextprotocol/server-filesystem /tmp
185
+ ```
186
+
187
+ ### More Examples
188
+
189
+ ```bash
190
+ # Scan remote servers via HTTP
191
+ npx mcp-sentinel http://localhost:3000/mcp
192
+
193
+ # Scan all servers from your config (Claude Desktop, Cursor, Windsurf)
194
+ npx mcp-sentinel --config
195
+
196
+ # Diff mode: detect changes between server versions
197
+ npx mcp-sentinel npx @mcp/server --json > baseline.json
198
+ npx mcp-sentinel npx @mcp/server --diff baseline.json
199
+
200
+ # Scan multiple servers at once
201
+ npx mcp-sentinel npx @mcp/server-a --- npx @mcp/server-b
202
+
203
+ # JSON output for scripting
204
+ npx mcp-sentinel --json npx @mcp/server
205
+
206
+ # Markdown report
207
+ npx mcp-sentinel --markdown report.md npx @mcp/server
208
+ ```
209
+
210
+ ### With Aguara (recommended)
211
+
212
+ Install [Aguara](https://github.com/garagon/aguara) for deep security analysis (prompt injection, exfiltration, supply chain, credential leaks):
213
+
214
+ ```bash
215
+ curl -fsSL https://raw.githubusercontent.com/garagon/aguara/main/install.sh | bash
216
+ ```
217
+
218
+ MCP Sentinel auto-detects Aguara and runs its 177-rule engine against tool descriptions. Combine with `require.aguara: clean` in your policy to enforce zero findings.
219
+
220
+ ## Options
221
+
222
+ | Flag | Description |
223
+ |------|-------------|
224
+ | `--policy <file>` | Enforce security policy (auto-detects `.mcp-policy.yml`) |
225
+ | `--config` | Auto-detect and scan servers from config files |
226
+ | `--diff <file.json>` | Compare against a previous JSON scan |
227
+ | `--transport <type>` | Force transport: `stdio`, `sse`, `streamable-http` |
228
+ | `--json` | Structured JSON output |
229
+ | `--markdown <file>` | Export report as Markdown |
230
+ | `--fail-on-findings` | Exit code 2 if aguara finds issues |
231
+ | `--no-color` | Disable colored output |
232
+ | `--timeout <ms>` | Connection timeout (default: 30000) |
233
+ | `-h, --help` | Show help |
234
+ | `-v, --version` | Show version |
235
+
236
+ ## How It Works
237
+
238
+ ```
239
+ ┌────────────────┐
240
+ stdio │ MCP Server │
241
+ ┌──────── │ (local) │
242
+ │ └────────────────┘
243
+ ┌───────────┤
244
+ │ mcp- │ HTTP/ ┌────────────────┐
245
+ │ inspector │ SSE │ MCP Server │
246
+ │ ├──────── │ (remote) │
247
+ │ Scan │ └────────────────┘
248
+ │ Enforce │
249
+ │ Diff │ ┌──────────────────┐
250
+ │ Report │ ──────► │ Aguara (177 │
251
+ └───────────┘ │ security rules) │
252
+ │ └──────────────────┘
253
+
254
+ .mcp-policy.yml
255
+ (deny / require / allow)
256
+ ```
257
+
258
+ ## Ecosystem
259
+
260
+ MCP Sentinel is part of the [Aguara](https://github.com/garagon/aguara) security ecosystem:
261
+
262
+ | Tool | What it does |
263
+ |------|-------------|
264
+ | **[Aguara](https://github.com/garagon/aguara)** | Security scanner — 177 rules, NLP, toxic-flow analysis |
265
+ | **[Aguara MCP](https://github.com/garagon/aguara-mcp)** | MCP server — gives AI agents security scanning as a tool |
266
+ | **MCP Sentinel** | Policy enforcement — audit, enforce, and monitor MCP servers |
267
+ | **[Aguara Watch](https://aguarascan.com)** | Cloud platform — continuous monitoring of MCP registries |
268
+
269
+ ## Roadmap
270
+
271
+ - [x] Runtime introspection (tools, resources, prompts, capabilities)
272
+ - [x] **Policy enforcement engine**
273
+ - [x] Aguara integration
274
+ - [x] HTTP/SSE transport support
275
+ - [x] Diff mode
276
+ - [x] Config auto-detection (Claude Desktop, Cursor, Windsurf)
277
+ - [ ] Per-server policy overrides
278
+ - [ ] Registry integration (Smithery, mcp.run)
279
+ - [ ] GitHub Action (reusable workflow)
280
+ - [ ] VS Code extension
281
+
282
+ ## Contributing
283
+
284
+ Contributions welcome. Please open an issue first to discuss what you'd like to change.
285
+
286
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for development standards.
287
+
288
+ ## License
289
+
290
+ [MIT](LICENSE) — Gustavo Aragon ([@oktsec](https://github.com/oktsec))
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ import "../dist/index.js";
@@ -0,0 +1,4 @@
1
+ import type { ToolInfo, AguaraResult } from "./types.js";
2
+ export declare function isAguaraInstalled(): Promise<boolean>;
3
+ export declare function scanWithAguara(tools: ToolInfo[]): Promise<AguaraResult>;
4
+ //# sourceMappingURL=aguara.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aguara.d.ts","sourceRoot":"","sources":["../src/aguara.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAiB,MAAM,YAAY,CAAC;AAIxE,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,OAAO,CAAC,CAO1D;AAyDD,wBAAsB,cAAc,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,YAAY,CAAC,CAgD7E"}
package/dist/aguara.js ADDED
@@ -0,0 +1,105 @@
1
+ import { execFile } from "node:child_process";
2
+ import { writeFile, unlink, mkdtemp } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { tmpdir } from "node:os";
5
+ import { promisify } from "node:util";
6
+ const execFileAsync = promisify(execFile);
7
+ export async function isAguaraInstalled() {
8
+ try {
9
+ await execFileAsync("aguara", ["version"]);
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
16
+ function buildScanContent(tools) {
17
+ const lines = [];
18
+ for (const tool of tools) {
19
+ lines.push(`## Tool: ${tool.name}`);
20
+ lines.push("");
21
+ lines.push(tool.description);
22
+ lines.push("");
23
+ if (tool.parameters.length > 0) {
24
+ lines.push(`Parameters: ${tool.parameters.map((p) => p.name).join(", ")}`);
25
+ lines.push("");
26
+ }
27
+ }
28
+ return lines.join("\n");
29
+ }
30
+ function parseAguaraOutput(stdout) {
31
+ try {
32
+ const parsed = JSON.parse(stdout);
33
+ const findings = [];
34
+ if (Array.isArray(parsed.findings)) {
35
+ for (const f of parsed.findings) {
36
+ findings.push({
37
+ severity: String(f.severity ?? "UNKNOWN"),
38
+ ruleId: String(f.rule_id ?? ""),
39
+ ruleName: String(f.rule_name ?? ""),
40
+ matchedText: String(f.matched_text ?? ""),
41
+ line: typeof f.line === "number" ? f.line : undefined,
42
+ });
43
+ }
44
+ }
45
+ return {
46
+ findings,
47
+ summary: typeof parsed.summary === "string" ? parsed.summary : `${findings.length} finding(s)`,
48
+ };
49
+ }
50
+ catch {
51
+ return { findings: [], summary: "Failed to parse aguara output" };
52
+ }
53
+ }
54
+ export async function scanWithAguara(tools) {
55
+ const installed = await isAguaraInstalled();
56
+ if (!installed) {
57
+ return {
58
+ available: false,
59
+ findings: [],
60
+ summary: "aguara not installed — install from https://github.com/garagon/aguara for deep security analysis",
61
+ };
62
+ }
63
+ const content = buildScanContent(tools);
64
+ const tmpDir = await mkdtemp(join(tmpdir(), "mcp-sentinel-"));
65
+ const tmpFile = join(tmpDir, "tools.md");
66
+ try {
67
+ await writeFile(tmpFile, content, "utf-8");
68
+ const { stdout } = await execFileAsync("aguara", [
69
+ "scan", tmpFile, "--format", "json", "--severity", "low",
70
+ ], {
71
+ timeout: 30_000,
72
+ env: process.env,
73
+ maxBuffer: 10 * 1024 * 1024,
74
+ });
75
+ const result = parseAguaraOutput(stdout);
76
+ return { available: true, ...result };
77
+ }
78
+ catch (err) {
79
+ // aguara exits with code 1 when findings are found, but still outputs JSON
80
+ if (typeof err === "object" && err !== null && "stdout" in err) {
81
+ const stdout = String(err.stdout);
82
+ if (stdout.trim().length > 0) {
83
+ const result = parseAguaraOutput(stdout);
84
+ return { available: true, ...result };
85
+ }
86
+ }
87
+ return {
88
+ available: true,
89
+ findings: [],
90
+ summary: `aguara scan failed: ${err instanceof Error ? err.message : "unknown error"}`,
91
+ };
92
+ }
93
+ finally {
94
+ try {
95
+ await unlink(tmpFile);
96
+ }
97
+ catch { /* cleanup */ }
98
+ try {
99
+ const { rmdir } = await import("node:fs/promises");
100
+ await rmdir(tmpDir);
101
+ }
102
+ catch { /* cleanup */ }
103
+ }
104
+ }
105
+ //# sourceMappingURL=aguara.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"aguara.js","sourceRoot":"","sources":["../src/aguara.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ToolInfo, AnalyzedTool, ToolCategory } from "./types.js";
2
+ export declare function categorizeTool(tool: ToolInfo): ToolCategory;
3
+ export declare function analyzeTools(tools: ToolInfo[]): AnalyzedTool[];
4
+ export declare function summarize(tools: AnalyzedTool[]): {
5
+ read: number;
6
+ write: number;
7
+ admin: number;
8
+ };
9
+ //# sourceMappingURL=analyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.d.ts","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,YAAY,EACZ,YAAY,EACb,MAAM,YAAY,CAAC;AAepB,wBAAgB,cAAc,CAAC,IAAI,EAAE,QAAQ,GAAG,YAAY,CAU3D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,YAAY,EAAE,CAK9D;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAY/F"}
@@ -0,0 +1,42 @@
1
+ const WRITE_HINTS = [
2
+ /\bwrite\b/i, /\bcreate\b/i, /\bupdate\b/i, /\bmodify\b/i,
3
+ /\bset\b/i, /\bput\b/i, /\bpatch\b/i, /\bpush\b/i,
4
+ /\binsert\b/i, /\bupload\b/i, /\bsave\b/i, /\bmove\b/i,
5
+ ];
6
+ const ADMIN_HINTS = [
7
+ /\bdelete\b/i, /\bremove\b/i, /\bdrop\b/i, /\bdestroy\b/i,
8
+ /\bpurge\b/i, /\btruncate\b/i, /\bexec\b/i, /\bexecute\b/i,
9
+ /\bshell\b/i, /\bbash\b/i, /\bspawn\b/i, /\beval\b/i,
10
+ /\buninstall\b/i, /\breset\b/i, /\bkill\b/i, /\bforce\b/i,
11
+ ];
12
+ export function categorizeTool(tool) {
13
+ const text = `${tool.name} ${tool.description}`;
14
+ if (ADMIN_HINTS.some((p) => p.test(text))) {
15
+ return "admin";
16
+ }
17
+ if (WRITE_HINTS.some((p) => p.test(text))) {
18
+ return "write";
19
+ }
20
+ return "read";
21
+ }
22
+ export function analyzeTools(tools) {
23
+ return tools.map((tool) => ({
24
+ tool,
25
+ category: categorizeTool(tool),
26
+ }));
27
+ }
28
+ export function summarize(tools) {
29
+ let read = 0;
30
+ let write = 0;
31
+ let admin = 0;
32
+ for (const t of tools) {
33
+ if (t.category === "admin")
34
+ admin++;
35
+ else if (t.category === "write")
36
+ write++;
37
+ else
38
+ read++;
39
+ }
40
+ return { read, write, admin };
41
+ }
42
+ //# sourceMappingURL=analyzer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analyzer.js","sourceRoot":"","sources":["../src/analyzer.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,GAAG;IAClB,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,aAAa;IACzD,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW;IACjD,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW;CACvD,CAAC;AAEF,MAAM,WAAW,GAAG;IAClB,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,cAAc;IACzD,YAAY,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc;IAC1D,YAAY,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW;IACpD,gBAAgB,EAAE,YAAY,EAAE,WAAW,EAAE,YAAY;CAC1D,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,IAAc;IAC3C,MAAM,IAAI,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;IAEhD,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC;QAC1C,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAiB;IAC5C,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1B,IAAI;QACJ,QAAQ,EAAE,cAAc,CAAC,IAAI,CAAC;KAC/B,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAAqB;IAC7C,IAAI,IAAI,GAAG,CAAC,CAAC;IACb,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,CAAC,IAAI,KAAK,EAAE,CAAC;QACtB,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,KAAK,EAAE,CAAC;aAC/B,IAAI,CAAC,CAAC,QAAQ,KAAK,OAAO;YAAE,KAAK,EAAE,CAAC;;YACpC,IAAI,EAAE,CAAC;IACd,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAChC,CAAC"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { CliOptions } from "./types.js";
2
+ export declare function parseArgs(argv: string[]): CliOptions | null;
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAgB,MAAM,YAAY,CAAC;AAgD3D,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,GAAG,IAAI,CAqG3D"}