ferrum-mcp 1.0.0

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 (63) hide show
  1. checksums.yaml +7 -0
  2. data/.env.example +90 -0
  3. data/CHANGELOG.md +229 -0
  4. data/CONTRIBUTING.md +469 -0
  5. data/LICENSE +21 -0
  6. data/README.md +334 -0
  7. data/SECURITY.md +286 -0
  8. data/bin/ferrum-mcp +66 -0
  9. data/bin/lint +10 -0
  10. data/bin/serve +3 -0
  11. data/bin/test +4 -0
  12. data/docs/API_REFERENCE.md +1410 -0
  13. data/docs/CONFIGURATION.md +254 -0
  14. data/docs/DEPLOYMENT.md +846 -0
  15. data/docs/DOCKER.md +836 -0
  16. data/docs/DOCKER_BOTBROWSER.md +455 -0
  17. data/docs/GETTING_STARTED.md +249 -0
  18. data/docs/TROUBLESHOOTING.md +677 -0
  19. data/lib/ferrum_mcp/browser_manager.rb +101 -0
  20. data/lib/ferrum_mcp/cli/command_handler.rb +99 -0
  21. data/lib/ferrum_mcp/cli/server_runner.rb +166 -0
  22. data/lib/ferrum_mcp/configuration.rb +229 -0
  23. data/lib/ferrum_mcp/resource_manager.rb +223 -0
  24. data/lib/ferrum_mcp/server.rb +254 -0
  25. data/lib/ferrum_mcp/session.rb +227 -0
  26. data/lib/ferrum_mcp/session_manager.rb +183 -0
  27. data/lib/ferrum_mcp/tools/accept_cookies_tool.rb +458 -0
  28. data/lib/ferrum_mcp/tools/base_tool.rb +114 -0
  29. data/lib/ferrum_mcp/tools/clear_cookies_tool.rb +66 -0
  30. data/lib/ferrum_mcp/tools/click_tool.rb +218 -0
  31. data/lib/ferrum_mcp/tools/close_session_tool.rb +49 -0
  32. data/lib/ferrum_mcp/tools/create_session_tool.rb +146 -0
  33. data/lib/ferrum_mcp/tools/drag_and_drop_tool.rb +171 -0
  34. data/lib/ferrum_mcp/tools/evaluate_js_tool.rb +46 -0
  35. data/lib/ferrum_mcp/tools/execute_script_tool.rb +48 -0
  36. data/lib/ferrum_mcp/tools/fill_form_tool.rb +78 -0
  37. data/lib/ferrum_mcp/tools/find_by_text_tool.rb +153 -0
  38. data/lib/ferrum_mcp/tools/get_attribute_tool.rb +56 -0
  39. data/lib/ferrum_mcp/tools/get_cookies_tool.rb +70 -0
  40. data/lib/ferrum_mcp/tools/get_html_tool.rb +52 -0
  41. data/lib/ferrum_mcp/tools/get_session_info_tool.rb +40 -0
  42. data/lib/ferrum_mcp/tools/get_text_tool.rb +67 -0
  43. data/lib/ferrum_mcp/tools/get_title_tool.rb +42 -0
  44. data/lib/ferrum_mcp/tools/get_url_tool.rb +39 -0
  45. data/lib/ferrum_mcp/tools/go_back_tool.rb +49 -0
  46. data/lib/ferrum_mcp/tools/go_forward_tool.rb +49 -0
  47. data/lib/ferrum_mcp/tools/hover_tool.rb +76 -0
  48. data/lib/ferrum_mcp/tools/list_sessions_tool.rb +33 -0
  49. data/lib/ferrum_mcp/tools/navigate_tool.rb +59 -0
  50. data/lib/ferrum_mcp/tools/press_key_tool.rb +91 -0
  51. data/lib/ferrum_mcp/tools/query_shadow_dom_tool.rb +225 -0
  52. data/lib/ferrum_mcp/tools/refresh_tool.rb +49 -0
  53. data/lib/ferrum_mcp/tools/screenshot_tool.rb +121 -0
  54. data/lib/ferrum_mcp/tools/session_tool.rb +37 -0
  55. data/lib/ferrum_mcp/tools/set_cookie_tool.rb +77 -0
  56. data/lib/ferrum_mcp/tools/solve_captcha_tool.rb +528 -0
  57. data/lib/ferrum_mcp/transport/http_server.rb +93 -0
  58. data/lib/ferrum_mcp/transport/rate_limiter.rb +79 -0
  59. data/lib/ferrum_mcp/transport/stdio_server.rb +63 -0
  60. data/lib/ferrum_mcp/version.rb +5 -0
  61. data/lib/ferrum_mcp/whisper_service.rb +222 -0
  62. data/lib/ferrum_mcp.rb +35 -0
  63. metadata +248 -0
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module FerrumMCP
6
+ module Transport
7
+ # STDIO Server with MCP StdioTransport
8
+ class StdioServer
9
+ attr_reader :server, :config, :logger, :mcp_transport
10
+
11
+ def initialize(server, config)
12
+ @server = server
13
+ @config = config
14
+ @logger = config.logger
15
+ @mcp_transport = MCP::Server::Transports::StdioTransport.new(server.mcp_server)
16
+ server.mcp_server.transport = @mcp_transport
17
+ end
18
+
19
+ def start # rubocop:disable Metrics/AbcSize
20
+ logger.info 'Starting STDIO server'
21
+ logger.info 'Reading from STDIN and writing to STDOUT'
22
+
23
+ # Open the transport for stdio communication
24
+ @mcp_transport.open
25
+
26
+ # Read from stdin and process messages
27
+ loop do
28
+ line = $stdin.gets
29
+ break if line.nil? # EOF
30
+
31
+ begin
32
+ request = JSON.parse(line.strip)
33
+ response = @mcp_transport.handle_json_request(request)
34
+ $stdout.puts(response.to_json)
35
+ $stdout.flush
36
+ rescue JSON::ParserError => e
37
+ logger.error "Invalid JSON: #{e.message}"
38
+ error_response = {
39
+ jsonrpc: '2.0',
40
+ error: { code: -32_700, message: 'Parse error' },
41
+ id: nil
42
+ }
43
+ $stdout.puts(error_response.to_json)
44
+ $stdout.flush
45
+ rescue StandardError => e
46
+ logger.error "Request error: #{e.message}"
47
+ logger.error e.backtrace.join("\n")
48
+ end
49
+ end
50
+ rescue StandardError => e
51
+ logger.error "STDIO server error: #{e.message}"
52
+ logger.error e.backtrace.join("\n")
53
+ raise
54
+ end
55
+
56
+ def stop
57
+ logger.info 'Stopping STDIO server...'
58
+ @mcp_transport&.close
59
+ logger.info 'STDIO server stopped'
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module FerrumMCP
4
+ VERSION = '1.0.0'
5
+ end
@@ -0,0 +1,222 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'tempfile'
4
+ require 'open3'
5
+ require 'fileutils'
6
+ require 'net/http'
7
+ require 'uri'
8
+ require 'shellwords'
9
+
10
+ module FerrumMCP
11
+ # Service to handle Whisper speech recognition for CAPTCHA solving
12
+ # Uses whisper-cli (whisper.cpp) for fast, efficient transcription
13
+ class WhisperService
14
+ attr_reader :whisper_path, :model, :language, :logger
15
+
16
+ # Model URLs for whisper.cpp
17
+ MODEL_URLS = {
18
+ 'tiny' => 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-tiny.bin',
19
+ 'base' => 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.bin',
20
+ 'small' => 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-small.bin',
21
+ 'medium' => 'https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-medium.bin'
22
+ }.freeze
23
+
24
+ def initialize(model: nil, language: nil, logger: nil)
25
+ @whisper_path = ENV.fetch('WHISPER_PATH', 'whisper-cli')
26
+ @model = model || ENV.fetch('WHISPER_MODEL', 'base')
27
+ @language = language || ENV.fetch('WHISPER_LANGUAGE', 'en')
28
+ @logger = logger || Logger.new($stdout)
29
+
30
+ verify_whisper_available!
31
+ ensure_model_available!
32
+ end
33
+
34
+ # Transcribe audio file to text
35
+ # @param audio_path [String] Path to audio file
36
+ # @return [String] Transcribed and cleaned text
37
+ def transcribe(audio_path)
38
+ logger.debug "Transcribing audio: #{audio_path}"
39
+
40
+ cmd = build_whisper_command(audio_path)
41
+ logger.debug "Command: #{cmd.join(' ')}"
42
+
43
+ stdout, stderr, status = Open3.capture3(*cmd)
44
+
45
+ unless status.success?
46
+ logger.error "Whisper failed: #{stderr}"
47
+ raise ToolError, "Whisper transcription failed: #{stderr}"
48
+ end
49
+
50
+ logger.debug "Whisper stdout: #{stdout}"
51
+
52
+ # Extract transcription from output or file
53
+ transcription = extract_transcription_from_output(stdout) || read_transcription_file(audio_path)
54
+ clean_transcription(transcription)
55
+ ensure
56
+ # Cleanup .txt file created by whisper-cli
57
+ txt_file = "#{audio_path}.txt"
58
+ FileUtils.rm_f(txt_file)
59
+ end
60
+
61
+ # Download audio from URL using browser context
62
+ # @param browser [Ferrum::Browser] Browser instance
63
+ # @param url [String] Audio URL
64
+ # @return [Tempfile] Temporary audio file
65
+ def download_audio(_browser, url)
66
+ temp_file = Tempfile.new(['captcha_audio', '.mp3'])
67
+ temp_file.binmode
68
+
69
+ logger.info "Downloading audio from: #{url[0..80]}..."
70
+
71
+ # Use curl to download (simpler and more reliable)
72
+ _, stderr, status = Open3.capture3("curl -s -L -o #{temp_file.path} #{url.shellescape}")
73
+
74
+ unless status.success?
75
+ logger.error "curl failed: #{stderr}"
76
+ raise ToolError, "Failed to download audio with curl: #{stderr}"
77
+ end
78
+
79
+ # Verify file was downloaded
80
+ unless File.exist?(temp_file.path) && File.size(temp_file.path).positive?
81
+ raise ToolError, 'Audio file is empty or not downloaded'
82
+ end
83
+
84
+ logger.info "Downloaded: #{File.size(temp_file.path)} bytes"
85
+ temp_file
86
+ rescue StandardError => e
87
+ temp_file&.close
88
+ temp_file&.unlink
89
+ raise ToolError, "Failed to download audio: #{e.message}"
90
+ end
91
+
92
+ # Check if Whisper is available
93
+ # @return [Boolean]
94
+ def available?
95
+ verify_whisper_available!
96
+ true
97
+ rescue StandardError
98
+ false
99
+ end
100
+
101
+ private
102
+
103
+ def verify_whisper_available!
104
+ _, stderr, status = Open3.capture3("#{whisper_path} --help 2>&1")
105
+
106
+ unless status.success?
107
+ raise ToolError,
108
+ "Whisper not found at '#{whisper_path}'. " \
109
+ "Install with: brew install whisper-cpp\n" \
110
+ "Or on Linux: follow instructions at https://github.com/ggerganov/whisper.cpp\n" \
111
+ "Or set WHISPER_PATH environment variable.\n" \
112
+ "Error: #{stderr}"
113
+ end
114
+
115
+ logger.info "Whisper CLI available at: #{whisper_path}"
116
+ end
117
+
118
+ def ensure_model_available!
119
+ model_path = get_model_path
120
+
121
+ # Check if model already exists
122
+ if File.exist?(model_path)
123
+ logger.debug "Model already available: #{model_path}"
124
+ return
125
+ end
126
+
127
+ # Download model
128
+ logger.info "Model '#{model}' not found, downloading..."
129
+ download_model(model_path)
130
+ end
131
+
132
+ def get_model_path # rubocop:disable Naming/AccessorMethodName
133
+ models_dir = File.expand_path('~/.whisper.cpp/models')
134
+ FileUtils.mkdir_p(models_dir)
135
+ File.join(models_dir, "ggml-#{model}.bin")
136
+ end
137
+
138
+ def download_model(model_path) # rubocop:disable Metrics/AbcSize
139
+ url = MODEL_URLS[model]
140
+
141
+ raise ToolError, "Unknown model: #{model}. Available: #{MODEL_URLS.keys.join(', ')}" unless url
142
+
143
+ logger.info "Downloading model from: #{url}"
144
+ logger.info 'This may take a few minutes...'
145
+
146
+ # Download with progress
147
+ uri = URI(url)
148
+ Net::HTTP.start(uri.host, uri.port, use_ssl: uri.scheme == 'https') do |http|
149
+ request = Net::HTTP::Get.new(uri)
150
+
151
+ http.request(request) do |response|
152
+ raise ToolError, "Failed to download model: HTTP #{response.code}" unless response.is_a?(Net::HTTPSuccess)
153
+
154
+ total_size = response['content-length'].to_i
155
+ downloaded = 0
156
+
157
+ File.open(model_path, 'wb') do |file|
158
+ response.read_body do |chunk|
159
+ file.write(chunk)
160
+ downloaded += chunk.size
161
+
162
+ # Log progress every 10MB
163
+ if (downloaded % (10 * 1024 * 1024)).zero? || downloaded == total_size
164
+ progress = (downloaded * 100.0 / total_size).round(1)
165
+ mb_downloaded = downloaded / 1024 / 1024
166
+ mb_total = total_size / 1024 / 1024
167
+ logger.info "Download progress: #{progress}% (#{mb_downloaded}MB / #{mb_total}MB)"
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
173
+
174
+ logger.info "Model downloaded successfully: #{model_path}"
175
+ rescue StandardError => e
176
+ FileUtils.rm_f(model_path)
177
+ raise ToolError, "Failed to download model: #{e.message}"
178
+ end
179
+
180
+ def build_whisper_command(audio_path)
181
+ model_path = get_model_path
182
+
183
+ [
184
+ whisper_path,
185
+ '--model', model_path,
186
+ '--language', language,
187
+ '--output-txt',
188
+ '--file', audio_path
189
+ ]
190
+ end
191
+
192
+ def extract_transcription_from_output(output)
193
+ # whisper-cli outputs the transcription in the stdout
194
+ # Format: [00:00:00.000 --> 00:00:05.000] transcription text here
195
+ lines = output.lines.grep(/\]\s+\w/)
196
+ return nil if lines.empty?
197
+
198
+ # Extract text after the timestamp
199
+ transcription = lines.map do |line|
200
+ line.sub(/\[.*?\]\s*/, '').strip
201
+ end.join(' ')
202
+
203
+ transcription.empty? ? nil : transcription
204
+ end
205
+
206
+ def read_transcription_file(audio_path)
207
+ # whisper-cli creates audio_path.txt in the same directory
208
+ txt_file = "#{audio_path}.txt"
209
+
210
+ raise ToolError, "Whisper output file not found: #{txt_file}" unless File.exist?(txt_file)
211
+
212
+ File.read(txt_file).strip
213
+ end
214
+
215
+ def clean_transcription(text)
216
+ text.strip
217
+ .gsub(/\s+/, ' ') # normalize whitespace
218
+ .gsub(/[^\w\s]/, '') # remove punctuation
219
+ .downcase # lowercase for consistency
220
+ end
221
+ end
222
+ end
data/lib/ferrum_mcp.rb ADDED
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'mcp'
4
+ require 'ferrum'
5
+ require 'logger'
6
+ require 'json'
7
+ require 'zeitwerk'
8
+
9
+ # Setup Zeitwerk loader
10
+ loader = Zeitwerk::Loader.for_gem
11
+
12
+ # Custom inflector for acronyms
13
+ loader.inflector.inflect(
14
+ 'ferrum_mcp' => 'FerrumMCP',
15
+ 'mcp' => 'MCP',
16
+ 'cli' => 'CLI',
17
+ 'get_html_tool' => 'GetHTMLTool',
18
+ 'get_url_tool' => 'GetURLTool',
19
+ 'evaluate_js_tool' => 'EvaluateJSTool',
20
+ 'http_server' => 'HTTPServer',
21
+ 'query_shadow_dom_tool' => 'QueryShadowDOMTool'
22
+ )
23
+
24
+ loader.setup
25
+
26
+ # Main module for Ferrum MCP Server
27
+ module FerrumMCP
28
+ class Error < StandardError; end
29
+ class BrowserError < Error; end
30
+ class ToolError < Error; end
31
+ end
32
+
33
+ # Eager load in production, lazy load in development
34
+ # Note: bin/ferrum-mcp loads this file first to avoid circular dependencies
35
+ loader.eager_load unless ENV['RACK_ENV'] == 'development'
metadata ADDED
@@ -0,0 +1,248 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ferrum-mcp
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Eth3rnit3
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2025-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: dotenv
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: ferrum
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.17.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.17.1
41
+ - !ruby/object:Gem::Dependency
42
+ name: json
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.16'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: logger
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.7'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.7'
69
+ - !ruby/object:Gem::Dependency
70
+ name: mcp
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: 0.4.0
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: 0.4.0
83
+ - !ruby/object:Gem::Dependency
84
+ name: puma
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '7.1'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '7.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rack
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '3.2'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '3.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: ruby-vips
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.2'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.2'
125
+ - !ruby/object:Gem::Dependency
126
+ name: zeitwerk
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.7'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '2.7'
139
+ description: |
140
+ FerrumMCP is a browser automation server that implements the Model Context Protocol (MCP),
141
+ enabling AI assistants to interact with web pages through a standardized interface.
142
+ Features include navigation, form interaction, content extraction, screenshot capture,
143
+ JavaScript execution, cookie management, and advanced capabilities like smart cookie banner
144
+ detection and AI-powered CAPTCHA solving.
145
+ email:
146
+ - eth3rnit3@gmail.com
147
+ executables:
148
+ - ferrum-mcp
149
+ extensions: []
150
+ extra_rdoc_files: []
151
+ files:
152
+ - ".env.example"
153
+ - CHANGELOG.md
154
+ - CONTRIBUTING.md
155
+ - LICENSE
156
+ - README.md
157
+ - SECURITY.md
158
+ - bin/ferrum-mcp
159
+ - bin/lint
160
+ - bin/serve
161
+ - bin/test
162
+ - docs/API_REFERENCE.md
163
+ - docs/CONFIGURATION.md
164
+ - docs/DEPLOYMENT.md
165
+ - docs/DOCKER.md
166
+ - docs/DOCKER_BOTBROWSER.md
167
+ - docs/GETTING_STARTED.md
168
+ - docs/TROUBLESHOOTING.md
169
+ - lib/ferrum_mcp.rb
170
+ - lib/ferrum_mcp/browser_manager.rb
171
+ - lib/ferrum_mcp/cli/command_handler.rb
172
+ - lib/ferrum_mcp/cli/server_runner.rb
173
+ - lib/ferrum_mcp/configuration.rb
174
+ - lib/ferrum_mcp/resource_manager.rb
175
+ - lib/ferrum_mcp/server.rb
176
+ - lib/ferrum_mcp/session.rb
177
+ - lib/ferrum_mcp/session_manager.rb
178
+ - lib/ferrum_mcp/tools/accept_cookies_tool.rb
179
+ - lib/ferrum_mcp/tools/base_tool.rb
180
+ - lib/ferrum_mcp/tools/clear_cookies_tool.rb
181
+ - lib/ferrum_mcp/tools/click_tool.rb
182
+ - lib/ferrum_mcp/tools/close_session_tool.rb
183
+ - lib/ferrum_mcp/tools/create_session_tool.rb
184
+ - lib/ferrum_mcp/tools/drag_and_drop_tool.rb
185
+ - lib/ferrum_mcp/tools/evaluate_js_tool.rb
186
+ - lib/ferrum_mcp/tools/execute_script_tool.rb
187
+ - lib/ferrum_mcp/tools/fill_form_tool.rb
188
+ - lib/ferrum_mcp/tools/find_by_text_tool.rb
189
+ - lib/ferrum_mcp/tools/get_attribute_tool.rb
190
+ - lib/ferrum_mcp/tools/get_cookies_tool.rb
191
+ - lib/ferrum_mcp/tools/get_html_tool.rb
192
+ - lib/ferrum_mcp/tools/get_session_info_tool.rb
193
+ - lib/ferrum_mcp/tools/get_text_tool.rb
194
+ - lib/ferrum_mcp/tools/get_title_tool.rb
195
+ - lib/ferrum_mcp/tools/get_url_tool.rb
196
+ - lib/ferrum_mcp/tools/go_back_tool.rb
197
+ - lib/ferrum_mcp/tools/go_forward_tool.rb
198
+ - lib/ferrum_mcp/tools/hover_tool.rb
199
+ - lib/ferrum_mcp/tools/list_sessions_tool.rb
200
+ - lib/ferrum_mcp/tools/navigate_tool.rb
201
+ - lib/ferrum_mcp/tools/press_key_tool.rb
202
+ - lib/ferrum_mcp/tools/query_shadow_dom_tool.rb
203
+ - lib/ferrum_mcp/tools/refresh_tool.rb
204
+ - lib/ferrum_mcp/tools/screenshot_tool.rb
205
+ - lib/ferrum_mcp/tools/session_tool.rb
206
+ - lib/ferrum_mcp/tools/set_cookie_tool.rb
207
+ - lib/ferrum_mcp/tools/solve_captcha_tool.rb
208
+ - lib/ferrum_mcp/transport/http_server.rb
209
+ - lib/ferrum_mcp/transport/rate_limiter.rb
210
+ - lib/ferrum_mcp/transport/stdio_server.rb
211
+ - lib/ferrum_mcp/version.rb
212
+ - lib/ferrum_mcp/whisper_service.rb
213
+ homepage: https://github.com/Eth3rnit3/FerrumMCP
214
+ licenses:
215
+ - MIT
216
+ metadata:
217
+ homepage_uri: https://github.com/Eth3rnit3/FerrumMCP
218
+ source_code_uri: https://github.com/Eth3rnit3/FerrumMCP
219
+ changelog_uri: https://github.com/Eth3rnit3/FerrumMCP/blob/main/CHANGELOG.md
220
+ bug_tracker_uri: https://github.com/Eth3rnit3/FerrumMCP/issues
221
+ documentation_uri: https://github.com/Eth3rnit3/FerrumMCP/tree/main/docs
222
+ rubygems_mfa_required: 'true'
223
+ post_install_message: "\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\nThank
224
+ you for installing FerrumMCP 1.0.0!\n\n\U0001F4DA Documentation: https://github.com/Eth3rnit3/FerrumMCP/tree/main/docs\n\U0001F680
225
+ Quick Start: https://github.com/Eth3rnit3/FerrumMCP/blob/main/docs/GETTING_STARTED.md\n\U0001F41B
226
+ Issues: https://github.com/Eth3rnit3/FerrumMCP/issues\n\nTo start the server:\n\n
227
+ \ ferrum-mcp start [OPTIONS]\n\nFor help:\n\n ferrum-mcp --help\n\n⚠️ Requirements:\n
228
+ \ - Chrome/Chromium browser must be installed\n - Optional: whisper-cli for CAPTCHA
229
+ solving\n - Optional: BotBrowser for anti-detection\n\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\n"
230
+ rdoc_options: []
231
+ require_paths:
232
+ - lib
233
+ required_ruby_version: !ruby/object:Gem::Requirement
234
+ requirements:
235
+ - - ">="
236
+ - !ruby/object:Gem::Version
237
+ version: 3.2.0
238
+ required_rubygems_version: !ruby/object:Gem::Requirement
239
+ requirements:
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: '0'
243
+ requirements: []
244
+ rubygems_version: 3.5.22
245
+ signing_key:
246
+ specification_version: 4
247
+ summary: Browser automation server implementing the Model Context Protocol
248
+ test_files: []