dryhack-mcp 2.3.0__tar.gz → 2.3.2__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.
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/PKG-INFO +26 -9
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/README.md +25 -8
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/__init__.py +1 -1
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/browser_tool.py +21 -3
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/PKG-INFO +26 -9
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/pyproject.toml +1 -1
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/tests/test_browser.py +24 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/LICENSE +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/__main__.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/config.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/server.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/__init__.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/authorize.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/curl_tool.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/exec.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/python_tool.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/scope.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp/tools/shell_tool.py +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/SOURCES.txt +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/dependency_links.txt +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/entry_points.txt +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/requires.txt +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/dryhack_mcp.egg-info/top_level.txt +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/setup.cfg +0 -0
- {dryhack_mcp-2.3.0 → dryhack_mcp-2.3.2}/tests/test_authorize.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dryhack-mcp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: DryHack-MCP - an MCP server providing offensive-security tooling (curl, python, shell, Playwright browser) and a scope-gated authorize engine for authorized penetration testing.
|
|
5
5
|
Author: Afrizal F.A
|
|
6
6
|
License: MIT License
|
|
@@ -74,13 +74,19 @@ With [uv](https://docs.astral.sh/uv/) you can run the server directly from PyPI
|
|
|
74
74
|
— no manual install needed:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
|
|
77
|
+
# recommended: pull the browser extra so the `browser` tool is guaranteed
|
|
78
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
78
79
|
# http transport
|
|
79
|
-
uvx dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
80
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
80
81
|
# pin a version
|
|
81
|
-
uvx dryhack-mcp
|
|
82
|
+
uvx --from 'dryhack-mcp[browser]==2.3.2' dryhack-mcp
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
> `uvx dryhack-mcp` (without `--from`) also works — Playwright is a core
|
|
86
|
+
> dependency — but the explicit `--from 'dryhack-mcp[browser]'` form is the
|
|
87
|
+
> recommended, unambiguous way to get everything the `browser` tool needs even
|
|
88
|
+
> if a stale cache is involved.
|
|
89
|
+
|
|
84
90
|
For development:
|
|
85
91
|
|
|
86
92
|
```bash
|
|
@@ -90,10 +96,17 @@ python3 -m pip install -e ".[dev]"
|
|
|
90
96
|
### Browser tool (Playwright)
|
|
91
97
|
|
|
92
98
|
Playwright is a **core dependency**, so a plain `pip install dryhack-mcp` /
|
|
93
|
-
`uvx dryhack-mcp` already ships it
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
`uvx dryhack-mcp` already ships it. For MCP clients using uvx, the recommended,
|
|
100
|
+
unambiguous invocation is:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The browser *binaries* (Chromium/Firefox/WebKit) are not distributed via pip, so
|
|
107
|
+
the first time the `browser` tool launches an engine it **auto-runs
|
|
108
|
+
`playwright install <browser>`** if the binary is missing (you'll see a
|
|
109
|
+
`[setup] auto-installed ...` note in the tool output on that first call).
|
|
97
110
|
|
|
98
111
|
To pre-install the binaries (optional, avoids the one-time first-use download):
|
|
99
112
|
|
|
@@ -157,7 +170,7 @@ CLI options:
|
|
|
157
170
|
"mcpServers": {
|
|
158
171
|
"dryhack": {
|
|
159
172
|
"command": "uvx",
|
|
160
|
-
"args": ["dryhack-mcp"],
|
|
173
|
+
"args": ["--from", "dryhack-mcp[browser]", "dryhack-mcp"],
|
|
161
174
|
"env": {
|
|
162
175
|
"DRYHACK_COMMAND_TIMEOUT": "120"
|
|
163
176
|
}
|
|
@@ -166,6 +179,10 @@ CLI options:
|
|
|
166
179
|
}
|
|
167
180
|
```
|
|
168
181
|
|
|
182
|
+
> The `--from dryhack-mcp[browser]` args ensure the `browser` tool's Playwright
|
|
183
|
+
> dependency is present. The browser binary still auto-installs on first use
|
|
184
|
+
> (`DRYHACK_BROWSER_AUTO_INSTALL`, default on).
|
|
185
|
+
|
|
169
186
|
### http
|
|
170
187
|
|
|
171
188
|
```json
|
|
@@ -28,13 +28,19 @@ With [uv](https://docs.astral.sh/uv/) you can run the server directly from PyPI
|
|
|
28
28
|
— no manual install needed:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
|
|
31
|
+
# recommended: pull the browser extra so the `browser` tool is guaranteed
|
|
32
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
32
33
|
# http transport
|
|
33
|
-
uvx dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
34
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
34
35
|
# pin a version
|
|
35
|
-
uvx dryhack-mcp
|
|
36
|
+
uvx --from 'dryhack-mcp[browser]==2.3.2' dryhack-mcp
|
|
36
37
|
```
|
|
37
38
|
|
|
39
|
+
> `uvx dryhack-mcp` (without `--from`) also works — Playwright is a core
|
|
40
|
+
> dependency — but the explicit `--from 'dryhack-mcp[browser]'` form is the
|
|
41
|
+
> recommended, unambiguous way to get everything the `browser` tool needs even
|
|
42
|
+
> if a stale cache is involved.
|
|
43
|
+
|
|
38
44
|
For development:
|
|
39
45
|
|
|
40
46
|
```bash
|
|
@@ -44,10 +50,17 @@ python3 -m pip install -e ".[dev]"
|
|
|
44
50
|
### Browser tool (Playwright)
|
|
45
51
|
|
|
46
52
|
Playwright is a **core dependency**, so a plain `pip install dryhack-mcp` /
|
|
47
|
-
`uvx dryhack-mcp` already ships it
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
53
|
+
`uvx dryhack-mcp` already ships it. For MCP clients using uvx, the recommended,
|
|
54
|
+
unambiguous invocation is:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The browser *binaries* (Chromium/Firefox/WebKit) are not distributed via pip, so
|
|
61
|
+
the first time the `browser` tool launches an engine it **auto-runs
|
|
62
|
+
`playwright install <browser>`** if the binary is missing (you'll see a
|
|
63
|
+
`[setup] auto-installed ...` note in the tool output on that first call).
|
|
51
64
|
|
|
52
65
|
To pre-install the binaries (optional, avoids the one-time first-use download):
|
|
53
66
|
|
|
@@ -111,7 +124,7 @@ CLI options:
|
|
|
111
124
|
"mcpServers": {
|
|
112
125
|
"dryhack": {
|
|
113
126
|
"command": "uvx",
|
|
114
|
-
"args": ["dryhack-mcp"],
|
|
127
|
+
"args": ["--from", "dryhack-mcp[browser]", "dryhack-mcp"],
|
|
115
128
|
"env": {
|
|
116
129
|
"DRYHACK_COMMAND_TIMEOUT": "120"
|
|
117
130
|
}
|
|
@@ -120,6 +133,10 @@ CLI options:
|
|
|
120
133
|
}
|
|
121
134
|
```
|
|
122
135
|
|
|
136
|
+
> The `--from dryhack-mcp[browser]` args ensure the `browser` tool's Playwright
|
|
137
|
+
> dependency is present. The browser binary still auto-installs on first use
|
|
138
|
+
> (`DRYHACK_BROWSER_AUTO_INSTALL`, default on).
|
|
139
|
+
|
|
123
140
|
### http
|
|
124
141
|
|
|
125
142
|
```json
|
|
@@ -449,8 +449,18 @@ def _auto_install_browser(browser_name: str) -> Tuple[bool, str]:
|
|
|
449
449
|
return False, f"{type(exc).__name__}: {exc}"
|
|
450
450
|
|
|
451
451
|
|
|
452
|
-
def _launch_browser(
|
|
453
|
-
|
|
452
|
+
def _launch_browser(
|
|
453
|
+
browser_type,
|
|
454
|
+
browser_name: str,
|
|
455
|
+
launch_kwargs: Dict[str, Any],
|
|
456
|
+
session: Optional[Dict[str, Any]] = None,
|
|
457
|
+
):
|
|
458
|
+
"""Launch the browser, auto-installing its binary on first use if missing.
|
|
459
|
+
|
|
460
|
+
On a successful on-demand install, records a human-readable note in
|
|
461
|
+
``session["_install_note"]`` so the caller can surface the one-time download
|
|
462
|
+
in the tool output (otherwise the first call looks like a silent hang).
|
|
463
|
+
"""
|
|
454
464
|
try:
|
|
455
465
|
return browser_type.launch(**launch_kwargs)
|
|
456
466
|
except Exception as exc: # noqa: BLE001
|
|
@@ -463,6 +473,11 @@ def _launch_browser(browser_type, browser_name: str, launch_kwargs: Dict[str, An
|
|
|
463
473
|
if binary_missing and config.BROWSER_AUTO_INSTALL:
|
|
464
474
|
ok, log = _auto_install_browser(browser_name)
|
|
465
475
|
if ok:
|
|
476
|
+
if session is not None:
|
|
477
|
+
session["_install_note"] = (
|
|
478
|
+
f"auto-installed the '{browser_name}' browser binary on "
|
|
479
|
+
"first use (one-time download)"
|
|
480
|
+
)
|
|
466
481
|
return browser_type.launch(**launch_kwargs)
|
|
467
482
|
raise RuntimeError(
|
|
468
483
|
f"{browser_name} binary missing and auto-install failed. "
|
|
@@ -504,7 +519,7 @@ def _run_session(
|
|
|
504
519
|
if session.get("proxy") is not None:
|
|
505
520
|
launch_kwargs["proxy"] = session["proxy"]
|
|
506
521
|
|
|
507
|
-
browser = _launch_browser(browser_type, browser_name, launch_kwargs)
|
|
522
|
+
browser = _launch_browser(browser_type, browser_name, launch_kwargs, session)
|
|
508
523
|
try:
|
|
509
524
|
context = browser.new_context(**_build_context_options(session))
|
|
510
525
|
if default_timeout is not None:
|
|
@@ -690,6 +705,9 @@ def browser(
|
|
|
690
705
|
f"$ browser[{session['browser_type']}, "
|
|
691
706
|
f"headless={session['headless']}] {len(normalized)} step(s)"
|
|
692
707
|
)
|
|
708
|
+
install_note = session.get("_install_note")
|
|
709
|
+
if install_note:
|
|
710
|
+
header = f"{header}\n[setup] {install_note}"
|
|
693
711
|
if error:
|
|
694
712
|
return f"{header}\n[error] {error}"
|
|
695
713
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dryhack-mcp
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: DryHack-MCP - an MCP server providing offensive-security tooling (curl, python, shell, Playwright browser) and a scope-gated authorize engine for authorized penetration testing.
|
|
5
5
|
Author: Afrizal F.A
|
|
6
6
|
License: MIT License
|
|
@@ -74,13 +74,19 @@ With [uv](https://docs.astral.sh/uv/) you can run the server directly from PyPI
|
|
|
74
74
|
— no manual install needed:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
|
|
77
|
+
# recommended: pull the browser extra so the `browser` tool is guaranteed
|
|
78
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
78
79
|
# http transport
|
|
79
|
-
uvx dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
80
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp --transport http --host 0.0.0.0 --port 8000
|
|
80
81
|
# pin a version
|
|
81
|
-
uvx dryhack-mcp
|
|
82
|
+
uvx --from 'dryhack-mcp[browser]==2.3.2' dryhack-mcp
|
|
82
83
|
```
|
|
83
84
|
|
|
85
|
+
> `uvx dryhack-mcp` (without `--from`) also works — Playwright is a core
|
|
86
|
+
> dependency — but the explicit `--from 'dryhack-mcp[browser]'` form is the
|
|
87
|
+
> recommended, unambiguous way to get everything the `browser` tool needs even
|
|
88
|
+
> if a stale cache is involved.
|
|
89
|
+
|
|
84
90
|
For development:
|
|
85
91
|
|
|
86
92
|
```bash
|
|
@@ -90,10 +96,17 @@ python3 -m pip install -e ".[dev]"
|
|
|
90
96
|
### Browser tool (Playwright)
|
|
91
97
|
|
|
92
98
|
Playwright is a **core dependency**, so a plain `pip install dryhack-mcp` /
|
|
93
|
-
`uvx dryhack-mcp` already ships it
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
99
|
+
`uvx dryhack-mcp` already ships it. For MCP clients using uvx, the recommended,
|
|
100
|
+
unambiguous invocation is:
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
uvx --from 'dryhack-mcp[browser]' dryhack-mcp
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
The browser *binaries* (Chromium/Firefox/WebKit) are not distributed via pip, so
|
|
107
|
+
the first time the `browser` tool launches an engine it **auto-runs
|
|
108
|
+
`playwright install <browser>`** if the binary is missing (you'll see a
|
|
109
|
+
`[setup] auto-installed ...` note in the tool output on that first call).
|
|
97
110
|
|
|
98
111
|
To pre-install the binaries (optional, avoids the one-time first-use download):
|
|
99
112
|
|
|
@@ -157,7 +170,7 @@ CLI options:
|
|
|
157
170
|
"mcpServers": {
|
|
158
171
|
"dryhack": {
|
|
159
172
|
"command": "uvx",
|
|
160
|
-
"args": ["dryhack-mcp"],
|
|
173
|
+
"args": ["--from", "dryhack-mcp[browser]", "dryhack-mcp"],
|
|
161
174
|
"env": {
|
|
162
175
|
"DRYHACK_COMMAND_TIMEOUT": "120"
|
|
163
176
|
}
|
|
@@ -166,6 +179,10 @@ CLI options:
|
|
|
166
179
|
}
|
|
167
180
|
```
|
|
168
181
|
|
|
182
|
+
> The `--from dryhack-mcp[browser]` args ensure the `browser` tool's Playwright
|
|
183
|
+
> dependency is present. The browser binary still auto-installs on first use
|
|
184
|
+
> (`DRYHACK_BROWSER_AUTO_INSTALL`, default on).
|
|
185
|
+
|
|
169
186
|
### http
|
|
170
187
|
|
|
171
188
|
```json
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "dryhack-mcp"
|
|
7
|
-
version = "2.3.
|
|
7
|
+
version = "2.3.2"
|
|
8
8
|
description = "DryHack-MCP - an MCP server providing offensive-security tooling (curl, python, shell, Playwright browser) and a scope-gated authorize engine for authorized penetration testing."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { file = "LICENSE" }
|
|
@@ -124,6 +124,30 @@ class BrowserToolTests(unittest.TestCase):
|
|
|
124
124
|
self.assertEqual(calls["launch"], 2) # failed once, retried once
|
|
125
125
|
self.assertEqual(calls["install"], 1)
|
|
126
126
|
|
|
127
|
+
def test_launch_records_install_note_in_session(self):
|
|
128
|
+
state = {"launch": 0}
|
|
129
|
+
|
|
130
|
+
class FakeBrowserType:
|
|
131
|
+
def launch(self, **kwargs):
|
|
132
|
+
state["launch"] += 1
|
|
133
|
+
if state["launch"] == 1:
|
|
134
|
+
raise RuntimeError("Executable doesn't exist; playwright install")
|
|
135
|
+
return "handle"
|
|
136
|
+
|
|
137
|
+
session = {}
|
|
138
|
+
original = browser_tool._auto_install_browser
|
|
139
|
+
browser_tool._auto_install_browser = lambda name: (True, "ok")
|
|
140
|
+
original_flag = browser_tool.config.BROWSER_AUTO_INSTALL
|
|
141
|
+
browser_tool.config.BROWSER_AUTO_INSTALL = True
|
|
142
|
+
try:
|
|
143
|
+
browser_tool._launch_browser(FakeBrowserType(), "chromium", {}, session)
|
|
144
|
+
finally:
|
|
145
|
+
browser_tool._auto_install_browser = original
|
|
146
|
+
browser_tool.config.BROWSER_AUTO_INSTALL = original_flag
|
|
147
|
+
|
|
148
|
+
self.assertIn("_install_note", session)
|
|
149
|
+
self.assertIn("chromium", session["_install_note"])
|
|
150
|
+
|
|
127
151
|
def test_launch_does_not_autoinstall_when_disabled(self):
|
|
128
152
|
class FakeBrowserType:
|
|
129
153
|
def launch(self, **kwargs):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|