@mcp-shark/mcp-shark 1.6.0 → 1.7.2
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 +140 -51
- package/core/cli/ListCommand.js +31 -0
- package/core/cli/data/rule-packs/aauth-visibility.json +117 -0
- package/core/configs/environment.js +3 -1
- package/core/configs/index.js +3 -64
- package/core/mcp-server/index.js +4 -1
- package/core/mcp-server/server/external/all.js +10 -3
- package/core/mcp-server/server/external/config.js +62 -5
- package/core/models/RequestFilters.js +3 -0
- package/core/services/ConfigService.js +9 -1
- package/core/services/ConfigTransformService.js +34 -2
- package/core/services/RequestService.js +58 -5
- package/core/services/ServerManagementService.js +59 -4
- package/core/services/security/aauthGraph.js +199 -0
- package/core/services/security/aauthParser.js +274 -0
- package/core/services/security/aauthSelfTest.js +346 -0
- package/package.json +2 -2
- package/ui/dist/assets/index-D6zDrtMV.js +81 -0
- package/ui/dist/index.html +1 -1
- package/ui/server/controllers/AauthController.js +279 -0
- package/ui/server/controllers/RequestController.js +3 -0
- package/ui/server/routes/aauth.js +18 -0
- package/ui/server/setup.js +222 -6
- package/ui/server/swagger/paths.js +0 -2
- package/ui/server/swagger/swagger.js +0 -1
- package/ui/server.js +1 -1
- package/ui/src/App.jsx +26 -52
- package/ui/src/PacketFilters.jsx +31 -1
- package/ui/src/PacketList.jsx +2 -2
- package/ui/src/TabNavigation.jsx +8 -0
- package/ui/src/components/AAuthBadge.jsx +92 -0
- package/ui/src/components/AauthExplorer/AauthExplorerGraph.jsx +231 -0
- package/ui/src/components/AauthExplorer/AauthExplorerView.jsx +387 -0
- package/ui/src/components/AauthExplorer/NodeDetailPanel.jsx +272 -0
- package/ui/src/components/App/ActionMenu.jsx +4 -31
- package/ui/src/components/App/ApiDocsButton.jsx +0 -1
- package/ui/src/components/App/ShutdownButton.jsx +0 -1
- package/ui/src/components/App/useAppState.js +19 -26
- package/ui/src/components/DetailsTab/AAuthIdentitySection.jsx +119 -0
- package/ui/src/components/DetailsTab/RequestDetailsSection.jsx +2 -0
- package/ui/src/components/DetailsTab/ResponseDetailsSection.jsx +2 -0
- package/ui/src/components/DetectedPathsList.jsx +1 -5
- package/ui/src/components/FileInput.jsx +0 -1
- package/ui/src/components/PacketFilters/AAuthPostureFilter.jsx +81 -0
- package/ui/src/components/RequestRow/RequestRowMain.jsx +7 -1
- package/ui/src/components/Security/AAuthPosturePanel.jsx +360 -0
- package/ui/src/components/Security/ScannerContent.jsx +3 -0
- package/ui/src/components/ServerControl.jsx +0 -1
- package/ui/src/components/TabNavigation/DesktopTabs.jsx +0 -11
- package/ui/src/components/TabNavigationIcons.jsx +5 -0
- package/ui/src/components/ViewModeTabs.jsx +0 -1
- package/ui/src/utils/animations.js +26 -9
- package/ui/dist/assets/index-Buah4fNS.js +0 -97
- package/ui/server/routes/help.js +0 -44
- package/ui/server/swagger/paths/help.js +0 -82
- package/ui/src/HelpGuide/HelpGuideContent.jsx +0 -118
- package/ui/src/HelpGuide/HelpGuideFooter.jsx +0 -59
- package/ui/src/HelpGuide/HelpGuideHeader.jsx +0 -57
- package/ui/src/HelpGuide.jsx +0 -78
- package/ui/src/IntroTour.jsx +0 -154
- package/ui/src/components/App/HelpButton.jsx +0 -90
- package/ui/src/components/TourOverlay.jsx +0 -117
- package/ui/src/components/TourTooltip/TourTooltipButtons.jsx +0 -120
- package/ui/src/components/TourTooltip/TourTooltipHeader.jsx +0 -71
- package/ui/src/components/TourTooltip/TourTooltipIcons.jsx +0 -54
- package/ui/src/components/TourTooltip/useTooltipPosition.js +0 -135
- package/ui/src/components/TourTooltip.jsx +0 -91
- package/ui/src/config/tourSteps.jsx +0 -140
package/README.md
CHANGED
|
@@ -4,21 +4,71 @@
|
|
|
4
4
|
|
|
5
5
|
<h1>mcp-shark</h1>
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
7
|
+
<div style="text-align: left; max-width: 42rem; margin: 0 auto;">
|
|
8
|
+
<p><strong>Security scanner for AI agent tools</strong> — built for security and platform engineers working with MCP in the IDE.</p>
|
|
9
|
+
<p>Run a local static <code>scan</code> over MCP <strong>IDE configs</strong> and embedded tool metadata: <strong>41 rules</strong> (including <strong>AAuth visibility</strong>), toxic-flow heuristics, and <strong>SARIF</strong> / <strong>HTML</strong> / <strong>JSON</strong> reports. There is no hosted config-scan backend.</p>
|
|
10
|
+
<p>Add an optional <strong>local HTTP proxy</strong> with an <strong>in-browser dashboard</strong> so live traffic, findings, AAuth signals, and playground checks stay in one place—without sending your configs to a vendor.</p>
|
|
11
|
+
<p><strong>You can</strong></p>
|
|
12
|
+
<ul style="margin: 0.35rem 0 0.75rem; padding-left: 1.25rem;">
|
|
13
|
+
<li>Use <strong>Traffic</strong> for live JSON-RPC capture, filters, export, and AAuth posture chips</li>
|
|
14
|
+
<li>Run <strong>Local Analysis</strong> for OWASP-style findings over captured traffic</li>
|
|
15
|
+
<li>Run <strong>YARA Detection</strong> for traffic pattern rules (native engine when installed, regex fallback otherwise)</li>
|
|
16
|
+
<li>Open <strong>AAuth Explorer</strong> for a graph of agents, missions, resources, and signing / access signals</li>
|
|
17
|
+
<li>Use <strong>MCP Playground</strong> to call tools, prompts, and resources through the proxy</li>
|
|
18
|
+
<li>Optionally run <strong>Smart Scan</strong> (AI-backed; uses <strong>your</strong> API token when enabled)</li>
|
|
19
|
+
<li>Use <strong>Server setup</strong> to detect configs, convert format, and route the editor through the proxy</li>
|
|
20
|
+
</ul>
|
|
21
|
+
<p><strong>Privacy:</strong> static scans need no cloud and send no telemetry. Refreshing rule catalogs is opt-in HTTPS (<code>update-rules</code>).</p>
|
|
22
|
+
</div>
|
|
9
23
|
|
|
10
24
|
[](https://www.npmjs.com/package/@mcp-shark/mcp-shark)
|
|
11
25
|
[](LICENSE)
|
|
12
26
|
|
|
13
27
|
</div>
|
|
14
28
|
|
|
15
|
-
|
|
29
|
+
## Dashboard at a glance
|
|
16
30
|
|
|
17
|
-
|
|
18
|
-
npx @mcp-shark/mcp-shark
|
|
19
|
-
```
|
|
31
|
+
These captures are from the live **dashboard** with **real captured traffic** (dummy MCP or your own upstreams). Start with `npx @mcp-shark/mcp-shark serve --open`. **Smart Scan** is not shown below — it depends on an optional remote API token. **MCP Playground** appears once you have at least one MCP upstream configured (the Playground capture uses a demo server with tools loaded).
|
|
20
32
|
|
|
21
|
-
|
|
33
|
+
### Live traffic capture
|
|
34
|
+
|
|
35
|
+
Every JSON-RPC frame between your IDE and each MCP upstream is captured with full headers, body, timing, and an AAuth posture chip. Filter by method, status, server, session, AAuth agent / mission / posture.
|
|
36
|
+
|
|
37
|
+

|
|
38
|
+
|
|
39
|
+
### MCP Playground
|
|
40
|
+
|
|
41
|
+
Pick an upstream, load **tools**, **prompts**, and **resources** from that server, then call tools or read resources through the proxy — useful for validating behavior before it hits your IDE. The view below shows the tools list for a configured demo MCP.
|
|
42
|
+
|
|
43
|
+

|
|
44
|
+
|
|
45
|
+
### AAuth Explorer
|
|
46
|
+
|
|
47
|
+
Force-directed knowledge graph of every Agent / Mission / Resource / Signing algorithm / Access mode observed across captured traffic. Use **Generate sample data** for a quick demo graph, or capture real AAuth-shaped traffic through the proxy.
|
|
48
|
+
|
|
49
|
+

|
|
50
|
+
|
|
51
|
+
### Local Analysis
|
|
52
|
+
|
|
53
|
+
Offline rule-based scanner over captured traffic. The **AAuth Posture** card summarizes signed / aauth-aware / bearer / no-auth distribution; the **Toxic flows (proxy traffic)** panel infers cross-server pairings from observed `tools/list` responses. With packets already in the database, use **Replay from DB** (when no live MCP is attached) and then **Analyse** to populate findings — the view below is after that run.
|
|
54
|
+
|
|
55
|
+

|
|
56
|
+
|
|
57
|
+
### YARA Detection
|
|
58
|
+
|
|
59
|
+
Same **Local Analysis** tab: switch to **YARA Detection** for the traffic rule engine — engine status, eight predefined rules (toggle, edit, delete), and **New Rule** for your own patterns. When the native `yara` module is not installed, scans still run using the built-in regex fallback (see [docs/local-analysis.md](docs/local-analysis.md)).
|
|
60
|
+
|
|
61
|
+

|
|
62
|
+
|
|
63
|
+
**New Rule** opens the editor with a starter template (meta, `strings`, and `condition`). Edit the rule text, then **Save Rule** to add it as a custom pattern alongside the built-ins.
|
|
64
|
+
|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
### Server setup
|
|
68
|
+
|
|
69
|
+
Auto-detects Cursor / Codex / Windsurf configs, converts them to mcp-shark format, and patches the IDE to route through the proxy on start.
|
|
70
|
+
|
|
71
|
+

|
|
22
72
|
|
|
23
73
|
## Why mcp-shark?
|
|
24
74
|
|
|
@@ -45,7 +95,7 @@ Use mcp-shark findings as input to your own threat model, not as a complete audi
|
|
|
45
95
|
|
|
46
96
|
| Feature | Description |
|
|
47
97
|
|---------|-------------|
|
|
48
|
-
| **
|
|
98
|
+
| **41 security rules** | OWASP MCP Top 10 + Agentic Security Initiative + AAuth visibility + general checks |
|
|
49
99
|
| **Toxic flow analysis** | Cross-server attack path detection from tool capability heuristics |
|
|
50
100
|
| **Attack walkthroughs** | Step-by-step exploit narratives from findings |
|
|
51
101
|
| **Shark Score** | Transparent security posture score (0-100, A-F) |
|
|
@@ -61,8 +111,9 @@ Use mcp-shark findings as input to your own threat model, not as a complete audi
|
|
|
61
111
|
| **YAML rules** | Per-project custom rules via `.mcp-shark/rules/` |
|
|
62
112
|
| **GitHub Action** | CI/CD integration with SARIF upload |
|
|
63
113
|
| **Interactive TUI** | lazygit-style terminal UI for scan, fix, and server browsing |
|
|
64
|
-
| **
|
|
114
|
+
| **Browser dashboard** | Live traffic, Local Analysis, YARA rules, AAuth Explorer, Playground, setup, and logs |
|
|
65
115
|
| **Proxy toxic flows** | Local Analysis panel + `GET/POST /api/security/traffic-toxic-flows*` infer cross-server pairs from captured **tools/list** traffic (see [docs/local-analysis.md](docs/local-analysis.md)) |
|
|
116
|
+
| **YARA-style traffic rules** | In **Local Analysis → YARA Detection**, enable or edit built-in pattern rules, add custom rules, and inspect engine status (native YARA when available, regex fallback otherwise) |
|
|
66
117
|
| **Local static scans** | No hosted scan backend; `update-rules` is opt-in HTTPS to the registry |
|
|
67
118
|
|
|
68
119
|
## Quick Start
|
|
@@ -106,7 +157,7 @@ npx @mcp-shark/mcp-shark scan --ci --format sarif
|
|
|
106
157
|
|
|
107
158
|
| Command | Description |
|
|
108
159
|
|---------|-------------|
|
|
109
|
-
| `scan` (default) | Run security scan with
|
|
160
|
+
| `scan` (default) | Run security scan with 41 rules |
|
|
110
161
|
| `lock` | Create `.mcp-shark.lock` file |
|
|
111
162
|
| `lock --verify` | Verify current state matches lockfile |
|
|
112
163
|
| `diff` | Show tool definition changes since last lock |
|
|
@@ -115,7 +166,7 @@ npx @mcp-shark/mcp-shark scan --ci --format sarif
|
|
|
115
166
|
| `update-rules` | Download latest rule packs from remote registry |
|
|
116
167
|
| `watch` | Watch config files and re-scan on changes |
|
|
117
168
|
| `tui` | Interactive terminal UI (lazygit-style) |
|
|
118
|
-
| `serve` | Start the
|
|
169
|
+
| `serve` | Start the local proxy and monitoring dashboard |
|
|
119
170
|
|
|
120
171
|
## CLI flags
|
|
121
172
|
|
|
@@ -160,12 +211,12 @@ mcp-shark is aimed at **config and metadata you already have on disk** (plus opt
|
|
|
160
211
|
| Area | Notes |
|
|
161
212
|
|------|--------|
|
|
162
213
|
| Install / run | Node.js 20+; `npx @mcp-shark/mcp-shark` |
|
|
163
|
-
| Security rules |
|
|
214
|
+
| Security rules | 41 checks — 30 declarative JSON packs, 11 JS where heuristics need code |
|
|
164
215
|
| Toxic flow analysis | Heuristic cross-server paths; quality depends on embedded `tools` / classifications |
|
|
165
216
|
| Attack walkthroughs | Narratives derived from findings |
|
|
166
217
|
| Auto-fix | Supported for a subset of issues; confirm changes in your repo |
|
|
167
218
|
| Tool pinning | `.mcp-shark.lock` with SHA-256 hashes |
|
|
168
|
-
| Live traffic |
|
|
219
|
+
| Live traffic | Dashboard (`serve`) for monitoring; separate from static `scan` |
|
|
169
220
|
| Custom rules | YAML under `.mcp-shark/rules/` and JSON rule packs |
|
|
170
221
|
| Findings & score | confirmed / advisory tiers plus Shark Score (0–100, A–F) |
|
|
171
222
|
| IDE configs | 15 built-in paths + project-local `mcp.json` variants — see [Supported IDEs](#supported-ides) |
|
|
@@ -181,7 +232,7 @@ mcp-shark is aimed at **config and metadata you already have on disk** (plus opt
|
|
|
181
232
|
|
|
182
233
|
The canonical **registry** (manifest, pack files, validation CI, and schema notes) lives in **[mcp-shark/rule-packs](https://github.com/mcp-shark/rule-packs)**. The npm package embeds copies; `update-rules` pulls the same artifacts into `.mcp-shark/rule-packs/`.
|
|
183
234
|
|
|
184
|
-
mcp-shark ships with
|
|
235
|
+
mcp-shark ships with 30 declarative rules as JSON packs (OWASP MCP, Agentic Security Initiative, General Security, AAuth Visibility), plus a **`toxic-flow-heuristics`** pack (`toxic_flow_rules` for cross-server composition). New vulnerability catalogs can be added as `.json` files — no JavaScript, no code changes.
|
|
185
236
|
|
|
186
237
|
```bash
|
|
187
238
|
# Fetch latest rule packs from the registry
|
|
@@ -294,38 +345,48 @@ jobs:
|
|
|
294
345
|
| Roo Code | `~/.roo-code/mcp.json` | ✅ |
|
|
295
346
|
| Project (local) | `./mcp.json`, `./.mcp.json`, `./.mcp/config.json` | ✅ |
|
|
296
347
|
|
|
297
|
-
## Security Rules (
|
|
348
|
+
## Security Rules (41)
|
|
298
349
|
|
|
299
350
|
<details>
|
|
300
351
|
<summary>Full rule list</summary>
|
|
301
352
|
|
|
302
353
|
### OWASP MCP Top 10
|
|
303
|
-
| ID | Rule | Severity |
|
|
304
|
-
|
|
305
|
-
| MCP01 | Token Mismanagement | Critical |
|
|
306
|
-
| MCP02 | Scope Creep | High |
|
|
307
|
-
| MCP03 | Tool Poisoning | Critical |
|
|
308
|
-
| MCP04 | Supply Chain | High |
|
|
309
|
-
| MCP05 | Command Injection | Critical |
|
|
310
|
-
| MCP06 | Prompt Injection | High |
|
|
311
|
-
| MCP07 | Insufficient Auth | High |
|
|
312
|
-
| MCP08 | Lack of Audit | Medium |
|
|
313
|
-
| MCP09 | Shadow Servers | High |
|
|
314
|
-
| MCP10 | Context Injection | High |
|
|
354
|
+
| ID | Rule | Severity | Source |
|
|
355
|
+
|----|------|----------|--------|
|
|
356
|
+
| MCP01 | Token Mismanagement | Critical | declarative |
|
|
357
|
+
| MCP02 | Scope Creep | High | declarative |
|
|
358
|
+
| MCP03 | Tool Poisoning | Critical | declarative |
|
|
359
|
+
| MCP04 | Supply Chain | High | declarative |
|
|
360
|
+
| MCP05 | Command Injection | Critical | JS plugin |
|
|
361
|
+
| MCP06 | Prompt Injection | High | declarative |
|
|
362
|
+
| MCP07 | Insufficient Auth | High | declarative |
|
|
363
|
+
| MCP08 | Lack of Audit | Medium | declarative |
|
|
364
|
+
| MCP09 | Shadow Servers | High | declarative |
|
|
365
|
+
| MCP10 | Context Injection | High | declarative |
|
|
315
366
|
|
|
316
367
|
### Agentic Security Initiative (ASI)
|
|
317
|
-
| ID | Rule | Severity |
|
|
318
|
-
|
|
319
|
-
| ASI01 | Goal Hijack | Critical |
|
|
320
|
-
| ASI02 | Tool Misuse | High |
|
|
321
|
-
| ASI03 | Identity Abuse | High |
|
|
322
|
-
| ASI04 | Supply Chain | High |
|
|
323
|
-
| ASI05 | Remote Code Execution | Critical |
|
|
324
|
-
| ASI06 | Memory Poisoning | High |
|
|
325
|
-
| ASI07 | Insecure Communication | Medium |
|
|
326
|
-
| ASI08 | Cascading Failures | Medium |
|
|
327
|
-
| ASI09 | Trust Exploitation | High |
|
|
328
|
-
| ASI10 | Rogue Agent | Critical |
|
|
368
|
+
| ID | Rule | Severity | Source |
|
|
369
|
+
|----|------|----------|--------|
|
|
370
|
+
| ASI01 | Goal Hijack | Critical | declarative |
|
|
371
|
+
| ASI02 | Tool Misuse | High | declarative |
|
|
372
|
+
| ASI03 | Identity Abuse | High | declarative |
|
|
373
|
+
| ASI04 | Supply Chain | High | declarative |
|
|
374
|
+
| ASI05 | Remote Code Execution | Critical | JS plugin |
|
|
375
|
+
| ASI06 | Memory Poisoning | High | declarative |
|
|
376
|
+
| ASI07 | Insecure Communication | Medium | declarative |
|
|
377
|
+
| ASI08 | Cascading Failures | Medium | declarative |
|
|
378
|
+
| ASI09 | Trust Exploitation | High | declarative |
|
|
379
|
+
| ASI10 | Rogue Agent | Critical | declarative |
|
|
380
|
+
|
|
381
|
+
### AAuth Visibility (informational)
|
|
382
|
+
| ID | Description | Severity |
|
|
383
|
+
|----|-------------|----------|
|
|
384
|
+
| `aauth-agent-identity-observed` | `aauth:<local>@<domain>` agent identity in tool/prompt/resource/packet | Low |
|
|
385
|
+
| `aauth-jwks-discovery-url` | URLs containing `/.well-known/aauth` or `/jwks` | Low |
|
|
386
|
+
| `aauth-http-message-signature-observed` | RFC 9421 `Signature-Input` / `Signature` headers in captured traffic | Low |
|
|
387
|
+
| `aauth-mission-context-observed` | `AAuth-Mission` headers in captured traffic | Low |
|
|
388
|
+
| `aauth-requirement-challenge-observed` | `AAuth-Requirement` response headers (resource asking for AAuth) | Low |
|
|
389
|
+
| `aauth-bearer-token-coexists-with-aauth` | Same packet has both Bearer token and AAuth signature | Medium |
|
|
329
390
|
|
|
330
391
|
### General Security
|
|
331
392
|
| Rule | Severity |
|
|
@@ -348,9 +409,9 @@ jobs:
|
|
|
348
409
|
|
|
349
410
|
</details>
|
|
350
411
|
|
|
351
|
-
##
|
|
412
|
+
## Browser dashboard
|
|
352
413
|
|
|
353
|
-
MCP Shark
|
|
414
|
+
MCP Shark ships an **in-browser dashboard** on the local proxy for real-time MCP traffic, analysis, and exploration:
|
|
354
415
|
|
|
355
416
|
```bash
|
|
356
417
|
npx @mcp-shark/mcp-shark serve --open
|
|
@@ -362,11 +423,23 @@ Same as the older shortcut (no `serve` subcommand):
|
|
|
362
423
|
npx @mcp-shark/mcp-shark --open
|
|
363
424
|
```
|
|
364
425
|
|
|
365
|
-
The
|
|
366
|
-
- Multi-server aggregation and real-time
|
|
367
|
-
-
|
|
368
|
-
- Local
|
|
369
|
-
-
|
|
426
|
+
The dashboard provides:
|
|
427
|
+
- Multi-server aggregation and real-time traffic capture (filters, export, AAuth posture chips)
|
|
428
|
+
- **MCP Playground** — call tools, prompts, and resources through the proxy against a selected upstream
|
|
429
|
+
- **Local Analysis** — OWASP-style static scan over captured traffic; **YARA Detection** for traffic pattern rules (native engine when installed, regex fallback otherwise)
|
|
430
|
+
- **AAuth Explorer** — graph of Agent / Mission / Resource / signing / access signals observed in traffic
|
|
431
|
+
- **Smart Scan** — optional AI-backed scan (requires a configured API token)
|
|
432
|
+
- In-app API docs, server setup, logs, and graceful shutdown
|
|
433
|
+
|
|
434
|
+
### Zero-touch first boot
|
|
435
|
+
|
|
436
|
+
The dashboard bootstraps itself the first time you launch it on a new machine — no Setup wizard click required:
|
|
437
|
+
|
|
438
|
+
1. If `~/.mcp-shark/mcps.json` already declares upstreams (e.g. from a previous run, hand-edit, or `testbed:up`), the proxy starts directly with that config.
|
|
439
|
+
2. Otherwise, on a brand-new install (no `~/.mcp-shark` at all), MCP Shark scans for a real editor MCP config (`~/.cursor/mcp.json`, `~/.codeium/windsurf/mcp_config.json`, `~/.codex/config.toml`). If one is found with actual upstreams, it auto-imports them, writes `~/.mcp-shark/mcps.json`, starts the proxy, and patches the editor config so the editor routes through the proxy.
|
|
440
|
+
3. If neither path applies, the UI starts in monitoring-only mode and the Setup panel is still available for manual configuration.
|
|
441
|
+
|
|
442
|
+
To re-trigger first-boot behavior on a machine, remove `~/.mcp-shark/` and restart the UI.
|
|
370
443
|
|
|
371
444
|
## Architecture
|
|
372
445
|
|
|
@@ -377,24 +450,24 @@ The web UI provides:
|
|
|
377
450
|
│ update-rules · serve │
|
|
378
451
|
├──────────────┬──────────────┬──────────────────────┤
|
|
379
452
|
│ ConfigScanner│ ScanService │ StaticRulesService │
|
|
380
|
-
│ 15 IDEs │ orchestrator │
|
|
453
|
+
│ 15 IDEs │ orchestrator │ 41 rules │
|
|
381
454
|
├──────────────┴──────────────┴──────────────────────┤
|
|
382
455
|
│ Data layer (JSON + user YAML/JSON overrides) │
|
|
383
456
|
│ ┌────────────┬──────────────┬───────────────────┐ │
|
|
384
457
|
│ │ rule-packs │ secret- │ tool- │ │
|
|
385
|
-
│ │ (
|
|
458
|
+
│ │ (30 rules) │ patterns.json│ classifications │ │
|
|
386
459
|
│ ├────────────┼──────────────┼───────────────────┤ │
|
|
387
460
|
│ │ toxic-flow │ rule- │ .mcp-shark/*.yaml │ │
|
|
388
461
|
│ │ rules.json │ sources.json │ (user overrides) │ │
|
|
389
462
|
│ └────────────┴──────────────┴───────────────────┘ │
|
|
390
463
|
├────────────────────────────────────────────────────┤
|
|
391
464
|
│ JS plugins (11 rules needing algorithmic logic) │
|
|
392
|
-
│ + DeclarativeRuleEngine (
|
|
465
|
+
│ + DeclarativeRuleEngine (30 pattern-based rules) │
|
|
393
466
|
└────────────────────────────────────────────────────┘
|
|
394
467
|
```
|
|
395
468
|
|
|
396
469
|
**Design principles:**
|
|
397
|
-
- **Data-first** — Declarative rules, secret patterns, tool classifications, and toxic-flow defaults ship as JSON; **
|
|
470
|
+
- **Data-first** — Declarative rules, secret patterns, tool classifications, and toxic-flow defaults ship as JSON; **30** of **41** rules are pattern packs you can extend or override without forking those definitions.
|
|
398
471
|
- **User-overridable** — Built-in data can be extended via `.mcp-shark/*.yaml` (and JSON pack drops) as documented above.
|
|
399
472
|
- **Hybrid rule engine** — The other **11** rules are JS plugins where heuristics need code. Both sources are merged at scan time.
|
|
400
473
|
- **Zero-config scanning** — `npx` and go. Auto-detects the IDE paths below plus project-local `mcp.json` variants.
|
|
@@ -405,9 +478,15 @@ The web UI provides:
|
|
|
405
478
|
- **[Getting Started](docs/getting-started.md)** — Installation & setup
|
|
406
479
|
- **[Features](docs/features.md)** — Detailed feature documentation
|
|
407
480
|
- **[User Guide](docs/user-guide.md)** — Complete usage guide
|
|
408
|
-
- **[
|
|
481
|
+
- **[Configuration](docs/configuration.md)** — Configuration files & environment variables
|
|
482
|
+
- **[Local Analysis](docs/local-analysis.md)** — Static security analysis & YARA traffic rules
|
|
483
|
+
- **[AAuth Visibility](docs/aauth-visibility.md)** — RFC 9421 / AAuth observability
|
|
409
484
|
- **[Architecture](docs/architecture.md)** — System design
|
|
485
|
+
- **[Database Architecture](docs/database-architecture.md)** — SQLite schema reference
|
|
410
486
|
- **[API Reference](docs/api-reference.md)** — API endpoints
|
|
487
|
+
- **[Troubleshooting](docs/troubleshooting.md)** — Common issues & fixes
|
|
488
|
+
- **[Development](docs/development.md)** — Contributing & project conventions
|
|
489
|
+
- **[Package inspection](docs/package-inspection.md)** — npm package layout
|
|
411
490
|
|
|
412
491
|
## Requirements
|
|
413
492
|
|
|
@@ -423,6 +502,16 @@ Source-Available Non-Commercial License
|
|
|
423
502
|
|
|
424
503
|
See [LICENSE](LICENSE) for full terms.
|
|
425
504
|
|
|
505
|
+
## CLI demo
|
|
506
|
+
|
|
507
|
+
Same one-liner as **[Quick Start](#quick-start)** (default `scan`). Terminal output depends on your config:
|
|
508
|
+
|
|
509
|
+
```bash
|
|
510
|
+
npx @mcp-shark/mcp-shark
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+

|
|
514
|
+
|
|
426
515
|
## Support
|
|
427
516
|
|
|
428
517
|
- **Issues**: [GitHub Issues](https://github.com/mcp-shark/mcp-shark/issues)
|
package/core/cli/ListCommand.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import Table from 'cli-table3';
|
|
7
7
|
import kleur from 'kleur';
|
|
8
|
+
import { inspectServerConfigForAauth } from '#core/services/security/aauthParser.js';
|
|
8
9
|
import { getAllServers, scanIdeConfigs } from './ConfigScanner.js';
|
|
9
10
|
import { TOOL_CLASSIFICATIONS } from './ToolClassifications.js';
|
|
10
11
|
import { S } from './symbols.js';
|
|
@@ -91,11 +92,40 @@ function renderTerminalInventory(servers, ideResults) {
|
|
|
91
92
|
console.log(table.toString());
|
|
92
93
|
console.log('');
|
|
93
94
|
|
|
95
|
+
renderAauthSummary(servers);
|
|
94
96
|
renderIdeSummary(ideResults);
|
|
95
97
|
|
|
96
98
|
return 0;
|
|
97
99
|
}
|
|
98
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Print a single-line AAuth advertisement summary across all detected servers.
|
|
103
|
+
* Visibility-only: this only checks for AAuth-shaped fields in the static config
|
|
104
|
+
* (agent IDs, JWKS URLs, .well-known/aauth references). It does not connect.
|
|
105
|
+
*/
|
|
106
|
+
function renderAauthSummary(servers) {
|
|
107
|
+
let advertised = 0;
|
|
108
|
+
for (const server of servers) {
|
|
109
|
+
if (inspectServerConfigForAauth(server.config)) {
|
|
110
|
+
advertised += 1;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
const remaining = servers.length - advertised;
|
|
114
|
+
if (servers.length === 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
console.log(kleur.bold(' AAuth Visibility'));
|
|
118
|
+
console.log(
|
|
119
|
+
` ${kleur.green(S.pass)} ${advertised} server${advertised === 1 ? '' : 's'} advertise AAuth · ${kleur.dim(`${remaining} do not`)}`
|
|
120
|
+
);
|
|
121
|
+
console.log(
|
|
122
|
+
kleur.dim(
|
|
123
|
+
' Observed only — mcp-shark does not verify AAuth identity. See https://www.aauth.dev'
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
console.log('');
|
|
127
|
+
}
|
|
128
|
+
|
|
99
129
|
/**
|
|
100
130
|
* Render a summary of which IDEs were detected
|
|
101
131
|
*/
|
|
@@ -128,6 +158,7 @@ function renderJsonInventory(servers, ideResults) {
|
|
|
128
158
|
tool_count: getToolCount(s),
|
|
129
159
|
command: s.config?.command || null,
|
|
130
160
|
args_present: hasArgsPresent(s.config),
|
|
161
|
+
aauth: inspectServerConfigForAauth(s.config),
|
|
131
162
|
})),
|
|
132
163
|
};
|
|
133
164
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": "1.0",
|
|
3
|
+
"pack_id": "aauth-visibility",
|
|
4
|
+
"pack_name": "AAuth Visibility",
|
|
5
|
+
"version": "1.0.0",
|
|
6
|
+
"updated": "2026-04-26",
|
|
7
|
+
"source_url": "https://github.com/dickhardt/AAuth",
|
|
8
|
+
"rules": [
|
|
9
|
+
{
|
|
10
|
+
"id": "aauth-agent-identity-observed",
|
|
11
|
+
"name": "AAuth Agent Identity Observed",
|
|
12
|
+
"owasp_id": "INFO",
|
|
13
|
+
"severity": "low",
|
|
14
|
+
"type": "aauth-visibility",
|
|
15
|
+
"description": "An AAuth agent identifier (aauth:local@domain) was observed in metadata. This is informational only — mcp-shark does not verify cryptographic identity.",
|
|
16
|
+
"recommendation": "Confirm the agent identifier is expected for this server. See https://www.aauth.dev for background on AAuth agent identities.",
|
|
17
|
+
"scope": ["tool", "prompt", "resource", "packet"],
|
|
18
|
+
"match_mode": "first",
|
|
19
|
+
"patterns": [
|
|
20
|
+
{
|
|
21
|
+
"regex": "aauth:[A-Za-z0-9._-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}",
|
|
22
|
+
"label": "AAuth agent ID",
|
|
23
|
+
"flags": ""
|
|
24
|
+
}
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "aauth-jwks-discovery-url",
|
|
29
|
+
"name": "AAuth JWKS or Well-Known URL Observed",
|
|
30
|
+
"owasp_id": "INFO",
|
|
31
|
+
"severity": "low",
|
|
32
|
+
"type": "aauth-visibility",
|
|
33
|
+
"description": "A JWKS or .well-known/aauth URL was observed. This typically indicates the resource is AAuth-aware.",
|
|
34
|
+
"recommendation": "If this URL is unexpected, investigate which agent or resource is publishing it.",
|
|
35
|
+
"scope": ["tool", "prompt", "resource", "packet"],
|
|
36
|
+
"match_mode": "first",
|
|
37
|
+
"patterns": [
|
|
38
|
+
{ "regex": "/\\.well-known/aauth", "label": ".well-known/aauth path", "flags": "i" },
|
|
39
|
+
{ "regex": "https?://[^\\s\"']+/jwks(?:\\.json)?", "label": "JWKS URL", "flags": "i" }
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"id": "aauth-http-message-signature-observed",
|
|
44
|
+
"name": "HTTP Message Signature Observed",
|
|
45
|
+
"owasp_id": "INFO",
|
|
46
|
+
"severity": "low",
|
|
47
|
+
"type": "aauth-visibility",
|
|
48
|
+
"description": "An RFC 9421 HTTP Message Signature appears in captured traffic. mcp-shark records this as observed only — it does not verify the signature.",
|
|
49
|
+
"recommendation": "Use an AAuth-capable verifier to confirm signature validity. mcp-shark's role here is observability.",
|
|
50
|
+
"scope": ["packet"],
|
|
51
|
+
"match_mode": "first",
|
|
52
|
+
"patterns": [
|
|
53
|
+
{
|
|
54
|
+
"regex": "\"signature-input\"\\s*:\\s*\"[^\"]+\"",
|
|
55
|
+
"label": "Signature-Input header",
|
|
56
|
+
"flags": "i"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"regex": "\"signature\"\\s*:\\s*\"[^\"]*=:[A-Za-z0-9+/=]+:\"",
|
|
60
|
+
"label": "Signature header (sf-binary)",
|
|
61
|
+
"flags": "i"
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
"id": "aauth-mission-context-observed",
|
|
67
|
+
"name": "AAuth Mission Context Observed",
|
|
68
|
+
"owasp_id": "INFO",
|
|
69
|
+
"severity": "low",
|
|
70
|
+
"type": "aauth-visibility",
|
|
71
|
+
"description": "An AAuth-Mission header was observed in captured traffic. Missions group calls under a single user-consented scope.",
|
|
72
|
+
"recommendation": "Use the mission timeline view in the UI to inspect related calls under the same mission.",
|
|
73
|
+
"scope": ["packet"],
|
|
74
|
+
"match_mode": "first",
|
|
75
|
+
"patterns": [
|
|
76
|
+
{ "regex": "\"aauth-mission\"\\s*:", "label": "AAuth-Mission header", "flags": "i" }
|
|
77
|
+
]
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
"id": "aauth-requirement-challenge-observed",
|
|
81
|
+
"name": "AAuth Requirement Challenge Observed",
|
|
82
|
+
"owasp_id": "INFO",
|
|
83
|
+
"severity": "low",
|
|
84
|
+
"type": "aauth-visibility",
|
|
85
|
+
"description": "An AAuth-Requirement response header was observed. The resource is asking the client to upgrade to AAuth.",
|
|
86
|
+
"recommendation": "If you control the calling agent, consider upgrading to AAuth — see https://www.aauth.dev.",
|
|
87
|
+
"scope": ["packet"],
|
|
88
|
+
"match_mode": "first",
|
|
89
|
+
"patterns": [
|
|
90
|
+
{ "regex": "\"aauth-requirement\"\\s*:", "label": "AAuth-Requirement header", "flags": "i" }
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"id": "aauth-bearer-token-coexists-with-aauth",
|
|
95
|
+
"name": "Bearer Token Coexists With AAuth Headers",
|
|
96
|
+
"owasp_id": "MCP01",
|
|
97
|
+
"severity": "medium",
|
|
98
|
+
"type": "aauth-visibility",
|
|
99
|
+
"description": "A request carries both a Bearer token and AAuth identity headers. AAuth replaces bearer credentials — coexisting both is the worst-of-both-worlds pattern called out by the AAuth project.",
|
|
100
|
+
"recommendation": "Migrate the calling agent fully to AAuth; remove the long-lived bearer token once signed identity is in place.",
|
|
101
|
+
"scope": ["packet"],
|
|
102
|
+
"match_mode": "all_matches",
|
|
103
|
+
"patterns": [
|
|
104
|
+
{
|
|
105
|
+
"regex": "\"authorization\"\\s*:\\s*\"Bearer\\s+[^\"]+\"[\\s\\S]*\"signature-input\"",
|
|
106
|
+
"label": "Bearer + Signature-Input",
|
|
107
|
+
"flags": "i"
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"regex": "\"signature-input\"[\\s\\S]*\"authorization\"\\s*:\\s*\"Bearer",
|
|
111
|
+
"label": "Signature-Input + Bearer",
|
|
112
|
+
"flags": "i"
|
|
113
|
+
}
|
|
114
|
+
]
|
|
115
|
+
}
|
|
116
|
+
]
|
|
117
|
+
}
|
|
@@ -12,10 +12,12 @@ export const Environment = {
|
|
|
12
12
|
},
|
|
13
13
|
/**
|
|
14
14
|
* Get UI server port
|
|
15
|
+
* Honours UI_PORT first, then the documented MCP_SHARK_PORT alias.
|
|
15
16
|
* @returns {number} UI server port (default: 9853)
|
|
16
17
|
*/
|
|
17
18
|
getUiPort() {
|
|
18
|
-
const
|
|
19
|
+
const raw = process.env.UI_PORT ?? process.env.MCP_SHARK_PORT;
|
|
20
|
+
const port = Number.parseInt(raw, 10);
|
|
19
21
|
return Number.isNaN(port) ? Server.DEFAULT_UI_PORT : port;
|
|
20
22
|
},
|
|
21
23
|
|
package/core/configs/index.js
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import { existsSync, mkdirSync,
|
|
2
|
-
import { homedir } from 'node:os';
|
|
1
|
+
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
3
2
|
import { dirname, join } from 'node:path';
|
|
3
|
+
import { Environment } from './environment.js';
|
|
4
4
|
|
|
5
5
|
export { Environment } from './environment.js';
|
|
6
6
|
|
|
7
|
-
const WORKING_DIRECTORY_NAME = '.mcp-shark';
|
|
8
7
|
const MCP_CONFIG_NAME = 'mcps.json';
|
|
9
8
|
const APP_DB_DIR_NAME = 'db';
|
|
10
9
|
const APP_DB_FILE_NAME = 'mcp-shark.sqlite';
|
|
11
|
-
const HELP_STATE_NAME = 'help-state.json';
|
|
12
10
|
|
|
13
11
|
export function getWorkingDirectory() {
|
|
14
|
-
return
|
|
12
|
+
return Environment.getMcpSharkHome();
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
export function getDatabasePath() {
|
|
@@ -61,62 +59,3 @@ export function ensureDirectoryExists(filePath) {
|
|
|
61
59
|
mkdirSync(dir, { recursive: true });
|
|
62
60
|
}
|
|
63
61
|
}
|
|
64
|
-
|
|
65
|
-
export function getHelpStatePath() {
|
|
66
|
-
return join(getWorkingDirectory(), HELP_STATE_NAME);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function readHelpState() {
|
|
70
|
-
try {
|
|
71
|
-
const helpStatePath = getHelpStatePath();
|
|
72
|
-
if (existsSync(helpStatePath)) {
|
|
73
|
-
const content = readFileSync(helpStatePath, 'utf8');
|
|
74
|
-
const state = JSON.parse(content);
|
|
75
|
-
// Ensure we have the expected structure
|
|
76
|
-
return {
|
|
77
|
-
dismissed: state.dismissed || false,
|
|
78
|
-
tourCompleted: state.tourCompleted || false,
|
|
79
|
-
dismissedAt: state.dismissedAt || null,
|
|
80
|
-
version: state.version || '1.0.0',
|
|
81
|
-
};
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
dismissed: false,
|
|
85
|
-
tourCompleted: false,
|
|
86
|
-
dismissedAt: null,
|
|
87
|
-
version: '1.0.0',
|
|
88
|
-
};
|
|
89
|
-
} catch (_error) {
|
|
90
|
-
// Error reading help state - return defaults
|
|
91
|
-
return {
|
|
92
|
-
dismissed: false,
|
|
93
|
-
tourCompleted: false,
|
|
94
|
-
dismissedAt: null,
|
|
95
|
-
version: '1.0.0',
|
|
96
|
-
};
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function writeHelpState(state) {
|
|
101
|
-
try {
|
|
102
|
-
const helpStatePath = getHelpStatePath();
|
|
103
|
-
prepareAppDataSpaces(); // Ensure directory exists
|
|
104
|
-
|
|
105
|
-
// Merge with existing state to preserve other fields
|
|
106
|
-
const existingState = readHelpState();
|
|
107
|
-
const newState = {
|
|
108
|
-
...existingState,
|
|
109
|
-
...state,
|
|
110
|
-
dismissedAt:
|
|
111
|
-
state.dismissed || state.tourCompleted
|
|
112
|
-
? new Date().toISOString()
|
|
113
|
-
: existingState.dismissedAt,
|
|
114
|
-
version: '1.0.0',
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
writeFileSync(helpStatePath, JSON.stringify(newState, null, 2));
|
|
118
|
-
return true;
|
|
119
|
-
} catch (_error) {
|
|
120
|
-
return false;
|
|
121
|
-
}
|
|
122
|
-
}
|
package/core/mcp-server/index.js
CHANGED
|
@@ -142,7 +142,10 @@ export async function startMcpSharkServer(options = {}) {
|
|
|
142
142
|
throw new Error('auditLogger is required. Call initAuditLogger() and pass it in options.');
|
|
143
143
|
}
|
|
144
144
|
const auditLogger = providedAuditLogger;
|
|
145
|
-
const externalServersResult = await runAllExternalServers(serverLogger, configPath
|
|
145
|
+
const externalServersResult = await runAllExternalServers(serverLogger, configPath, {
|
|
146
|
+
selfPort: port,
|
|
147
|
+
allowEmpty: true,
|
|
148
|
+
});
|
|
146
149
|
|
|
147
150
|
if (isError(externalServersResult)) {
|
|
148
151
|
serverLogger.error(
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CompositeError, getErrors } from '#core/libraries/ErrorLibrary.js';
|
|
1
|
+
import { CompositeError, getErrors, isError } from '#core/libraries/ErrorLibrary.js';
|
|
2
2
|
import { normalizeConfig } from './config.js';
|
|
3
3
|
import { buildKv } from './kv.js';
|
|
4
4
|
import { runExternalServer } from './single/run.js';
|
|
@@ -10,8 +10,15 @@ export class RunAllExternalServersError extends CompositeError {
|
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export async function runAllExternalServers(logger, parsedConfig) {
|
|
14
|
-
const configs = normalizeConfig(parsedConfig);
|
|
13
|
+
export async function runAllExternalServers(logger, parsedConfig, options = {}) {
|
|
14
|
+
const configs = normalizeConfig(parsedConfig, { ...options, logger });
|
|
15
|
+
if (isError(configs)) {
|
|
16
|
+
return new RunAllExternalServersError(
|
|
17
|
+
`Failed to normalize upstream config: ${configs.message}`,
|
|
18
|
+
configs,
|
|
19
|
+
[configs]
|
|
20
|
+
);
|
|
21
|
+
}
|
|
15
22
|
const results = await Promise.all(
|
|
16
23
|
Object.entries(configs).map(([name, config]) => runExternalServer({ logger, name, config }))
|
|
17
24
|
);
|