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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4771fcbedbfd9b7b399fe26a494fb532a7de06a43fc94542fa46aa5e6180d474
4
- data.tar.gz: d52db121156f2b816656236c92a3d3d2ac49d9f055f7d9e20ed4b9dcbd9e89ef
3
+ metadata.gz: 5d114209ba5bb5fa541f65747389b48c4b074bf947482697e9438994a2fca193
4
+ data.tar.gz: 27bac72f974e0af3397d8a6da864f8672a6f8a5535c7bc5a8eb34821cd23cf2c
5
5
  SHA512:
6
- metadata.gz: ffb2ba2fd2519eee9b10b9f278bf539d9236e38761ee7aae5b5625dac7b48c7bf09d202a9fd9617c7b7b1cf054d4df8a636af1041ed6b28d64ad78540004910a
7
- data.tar.gz: f32e845e2211187384d3230e0c883c15218a46c4c46ccb8af5847a1739117f06f6ca33c288f812749e9a4565cb56051f399023bf5499c82219247a906f2fc9ad
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": "ruby",
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
- ## Sessions
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
- module_function
12
-
13
- # Returns all stealth tools
14
- # @param sessions [SessionManager]
15
- # @param config [Configuration]
16
- # @return [Array<MCP::Tool>]
17
- def tools(sessions, config)
18
- [
19
- enable_stealth_tool(sessions, config),
20
- disable_stealth_tool(sessions),
21
- get_stealth_status_tool(sessions),
22
- set_stealth_profile_tool(sessions, config)
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
- # Tool: Enable stealth mode for a session
27
- def enable_stealth_tool(sessions, _config)
28
- MCP::Tool.new(
29
- name: 'enable_stealth',
30
- description: 'Enable stealth mode for a browser session. Stealth mode applies various ' \
31
- 'evasion techniques to make the browser appear as a regular user browser ' \
32
- 'to bot detection systems.',
33
- input_schema: {
34
- type: 'object',
35
- properties: {
36
- session: {
37
- type: 'string',
38
- description: 'Session name',
39
- default: 'default'
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
- profile: {
42
- type: 'string',
43
- enum: VALID_PROFILES,
44
- description: 'Stealth profile: minimal (basic evasions), moderate (common evasions), ' \
45
- 'or maximum (all evasions for strictest detection)'
46
- }
47
- },
48
- required: []
49
- }
50
- ) do |args|
51
- session = args['session'] || 'default'
52
- profile = args['profile']&.to_sym
53
-
54
- sessions.enable_stealth(session, profile: profile)
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
- sessions.disable_stealth(session)
86
-
87
- build_result("Stealth mode disabled for session '#{session}'")
88
- rescue Crucible::SessionNotFoundError => e
89
- build_error(e.message)
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
- info = sessions.stealth_info(session)
113
-
114
- status = {
115
- session: session,
116
- stealth_enabled: info[:enabled],
117
- profile: info[:profile]
118
- }
119
-
120
- build_result(JSON.pretty_generate(status))
121
- rescue Crucible::SessionNotFoundError => e
122
- build_error(e.message)
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
- # Tool: Set stealth profile (change profile for existing session)
127
- def set_stealth_profile_tool(sessions, _config)
128
- MCP::Tool.new(
129
- name: 'set_stealth_profile',
130
- description: 'Change the stealth profile for a session. This enables stealth mode with ' \
131
- 'the new profile if not already enabled.',
132
- input_schema: {
133
- type: 'object',
134
- properties: {
135
- session: {
136
- type: 'string',
137
- description: 'Session name',
138
- default: 'default'
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
- profile: {
141
- type: 'string',
142
- enum: VALID_PROFILES,
143
- description: 'Stealth profile: minimal, moderate, or maximum'
144
- }
145
- },
146
- required: ['profile']
147
- }
148
- ) do |args|
149
- session = args['session'] || 'default'
150
- profile = args['profile']&.to_sym
151
-
152
- unless VALID_PROFILES.include?(args['profile'])
153
- raise Crucible::Error, "Invalid profile: #{args['profile']}. Must be one of: #{VALID_PROFILES.join(', ')}"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Crucible
4
- VERSION = '0.1.4'
4
+ VERSION = '0.1.5'
5
5
  end
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
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: 2026-01-03 00:00:00.000000000 Z
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: 3.5.22
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: []