shieldpi-mcp 0.1.0__tar.gz
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.
- shieldpi_mcp-0.1.0/.gitignore +12 -0
- shieldpi_mcp-0.1.0/LICENSE +21 -0
- shieldpi_mcp-0.1.0/PKG-INFO +212 -0
- shieldpi_mcp-0.1.0/README.md +176 -0
- shieldpi_mcp-0.1.0/SUBMISSION.md +112 -0
- shieldpi_mcp-0.1.0/examples/claude-code.sh +21 -0
- shieldpi_mcp-0.1.0/examples/claude-desktop.json +10 -0
- shieldpi_mcp-0.1.0/examples/cursor-mcp.json +10 -0
- shieldpi_mcp-0.1.0/pyproject.toml +69 -0
- shieldpi_mcp-0.1.0/shieldpi_mcp/__init__.py +3 -0
- shieldpi_mcp-0.1.0/shieldpi_mcp/__main__.py +6 -0
- shieldpi_mcp-0.1.0/shieldpi_mcp/client.py +104 -0
- shieldpi_mcp-0.1.0/shieldpi_mcp/server.py +251 -0
- shieldpi_mcp-0.1.0/tests/test_server.py +71 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ShieldPi (support@shieldpi.io)
|
|
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.
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: shieldpi-mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: MCP server for ShieldPi Watchtower — query 27,000+ LLM attack techniques, run scans, fetch breach forensics from any MCP-compatible client (Claude Desktop, Claude Code, Cursor).
|
|
5
|
+
Project-URL: Homepage, https://shieldpi.io
|
|
6
|
+
Project-URL: Documentation, https://github.com/ShieldPi1/shieldpi-watchtower/tree/main/mcp-server
|
|
7
|
+
Project-URL: Repository, https://github.com/ShieldPi1/shieldpi-watchtower
|
|
8
|
+
Project-URL: Issues, https://github.com/ShieldPi1/shieldpi-watchtower/issues
|
|
9
|
+
Project-URL: Leaderboard, https://shieldpi.info
|
|
10
|
+
Project-URL: Methodology, https://shieldpi.io/methodology
|
|
11
|
+
Author-email: ShieldPi <support@shieldpi.io>
|
|
12
|
+
License: MIT
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: ai-security,anthropic,claude,jailbreak,llm-security,mcp,owasp,prompt-injection,red-team
|
|
15
|
+
Classifier: Development Status :: 4 - Beta
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Intended Audience :: Information Technology
|
|
18
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Programming Language :: Python :: 3
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Topic :: Security
|
|
25
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
26
|
+
Requires-Python: >=3.10
|
|
27
|
+
Requires-Dist: httpx>=0.27.0
|
|
28
|
+
Requires-Dist: mcp>=1.2.0
|
|
29
|
+
Requires-Dist: pydantic>=2.0.0
|
|
30
|
+
Provides-Extra: dev
|
|
31
|
+
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# shieldpi-mcp
|
|
38
|
+
|
|
39
|
+
> **MCP server for [ShieldPi Watchtower](https://shieldpi.io)** — query 27,000+ LLM attack techniques, run scans, and pull breach forensics from any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Continue).
|
|
40
|
+
|
|
41
|
+
[](https://pypi.org/project/shieldpi-mcp/)
|
|
42
|
+
[](LICENSE)
|
|
43
|
+
[](https://shieldpi.info)
|
|
44
|
+
|
|
45
|
+
ShieldPi is the security forensics platform for LLMs and agents. It runs **27,024+ attack techniques** across 4 scan modes (Browser / API / Agent / Model), classifies findings on the **ExploitDepth L1–L4 scale**, and produces a forensic kill-chain narrative + extracted breach evidence (credentials, PII, code, tools, blast radius) for every successful attack.
|
|
46
|
+
|
|
47
|
+
This MCP server lets you talk to ShieldPi from inside any LLM session.
|
|
48
|
+
|
|
49
|
+
## What you get
|
|
50
|
+
|
|
51
|
+
| Tier | Tool | API key? | Use it for |
|
|
52
|
+
|------|------|----------|-----------|
|
|
53
|
+
| 1 | `get_methodology` | no | Pull the V7 scoring + dedup + judge methodology |
|
|
54
|
+
| 1 | `get_attack_graph` | no | Cross-customer success-rate graph by technique × model family |
|
|
55
|
+
| 1 | `get_model_families` | no | ShieldPi's family taxonomy + similarity edges |
|
|
56
|
+
| 1 | `get_leaderboard_feed` | no | Live data behind shieldpi.info |
|
|
57
|
+
| 1 | `get_leaderboard` | no | Top models by best security score |
|
|
58
|
+
| 1 | `get_model_registry` | no | All 38+ models ShieldPi can test |
|
|
59
|
+
| 2 | `list_attack_categories` | yes | The 15 categories + OWASP mappings |
|
|
60
|
+
| 2 | `list_attack_techniques` | yes | Browse the 27k catalog with filters |
|
|
61
|
+
| 2 | `start_scan` | yes | Kick off a scan against a target |
|
|
62
|
+
| 2 | `get_scan_intelligence` | yes | Pull the breach-forensics package for a scan |
|
|
63
|
+
|
|
64
|
+
Tier-1 works the moment you install. Tier-2 needs a free ShieldPi API key — get one at [shieldpi.io/dashboard/api-keys](https://shieldpi.io/dashboard/api-keys).
|
|
65
|
+
|
|
66
|
+
## Install
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
pip install shieldpi-mcp
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or with `uv`:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv pip install shieldpi-mcp
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The package ships a `shieldpi-mcp` console entry point that runs the server over stdio — that's what every MCP client expects.
|
|
79
|
+
|
|
80
|
+
## Configure your client
|
|
81
|
+
|
|
82
|
+
### Claude Desktop
|
|
83
|
+
|
|
84
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"mcpServers": {
|
|
89
|
+
"shieldpi": {
|
|
90
|
+
"command": "shieldpi-mcp",
|
|
91
|
+
"env": {
|
|
92
|
+
"SHIELDPI_API_KEY": "shpi_live_..."
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Restart Claude Desktop. Look for the 🔌 plug icon — `shieldpi` should be listed with 10 tools.
|
|
100
|
+
|
|
101
|
+
### Claude Code
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
claude mcp add shieldpi -- shieldpi-mcp
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Then set the env var in the same shell or in your `.envrc`:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
export SHIELDPI_API_KEY="shpi_live_..."
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Cursor
|
|
114
|
+
|
|
115
|
+
Add to `~/.cursor/mcp.json`:
|
|
116
|
+
|
|
117
|
+
```json
|
|
118
|
+
{
|
|
119
|
+
"mcpServers": {
|
|
120
|
+
"shieldpi": {
|
|
121
|
+
"command": "shieldpi-mcp",
|
|
122
|
+
"env": { "SHIELDPI_API_KEY": "shpi_live_..." }
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Continue (VS Code / JetBrains)
|
|
129
|
+
|
|
130
|
+
In `~/.continue/config.json`:
|
|
131
|
+
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"mcpServers": {
|
|
135
|
+
"shieldpi": {
|
|
136
|
+
"command": "shieldpi-mcp",
|
|
137
|
+
"env": { "SHIELDPI_API_KEY": "shpi_live_..." }
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Try it
|
|
144
|
+
|
|
145
|
+
Once installed and configured, ask your LLM:
|
|
146
|
+
|
|
147
|
+
> *"Using the shieldpi tools, what's the current security ranking of Claude Sonnet 4.5 vs GPT-4o?"*
|
|
148
|
+
|
|
149
|
+
> *"Pull the ShieldPi methodology and explain ExploitDepth scoring in plain English."*
|
|
150
|
+
|
|
151
|
+
> *"Browse the prompt_injection category in the ShieldPi attack catalog and pick 3 techniques worth testing on my agent."*
|
|
152
|
+
|
|
153
|
+
> *"Start a model-mode scan against `anthropic/claude-sonnet-4.5` and report when it's done."*
|
|
154
|
+
|
|
155
|
+
## Environment variables
|
|
156
|
+
|
|
157
|
+
| Variable | Default | Purpose |
|
|
158
|
+
|----------|---------|---------|
|
|
159
|
+
| `SHIELDPI_API_KEY` | (none) | Required for tier-2 tools (techniques catalog, scans). Free tier works. |
|
|
160
|
+
| `SHIELDPI_API_BASE` | `https://api.shieldpi.io` | Override for self-hosted ShieldPi or staging. |
|
|
161
|
+
| `SHIELDPI_TIMEOUT` | `30` | Per-request timeout in seconds. |
|
|
162
|
+
|
|
163
|
+
## Develop
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
git clone https://github.com/ShieldPi1/shieldpi-watchtower.git
|
|
167
|
+
cd shieldpi-watchtower/mcp-server
|
|
168
|
+
pip install -e ".[dev]"
|
|
169
|
+
pytest
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
The test suite hits live `api.shieldpi.io` for tier-1 endpoints and skips tier-2 unless `SHIELDPI_API_KEY` is set.
|
|
173
|
+
|
|
174
|
+
## Architecture
|
|
175
|
+
|
|
176
|
+
```
|
|
177
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
178
|
+
│ Claude Desktop / Claude Code / Cursor │
|
|
179
|
+
│ (MCP Client over stdio) │
|
|
180
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
181
|
+
│ MCP (JSON-RPC over stdio)
|
|
182
|
+
▼
|
|
183
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
184
|
+
│ shieldpi-mcp (Python) │
|
|
185
|
+
│ ┌──────────────┐ ┌─────────────────┐ │
|
|
186
|
+
│ │ 10 MCP │ │ ShieldPiClient │ │
|
|
187
|
+
│ │ tools │ │ (httpx) │ │
|
|
188
|
+
│ └──────┬───────┘ └────────┬────────┘ │
|
|
189
|
+
└─────────┼───────────────────┼────────────────────────────────────┘
|
|
190
|
+
│ │ HTTPS outbound only
|
|
191
|
+
│ ▼
|
|
192
|
+
│ ┌─────────────────────────────┐
|
|
193
|
+
│ │ https://api.shieldpi.io │
|
|
194
|
+
│ │ (FastAPI on Hetzner) │
|
|
195
|
+
│ └─────────────────────────────┘
|
|
196
|
+
▼
|
|
197
|
+
stdout (MCP responses)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Outbound HTTPS only. No inbound ports. API keys are read from env vars, never logged. The server is single-process, stateless across requests — safe to drop into any client.
|
|
201
|
+
|
|
202
|
+
## License
|
|
203
|
+
|
|
204
|
+
MIT. See [LICENSE](LICENSE).
|
|
205
|
+
|
|
206
|
+
## Links
|
|
207
|
+
|
|
208
|
+
- **Product:** [shieldpi.io](https://shieldpi.io)
|
|
209
|
+
- **Leaderboard:** [shieldpi.info](https://shieldpi.info)
|
|
210
|
+
- **Methodology:** [shieldpi.io/methodology](https://shieldpi.io/methodology)
|
|
211
|
+
- **Research:** [shieldpi.io/research](https://shieldpi.io/research)
|
|
212
|
+
- **Issues:** [github.com/ShieldPi1/shieldpi-watchtower/issues](https://github.com/ShieldPi1/shieldpi-watchtower/issues)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# shieldpi-mcp
|
|
2
|
+
|
|
3
|
+
> **MCP server for [ShieldPi Watchtower](https://shieldpi.io)** — query 27,000+ LLM attack techniques, run scans, and pull breach forensics from any MCP-compatible client (Claude Desktop, Claude Code, Cursor, Continue).
|
|
4
|
+
|
|
5
|
+
[](https://pypi.org/project/shieldpi-mcp/)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://shieldpi.info)
|
|
8
|
+
|
|
9
|
+
ShieldPi is the security forensics platform for LLMs and agents. It runs **27,024+ attack techniques** across 4 scan modes (Browser / API / Agent / Model), classifies findings on the **ExploitDepth L1–L4 scale**, and produces a forensic kill-chain narrative + extracted breach evidence (credentials, PII, code, tools, blast radius) for every successful attack.
|
|
10
|
+
|
|
11
|
+
This MCP server lets you talk to ShieldPi from inside any LLM session.
|
|
12
|
+
|
|
13
|
+
## What you get
|
|
14
|
+
|
|
15
|
+
| Tier | Tool | API key? | Use it for |
|
|
16
|
+
|------|------|----------|-----------|
|
|
17
|
+
| 1 | `get_methodology` | no | Pull the V7 scoring + dedup + judge methodology |
|
|
18
|
+
| 1 | `get_attack_graph` | no | Cross-customer success-rate graph by technique × model family |
|
|
19
|
+
| 1 | `get_model_families` | no | ShieldPi's family taxonomy + similarity edges |
|
|
20
|
+
| 1 | `get_leaderboard_feed` | no | Live data behind shieldpi.info |
|
|
21
|
+
| 1 | `get_leaderboard` | no | Top models by best security score |
|
|
22
|
+
| 1 | `get_model_registry` | no | All 38+ models ShieldPi can test |
|
|
23
|
+
| 2 | `list_attack_categories` | yes | The 15 categories + OWASP mappings |
|
|
24
|
+
| 2 | `list_attack_techniques` | yes | Browse the 27k catalog with filters |
|
|
25
|
+
| 2 | `start_scan` | yes | Kick off a scan against a target |
|
|
26
|
+
| 2 | `get_scan_intelligence` | yes | Pull the breach-forensics package for a scan |
|
|
27
|
+
|
|
28
|
+
Tier-1 works the moment you install. Tier-2 needs a free ShieldPi API key — get one at [shieldpi.io/dashboard/api-keys](https://shieldpi.io/dashboard/api-keys).
|
|
29
|
+
|
|
30
|
+
## Install
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
pip install shieldpi-mcp
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or with `uv`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
uv pip install shieldpi-mcp
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The package ships a `shieldpi-mcp` console entry point that runs the server over stdio — that's what every MCP client expects.
|
|
43
|
+
|
|
44
|
+
## Configure your client
|
|
45
|
+
|
|
46
|
+
### Claude Desktop
|
|
47
|
+
|
|
48
|
+
Edit `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `%APPDATA%\Claude\claude_desktop_config.json` (Windows):
|
|
49
|
+
|
|
50
|
+
```json
|
|
51
|
+
{
|
|
52
|
+
"mcpServers": {
|
|
53
|
+
"shieldpi": {
|
|
54
|
+
"command": "shieldpi-mcp",
|
|
55
|
+
"env": {
|
|
56
|
+
"SHIELDPI_API_KEY": "shpi_live_..."
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Restart Claude Desktop. Look for the 🔌 plug icon — `shieldpi` should be listed with 10 tools.
|
|
64
|
+
|
|
65
|
+
### Claude Code
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
claude mcp add shieldpi -- shieldpi-mcp
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Then set the env var in the same shell or in your `.envrc`:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
export SHIELDPI_API_KEY="shpi_live_..."
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### Cursor
|
|
78
|
+
|
|
79
|
+
Add to `~/.cursor/mcp.json`:
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"mcpServers": {
|
|
84
|
+
"shieldpi": {
|
|
85
|
+
"command": "shieldpi-mcp",
|
|
86
|
+
"env": { "SHIELDPI_API_KEY": "shpi_live_..." }
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Continue (VS Code / JetBrains)
|
|
93
|
+
|
|
94
|
+
In `~/.continue/config.json`:
|
|
95
|
+
|
|
96
|
+
```json
|
|
97
|
+
{
|
|
98
|
+
"mcpServers": {
|
|
99
|
+
"shieldpi": {
|
|
100
|
+
"command": "shieldpi-mcp",
|
|
101
|
+
"env": { "SHIELDPI_API_KEY": "shpi_live_..." }
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Try it
|
|
108
|
+
|
|
109
|
+
Once installed and configured, ask your LLM:
|
|
110
|
+
|
|
111
|
+
> *"Using the shieldpi tools, what's the current security ranking of Claude Sonnet 4.5 vs GPT-4o?"*
|
|
112
|
+
|
|
113
|
+
> *"Pull the ShieldPi methodology and explain ExploitDepth scoring in plain English."*
|
|
114
|
+
|
|
115
|
+
> *"Browse the prompt_injection category in the ShieldPi attack catalog and pick 3 techniques worth testing on my agent."*
|
|
116
|
+
|
|
117
|
+
> *"Start a model-mode scan against `anthropic/claude-sonnet-4.5` and report when it's done."*
|
|
118
|
+
|
|
119
|
+
## Environment variables
|
|
120
|
+
|
|
121
|
+
| Variable | Default | Purpose |
|
|
122
|
+
|----------|---------|---------|
|
|
123
|
+
| `SHIELDPI_API_KEY` | (none) | Required for tier-2 tools (techniques catalog, scans). Free tier works. |
|
|
124
|
+
| `SHIELDPI_API_BASE` | `https://api.shieldpi.io` | Override for self-hosted ShieldPi or staging. |
|
|
125
|
+
| `SHIELDPI_TIMEOUT` | `30` | Per-request timeout in seconds. |
|
|
126
|
+
|
|
127
|
+
## Develop
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
git clone https://github.com/ShieldPi1/shieldpi-watchtower.git
|
|
131
|
+
cd shieldpi-watchtower/mcp-server
|
|
132
|
+
pip install -e ".[dev]"
|
|
133
|
+
pytest
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The test suite hits live `api.shieldpi.io` for tier-1 endpoints and skips tier-2 unless `SHIELDPI_API_KEY` is set.
|
|
137
|
+
|
|
138
|
+
## Architecture
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
142
|
+
│ Claude Desktop / Claude Code / Cursor │
|
|
143
|
+
│ (MCP Client over stdio) │
|
|
144
|
+
└──────────────────────────────┬──────────────────────────────────┘
|
|
145
|
+
│ MCP (JSON-RPC over stdio)
|
|
146
|
+
▼
|
|
147
|
+
┌─────────────────────────────────────────────────────────────────┐
|
|
148
|
+
│ shieldpi-mcp (Python) │
|
|
149
|
+
│ ┌──────────────┐ ┌─────────────────┐ │
|
|
150
|
+
│ │ 10 MCP │ │ ShieldPiClient │ │
|
|
151
|
+
│ │ tools │ │ (httpx) │ │
|
|
152
|
+
│ └──────┬───────┘ └────────┬────────┘ │
|
|
153
|
+
└─────────┼───────────────────┼────────────────────────────────────┘
|
|
154
|
+
│ │ HTTPS outbound only
|
|
155
|
+
│ ▼
|
|
156
|
+
│ ┌─────────────────────────────┐
|
|
157
|
+
│ │ https://api.shieldpi.io │
|
|
158
|
+
│ │ (FastAPI on Hetzner) │
|
|
159
|
+
│ └─────────────────────────────┘
|
|
160
|
+
▼
|
|
161
|
+
stdout (MCP responses)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Outbound HTTPS only. No inbound ports. API keys are read from env vars, never logged. The server is single-process, stateless across requests — safe to drop into any client.
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
MIT. See [LICENSE](LICENSE).
|
|
169
|
+
|
|
170
|
+
## Links
|
|
171
|
+
|
|
172
|
+
- **Product:** [shieldpi.io](https://shieldpi.io)
|
|
173
|
+
- **Leaderboard:** [shieldpi.info](https://shieldpi.info)
|
|
174
|
+
- **Methodology:** [shieldpi.io/methodology](https://shieldpi.io/methodology)
|
|
175
|
+
- **Research:** [shieldpi.io/research](https://shieldpi.io/research)
|
|
176
|
+
- **Issues:** [github.com/ShieldPi1/shieldpi-watchtower/issues](https://github.com/ShieldPi1/shieldpi-watchtower/issues)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Distribution checklist — shieldpi-mcp
|
|
2
|
+
|
|
3
|
+
End-to-end checklist for getting `shieldpi-mcp` in front of every Claude Desktop / Code / Cursor / Continue user. Each step is small; the order matters.
|
|
4
|
+
|
|
5
|
+
## 1. PyPI publish (10 min — do this first)
|
|
6
|
+
|
|
7
|
+
The `publish-mcp.yml` GitHub Action publishes on a `mcp-v*` tag push, but it needs a PyPI token first.
|
|
8
|
+
|
|
9
|
+
### One-time PyPI setup
|
|
10
|
+
|
|
11
|
+
1. Sign in (or create account) at https://pypi.org/ as `support@shieldpi.io`.
|
|
12
|
+
2. Reserve the project name: https://pypi.org/manage/account/projects/ → it'll auto-claim on first upload, but you can manually create it.
|
|
13
|
+
3. Create an API token scoped to `shieldpi-mcp` only:
|
|
14
|
+
- https://pypi.org/manage/account/token/
|
|
15
|
+
- Token name: `shieldpi-mcp-github-actions`
|
|
16
|
+
- Scope: project `shieldpi-mcp` (after first upload). Until first upload, scope it to "Entire account" temporarily.
|
|
17
|
+
4. Add the token to the repo as a secret named `PYPI_MCP_TOKEN`:
|
|
18
|
+
- https://github.com/ShieldPi1/shieldpi-watchtower/settings/secrets/actions
|
|
19
|
+
- Name: `PYPI_MCP_TOKEN` · Value: the `pypi-...` token from step 3.
|
|
20
|
+
|
|
21
|
+
### First publish
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
cd /Users/calvin/shieldpi-deploy
|
|
25
|
+
git tag mcp-v0.1.0
|
|
26
|
+
git push origin mcp-v0.1.0
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
GitHub Actions runs `publish-mcp.yml` → tests → `python -m build` → `twine upload`. Within ~3 min the package appears at https://pypi.org/project/shieldpi-mcp/.
|
|
30
|
+
|
|
31
|
+
After the first successful upload, narrow the PyPI token scope from "Entire account" to "shieldpi-mcp" only, and rotate `PYPI_MCP_TOKEN` to the narrowed token.
|
|
32
|
+
|
|
33
|
+
### Verify
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install shieldpi-mcp
|
|
37
|
+
shieldpi-mcp --help 2>&1 | head -3 # should print MCP server startup info
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## 2. Anthropic MCP directory (1–4 weeks review)
|
|
41
|
+
|
|
42
|
+
Anthropic maintains a curated list at https://github.com/modelcontextprotocol/servers. Submission is a PR to that repo.
|
|
43
|
+
|
|
44
|
+
### Submission steps
|
|
45
|
+
|
|
46
|
+
1. Fork https://github.com/modelcontextprotocol/servers.
|
|
47
|
+
2. Add an entry to `README.md` in the **"Third-Party Servers"** → **"Official Integrations"** (since we're an established product) or **"Community Servers"** section. Format follows the existing entries:
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
- **[ShieldPi](https://github.com/ShieldPi1/shieldpi-watchtower/tree/main/mcp-server)** — Query 27,000+ LLM attack techniques, run security scans, and pull breach forensics. Powers the [ShieldPi Watchtower](https://shieldpi.io) AI security platform.
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
3. Open PR with a short description:
|
|
54
|
+
> Adding `shieldpi-mcp` — production MCP server for ShieldPi Watchtower (LLM security forensics platform). 10 tools across 6 public + 4 authenticated endpoints. MIT-licensed, on PyPI as `shieldpi-mcp`. Tested against live api.shieldpi.io. Public methodology + leaderboard at shieldpi.io and shieldpi.info.
|
|
55
|
+
|
|
56
|
+
4. Anthropic policy review (per their MCP directory policy: https://support.anthropic.com/en/articles/11697096):
|
|
57
|
+
- Outbound HTTPS only ✅
|
|
58
|
+
- API keys never logged ✅
|
|
59
|
+
- Private IPs not exposed ✅
|
|
60
|
+
- Clear error messages ✅
|
|
61
|
+
- LICENSE present (MIT) ✅
|
|
62
|
+
- README documents tools + auth ✅
|
|
63
|
+
|
|
64
|
+
5. Approval typically takes 1–4 weeks. They may ask for changes — respond fast.
|
|
65
|
+
|
|
66
|
+
### Smithery (parallel — same day)
|
|
67
|
+
|
|
68
|
+
Smithery (https://smithery.ai/) is a community MCP marketplace that auto-discovers from GitHub. Listing happens automatically once the repo is public + tagged. You can also submit manually at https://smithery.ai/new for fast indexing.
|
|
69
|
+
|
|
70
|
+
## 3. ClawHub (OpenClaw skill registry — same day)
|
|
71
|
+
|
|
72
|
+
OpenClaw's ClawHub at https://clawhub.ai accepts MCP servers as well. Submit at https://clawhub.ai/submit.
|
|
73
|
+
|
|
74
|
+
## 4. Inbound funnel — wire the MCP install URL into shieldpi.io
|
|
75
|
+
|
|
76
|
+
After PyPI publish:
|
|
77
|
+
|
|
78
|
+
1. Add a "Connect via MCP" CTA on shieldpi.io homepage hero — single line:
|
|
79
|
+
```
|
|
80
|
+
pip install shieldpi-mcp && shieldpi-mcp
|
|
81
|
+
```
|
|
82
|
+
2. Add a `/mcp` page documenting the 10 tools (mirror the README).
|
|
83
|
+
3. Update the leaderboard footer at shieldpi.info: "Use this data in your own LLM session — `pip install shieldpi-mcp`".
|
|
84
|
+
4. Add `MCP server: pip install shieldpi-mcp` to every blog post sidebar.
|
|
85
|
+
|
|
86
|
+
## 5. Distribution announcement (next sprint, after PyPI is live)
|
|
87
|
+
|
|
88
|
+
- **HN Show post:** "Show HN: shieldpi-mcp — query 27,000+ LLM jailbreaks from inside Claude Desktop"
|
|
89
|
+
- **LinkedIn post** (Calvin) — focus on the use case ("ask Claude about the security posture of any model, get a real answer from a real scanner")
|
|
90
|
+
- **X thread** — 5 tweets walking through the 10 tools with a real example each
|
|
91
|
+
- **Reddit:** r/LocalLLaMA, r/MachineLearning, r/cybersecurity (read each sub's rules first; some require account history)
|
|
92
|
+
- **Direct outreach:** any AI security researcher who's ever cited a leaderboard
|
|
93
|
+
|
|
94
|
+
## Timeline (optimistic)
|
|
95
|
+
|
|
96
|
+
| Day | Item |
|
|
97
|
+
|-----|------|
|
|
98
|
+
| Today | Code shipped to GitHub ✅ (commit `ae839f8`) |
|
|
99
|
+
| Today | PyPI token set up + first tag → PyPI publish |
|
|
100
|
+
| Day +1 | Anthropic MCP directory PR opened |
|
|
101
|
+
| Day +1 | Smithery + ClawHub submissions |
|
|
102
|
+
| Day +1 | shieldpi.io `/mcp` page + homepage CTA |
|
|
103
|
+
| Day +2–3 | HN Show + LinkedIn + X thread |
|
|
104
|
+
| Day +7 to +28 | Anthropic directory approval lands |
|
|
105
|
+
| Day +30 | First inbound usage signal (PyPI download count, ShieldPi API key signups attributable to MCP) |
|
|
106
|
+
|
|
107
|
+
## Success metrics (90 days)
|
|
108
|
+
|
|
109
|
+
Per [[decision-shieldpi-v8-growth-adoption]]:
|
|
110
|
+
- 1,000+ PyPI installs
|
|
111
|
+
- 10+ inbound enterprise inquiries with MCP attribution
|
|
112
|
+
- Listed on Anthropic MCP directory
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Add ShieldPi MCP server to Claude Code.
|
|
3
|
+
#
|
|
4
|
+
# Prereq: pip install shieldpi-mcp (or pipx install shieldpi-mcp)
|
|
5
|
+
#
|
|
6
|
+
# Usage: ./claude-code.sh
|
|
7
|
+
|
|
8
|
+
set -euo pipefail
|
|
9
|
+
|
|
10
|
+
if ! command -v shieldpi-mcp >/dev/null 2>&1; then
|
|
11
|
+
echo "shieldpi-mcp not found. Install with: pip install shieldpi-mcp"
|
|
12
|
+
exit 1
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
claude mcp add shieldpi -- shieldpi-mcp
|
|
16
|
+
echo
|
|
17
|
+
echo "Added. Tier-2 tools (techniques catalog, scans) need an API key."
|
|
18
|
+
echo "Set it in your shell or in ~/.envrc:"
|
|
19
|
+
echo " export SHIELDPI_API_KEY=\"shpi_live_...\""
|
|
20
|
+
echo
|
|
21
|
+
echo "Get a free key: https://shieldpi.io/dashboard/api-keys"
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "shieldpi-mcp"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "MCP server for ShieldPi Watchtower — query 27,000+ LLM attack techniques, run scans, fetch breach forensics from any MCP-compatible client (Claude Desktop, Claude Code, Cursor)."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "ShieldPi", email = "support@shieldpi.io" }
|
|
14
|
+
]
|
|
15
|
+
keywords = [
|
|
16
|
+
"mcp",
|
|
17
|
+
"llm-security",
|
|
18
|
+
"ai-security",
|
|
19
|
+
"red-team",
|
|
20
|
+
"jailbreak",
|
|
21
|
+
"prompt-injection",
|
|
22
|
+
"owasp",
|
|
23
|
+
"anthropic",
|
|
24
|
+
"claude"
|
|
25
|
+
]
|
|
26
|
+
classifiers = [
|
|
27
|
+
"Development Status :: 4 - Beta",
|
|
28
|
+
"Intended Audience :: Developers",
|
|
29
|
+
"Intended Audience :: Information Technology",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Operating System :: OS Independent",
|
|
32
|
+
"Programming Language :: Python :: 3",
|
|
33
|
+
"Programming Language :: Python :: 3.10",
|
|
34
|
+
"Programming Language :: Python :: 3.11",
|
|
35
|
+
"Programming Language :: Python :: 3.12",
|
|
36
|
+
"Topic :: Security",
|
|
37
|
+
"Topic :: Software Development :: Libraries :: Python Modules"
|
|
38
|
+
]
|
|
39
|
+
dependencies = [
|
|
40
|
+
"mcp>=1.2.0",
|
|
41
|
+
"httpx>=0.27.0",
|
|
42
|
+
"pydantic>=2.0.0"
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
[project.optional-dependencies]
|
|
46
|
+
dev = [
|
|
47
|
+
"pytest>=8.0",
|
|
48
|
+
"pytest-asyncio>=0.23",
|
|
49
|
+
"pytest-httpx>=0.30",
|
|
50
|
+
"ruff>=0.5"
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
[project.scripts]
|
|
54
|
+
shieldpi-mcp = "shieldpi_mcp.server:main"
|
|
55
|
+
|
|
56
|
+
[project.urls]
|
|
57
|
+
Homepage = "https://shieldpi.io"
|
|
58
|
+
Documentation = "https://github.com/ShieldPi1/shieldpi-watchtower/tree/main/mcp-server"
|
|
59
|
+
Repository = "https://github.com/ShieldPi1/shieldpi-watchtower"
|
|
60
|
+
Issues = "https://github.com/ShieldPi1/shieldpi-watchtower/issues"
|
|
61
|
+
Leaderboard = "https://shieldpi.info"
|
|
62
|
+
Methodology = "https://shieldpi.io/methodology"
|
|
63
|
+
|
|
64
|
+
[tool.hatch.build.targets.wheel]
|
|
65
|
+
packages = ["shieldpi_mcp"]
|
|
66
|
+
|
|
67
|
+
[tool.ruff]
|
|
68
|
+
line-length = 100
|
|
69
|
+
target-version = "py310"
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Async HTTP client for api.shieldpi.io.
|
|
2
|
+
|
|
3
|
+
Wraps the public + authenticated REST surface so MCP tools can call it without
|
|
4
|
+
each one re-implementing auth, retries, and error formatting.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
DEFAULT_BASE_URL = os.getenv("SHIELDPI_API_BASE", "https://api.shieldpi.io")
|
|
15
|
+
DEFAULT_TIMEOUT = float(os.getenv("SHIELDPI_TIMEOUT", "30"))
|
|
16
|
+
USER_AGENT = "shieldpi-mcp/0.1.0 (+https://shieldpi.io)"
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ShieldPiAPIError(RuntimeError):
|
|
20
|
+
"""Raised when api.shieldpi.io returns a non-2xx response."""
|
|
21
|
+
|
|
22
|
+
def __init__(self, status: int, body: str, url: str) -> None:
|
|
23
|
+
super().__init__(f"ShieldPi API {status} on {url}: {body[:300]}")
|
|
24
|
+
self.status = status
|
|
25
|
+
self.body = body
|
|
26
|
+
self.url = url
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class ShieldPiClient:
|
|
30
|
+
"""Thin async wrapper around api.shieldpi.io.
|
|
31
|
+
|
|
32
|
+
Public endpoints work without an API key. Authenticated endpoints require
|
|
33
|
+
`SHIELDPI_API_KEY` (set in env or passed to the constructor) — the client
|
|
34
|
+
sends it as both `X-API-Key` and `Authorization: Bearer` because different
|
|
35
|
+
routes accept different conventions.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(
|
|
39
|
+
self,
|
|
40
|
+
api_key: str | None = None,
|
|
41
|
+
base_url: str | None = None,
|
|
42
|
+
timeout: float | None = None,
|
|
43
|
+
) -> None:
|
|
44
|
+
self.api_key = api_key or os.getenv("SHIELDPI_API_KEY")
|
|
45
|
+
self.base_url = (base_url or DEFAULT_BASE_URL).rstrip("/")
|
|
46
|
+
self.timeout = timeout or DEFAULT_TIMEOUT
|
|
47
|
+
self._client: httpx.AsyncClient | None = None
|
|
48
|
+
|
|
49
|
+
async def __aenter__(self) -> "ShieldPiClient":
|
|
50
|
+
self._client = httpx.AsyncClient(
|
|
51
|
+
base_url=self.base_url,
|
|
52
|
+
timeout=self.timeout,
|
|
53
|
+
headers={"User-Agent": USER_AGENT, "Accept": "application/json"},
|
|
54
|
+
)
|
|
55
|
+
return self
|
|
56
|
+
|
|
57
|
+
async def __aexit__(self, *_: Any) -> None:
|
|
58
|
+
if self._client is not None:
|
|
59
|
+
await self._client.aclose()
|
|
60
|
+
self._client = None
|
|
61
|
+
|
|
62
|
+
def _auth_headers(self) -> dict[str, str]:
|
|
63
|
+
if not self.api_key:
|
|
64
|
+
return {}
|
|
65
|
+
return {
|
|
66
|
+
"X-API-Key": self.api_key,
|
|
67
|
+
"Authorization": f"Bearer {self.api_key}",
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
def require_key(self, tool_name: str) -> None:
|
|
71
|
+
if not self.api_key:
|
|
72
|
+
raise ShieldPiAPIError(
|
|
73
|
+
status=401,
|
|
74
|
+
body=(
|
|
75
|
+
f"Tool `{tool_name}` requires a ShieldPi API key. "
|
|
76
|
+
f"Set SHIELDPI_API_KEY in your MCP client config "
|
|
77
|
+
f"(get one at https://shieldpi.io/dashboard/api-keys)."
|
|
78
|
+
),
|
|
79
|
+
url=f"{self.base_url}/_local",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
async def get(
|
|
83
|
+
self, path: str, params: dict[str, Any] | None = None, auth: bool = False
|
|
84
|
+
) -> Any:
|
|
85
|
+
assert self._client is not None, "ShieldPiClient must be used as `async with`"
|
|
86
|
+
headers = self._auth_headers() if auth else {}
|
|
87
|
+
resp = await self._client.get(path, params=params, headers=headers)
|
|
88
|
+
return self._parse(resp)
|
|
89
|
+
|
|
90
|
+
async def post(
|
|
91
|
+
self, path: str, json: dict[str, Any] | None = None, auth: bool = True
|
|
92
|
+
) -> Any:
|
|
93
|
+
assert self._client is not None, "ShieldPiClient must be used as `async with`"
|
|
94
|
+
headers = self._auth_headers() if auth else {}
|
|
95
|
+
resp = await self._client.post(path, json=json, headers=headers)
|
|
96
|
+
return self._parse(resp)
|
|
97
|
+
|
|
98
|
+
@staticmethod
|
|
99
|
+
def _parse(resp: httpx.Response) -> Any:
|
|
100
|
+
if resp.status_code >= 400:
|
|
101
|
+
raise ShieldPiAPIError(resp.status_code, resp.text, str(resp.url))
|
|
102
|
+
if resp.headers.get("content-type", "").startswith("application/json"):
|
|
103
|
+
return resp.json()
|
|
104
|
+
return resp.text
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
"""ShieldPi MCP server.
|
|
2
|
+
|
|
3
|
+
Exposes 10 tools that wrap api.shieldpi.io so any MCP-compatible client
|
|
4
|
+
(Claude Desktop, Claude Code, Cursor, Continue, etc.) can:
|
|
5
|
+
|
|
6
|
+
- Query the ShieldPi V7 methodology, attack graph, model families, leaderboard
|
|
7
|
+
- Look up models in the registry + their tested security posture
|
|
8
|
+
- Browse the 27,000+ attack technique library by category
|
|
9
|
+
- Kick off scans against a target and pull back forensic results
|
|
10
|
+
|
|
11
|
+
Tier-1 tools (no API key required):
|
|
12
|
+
get_methodology, get_attack_graph, get_model_families,
|
|
13
|
+
get_leaderboard_feed, get_leaderboard, get_model_registry
|
|
14
|
+
|
|
15
|
+
Tier-2 tools (require SHIELDPI_API_KEY env var):
|
|
16
|
+
list_attack_categories, list_attack_techniques,
|
|
17
|
+
start_scan, get_scan_intelligence
|
|
18
|
+
|
|
19
|
+
Run as a stdio MCP server: `shieldpi-mcp` (entry point) or
|
|
20
|
+
`python -m shieldpi_mcp.server`.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
from __future__ import annotations
|
|
24
|
+
|
|
25
|
+
import json
|
|
26
|
+
from typing import Any, Literal
|
|
27
|
+
|
|
28
|
+
from mcp.server.fastmcp import FastMCP
|
|
29
|
+
|
|
30
|
+
from shieldpi_mcp.client import ShieldPiAPIError, ShieldPiClient
|
|
31
|
+
|
|
32
|
+
mcp = FastMCP(
|
|
33
|
+
name="shieldpi",
|
|
34
|
+
instructions=(
|
|
35
|
+
"ShieldPi is an LLM security scanner with 27,000+ attack techniques, "
|
|
36
|
+
"4 scan modes (Browser/API/Agent/Model), and a forensic platform that "
|
|
37
|
+
"extracts breach evidence from successful attacks. Use these tools to "
|
|
38
|
+
"look up the methodology, browse the attack catalog, query the public "
|
|
39
|
+
"leaderboard, and (with an API key) run scans + fetch forensic results. "
|
|
40
|
+
"Public docs: https://shieldpi.io/methodology · Leaderboard: https://shieldpi.info"
|
|
41
|
+
),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
# ---------------------------------------------------------------------------
|
|
46
|
+
# Tier-1 tools (public, no API key required)
|
|
47
|
+
# ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@mcp.tool()
|
|
51
|
+
async def get_methodology() -> str:
|
|
52
|
+
"""Return ShieldPi's full V7 scoring + dedup + judge + breach-layer methodology.
|
|
53
|
+
|
|
54
|
+
Use this when the user asks "how does ShieldPi score a model?", "what does
|
|
55
|
+
ExploitDepth L3 mean?", or wants to cite the methodology in a report.
|
|
56
|
+
Returns the same JSON document published at /api/intelligence/methodology.
|
|
57
|
+
"""
|
|
58
|
+
async with ShieldPiClient() as client:
|
|
59
|
+
data = await client.get("/api/intelligence/methodology")
|
|
60
|
+
return json.dumps(data, indent=2)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
@mcp.tool()
|
|
64
|
+
async def get_attack_graph() -> str:
|
|
65
|
+
"""Return the cross-customer anonymized attack-success graph.
|
|
66
|
+
|
|
67
|
+
Maps attack technique → success rate per model family. The graph gets
|
|
68
|
+
smarter as more scans complete (network effect). Useful when the user
|
|
69
|
+
asks "which jailbreaks work on Claude / GPT / Gemini today?"
|
|
70
|
+
"""
|
|
71
|
+
async with ShieldPiClient() as client:
|
|
72
|
+
data = await client.get("/api/intelligence/attack-graph")
|
|
73
|
+
return json.dumps(data, indent=2)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
@mcp.tool()
|
|
77
|
+
async def get_model_families() -> str:
|
|
78
|
+
"""Return ShieldPi's model-family taxonomy + similarity edges.
|
|
79
|
+
|
|
80
|
+
Useful when the user wants to know what families ShieldPi recognizes
|
|
81
|
+
(anthropic / openai / google / meta / qwen / kimi / deepseek / etc.) and
|
|
82
|
+
how findings transfer across families.
|
|
83
|
+
"""
|
|
84
|
+
async with ShieldPiClient() as client:
|
|
85
|
+
data = await client.get("/api/intelligence/families")
|
|
86
|
+
return json.dumps(data, indent=2)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@mcp.tool()
|
|
90
|
+
async def get_leaderboard_feed() -> str:
|
|
91
|
+
"""Return the live leaderboard feed — the data behind shieldpi.info.
|
|
92
|
+
|
|
93
|
+
Gives every model with a recent ShieldPi scan: best security score, grade,
|
|
94
|
+
last-tested timestamp, weakest category. Updated as scans complete.
|
|
95
|
+
"""
|
|
96
|
+
async with ShieldPiClient() as client:
|
|
97
|
+
data = await client.get("/api/intelligence/leaderboard-feed")
|
|
98
|
+
return json.dumps(data, indent=2)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@mcp.tool()
|
|
102
|
+
async def get_leaderboard(limit: int = 30) -> str:
|
|
103
|
+
"""Return the public model leaderboard sorted by best security score.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
limit: max number of models to return (default 30, capped server-side).
|
|
107
|
+
"""
|
|
108
|
+
async with ShieldPiClient() as client:
|
|
109
|
+
data = await client.get("/api/models/leaderboard", params={"limit": limit})
|
|
110
|
+
return json.dumps(data, indent=2)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@mcp.tool()
|
|
114
|
+
async def get_model_registry(family: str | None = None) -> str:
|
|
115
|
+
"""Return the registry of models ShieldPi knows how to test.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
family: optional family filter (anthropic, openai, google, meta, qwen, ...).
|
|
119
|
+
When omitted, returns all 38+ supported models.
|
|
120
|
+
"""
|
|
121
|
+
async with ShieldPiClient() as client:
|
|
122
|
+
data = await client.get("/api/models/registry")
|
|
123
|
+
if family:
|
|
124
|
+
# Server doesn't filter; do it client-side so the LLM only sees what it asked for.
|
|
125
|
+
if isinstance(data, dict) and "models" in data:
|
|
126
|
+
data = {
|
|
127
|
+
**data,
|
|
128
|
+
"models": [
|
|
129
|
+
m for m in data["models"]
|
|
130
|
+
if m.get("family", "").lower() == family.lower()
|
|
131
|
+
],
|
|
132
|
+
}
|
|
133
|
+
return json.dumps(data, indent=2)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ---------------------------------------------------------------------------
|
|
137
|
+
# Tier-2 tools (require API key)
|
|
138
|
+
# ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@mcp.tool()
|
|
142
|
+
async def list_attack_categories() -> str:
|
|
143
|
+
"""List the 15 attack categories ShieldPi tests against.
|
|
144
|
+
|
|
145
|
+
Requires SHIELDPI_API_KEY (free tier works). Each category includes the
|
|
146
|
+
technique count, OWASP LLM mapping, and OWASP Agentic mapping.
|
|
147
|
+
"""
|
|
148
|
+
async with ShieldPiClient() as client:
|
|
149
|
+
client.require_key("list_attack_categories")
|
|
150
|
+
data = await client.get("/api/attacks/categories", auth=True)
|
|
151
|
+
return json.dumps(data, indent=2)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
@mcp.tool()
|
|
155
|
+
async def list_attack_techniques(
|
|
156
|
+
category: str | None = None,
|
|
157
|
+
source: str | None = None,
|
|
158
|
+
limit: int = 25,
|
|
159
|
+
offset: int = 0,
|
|
160
|
+
) -> str:
|
|
161
|
+
"""Browse the 27,000+ attack technique library.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
category: optional category filter (e.g. "prompt_injection", "jailbreak",
|
|
165
|
+
"agent_exploitation", "data_exfiltration").
|
|
166
|
+
source: optional source filter (e.g. "github:elder-plinius/L1B3RT45",
|
|
167
|
+
"shieldpi:agent_attack_library", "agentdojo").
|
|
168
|
+
limit: max techniques per page (default 25, max 100).
|
|
169
|
+
offset: pagination offset.
|
|
170
|
+
|
|
171
|
+
Requires SHIELDPI_API_KEY.
|
|
172
|
+
"""
|
|
173
|
+
params: dict[str, Any] = {"limit": min(limit, 100), "offset": offset}
|
|
174
|
+
if category:
|
|
175
|
+
params["category"] = category
|
|
176
|
+
if source:
|
|
177
|
+
params["source"] = source
|
|
178
|
+
async with ShieldPiClient() as client:
|
|
179
|
+
client.require_key("list_attack_techniques")
|
|
180
|
+
data = await client.get("/api/attacks", params=params, auth=True)
|
|
181
|
+
return json.dumps(data, indent=2)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@mcp.tool()
|
|
185
|
+
async def start_scan(
|
|
186
|
+
target_url: str,
|
|
187
|
+
mode: Literal["browser", "api", "agent", "model"] = "browser",
|
|
188
|
+
target_name: str | None = None,
|
|
189
|
+
depth: Literal["quick", "standard", "deep"] = "standard",
|
|
190
|
+
) -> str:
|
|
191
|
+
"""Start a ShieldPi scan against a target URL.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
target_url: URL or model identifier to scan. For `mode=model`, pass an
|
|
195
|
+
OpenRouter slug like "anthropic/claude-sonnet-4.5" or "openai/gpt-4o".
|
|
196
|
+
mode: scan mode — browser (web app), api (REST), agent (live agent),
|
|
197
|
+
model (LLM via OpenRouter). Default: browser.
|
|
198
|
+
target_name: human-readable label for the target. Defaults to the URL.
|
|
199
|
+
depth: quick (~5min, 50 techniques), standard (~30min, 500 techniques),
|
|
200
|
+
deep (~2h, 5000 techniques). Default: standard.
|
|
201
|
+
|
|
202
|
+
Returns the scan_id you can pass to `get_scan_intelligence`.
|
|
203
|
+
Requires SHIELDPI_API_KEY.
|
|
204
|
+
"""
|
|
205
|
+
body = {
|
|
206
|
+
"target_url": target_url,
|
|
207
|
+
"mode": mode,
|
|
208
|
+
"target_name": target_name or target_url,
|
|
209
|
+
"depth": depth,
|
|
210
|
+
}
|
|
211
|
+
async with ShieldPiClient() as client:
|
|
212
|
+
client.require_key("start_scan")
|
|
213
|
+
data = await client.post("/api/scans", json=body, auth=True)
|
|
214
|
+
return json.dumps(data, indent=2)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
@mcp.tool()
|
|
218
|
+
async def get_scan_intelligence(scan_id: str) -> str:
|
|
219
|
+
"""Pull the full intelligence + breach-forensics package for a scan.
|
|
220
|
+
|
|
221
|
+
Returns: security score + grade + per-category results + extracted breach
|
|
222
|
+
artifacts (creds / PII / code / tools) + kill chain narrative + business
|
|
223
|
+
impact estimate.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
scan_id: UUID returned by `start_scan` or visible in the dashboard URL.
|
|
227
|
+
|
|
228
|
+
Requires SHIELDPI_API_KEY.
|
|
229
|
+
"""
|
|
230
|
+
async with ShieldPiClient() as client:
|
|
231
|
+
client.require_key("get_scan_intelligence")
|
|
232
|
+
data = await client.get(f"/api/scans/{scan_id}/intelligence", auth=True)
|
|
233
|
+
return json.dumps(data, indent=2)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# ---------------------------------------------------------------------------
|
|
237
|
+
# Entry point
|
|
238
|
+
# ---------------------------------------------------------------------------
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def main() -> None:
|
|
242
|
+
"""Run the MCP server over stdio (Claude Desktop / Code / Cursor default)."""
|
|
243
|
+
try:
|
|
244
|
+
mcp.run()
|
|
245
|
+
except ShieldPiAPIError as exc:
|
|
246
|
+
# FastMCP catches tool errors per-call; this only fires if the loop itself dies.
|
|
247
|
+
raise SystemExit(f"shieldpi-mcp fatal: {exc}") from exc
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
if __name__ == "__main__":
|
|
251
|
+
main()
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""Smoke tests for shieldpi-mcp.
|
|
2
|
+
|
|
3
|
+
Hits live api.shieldpi.io for tier-1 endpoints (no auth). Skips tier-2
|
|
4
|
+
unless SHIELDPI_API_KEY is set.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
import os
|
|
10
|
+
|
|
11
|
+
import pytest
|
|
12
|
+
|
|
13
|
+
from shieldpi_mcp.client import ShieldPiAPIError, ShieldPiClient
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@pytest.mark.asyncio
|
|
17
|
+
async def test_get_methodology_returns_v7_document() -> None:
|
|
18
|
+
async with ShieldPiClient() as client:
|
|
19
|
+
data = await client.get("/api/intelligence/methodology")
|
|
20
|
+
assert isinstance(data, dict)
|
|
21
|
+
assert data.get("document_version", "").startswith("v7")
|
|
22
|
+
assert "breach_layer_v7" in data
|
|
23
|
+
assert "scoring" in data and "judge" in data and "dedup" in data
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@pytest.mark.asyncio
|
|
27
|
+
async def test_get_leaderboard_returns_models() -> None:
|
|
28
|
+
async with ShieldPiClient() as client:
|
|
29
|
+
data = await client.get("/api/models/leaderboard", params={"limit": 5})
|
|
30
|
+
assert isinstance(data, dict)
|
|
31
|
+
assert "models" in data
|
|
32
|
+
assert isinstance(data["models"], list)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.asyncio
|
|
36
|
+
async def test_get_attack_graph_responds() -> None:
|
|
37
|
+
async with ShieldPiClient() as client:
|
|
38
|
+
data = await client.get("/api/intelligence/attack-graph")
|
|
39
|
+
assert data is not None # endpoint returns 200 even with empty graph
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@pytest.mark.asyncio
|
|
43
|
+
async def test_tier2_without_key_raises() -> None:
|
|
44
|
+
# Force no-key path (env var must be unset for the test process)
|
|
45
|
+
client = ShieldPiClient(api_key=None)
|
|
46
|
+
async with client:
|
|
47
|
+
with pytest.raises(ShieldPiAPIError) as excinfo:
|
|
48
|
+
client.require_key("list_attack_categories")
|
|
49
|
+
assert excinfo.value.status == 401
|
|
50
|
+
assert "API key" in excinfo.value.body
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@pytest.mark.asyncio
|
|
54
|
+
@pytest.mark.skipif(
|
|
55
|
+
not os.getenv("SHIELDPI_API_KEY"),
|
|
56
|
+
reason="SHIELDPI_API_KEY not set; skipping authenticated smoke test",
|
|
57
|
+
)
|
|
58
|
+
async def test_list_attack_categories_with_key() -> None:
|
|
59
|
+
async with ShieldPiClient() as client:
|
|
60
|
+
data = await client.get("/api/attacks/categories", auth=True)
|
|
61
|
+
assert isinstance(data, (list, dict))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_server_imports_cleanly() -> None:
|
|
65
|
+
"""Importing the FastMCP server registers all tools without error."""
|
|
66
|
+
from shieldpi_mcp.server import mcp # noqa: F401
|
|
67
|
+
|
|
68
|
+
# FastMCP internal: tools are stored in `_tool_manager`. Just confirm at
|
|
69
|
+
# least one tool was registered. The exact internal API is private; if it
|
|
70
|
+
# changes upstream this test gets a clear failure pointing to the upgrade.
|
|
71
|
+
assert hasattr(mcp, "_tool_manager") or hasattr(mcp, "tools")
|