crucible 0.1.2

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +102 -0
  4. data/Gemfile +10 -0
  5. data/LICENSE +21 -0
  6. data/README.md +366 -0
  7. data/Rakefile +23 -0
  8. data/TESTING.md +319 -0
  9. data/config.sample.yml +48 -0
  10. data/crucible.gemspec +48 -0
  11. data/exe/crucible +122 -0
  12. data/lib/crucible/configuration.rb +212 -0
  13. data/lib/crucible/server.rb +123 -0
  14. data/lib/crucible/session_manager.rb +209 -0
  15. data/lib/crucible/stealth/evasions/chrome_app.js +75 -0
  16. data/lib/crucible/stealth/evasions/chrome_csi.js +33 -0
  17. data/lib/crucible/stealth/evasions/chrome_load_times.js +44 -0
  18. data/lib/crucible/stealth/evasions/chrome_runtime.js +190 -0
  19. data/lib/crucible/stealth/evasions/iframe_content_window.js +101 -0
  20. data/lib/crucible/stealth/evasions/media_codecs.js +65 -0
  21. data/lib/crucible/stealth/evasions/navigator_hardware_concurrency.js +18 -0
  22. data/lib/crucible/stealth/evasions/navigator_languages.js +18 -0
  23. data/lib/crucible/stealth/evasions/navigator_permissions.js +53 -0
  24. data/lib/crucible/stealth/evasions/navigator_plugins.js +261 -0
  25. data/lib/crucible/stealth/evasions/navigator_vendor.js +18 -0
  26. data/lib/crucible/stealth/evasions/navigator_webdriver.js +16 -0
  27. data/lib/crucible/stealth/evasions/webgl_vendor.js +43 -0
  28. data/lib/crucible/stealth/evasions/window_outerdimensions.js +18 -0
  29. data/lib/crucible/stealth/utils.js +266 -0
  30. data/lib/crucible/stealth.rb +213 -0
  31. data/lib/crucible/tools/cookies.rb +206 -0
  32. data/lib/crucible/tools/downloads.rb +273 -0
  33. data/lib/crucible/tools/extraction.rb +335 -0
  34. data/lib/crucible/tools/helpers.rb +46 -0
  35. data/lib/crucible/tools/interaction.rb +355 -0
  36. data/lib/crucible/tools/navigation.rb +181 -0
  37. data/lib/crucible/tools/sessions.rb +85 -0
  38. data/lib/crucible/tools/stealth.rb +167 -0
  39. data/lib/crucible/tools.rb +42 -0
  40. data/lib/crucible/version.rb +5 -0
  41. data/lib/crucible.rb +60 -0
  42. metadata +201 -0
@@ -0,0 +1,167 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crucible
4
+ module Tools
5
+ # MCP tools for stealth mode management
6
+ module Stealth
7
+ extend Helpers
8
+
9
+ VALID_PROFILES = %w[minimal moderate maximum].freeze
10
+
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
25
+
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'
40
+ },
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)
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
+
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)
90
+ 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
+
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)
123
+ end
124
+ end
125
+
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'
139
+ },
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(', ')}"
154
+ 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
+ end
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crucible
4
+ # Tool registry and loader for MCP browser automation tools
5
+ #
6
+ # Tools are organized by domain:
7
+ # - Navigation: navigate, wait_for, back, forward, refresh
8
+ # - Interaction: click, type, fill_form, select_option, scroll, hover
9
+ # - Extraction: screenshot, get_content, pdf, evaluate, get_url, get_title
10
+ # - Cookies: get_cookies, set_cookies, clear_cookies
11
+ # - Sessions: list_sessions, close_session
12
+ # - Downloads: set_download_path, list_downloads, wait_for_download, clear_downloads
13
+ #
14
+ module Tools
15
+ autoload :Helpers, 'crucible/tools/helpers'
16
+ autoload :Navigation, 'crucible/tools/navigation'
17
+ autoload :Interaction, 'crucible/tools/interaction'
18
+ autoload :Extraction, 'crucible/tools/extraction'
19
+ autoload :Cookies, 'crucible/tools/cookies'
20
+ autoload :Sessions, 'crucible/tools/sessions'
21
+ autoload :Downloads, 'crucible/tools/downloads'
22
+ autoload :Stealth, 'crucible/tools/stealth'
23
+
24
+ class << self
25
+ # Returns all tool definitions for the MCP server
26
+ # @param session_manager [SessionManager] the session manager instance
27
+ # @param config [Configuration] the server configuration
28
+ # @return [Array] array of MCP tool definitions
29
+ def all(session_manager, config)
30
+ [
31
+ *Navigation.tools(session_manager, config),
32
+ *Interaction.tools(session_manager, config),
33
+ *Extraction.tools(session_manager, config),
34
+ *Cookies.tools(session_manager, config),
35
+ *Sessions.tools(session_manager, config),
36
+ *Downloads.tools(session_manager, config),
37
+ *Stealth.tools(session_manager, config)
38
+ ]
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Crucible
4
+ VERSION = '0.1.2'
5
+ end
data/lib/crucible.rb ADDED
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'crucible/version'
4
+
5
+ # Crucible: MCP server for browser automation using Ferrum/Chrome
6
+ #
7
+ # Provides browser automation tools for AI agents via the Model Context Protocol.
8
+ # Supports multiple named sessions, navigation, screenshots, form interaction,
9
+ # JavaScript evaluation, cookie management, and PDF generation.
10
+ #
11
+ # @example Basic usage
12
+ # Crucible.configure do |config|
13
+ # config.headless = true
14
+ # config.timeout = 30
15
+ # end
16
+ # Crucible.run
17
+ #
18
+ module Crucible
19
+ class Error < StandardError; end
20
+ class SessionNotFoundError < Error; end
21
+ class ElementNotFoundError < Error; end
22
+ class TimeoutError < Error; end
23
+ class BrowserError < Error; end
24
+
25
+ autoload :Configuration, 'crucible/configuration'
26
+ autoload :Server, 'crucible/server'
27
+ autoload :SessionManager, 'crucible/session_manager'
28
+ autoload :Stealth, 'crucible/stealth'
29
+ autoload :Tools, 'crucible/tools'
30
+
31
+ class << self
32
+ attr_writer :configuration
33
+
34
+ # Returns the current configuration, initializing with defaults if needed
35
+ # @return [Configuration]
36
+ def configuration
37
+ @configuration ||= Configuration.new
38
+ end
39
+
40
+ # Yields the configuration for modification
41
+ # @yield [Configuration] the configuration instance
42
+ # @return [Configuration]
43
+ def configure
44
+ yield(configuration) if block_given?
45
+ configuration
46
+ end
47
+
48
+ # Starts the MCP server with the given options
49
+ # @param options [Hash] configuration overrides
50
+ def run(**options)
51
+ config = options.empty? ? configuration : configuration.merge(options)
52
+ Server.new(config).run
53
+ end
54
+
55
+ # Resets configuration to defaults (mainly for testing)
56
+ def reset!
57
+ @configuration = nil
58
+ end
59
+ end
60
+ end
metadata ADDED
@@ -0,0 +1,201 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: crucible
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Josh Frye
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: ferrum
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: mcp
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rake
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rspec
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rubocop-rake
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rubocop-rspec
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: simplecov
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ description: |
125
+ An MCP (Model Context Protocol) server that provides browser automation tools
126
+ for AI agents using Ferrum and headless Chrome. Features 25 tools covering
127
+ navigation, screenshots, form interaction, JavaScript evaluation, cookies,
128
+ file downloads, and multi-session management.
129
+ email:
130
+ - me@joshfrye.dev
131
+ executables:
132
+ - crucible
133
+ extensions: []
134
+ extra_rdoc_files: []
135
+ files:
136
+ - ".rspec"
137
+ - ".rubocop.yml"
138
+ - Gemfile
139
+ - LICENSE
140
+ - README.md
141
+ - Rakefile
142
+ - TESTING.md
143
+ - config.sample.yml
144
+ - crucible.gemspec
145
+ - exe/crucible
146
+ - lib/crucible.rb
147
+ - lib/crucible/configuration.rb
148
+ - lib/crucible/server.rb
149
+ - lib/crucible/session_manager.rb
150
+ - lib/crucible/stealth.rb
151
+ - lib/crucible/stealth/evasions/chrome_app.js
152
+ - lib/crucible/stealth/evasions/chrome_csi.js
153
+ - lib/crucible/stealth/evasions/chrome_load_times.js
154
+ - lib/crucible/stealth/evasions/chrome_runtime.js
155
+ - lib/crucible/stealth/evasions/iframe_content_window.js
156
+ - lib/crucible/stealth/evasions/media_codecs.js
157
+ - lib/crucible/stealth/evasions/navigator_hardware_concurrency.js
158
+ - lib/crucible/stealth/evasions/navigator_languages.js
159
+ - lib/crucible/stealth/evasions/navigator_permissions.js
160
+ - lib/crucible/stealth/evasions/navigator_plugins.js
161
+ - lib/crucible/stealth/evasions/navigator_vendor.js
162
+ - lib/crucible/stealth/evasions/navigator_webdriver.js
163
+ - lib/crucible/stealth/evasions/webgl_vendor.js
164
+ - lib/crucible/stealth/evasions/window_outerdimensions.js
165
+ - lib/crucible/stealth/utils.js
166
+ - lib/crucible/tools.rb
167
+ - lib/crucible/tools/cookies.rb
168
+ - lib/crucible/tools/downloads.rb
169
+ - lib/crucible/tools/extraction.rb
170
+ - lib/crucible/tools/helpers.rb
171
+ - lib/crucible/tools/interaction.rb
172
+ - lib/crucible/tools/navigation.rb
173
+ - lib/crucible/tools/sessions.rb
174
+ - lib/crucible/tools/stealth.rb
175
+ - lib/crucible/version.rb
176
+ homepage: https://github.com/joshfng/crucible
177
+ licenses:
178
+ - MIT
179
+ metadata:
180
+ homepage_uri: https://github.com/joshfng/crucible
181
+ source_code_uri: https://github.com/joshfng/crucible
182
+ changelog_uri: https://github.com/joshfng/crucible/blob/main/CHANGELOG.md
183
+ rubygems_mfa_required: 'true'
184
+ rdoc_options: []
185
+ require_paths:
186
+ - lib
187
+ required_ruby_version: !ruby/object:Gem::Requirement
188
+ requirements:
189
+ - - ">="
190
+ - !ruby/object:Gem::Version
191
+ version: 3.2.0
192
+ required_rubygems_version: !ruby/object:Gem::Requirement
193
+ requirements:
194
+ - - ">="
195
+ - !ruby/object:Gem::Version
196
+ version: '0'
197
+ requirements: []
198
+ rubygems_version: 4.0.3
199
+ specification_version: 4
200
+ summary: MCP server for browser automation using Ferrum/Chrome
201
+ test_files: []