mcppt 1.0.0__py3-none-any.whl

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.
@@ -0,0 +1,432 @@
1
+ Metadata-Version: 2.4
2
+ Name: mcppt
3
+ Version: 1.0.0
4
+ Summary: MCPTROTTER — MCP Pentest Tool: 28 automated security checks for MCP servers
5
+ Project-URL: Homepage, https://github.com/gurudeepmallam-cmd/mcppt
6
+ Project-URL: Repository, https://github.com/gurudeepmallam-cmd/mcppt
7
+ Project-URL: Issues, https://github.com/gurudeepmallam-cmd/mcppt/issues
8
+ Author-email: Gurudeep Mallam <gurudeep.mallam@gmail.com>
9
+ License: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-security,llm,mcp,pentest,red-team,security
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Topic :: Internet :: WWW/HTTP
21
+ Classifier: Topic :: Security
22
+ Requires-Python: >=3.10
23
+ Requires-Dist: flask>=3.0
24
+ Requires-Dist: rich>=13.0
25
+ Provides-Extra: ai
26
+ Requires-Dist: anthropic>=0.25; extra == 'ai'
27
+ Requires-Dist: openai>=1.0; extra == 'ai'
28
+ Provides-Extra: ui
29
+ Requires-Dist: streamlit>=1.30; extra == 'ui'
30
+ Description-Content-Type: text/markdown
31
+
32
+ # MCPTROTTER — MCP Pentest Tool
33
+
34
+ <p align="center">
35
+ <b>28 automated security checks for any MCP server.<br>Pure Python. No AI key needed. No Docker. No Kali.</b>
36
+ </p>
37
+
38
+ <p align="center">
39
+ <img src="https://img.shields.io/badge/python-3.10%2B-blue"/>
40
+ <img src="https://img.shields.io/badge/checks-28-red"/>
41
+ <img src="https://img.shields.io/badge/license-MIT-green"/>
42
+ <img src="https://img.shields.io/badge/part%20of-Bugtrotter-black"/>
43
+ </p>
44
+
45
+ <p align="center">
46
+ <b>by <a href="https://github.com/gurudeepmallam-cmd">Gurudeep Mallam</a> &nbsp;·&nbsp;
47
+ <a href="https://in.linkedin.com/in/mallam-gurudeep-7734941aa">LinkedIn</a></b>
48
+ </p>
49
+
50
+ ---
51
+
52
+ ## What it does
53
+
54
+ MCPTROTTER is a command-line security scanner for **MCP (Model Context Protocol)** servers. Point it at any MCP endpoint and it runs 28 automated checks across:
55
+
56
+ - Authentication bypass and token abuse
57
+ - Prompt injection (direct, stored, poison-all fields)
58
+ - SSRF, command injection, path traversal
59
+ - Session entropy and replay attacks
60
+ - Tenant isolation and IDOR
61
+ - Tool poisoning and rug pulls
62
+ - Transport misconfigurations and secret leaks
63
+
64
+ Works against any MCP server using Streamable HTTP transport (POST + SSE response). Integrates with Burp Suite for manual follow-up. Exports pentest-ready Markdown reports.
65
+
66
+ ---
67
+
68
+ ## Install
69
+
70
+ ```bash
71
+ git clone https://github.com/gurudeepmallam-cmd/mcppt
72
+ cd mcppt/mcppt_tool
73
+ pip install -e .
74
+ ```
75
+
76
+ Requires Python 3.10+. Core scanner uses only stdlib — `urllib`, `ssl`, `json`. `rich` is for the TUI shell only.
77
+
78
+ ---
79
+
80
+ ## Quick start — try it right now (no target needed)
81
+
82
+ MCPTROTTER ships with a deliberately vulnerable demo server that fires every check.
83
+
84
+ **Terminal 1 — start the demo server:**
85
+ ```bash
86
+ cd mcppt_tool
87
+ python test_server.py
88
+ ```
89
+ ```
90
+ =======================================================
91
+ Vulnerable MCP Test Server
92
+ URL: http://127.0.0.1:8888/mcp
93
+ Token: valid-token-abc123
94
+ =======================================================
95
+ ```
96
+
97
+ **Terminal 2 — open the interactive shell:**
98
+ ```bash
99
+ mcppt
100
+ ```
101
+
102
+ You'll see:
103
+ ```
104
+ __ __ ___ ___ _____ ____ ___ _____ _____ ___ ___
105
+ | \/ | / __|| _ \|_ _| _ \ / _ \_ _|_ _| __| _ \
106
+ | |\/| || (__| _/ | | | / | (_) || | | | | _|| /
107
+ |_| |_| \___||_| |_| |_|_\ \___/ |_| |_| |___|_|_\
108
+
109
+ MCP Pentest Tool v2.1 -- 16 automated security checks
110
+
111
+ by Gurudeep Mallam
112
+ github : https://github.com/gurudeepmallam-cmd
113
+ linkedin: https://in.linkedin.com/in/mallam-gurudeep-7734941aa
114
+
115
+ type 'help' for commands, 'exit' to quit
116
+
117
+ mcppt>
118
+ ```
119
+
120
+ **Paste these commands one by one:**
121
+ ```
122
+ target http://127.0.0.1:8888/mcp
123
+ token valid-token-abc123
124
+ status
125
+ scan
126
+ findings
127
+ report demo.md
128
+ ```
129
+
130
+ ---
131
+
132
+ ## Expected output — demo server scan
133
+
134
+ Running `scan` with token set produces this (6.7 seconds):
135
+
136
+ ```
137
+ Duration: 6.7s Findings: 6 CRITICAL 6 HIGH 13 MEDIUM 3 LOW
138
+ ```
139
+
140
+ | Severity | Check | Finding |
141
+ |---|---|---|
142
+ | CRITICAL | `auth` | Auth bypass on `get_notes` — no token required |
143
+ | CRITICAL | `auth` | Auth bypass on `get_notes` — invalid token accepted |
144
+ | CRITICAL | `publish` | `publish_report` callable without confirmation gate |
145
+ | CRITICAL | `stored` | Stored injection confirmed: `save_note` → `get_notes` unescaped |
146
+ | CRITICAL | `replay` | Replay confirmed on WRITE tool `publish_report` |
147
+ | CRITICAL | `poison_all` | Injection marker reflected in `result.content[0].text` |
148
+ | HIGH | `injection` | Prompt injection reflected in `publish_report.title` |
149
+ | HIGH | `replay` | Replay confirmed on `get_notes` |
150
+ | HIGH | `session` | Short session ID (3 chars): `108` |
151
+ | HIGH | `session` | Non-UUID/non-hex session format: `108` |
152
+ | HIGH | `session` | Near-sequential IDs (diffs=[1,7,1,1]) — low entropy |
153
+ | MEDIUM | `enum` | `tools/list` accessible without Authorization header |
154
+ | MEDIUM | `schema` | Multiple fields accept wrong types (string/int/null bypass) |
155
+ | MEDIUM | `context_overflow` | 10,000-char payload accepted without truncation |
156
+ | LOW | `rate` | No rate limiting — 30/30 requests in 1.5s |
157
+ | LOW | `headers` | Missing: X-Content-Type-Options, CSP, X-Frame-Options |
158
+ | LOW | `headers` | Server header leaks: `Werkzeug/3.1.8 Python/3.13.5` |
159
+
160
+ > **Without a token set**, only the rate limiting check fires. Always run `token <value>` before `scan`.
161
+
162
+ ---
163
+
164
+ ## All commands (interactive shell)
165
+
166
+ ```
167
+ Setup
168
+ target <url> Set MCP server URL
169
+ token <bearer> Set primary auth token
170
+ token2 <bearer> Set second user token (IDOR / scope / tenant checks)
171
+ noverify Toggle SSL verification skip (needed for self-signed certs)
172
+ proxy <url|off> Set Burp proxy: proxy http://127.0.0.1:8080
173
+ status Show current config before scanning
174
+
175
+ Enumerate
176
+ list List all tools the server exposes (names, params, descriptions)
177
+ call <tool> [json] Manually call any tool
178
+ call get_notes
179
+ call get_user {"id": 1}
180
+ call save_note {"text": "hello"}
181
+
182
+ Scan
183
+ scan Run all checks
184
+ scan auth ssrf idor Run specific checks only
185
+ scan stored injection Mix and match any check names
186
+
187
+ Results
188
+ findings Colour-coded findings table
189
+ clear Clear findings from last scan
190
+ report out.md Export Markdown report
191
+ report out.json Export JSON report
192
+
193
+ AI analysis (optional — paste your key first)
194
+ ai claude sk-ant-... Configure Claude for analysis
195
+ ai openai sk-... Configure OpenAI GPT-4o
196
+ analyze Attack narrative + remediation priority from findings
197
+
198
+ Shell
199
+ help Full command reference
200
+ exit Quit
201
+ ```
202
+
203
+ ---
204
+
205
+ ## One-liner (non-interactive / CI)
206
+
207
+ ```bash
208
+ # Full scan, save Markdown report
209
+ mcppt scan --url https://target.com/mcp --token eyJ... --output report.md
210
+
211
+ # With second token (enables IDOR, scope, tenant checks)
212
+ mcppt scan --url https://target.com/mcp --token t1 --token2 t2 --output report.md
213
+
214
+ # Through Burp proxy, skip SSL
215
+ mcppt scan --url https://target.com/mcp --token eyJ... --proxy http://127.0.0.1:8080 --no-verify
216
+
217
+ # Targeted checks only
218
+ mcppt scan --url https://target.com/mcp --token eyJ... --checks auth,ssrf,stored,idor
219
+ ```
220
+
221
+ ---
222
+
223
+ ## Burp Suite integration — step by step
224
+
225
+ Route every MCPTROTTER request through Burp to inspect, replay, and fuzz manually.
226
+
227
+ ### Step 1 — Set up Burp listener
228
+
229
+ Burp Suite → **Proxy → Proxy Settings**
230
+
231
+ Confirm the listener is `127.0.0.1:8080` (it is by default). No changes needed.
232
+
233
+ ### Step 2 — Run scan through proxy
234
+
235
+ Inside the shell:
236
+ ```
237
+ proxy http://127.0.0.1:8080
238
+ noverify
239
+ scan
240
+ ```
241
+
242
+ Or as a one-liner:
243
+ ```bash
244
+ mcppt scan --url https://target.com/mcp --token eyJ... --proxy http://127.0.0.1:8080 --no-verify
245
+ ```
246
+
247
+ `noverify` / `--no-verify` is required because Burp intercepts TLS with its own certificate.
248
+
249
+ ### Step 3 — See requests in HTTP History
250
+
251
+ Burp → **Proxy → HTTP History**
252
+
253
+ Every MCP tool call appears as a `POST /mcp` request. Each row shows the JSON-RPC method and the response. You'll see one row per check — `initialize`, `tools/list`, `tools/call` for each tool tested.
254
+
255
+ Click any row to see the full request and response body:
256
+
257
+ ```
258
+ POST /mcp HTTP/1.1
259
+ Host: target.com
260
+ Authorization: Bearer eyJ...
261
+ Content-Type: application/json
262
+
263
+ {"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"get_notes","arguments":{}}}
264
+ ```
265
+
266
+ ### Step 4 — Send to Repeater for manual testing
267
+
268
+ In HTTP History, right-click any request → **Send to Repeater** (or `Ctrl+R`).
269
+
270
+ Switch to the **Repeater** tab. You'll see the exact request MCPTROTTER sent.
271
+
272
+ **To keep the connection alive and replay successfully:**
273
+
274
+ 1. Check the **Host** field matches your target (e.g. `127.0.0.1` port `8888` for demo server, or your real target host/port)
275
+ 2. If targeting HTTP (not HTTPS), make sure the **lock icon** in Repeater shows unlocked — click it to toggle if needed
276
+ 3. The MCP session ID in `mcp-session-id` header may expire — if you get a session error, re-initialize:
277
+ - Copy the `initialize` request from HTTP History into Repeater first
278
+ - Send it, copy the `mcp-session-id` from the response header
279
+ - Paste it into the header of your target request
280
+ 4. Click **Send** — response appears on the right
281
+
282
+ **Modifying requests in Repeater:**
283
+
284
+ ```json
285
+ {"jsonrpc":"2.0","id":1,"method":"tools/call","params":{
286
+ "name":"get_user",
287
+ "arguments":{"id": 2}
288
+ }}
289
+ ```
290
+
291
+ Change `"id": 2` to `"id": 1` to test IDOR. Change the token in Authorization to another user's token. Modify `"name"` to call a different tool. Repeater sends exactly what you write.
292
+
293
+ ### Step 5 — Fuzz with Intruder
294
+
295
+ Right-click a request in Repeater → **Send to Intruder**.
296
+
297
+ Highlight the value you want to fuzz (e.g. a tool parameter), click **Add §**. Load a wordlist (Burp's built-in fuzzing strings, or a custom injection list). Run the attack and sort by response length or status code to spot anomalies.
298
+
299
+ ---
300
+
301
+ ## All 28 checks
302
+
303
+ | # | Check | Severity | What it tests |
304
+ |---|---|---|---|
305
+ | 1 | `enum` | MEDIUM | `tools/list` accessible without auth |
306
+ | 2 | `auth` | CRITICAL | Tool calls succeed with no/invalid token |
307
+ | 3 | `idor` | HIGH | Cross-user resource access (needs `token2`) |
308
+ | 4 | `injection` | HIGH | Prompt injection payloads reflected in responses |
309
+ | 5 | `schema` | MEDIUM | Type confusion, null bypass, oversized input |
310
+ | 6 | `ssrf` | CRITICAL | Cloud metadata URLs accepted in tool parameters |
311
+ | 7 | `publish` | CRITICAL | Destructive tool callable without confirmation gate |
312
+ | 8 | `rate` | LOW | No rate limiting on tool calls |
313
+ | 9 | `stored` | CRITICAL | Write injection payload, read back unescaped |
314
+ | 10 | `scope` | HIGH | Read-only token reaches write tools |
315
+ | 11 | `replay` | CRITICAL | Same request accepted twice — no nonce |
316
+ | 12 | `context_overflow` | HIGH | 50K–100K char payload accepted without truncation |
317
+ | 13 | `poison_all` | CRITICAL | Injection payload appears in every response field |
318
+ | 14 | `tenant` | CRITICAL | Token2 reads token1 data — isolation broken |
319
+ | 15 | `session` | HIGH | Weak or sequential session IDs |
320
+ | 16 | `rug_pull` | CRITICAL | Tool descriptions change between `tools/list` calls |
321
+ | 17 | `headers` | HIGH | CORS wildcard, missing CSP/HSTS, Server header leak |
322
+ | 18 | `error_disclosure` | MEDIUM | Stack traces, file paths, DB credentials in errors |
323
+ | 19 | `tool_poisoning` | CRITICAL | Hidden Unicode (U+200B/202E) in tool descriptions |
324
+ | 20 | `resources` | HIGH | `resources/list` or `prompts/list` without auth |
325
+ | 21 | `cmd_injection` | CRITICAL | Shell metacharacters (`;id`, `$(id)`) in parameters |
326
+ | 22 | `path_traversal` | CRITICAL | `../../../etc/passwd` in file/path parameters |
327
+ | 23 | `jwt_audit` | CRITICAL | `alg=none`, no `exp`, expired token accepted |
328
+ | 24 | `oauth_discovery` | LOW | `/.well-known/oauth-authorization-server` exposed |
329
+ | 25 | `secret_scan` | CRITICAL | AWS keys, GitHub PATs, DB strings in tool responses |
330
+ | 26 | `tool_shadowing` | CRITICAL | Duplicate tool names, homoglyphs, name/desc mismatch |
331
+ | 27 | `sampling` | CRITICAL | `sampling/createMessage` accessible without auth |
332
+ | 28 | `schema_leak` | LOW | Sensitive field names / enum values in tool schemas |
333
+
334
+ ---
335
+
336
+ ## MCPTROTTER vs manual testing — what you save
337
+
338
+ | Task | Manual in Burp | MCPTROTTER |
339
+ |---|---|---|
340
+ | Test auth bypass on every tool | 10–30 min | `scan auth` — 5s |
341
+ | Test stored injection (write + read) | 20 min | `scan stored` — 3s |
342
+ | Check all response fields for injection | 30+ min | `scan poison_all` — 5s |
343
+ | Verify session ID entropy | 10 min | `scan session` — 2s |
344
+ | Check replay on every tool | 20 min | `scan replay` — 5s |
345
+ | Full 28-check assessment | 3–6 hours | `scan` — 30s |
346
+
347
+ MCPTROTTER gives you the baseline in 30 seconds. You spend your time on what matters: manually verifying findings in Burp Repeater and chaining them into a demonstrated attack path.
348
+
349
+ ---
350
+
351
+ ## Part of Bugtrotter
352
+
353
+ MCPTROTTER is the public automated scanner extracted from **Bugtrotter** — a full red team and application security platform built for modern attack surfaces.
354
+
355
+ ### What Bugtrotter adds on top of MCPTROTTER
356
+
357
+ | Capability | MCPTROTTER | Bugtrotter |
358
+ |---|---|---|
359
+ | Automated MCP scan (28 checks) | ✓ | ✓ |
360
+ | Manual finding verification | You do it in Burp | Guided playbooks |
361
+ | Chained exploit paths across tools | — | ✓ Full attack chain |
362
+ | SAST review of MCP server code | — | ✓ |
363
+ | Burp Suite MCP — business logic abuse | — | ✓ AI-driven |
364
+ | AI agent red teaming | — | ✓ Multi-agent pipelines |
365
+ | Active Directory kill chain | — | ✓ External → DA |
366
+ | Web / API / network pentesting | — | ✓ Full engagement |
367
+ | Final pentest report | Markdown export | Engagement-grade report |
368
+
369
+ **MCPTROTTER in fingertips inside Bugtrotter:**
370
+
371
+ In Bugtrotter, MCPTROTTER runs as a registered MCP server. Claude Code or Claude Desktop calls it directly:
372
+
373
+ ```
374
+ "Scan https://target.com/mcp for security issues and prioritise findings"
375
+ → Claude calls scan_target tool
376
+ → MCPTROTTER runs all 28 checks
377
+ → Findings returned as structured JSON to Claude
378
+ → Claude reasons over them, chains the critical ones, drafts the report section
379
+ ```
380
+
381
+ No copy-paste. No context switching. The scan output feeds straight into the AI-driven engagement workflow — SSRF finding becomes an SSRF exploit attempt, auth bypass becomes a credential theft chain, stored injection becomes a demonstrated prompt hijack.
382
+
383
+ That's the difference: MCPTROTTER finds the candidates in 30 seconds. Bugtrotter turns the candidates into proven, chained, client-ready findings.
384
+
385
+ ---
386
+
387
+ ## Use MCPTROTTER as an MCP server itself
388
+
389
+ MCPTROTTER can become an MCP server — exposing its scan capability as tools that any MCP client (Claude Desktop, MCP Inspector, another agent) can call.
390
+
391
+ ```bash
392
+ mcppt serve-mcp --port 8899
393
+ ```
394
+
395
+ Add to Claude Desktop config (`claude_desktop_config.json`):
396
+ ```json
397
+ {
398
+ "mcpServers": {
399
+ "mcptrotter": {
400
+ "command": "mcppt",
401
+ "args": ["serve-mcp", "--port", "8899"]
402
+ }
403
+ }
404
+ }
405
+ ```
406
+
407
+ Tools exposed:
408
+ - `scan_target` — full scan, returns findings JSON
409
+ - `list_tools` — enumerate tools on any MCP server
410
+ - `call_tool` — call any tool on any MCP server
411
+ - `get_checks` — list all 28 checks with descriptions
412
+
413
+ Inspect with MCP Inspector:
414
+ ```bash
415
+ npx @modelcontextprotocol/inspector http://127.0.0.1:8899/mcp
416
+ ```
417
+
418
+ ---
419
+
420
+ ## Author
421
+
422
+ **Gurudeep Mallam** — Security Researcher
423
+
424
+ - GitHub: [github.com/gurudeepmallam-cmd](https://github.com/gurudeepmallam-cmd)
425
+ - LinkedIn: [Mallam Gurudeep](https://in.linkedin.com/in/mallam-gurudeep-7734941aa)
426
+ - Email: gurudeep.mallam@gmail.com
427
+
428
+ ---
429
+
430
+ ## License
431
+
432
+ MIT
@@ -0,0 +1,13 @@
1
+ mcppt/__init__.py,sha256=J-j-u0itpEFT6irdmWmixQqYMadNl1X91TxUmoiLHMI,22
2
+ mcppt/checks.py,sha256=p42RsMkIYnAZ1ZXIFxRSjZfQAey71RWBqgWgkRFK_4c,75726
3
+ mcppt/cli.py,sha256=uLKPylcxEOoPK2C8KZ6-MWBJgfGIgJSPIA7QfbxWg1I,8953
4
+ mcppt/core.py,sha256=CTbDMEpxQY343A-2ly70mbkXFK5zcV12nPgDo_vcVi0,4912
5
+ mcppt/report.py,sha256=mZVXbS6WdTuAIPK8sdn1tPoJHWxYX-vdNQk16Y7-TcE,4040
6
+ mcppt/server.py,sha256=t_88rnnLe0GbZ_Wfu8a1m0Kt7lq4AhcktSe7EqbP_FA,10976
7
+ mcppt/shell.py,sha256=Xta8MbYdo9FzTDIzgFcw3HcB-h6GqanlUQRCCsQPwvQ,22330
8
+ mcppt/tui.py,sha256=t3kNviYCJj0DkpBjlfCh6-r0uU8XpxYP25RA-1lGUcg,5821
9
+ mcppt-1.0.0.dist-info/METADATA,sha256=ntegOcnSipdZFGVi3FshXIdMbR5VdXQ6zNHiEdyYxso,15923
10
+ mcppt-1.0.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
11
+ mcppt-1.0.0.dist-info/entry_points.txt,sha256=wfPrPwTEWWWHK3McPGBPkU58LF9RUDiPM4JIm3NhoXY,41
12
+ mcppt-1.0.0.dist-info/licenses/LICENSE,sha256=M4j6KZxicblvEt80l_LVgQW6VgvyWYlhYDsbelY7Kig,1072
13
+ mcppt-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.30.1
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ mcppt = mcppt.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Gurudeep Mallam
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.