crucible 0.1.4 → 0.1.5
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.
- checksums.yaml +4 -4
- data/README.md +21 -15
- data/lib/crucible/tools/stealth.rb +144 -144
- data/lib/crucible/version.rb +1 -1
- metadata +3 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5d114209ba5bb5fa541f65747389b48c4b074bf947482697e9438994a2fca193
|
|
4
|
+
data.tar.gz: 27bac72f974e0af3397d8a6da864f8672a6f8a5535c7bc5a8eb34821cd23cf2c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bffe986b41fb2150493f5164ed1ad64bfbd5bf573cfc546d46ad88a4008b818502b60ddbb294ab7e327b088998ae8636452ec6b67b44b1e792dec7364da59340
|
|
7
|
+
data.tar.gz: 84e6552d826c7abe0b72300764cd7cdd1c1ada144b0148f26117ca225f16117fd9b0cff9032b42c9cba12c56dbffd97fe9eb804efef1ab04912fcd452231926b
|
data/README.md
CHANGED
|
@@ -4,6 +4,12 @@ A Ruby MCP (Model Context Protocol) server for browser automation using [Ferrum]
|
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
```bash
|
|
8
|
+
gem install crucible
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Or from source:
|
|
12
|
+
|
|
7
13
|
```bash
|
|
8
14
|
git clone https://github.com/joshfng/crucible.git
|
|
9
15
|
cd crucible
|
|
@@ -50,18 +56,18 @@ bundle install
|
|
|
50
56
|
|
|
51
57
|
### Claude Code Integration
|
|
52
58
|
|
|
59
|
+
```bash
|
|
60
|
+
gem install crucible
|
|
61
|
+
```
|
|
62
|
+
|
|
53
63
|
Add to your Claude Code MCP settings (`~/.claude/settings.json`):
|
|
54
64
|
|
|
55
65
|
```json
|
|
56
66
|
{
|
|
57
67
|
"mcpServers": {
|
|
58
68
|
"crucible": {
|
|
59
|
-
"command": "
|
|
60
|
-
"args": [
|
|
61
|
-
"-I",
|
|
62
|
-
"/path/to/crucible/lib",
|
|
63
|
-
"/path/to/crucible/exe/crucible"
|
|
64
|
-
]
|
|
69
|
+
"command": "crucible",
|
|
70
|
+
"args": ["--config", "~/.config/crucible/config.yml"]
|
|
65
71
|
}
|
|
66
72
|
}
|
|
67
73
|
}
|
|
@@ -134,11 +140,11 @@ Add to your Claude Code MCP settings (`~/.claude/settings.json`):
|
|
|
134
140
|
| `get_stealth_status` | Get stealth mode status for a session |
|
|
135
141
|
| `set_stealth_profile` | Change the stealth profile (minimal/moderate/maximum) |
|
|
136
142
|
|
|
137
|
-
##
|
|
143
|
+
## Session Management
|
|
138
144
|
|
|
139
145
|
All tools accept an optional `session` parameter to manage multiple independent browser instances:
|
|
140
146
|
|
|
141
|
-
```
|
|
147
|
+
```ruby
|
|
142
148
|
# These run in separate browsers
|
|
143
149
|
navigate(session: "login-flow", url: "https://example.com/login")
|
|
144
150
|
navigate(session: "signup-flow", url: "https://example.com/signup")
|
|
@@ -160,7 +166,7 @@ Sessions are created automatically on first use and persist until explicitly clo
|
|
|
160
166
|
|
|
161
167
|
### Basic Navigation
|
|
162
168
|
|
|
163
|
-
```
|
|
169
|
+
```ruby
|
|
164
170
|
navigate(url: "https://example.com")
|
|
165
171
|
wait_for(selector: ".content")
|
|
166
172
|
get_content(format: "text")
|
|
@@ -168,7 +174,7 @@ get_content(format: "text")
|
|
|
168
174
|
|
|
169
175
|
### Form Submission
|
|
170
176
|
|
|
171
|
-
```
|
|
177
|
+
```ruby
|
|
172
178
|
navigate(url: "https://example.com/login")
|
|
173
179
|
type(selector: "#email", text: "user@example.com")
|
|
174
180
|
type(selector: "#password", text: "secret123")
|
|
@@ -178,7 +184,7 @@ wait_for(selector: ".dashboard")
|
|
|
178
184
|
|
|
179
185
|
### Screenshots & PDFs
|
|
180
186
|
|
|
181
|
-
```
|
|
187
|
+
```ruby
|
|
182
188
|
# Viewport screenshot (returns base64)
|
|
183
189
|
screenshot()
|
|
184
190
|
|
|
@@ -200,7 +206,7 @@ pdf(format: "Letter", landscape: true) # Custom format
|
|
|
200
206
|
|
|
201
207
|
### JavaScript Execution
|
|
202
208
|
|
|
203
|
-
```
|
|
209
|
+
```ruby
|
|
204
210
|
# Get page dimensions
|
|
205
211
|
evaluate(expression: "[window.innerWidth, window.innerHeight]")
|
|
206
212
|
|
|
@@ -213,7 +219,7 @@ evaluate(expression: "document.querySelectorAll('a').length")
|
|
|
213
219
|
|
|
214
220
|
### File Downloads
|
|
215
221
|
|
|
216
|
-
```
|
|
222
|
+
```ruby
|
|
217
223
|
# Set download directory
|
|
218
224
|
set_download_path(path: "/tmp/downloads")
|
|
219
225
|
|
|
@@ -262,7 +268,7 @@ The stealth module includes evasions ported from [puppeteer-extra](https://githu
|
|
|
262
268
|
|
|
263
269
|
### Runtime Control
|
|
264
270
|
|
|
265
|
-
```
|
|
271
|
+
```ruby
|
|
266
272
|
# Check stealth status
|
|
267
273
|
get_stealth_status(session: "default")
|
|
268
274
|
|
|
@@ -296,7 +302,7 @@ server:
|
|
|
296
302
|
|
|
297
303
|
## Project Structure
|
|
298
304
|
|
|
299
|
-
```
|
|
305
|
+
```text
|
|
300
306
|
crucible/
|
|
301
307
|
├── exe/crucible # CLI executable
|
|
302
308
|
├── crucible.gemspec # Gem specification
|
|
@@ -4,162 +4,162 @@ module Crucible
|
|
|
4
4
|
module Tools
|
|
5
5
|
# MCP tools for stealth mode management
|
|
6
6
|
module Stealth
|
|
7
|
-
extend Helpers
|
|
8
|
-
|
|
9
7
|
VALID_PROFILES = %w[minimal moderate maximum].freeze
|
|
10
8
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
end
|
|
9
|
+
class << self
|
|
10
|
+
# Returns all stealth tools
|
|
11
|
+
# @param sessions [SessionManager]
|
|
12
|
+
# @param config [Configuration]
|
|
13
|
+
# @return [Array<MCP::Tool>]
|
|
14
|
+
def tools(sessions, config)
|
|
15
|
+
[
|
|
16
|
+
enable_stealth_tool(sessions, config),
|
|
17
|
+
disable_stealth_tool(sessions),
|
|
18
|
+
get_stealth_status_tool(sessions),
|
|
19
|
+
set_stealth_profile_tool(sessions, config)
|
|
20
|
+
]
|
|
21
|
+
end
|
|
25
22
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
# Tool: Enable stealth mode for a session
|
|
26
|
+
def enable_stealth_tool(sessions, _config)
|
|
27
|
+
MCP::Tool.define(
|
|
28
|
+
name: 'enable_stealth',
|
|
29
|
+
description: 'Enable stealth mode for a browser session. Stealth mode applies various ' \
|
|
30
|
+
'evasion techniques to make the browser appear as a regular user browser ' \
|
|
31
|
+
'to bot detection systems.',
|
|
32
|
+
input_schema: {
|
|
33
|
+
type: 'object',
|
|
34
|
+
properties: {
|
|
35
|
+
session: {
|
|
36
|
+
type: 'string',
|
|
37
|
+
description: 'Session name',
|
|
38
|
+
default: 'default'
|
|
39
|
+
},
|
|
40
|
+
profile: {
|
|
41
|
+
type: 'string',
|
|
42
|
+
enum: VALID_PROFILES,
|
|
43
|
+
description: 'Stealth profile: minimal (basic evasions), moderate (common evasions), ' \
|
|
44
|
+
'or maximum (all evasions for strictest detection)'
|
|
45
|
+
}
|
|
40
46
|
},
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
info = sessions.stealth_info(session)
|
|
57
|
-
build_result("Stealth mode enabled for session '#{session}' with profile: #{info[:profile]}")
|
|
58
|
-
rescue Crucible::SessionNotFoundError => e
|
|
59
|
-
build_error(e.message)
|
|
60
|
-
rescue Crucible::Error => e
|
|
61
|
-
build_error(e.message)
|
|
47
|
+
required: []
|
|
48
|
+
}
|
|
49
|
+
) do |session: 'default', profile: nil, **|
|
|
50
|
+
profile_sym = profile&.to_sym
|
|
51
|
+
|
|
52
|
+
sessions.enable_stealth(session, profile: profile_sym)
|
|
53
|
+
|
|
54
|
+
info = sessions.stealth_info(session)
|
|
55
|
+
MCP::Tool::Response.new([{
|
|
56
|
+
type: 'text',
|
|
57
|
+
text: "Stealth mode enabled for session '#{session}' with profile: #{info[:profile]}"
|
|
58
|
+
}])
|
|
59
|
+
rescue Crucible::SessionNotFoundError, Crucible::Error => e
|
|
60
|
+
MCP::Tool::Response.new([{ type: 'text', text: e.message }], is_error: true)
|
|
61
|
+
end
|
|
62
62
|
end
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Tool: Disable stealth mode for a session
|
|
66
|
-
def disable_stealth_tool(sessions)
|
|
67
|
-
MCP::Tool.new(
|
|
68
|
-
name: 'disable_stealth',
|
|
69
|
-
description: 'Disable stealth mode for a browser session. Note: Already-applied evasions ' \
|
|
70
|
-
'cannot be removed, but this prevents new evasions from being applied.',
|
|
71
|
-
input_schema: {
|
|
72
|
-
type: 'object',
|
|
73
|
-
properties: {
|
|
74
|
-
session: {
|
|
75
|
-
type: 'string',
|
|
76
|
-
description: 'Session name',
|
|
77
|
-
default: 'default'
|
|
78
|
-
}
|
|
79
|
-
},
|
|
80
|
-
required: []
|
|
81
|
-
}
|
|
82
|
-
) do |args|
|
|
83
|
-
session = args['session'] || 'default'
|
|
84
63
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
64
|
+
# Tool: Disable stealth mode for a session
|
|
65
|
+
def disable_stealth_tool(sessions)
|
|
66
|
+
MCP::Tool.define(
|
|
67
|
+
name: 'disable_stealth',
|
|
68
|
+
description: 'Disable stealth mode for a browser session. Note: Already-applied evasions ' \
|
|
69
|
+
'cannot be removed, but this prevents new evasions from being applied.',
|
|
70
|
+
input_schema: {
|
|
71
|
+
type: 'object',
|
|
72
|
+
properties: {
|
|
73
|
+
session: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Session name',
|
|
76
|
+
default: 'default'
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
required: []
|
|
80
|
+
}
|
|
81
|
+
) do |session: 'default', **|
|
|
82
|
+
sessions.disable_stealth(session)
|
|
83
|
+
|
|
84
|
+
MCP::Tool::Response.new([{
|
|
85
|
+
type: 'text',
|
|
86
|
+
text: "Stealth mode disabled for session '#{session}'"
|
|
87
|
+
}])
|
|
88
|
+
rescue Crucible::SessionNotFoundError => e
|
|
89
|
+
MCP::Tool::Response.new([{ type: 'text', text: e.message }], is_error: true)
|
|
90
|
+
end
|
|
90
91
|
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
# Tool: Get stealth status for a session
|
|
94
|
-
def get_stealth_status_tool(sessions)
|
|
95
|
-
MCP::Tool.new(
|
|
96
|
-
name: 'get_stealth_status',
|
|
97
|
-
description: 'Get the current stealth mode status for a browser session.',
|
|
98
|
-
input_schema: {
|
|
99
|
-
type: 'object',
|
|
100
|
-
properties: {
|
|
101
|
-
session: {
|
|
102
|
-
type: 'string',
|
|
103
|
-
description: 'Session name',
|
|
104
|
-
default: 'default'
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
required: []
|
|
108
|
-
}
|
|
109
|
-
) do |args|
|
|
110
|
-
session = args['session'] || 'default'
|
|
111
92
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
93
|
+
# Tool: Get stealth status for a session
|
|
94
|
+
def get_stealth_status_tool(sessions)
|
|
95
|
+
MCP::Tool.define(
|
|
96
|
+
name: 'get_stealth_status',
|
|
97
|
+
description: 'Get the current stealth mode status for a browser session.',
|
|
98
|
+
input_schema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
session: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
description: 'Session name',
|
|
104
|
+
default: 'default'
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
required: []
|
|
108
|
+
}
|
|
109
|
+
) do |session: 'default', **|
|
|
110
|
+
info = sessions.stealth_info(session)
|
|
111
|
+
|
|
112
|
+
status = {
|
|
113
|
+
session: session,
|
|
114
|
+
stealth_enabled: info[:enabled],
|
|
115
|
+
profile: info[:profile]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
MCP::Tool::Response.new([{
|
|
119
|
+
type: 'text',
|
|
120
|
+
text: JSON.pretty_generate(status)
|
|
121
|
+
}])
|
|
122
|
+
rescue Crucible::SessionNotFoundError => e
|
|
123
|
+
MCP::Tool::Response.new([{ type: 'text', text: e.message }], is_error: true)
|
|
124
|
+
end
|
|
123
125
|
end
|
|
124
|
-
end
|
|
125
126
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
127
|
+
# Tool: Set stealth profile (change profile for existing session)
|
|
128
|
+
def set_stealth_profile_tool(sessions, _config)
|
|
129
|
+
MCP::Tool.define(
|
|
130
|
+
name: 'set_stealth_profile',
|
|
131
|
+
description: 'Change the stealth profile for a session. This enables stealth mode with ' \
|
|
132
|
+
'the new profile if not already enabled.',
|
|
133
|
+
input_schema: {
|
|
134
|
+
type: 'object',
|
|
135
|
+
properties: {
|
|
136
|
+
session: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
description: 'Session name',
|
|
139
|
+
default: 'default'
|
|
140
|
+
},
|
|
141
|
+
profile: {
|
|
142
|
+
type: 'string',
|
|
143
|
+
enum: VALID_PROFILES,
|
|
144
|
+
description: 'Stealth profile: minimal, moderate, or maximum'
|
|
145
|
+
}
|
|
139
146
|
},
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
147
|
+
required: ['profile']
|
|
148
|
+
}
|
|
149
|
+
) do |profile:, session: 'default', **|
|
|
150
|
+
unless VALID_PROFILES.include?(profile)
|
|
151
|
+
raise Crucible::Error, "Invalid profile: #{profile}. Must be one of: #{VALID_PROFILES.join(', ')}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
sessions.enable_stealth(session, profile: profile.to_sym)
|
|
155
|
+
|
|
156
|
+
MCP::Tool::Response.new([{
|
|
157
|
+
type: 'text',
|
|
158
|
+
text: "Stealth profile set to '#{profile}' for session '#{session}'"
|
|
159
|
+
}])
|
|
160
|
+
rescue Crucible::SessionNotFoundError, Crucible::Error => e
|
|
161
|
+
MCP::Tool::Response.new([{ type: 'text', text: e.message }], is_error: true)
|
|
154
162
|
end
|
|
155
|
-
|
|
156
|
-
sessions.enable_stealth(session, profile: profile)
|
|
157
|
-
|
|
158
|
-
build_result("Stealth profile set to '#{profile}' for session '#{session}'")
|
|
159
|
-
rescue Crucible::SessionNotFoundError => e
|
|
160
|
-
build_error(e.message)
|
|
161
|
-
rescue Crucible::Error => e
|
|
162
|
-
build_error(e.message)
|
|
163
163
|
end
|
|
164
164
|
end
|
|
165
165
|
end
|
data/lib/crucible/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: crucible
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Josh Frye
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: exe
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
13
|
name: ferrum
|
|
@@ -182,7 +181,6 @@ metadata:
|
|
|
182
181
|
source_code_uri: https://github.com/joshfng/crucible
|
|
183
182
|
changelog_uri: https://github.com/joshfng/crucible/blob/main/CHANGELOG.md
|
|
184
183
|
rubygems_mfa_required: 'true'
|
|
185
|
-
post_install_message:
|
|
186
184
|
rdoc_options: []
|
|
187
185
|
require_paths:
|
|
188
186
|
- lib
|
|
@@ -197,8 +195,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
197
195
|
- !ruby/object:Gem::Version
|
|
198
196
|
version: '0'
|
|
199
197
|
requirements: []
|
|
200
|
-
rubygems_version:
|
|
201
|
-
signing_key:
|
|
198
|
+
rubygems_version: 4.0.3
|
|
202
199
|
specification_version: 4
|
|
203
200
|
summary: MCP server for browser automation using Ferrum/Chrome
|
|
204
201
|
test_files: []
|