shared_tools 0.3.1 → 0.4.1

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 (75) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +46 -16
  3. data/README.md +257 -262
  4. data/lib/shared_tools/browser_tool.rb +5 -0
  5. data/lib/shared_tools/calculator_tool.rb +4 -0
  6. data/lib/shared_tools/clipboard_tool.rb +4 -0
  7. data/lib/shared_tools/composite_analysis_tool.rb +4 -0
  8. data/lib/shared_tools/computer_tool.rb +5 -0
  9. data/lib/shared_tools/cron_tool.rb +4 -0
  10. data/lib/shared_tools/current_date_time_tool.rb +4 -0
  11. data/lib/shared_tools/data_science_kit.rb +4 -0
  12. data/lib/shared_tools/database.rb +4 -0
  13. data/lib/shared_tools/database_query_tool.rb +4 -0
  14. data/lib/shared_tools/database_tool.rb +5 -0
  15. data/lib/shared_tools/disk_tool.rb +5 -0
  16. data/lib/shared_tools/dns_tool.rb +4 -0
  17. data/lib/shared_tools/doc_tool.rb +5 -0
  18. data/lib/shared_tools/error_handling_tool.rb +4 -0
  19. data/lib/shared_tools/eval_tool.rb +5 -0
  20. data/lib/shared_tools/mcp/brave_search_client.rb +37 -0
  21. data/lib/shared_tools/mcp/chart_client.rb +32 -0
  22. data/lib/shared_tools/mcp/github_client.rb +38 -0
  23. data/lib/shared_tools/mcp/hugging_face_client.rb +43 -0
  24. data/lib/shared_tools/mcp/memory_client.rb +33 -0
  25. data/lib/shared_tools/mcp/notion_client.rb +40 -0
  26. data/lib/shared_tools/mcp/sequential_thinking_client.rb +33 -0
  27. data/lib/shared_tools/mcp/slack_client.rb +54 -0
  28. data/lib/shared_tools/mcp/streamable_http_patch.rb +42 -0
  29. data/lib/shared_tools/mcp/tavily_client.rb +41 -0
  30. data/lib/shared_tools/mcp.rb +45 -16
  31. data/lib/shared_tools/system_info_tool.rb +4 -0
  32. data/lib/shared_tools/tools/browser/base_tool.rb +8 -12
  33. data/lib/shared_tools/tools/browser/click_tool.rb +4 -2
  34. data/lib/shared_tools/tools/browser/ferrum_driver.rb +119 -0
  35. data/lib/shared_tools/tools/browser/inspect_tool.rb +4 -2
  36. data/lib/shared_tools/tools/browser/page_inspect_tool.rb +4 -2
  37. data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +19 -7
  38. data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +4 -2
  39. data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +4 -2
  40. data/lib/shared_tools/tools/browser/visit_tool.rb +4 -2
  41. data/lib/shared_tools/tools/browser.rb +31 -2
  42. data/lib/shared_tools/tools/browser_tool.rb +6 -0
  43. data/lib/shared_tools/tools/clipboard_tool.rb +69 -144
  44. data/lib/shared_tools/tools/composite_analysis_tool.rb +60 -4
  45. data/lib/shared_tools/tools/computer/mac_driver.rb +37 -4
  46. data/lib/shared_tools/tools/cron_tool.rb +237 -379
  47. data/lib/shared_tools/tools/current_date_time_tool.rb +54 -120
  48. data/lib/shared_tools/tools/data_science_kit.rb +63 -13
  49. data/lib/shared_tools/tools/dns_tool.rb +335 -269
  50. data/lib/shared_tools/tools/doc/docx_reader_tool.rb +107 -0
  51. data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +171 -0
  52. data/lib/shared_tools/tools/doc/text_reader_tool.rb +57 -0
  53. data/lib/shared_tools/tools/doc.rb +3 -0
  54. data/lib/shared_tools/tools/doc_tool.rb +101 -6
  55. data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
  56. data/lib/shared_tools/tools/enabler.rb +42 -0
  57. data/lib/shared_tools/tools/error_handling_tool.rb +3 -1
  58. data/lib/shared_tools/tools/notification/base_driver.rb +51 -0
  59. data/lib/shared_tools/tools/notification/linux_driver.rb +115 -0
  60. data/lib/shared_tools/tools/notification/mac_driver.rb +66 -0
  61. data/lib/shared_tools/tools/notification/null_driver.rb +29 -0
  62. data/lib/shared_tools/tools/notification.rb +12 -0
  63. data/lib/shared_tools/tools/notification_tool.rb +99 -0
  64. data/lib/shared_tools/tools/system_info_tool.rb +130 -343
  65. data/lib/shared_tools/tools/workflow_manager_tool.rb +32 -0
  66. data/lib/shared_tools/utilities.rb +193 -0
  67. data/lib/shared_tools/version.rb +1 -1
  68. data/lib/shared_tools/weather_tool.rb +4 -0
  69. data/lib/shared_tools/workflow_manager_tool.rb +4 -0
  70. data/lib/shared_tools.rb +28 -38
  71. metadata +74 -9
  72. data/lib/shared_tools/mcp/github_mcp_server.rb +0 -58
  73. data/lib/shared_tools/mcp/imcp.rb +0 -28
  74. data/lib/shared_tools/mcp/tavily_mcp_server.rb +0 -44
  75. data/lib/shared_tools/tools/devops_toolkit.rb +0 -420
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/browser_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/browser' # sub-tools (VisitTool, ClickTool, etc.)
5
+ require 'shared_tools/tools/browser_tool' # facade (BrowserTool)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/calculator_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/calculator_tool'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/clipboard_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/clipboard_tool'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/composite_analysis_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/composite_analysis_tool'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/computer_tool'
3
+ # Note: computer.rb already loads computer_tool.rb
4
+ require 'shared_tools'
5
+ require 'shared_tools/tools/computer' # platform driver + ComputerTool facade
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/cron_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/cron_tool'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/current_date_time_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/current_date_time_tool'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/data_science_kit'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/data_science_kit'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/database'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/database'
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/database_query_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/database_query_tool'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/database_tool'
3
+ # Note: database.rb already loads database_tool.rb
4
+ require 'shared_tools'
5
+ require 'shared_tools/tools/database' # drivers + DatabaseTool facade
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/disk_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/disk' # sub-tools (FileReadTool, DirectoryListTool, etc.)
5
+ require 'shared_tools/tools/disk_tool' # facade (DiskTool)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/dns_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/dns_tool'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/doc_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/doc' # sub-tools (PdfReaderTool, TextReaderTool)
5
+ require 'shared_tools/tools/doc_tool' # facade (DocTool)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/error_handling_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/error_handling_tool'
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/eval_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/eval' # sub-tools (RubyEvalTool, PythonEvalTool, ShellEvalTool)
5
+ require 'shared_tools/tools/eval_tool' # facade (EvalTool)
@@ -0,0 +1,37 @@
1
+ # shared_tools/mcp/brave_search_client.rb
2
+ #
3
+ # Brave Search MCP Server — npx auto-download (no pre-installation required)
4
+ #
5
+ # Provides web search and news search via the Brave Search API.
6
+ #
7
+ # Prerequisites:
8
+ # - Node.js and npx (https://nodejs.org)
9
+ # - A Brave Search API key (free tier available at https://brave.com/search/api/)
10
+ # The @modelcontextprotocol/server-brave-search package is downloaded
11
+ # automatically on first use via `npx -y`.
12
+ #
13
+ # Configuration:
14
+ # export BRAVE_API_KEY=your_api_key_here
15
+ #
16
+ # Usage:
17
+ # require 'shared_tools/mcp/brave_search_client'
18
+ # client = RubyLLM::MCP.clients["brave-search"]
19
+ # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ #
21
+ # Compatible with ruby_llm-mcp >= 0.7.0
22
+
23
+ require_relative "../utilities"
24
+
25
+ SharedTools.verify_envars("BRAVE_API_KEY")
26
+
27
+ require "ruby_llm/mcp"
28
+
29
+ RubyLLM::MCP.add_client(
30
+ name: "brave-search",
31
+ transport_type: :stdio,
32
+ config: {
33
+ command: "npx",
34
+ args: ["-y", "@modelcontextprotocol/server-brave-search"],
35
+ env: { "BRAVE_API_KEY" => ENV.fetch("BRAVE_API_KEY", "") },
36
+ },
37
+ )
@@ -0,0 +1,32 @@
1
+ # shared_tools/mcp/chart_client.rb
2
+ #
3
+ # AntV Chart MCP Server — npx auto-download (no pre-installation required)
4
+ #
5
+ # Generates charts and data visualisations (bar, line, pie, scatter, heatmap, etc.)
6
+ # from structured data. Returns chart URLs or base64-encoded images.
7
+ #
8
+ # Prerequisites:
9
+ # - Node.js and npx (https://nodejs.org)
10
+ # The @antv/mcp-server-chart package is downloaded automatically on first use
11
+ # via `npx -y`.
12
+ #
13
+ # No API key required.
14
+ #
15
+ # Usage:
16
+ # require 'shared_tools/mcp/chart_client'
17
+ # client = RubyLLM::MCP.clients["chart"]
18
+ # chat = RubyLLM.chat.with_tools(*client.tools)
19
+ #
20
+ # Compatible with ruby_llm-mcp >= 0.7.0
21
+
22
+ require "ruby_llm/mcp"
23
+
24
+ RubyLLM::MCP.add_client(
25
+ name: "chart",
26
+ transport_type: :stdio,
27
+ config: {
28
+ command: "npx",
29
+ args: ["-y", "@antv/mcp-server-chart"],
30
+ env: {},
31
+ },
32
+ )
@@ -0,0 +1,38 @@
1
+ # shared_tools/mcp/github_client.rb
2
+ #
3
+ # GitHub MCP Server Client — requires brew installation
4
+ #
5
+ # Provides full GitHub API access: repositories, issues, pull requests,
6
+ # code search, commits, branches, releases, and more.
7
+ #
8
+ # Prerequisites:
9
+ # - Homebrew (https://brew.sh)
10
+ # - A GitHub Personal Access Token
11
+ # The github-mcp-server binary is installed automatically via brew if missing.
12
+ #
13
+ # Configuration:
14
+ # export GITHUB_PERSONAL_ACCESS_TOKEN=your_token_here
15
+ #
16
+ # Usage:
17
+ # require 'shared_tools/mcp/github_client'
18
+ # client = RubyLLM::MCP.clients["github"]
19
+ # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ #
21
+ # Compatible with ruby_llm-mcp >= 0.7.0
22
+
23
+ require_relative "../utilities"
24
+
25
+ SharedTools.verify_envars("GITHUB_PERSONAL_ACCESS_TOKEN")
26
+ SharedTools.package_install("github-mcp-server")
27
+
28
+ require "ruby_llm/mcp"
29
+
30
+ RubyLLM::MCP.add_client(
31
+ name: "github",
32
+ transport_type: :stdio,
33
+ config: {
34
+ command: "github-mcp-server",
35
+ args: ["stdio"],
36
+ env: { "GITHUB_PERSONAL_ACCESS_TOKEN" => ENV.fetch("GITHUB_PERSONAL_ACCESS_TOKEN", "") },
37
+ },
38
+ )
@@ -0,0 +1,43 @@
1
+ # shared_tools/mcp/hugging_face_client.rb
2
+ #
3
+ # Hugging Face MCP Server Client — requires brew installation
4
+ #
5
+ # Provides access to the Hugging Face Hub: search and inspect models,
6
+ # datasets, and Spaces; read model cards; run inference; browse
7
+ # trending repositories.
8
+ #
9
+ # Prerequisites:
10
+ # - Homebrew (https://brew.sh)
11
+ # - A Hugging Face user access token (free)
12
+ # Create one at https://huggingface.co/settings/tokens
13
+ # The hf-mcp-server binary is installed automatically via brew if missing.
14
+ #
15
+ # Configuration:
16
+ # export HF_TOKEN=hf_your_access_token_here
17
+ #
18
+ # Usage:
19
+ # require 'shared_tools/mcp/hugging_face_client'
20
+ # client = RubyLLM::MCP.clients["hugging-face"]
21
+ # chat = RubyLLM.chat.with_tools(*client.tools)
22
+ #
23
+ # Compatible with ruby_llm-mcp >= 0.7.0
24
+
25
+ require_relative "../utilities"
26
+
27
+ SharedTools.verify_envars("HF_TOKEN")
28
+ SharedTools.package_install("hf-mcp-server")
29
+
30
+ require "ruby_llm/mcp"
31
+
32
+ RubyLLM::MCP.add_client(
33
+ name: "hugging-face",
34
+ transport_type: :stdio,
35
+ config: {
36
+ command: "hf-mcp-server",
37
+ args: [],
38
+ env: {
39
+ "TRANSPORT" => "stdio",
40
+ "DEFAULT_HF_TOKEN" => ENV.fetch("HF_TOKEN", ""),
41
+ },
42
+ },
43
+ )
@@ -0,0 +1,33 @@
1
+ # shared_tools/mcp/memory_client.rb
2
+ #
3
+ # MCP Memory Server — npx auto-download (no pre-installation required)
4
+ #
5
+ # Provides a persistent knowledge graph that the LLM can read and write across
6
+ # conversations: create/update/delete entities, add relations, search memories.
7
+ #
8
+ # Prerequisites:
9
+ # - Node.js and npx (https://nodejs.org)
10
+ # The @modelcontextprotocol/server-memory package is downloaded automatically
11
+ # on first use via `npx -y`.
12
+ #
13
+ # Configuration (all optional):
14
+ # export MEMORY_FILE_PATH=/path/to/memory.jsonl # default: memory.jsonl in cwd
15
+ #
16
+ # Usage:
17
+ # require 'shared_tools/mcp/memory_client'
18
+ # client = RubyLLM::MCP.clients["memory"]
19
+ # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ #
21
+ # Compatible with ruby_llm-mcp >= 0.7.0
22
+
23
+ require "ruby_llm/mcp"
24
+
25
+ RubyLLM::MCP.add_client(
26
+ name: "memory",
27
+ transport_type: :stdio,
28
+ config: {
29
+ command: "npx",
30
+ args: ["-y", "@modelcontextprotocol/server-memory"],
31
+ env: ENV['MEMORY_FILE_PATH'] ? { "MEMORY_FILE_PATH" => ENV['MEMORY_FILE_PATH'] } : {},
32
+ },
33
+ )
@@ -0,0 +1,40 @@
1
+ # shared_tools/mcp/notion_client.rb
2
+ #
3
+ # Notion MCP Server Client — requires brew installation
4
+ #
5
+ # Provides full Notion workspace access: search pages and databases,
6
+ # read and update content, create pages, and query databases.
7
+ #
8
+ # Prerequisites:
9
+ # - Homebrew (https://brew.sh)
10
+ # - A Notion internal integration token
11
+ # Create one at https://www.notion.so/profile/integrations
12
+ # then share the relevant pages/databases with the integration.
13
+ # The notion-mcp-server binary is installed automatically via brew if missing.
14
+ #
15
+ # Configuration:
16
+ # export NOTION_TOKEN=ntn_your_integration_token_here
17
+ #
18
+ # Usage:
19
+ # require 'shared_tools/mcp/notion_client'
20
+ # client = RubyLLM::MCP.clients["notion"]
21
+ # chat = RubyLLM.chat.with_tools(*client.tools)
22
+ #
23
+ # Compatible with ruby_llm-mcp >= 0.7.0
24
+
25
+ require_relative "../utilities"
26
+
27
+ SharedTools.verify_envars("NOTION_TOKEN")
28
+ SharedTools.package_install("notion-mcp-server")
29
+
30
+ require "ruby_llm/mcp"
31
+
32
+ RubyLLM::MCP.add_client(
33
+ name: "notion",
34
+ transport_type: :stdio,
35
+ config: {
36
+ command: "notion-mcp-server",
37
+ args: [],
38
+ env: { "NOTION_TOKEN" => ENV.fetch("NOTION_TOKEN", "") },
39
+ },
40
+ )
@@ -0,0 +1,33 @@
1
+ # shared_tools/mcp/sequential_thinking_client.rb
2
+ #
3
+ # MCP Sequential Thinking Server — npx auto-download (no pre-installation required)
4
+ #
5
+ # Provides a structured chain-of-thought reasoning tool. The LLM can break complex
6
+ # problems into numbered steps, revise earlier steps, and branch reasoning paths
7
+ # before committing to a conclusion.
8
+ #
9
+ # Prerequisites:
10
+ # - Node.js and npx (https://nodejs.org)
11
+ # The @modelcontextprotocol/server-sequential-thinking package is downloaded
12
+ # automatically on first use via `npx -y`.
13
+ #
14
+ # No API key required.
15
+ #
16
+ # Usage:
17
+ # require 'shared_tools/mcp/sequential_thinking_client'
18
+ # client = RubyLLM::MCP.clients["sequential-thinking"]
19
+ # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ #
21
+ # Compatible with ruby_llm-mcp >= 0.7.0
22
+
23
+ require "ruby_llm/mcp"
24
+
25
+ RubyLLM::MCP.add_client(
26
+ name: "sequential-thinking",
27
+ transport_type: :stdio,
28
+ config: {
29
+ command: "npx",
30
+ args: ["-y", "@modelcontextprotocol/server-sequential-thinking"],
31
+ env: {},
32
+ },
33
+ )
@@ -0,0 +1,54 @@
1
+ # shared_tools/mcp/slack_client.rb
2
+ #
3
+ # Slack MCP Server Client — requires brew installation
4
+ #
5
+ # Provides Slack workspace access: read channels, messages, and threads,
6
+ # search message history, list users, and (optionally) post messages.
7
+ #
8
+ # Prerequisites:
9
+ # - Homebrew (https://brew.sh)
10
+ # - A Slack token — at least one of the following must be set:
11
+ # SLACK_MCP_XOXP_TOKEN — user OAuth token (xoxp-...) — full access
12
+ # SLACK_MCP_XOXB_TOKEN — bot token (xoxb-...) — limited to invited channels, no search
13
+ # The slack-mcp-server binary is installed automatically via brew if missing.
14
+ #
15
+ # Configuration:
16
+ # export SLACK_MCP_XOXP_TOKEN=xoxp-your-user-token # recommended
17
+ # # OR
18
+ # export SLACK_MCP_XOXB_TOKEN=xoxb-your-bot-token
19
+ #
20
+ # Optional — posting messages is disabled by default for safety:
21
+ # export SLACK_MCP_ADD_MESSAGE_TOOL=true # enable for all channels
22
+ # export SLACK_MCP_ADD_MESSAGE_TOOL=C012AB3CD,C98765 # enable for specific channels only
23
+ #
24
+ # Usage:
25
+ # require 'shared_tools/mcp/slack_client'
26
+ # client = RubyLLM::MCP.clients["slack"]
27
+ # chat = RubyLLM.chat.with_tools(*client.tools)
28
+ #
29
+ # Compatible with ruby_llm-mcp >= 0.7.0
30
+
31
+ require_relative "../utilities"
32
+
33
+ xoxp = ENV.fetch("SLACK_MCP_XOXP_TOKEN", "")
34
+ xoxb = ENV.fetch("SLACK_MCP_XOXB_TOKEN", "")
35
+ raise LoadError, "SLACK_MCP_XOXP_TOKEN or SLACK_MCP_XOXB_TOKEN must be set" if xoxp.empty? && xoxb.empty?
36
+
37
+ SharedTools.package_install("slack-mcp-server")
38
+
39
+ require "ruby_llm/mcp"
40
+
41
+ slack_env = {}
42
+ slack_env["SLACK_MCP_XOXP_TOKEN"] = xoxp unless xoxp.empty?
43
+ slack_env["SLACK_MCP_XOXB_TOKEN"] = xoxb unless xoxb.empty?
44
+ slack_env["SLACK_MCP_ADD_MESSAGE_TOOL"] = ENV["SLACK_MCP_ADD_MESSAGE_TOOL"] if ENV["SLACK_MCP_ADD_MESSAGE_TOOL"]
45
+
46
+ RubyLLM::MCP.add_client(
47
+ name: "slack",
48
+ transport_type: :stdio,
49
+ config: {
50
+ command: "slack-mcp-server",
51
+ args: [],
52
+ env: slack_env,
53
+ },
54
+ )
@@ -0,0 +1,42 @@
1
+ # lib/shared_tools/mcp/streamable_http_patch.rb
2
+ #
3
+ # Patch: normalize mixed CRLF/LF line endings in SSE event buffers.
4
+ #
5
+ # Some MCP servers (e.g. Tavily) use \n to terminate the SSE data line and
6
+ # \r\n for the blank separator line, producing the sequence \n\r\n instead
7
+ # of the uniform \n\n or \r\n\r\n that the upstream parser expects.
8
+ # Without this patch, extract_sse_event never finds an event boundary for
9
+ # such responses and every tools/list request times out.
10
+ #
11
+ # This is a server-side standards violation (RFC 8895 requires consistent
12
+ # line endings within an event), but it is common enough in hosted MCP
13
+ # servers to warrant a defensive client-side fix.
14
+ #
15
+ # The patch normalises the accumulation buffer in-place before the separator
16
+ # check, which is safe because:
17
+ # - the buffer is a mutable +String (prefixed with +)
18
+ # - it is always written back via buffer.replace(rest) by the caller
19
+ # - repeated normalisation of already-normalised content is a no-op
20
+
21
+ require "ruby_llm/mcp"
22
+
23
+ module RubyLLM
24
+ module MCP
25
+ module Native
26
+ module Transports
27
+ class StreamableHTTP
28
+ private
29
+
30
+ def extract_sse_event(buffer)
31
+ buffer.gsub!("\r\n", "\n")
32
+ buffer.gsub!("\r", "\n")
33
+ return nil unless buffer.include?("\n\n")
34
+
35
+ raw, rest = buffer.split("\n\n", 2)
36
+ [parse_sse_event(raw), rest || ""]
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,41 @@
1
+ # shared_tools/mcp/tavily_client.rb
2
+ #
3
+ # Tavily MCP Server Client — Remote HTTP (no local installation required)
4
+ #
5
+ # Connects directly to Tavily's hosted MCP endpoint via Streamable HTTP transport.
6
+ # No npx, no Node.js, no local binary required — only a Tavily API key.
7
+ #
8
+ # Provides:
9
+ # - AI-optimized web search
10
+ # - Research-grade content extraction
11
+ # - Real-time news and current events
12
+ #
13
+ # Configuration:
14
+ # export TAVILY_API_KEY=your_api_key_here
15
+ # Get a free key at: https://tavily.com
16
+ #
17
+ # Usage:
18
+ # require 'shared_tools/mcp/tavily_client'
19
+ # client = RubyLLM::MCP.clients["tavily"]
20
+ # chat = RubyLLM.chat.with_tools(*client.tools)
21
+ #
22
+ # Compatible with ruby_llm-mcp >= 0.7.0
23
+
24
+ require_relative "../utilities"
25
+ require_relative "streamable_http_patch"
26
+
27
+ SharedTools.verify_envars("TAVILY_API_KEY")
28
+
29
+ RubyLLM::MCP.add_client(
30
+ name: "tavily",
31
+ transport_type: :streamable,
32
+ request_timeout: 30_000,
33
+ config: {
34
+ url: "https://mcp.tavily.com/mcp/",
35
+ version: :http1,
36
+ headers: {
37
+ "Authorization" => "Bearer #{ENV.fetch('TAVILY_API_KEY', '')}",
38
+ "Accept" => "application/json, text/event-stream",
39
+ },
40
+ },
41
+ )
@@ -1,24 +1,53 @@
1
1
  # lib/shared_tools/mcp.rb
2
2
  #
3
- # MCP (Model Context Protocol) support for SharedTools using ruby_llm-mcp gem >= 0.7.0
3
+ # MCP (Model Context Protocol) client support for SharedTools.
4
+ # Requires the ruby_llm-mcp gem >= 0.7.0 and RubyLLM >= 1.9.0.
4
5
  #
5
- # This module provides integration with various MCP servers, allowing Ruby applications
6
- # to connect to external services and tools through the Model Context Protocol.
6
+ # @see https://github.com/patvice/ruby_llm-mcp
7
+ # @see https://www.rubyllm-mcp.com
7
8
  #
8
- # @see https://github.com/patvice/ruby_llm-mcp RubyLLM MCP documentation
9
- # @see https://www.rubyllm-mcp.com Official documentation
9
+ # Two categories of client are provided, both requiring no pre-installed binaries:
10
10
  #
11
- # Usage:
12
- # require 'shared_tools/mcp/imcp' # Load iMCP client
13
- # require 'shared_tools/mcp/github_mcp_server' # Load GitHub client
14
- # require 'shared_tools/mcp/tavily_mcp_server' # Load Tavily client
11
+ # REMOTE HTTP (transport: :streamable)
12
+ # Connect to cloud-hosted MCP servers. Requires only an API key.
15
13
  #
16
- # Requirements:
17
- # - ruby_llm-mcp >= 0.7.0
18
- # - RubyLLM >= 1.9.0
14
+ # require 'shared_tools/mcp/tavily_client' # Web search (TAVILY_API_KEY)
19
15
  #
20
- # Version 0.7.0 Changes:
21
- # - Complex parameter support is now enabled by default
22
- # - Requires RubyLLM 1.9+
23
- # - support_complex_parameters! method is deprecated
16
+ # NPX AUTO-DOWNLOAD (transport: :stdio via npx -y)
17
+ # The npm package is downloaded on first use. Requires Node.js / npx.
24
18
  #
19
+ # require 'shared_tools/mcp/memory_client' # Persistent knowledge graph
20
+ # require 'shared_tools/mcp/sequential_thinking_client' # Chain-of-thought reasoning
21
+ # require 'shared_tools/mcp/chart_client' # Chart / visualisation generation
22
+ # require 'shared_tools/mcp/brave_search_client' # Web search (BRAVE_API_KEY)
23
+ #
24
+ # Requiring this file loads ALL available clients concurrently using threads.
25
+ # Each client's transport connection is established in parallel, so total startup
26
+ # time equals the slowest single client rather than the sum of all clients.
27
+ #
28
+ # Clients whose API keys are missing are silently skipped.
29
+ #
30
+ # After loading, access clients via:
31
+ # client = RubyLLM::MCP.clients["client-name"]
32
+ # chat = RubyLLM.chat.with_tools(*client.tools)
33
+
34
+ require_relative "mcp/streamable_http_patch"
35
+
36
+ threads = Dir[File.join(__dir__, "mcp", "*_client.rb")].map do |path|
37
+ # Derive the canonical client name from the filename:
38
+ # "brave_search_client.rb" → "brave-search", "hugging_face_client.rb" → "hugging-face"
39
+ client_name = File.basename(path, "_client.rb").tr("_", "-")
40
+
41
+ Thread.new do
42
+ require path
43
+ SharedTools.record_mcp_result(client_name)
44
+ rescue Exception => e
45
+ # LoadError (missing env var / package) is a ScriptError, not a StandardError,
46
+ # so bare `rescue => e` does not catch it. We record the failure and warn so
47
+ # that missing-key clients are simply skipped when requiring mcp.rb as a whole.
48
+ SharedTools.record_mcp_result(client_name, error: e)
49
+ warn "SharedTools::MCP — skipping #{File.basename(path)}: #{e.message}"
50
+ end
51
+ end
52
+
53
+ threads.each(&:join)
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # Shim: require 'shared_tools/system_info_tool'
3
+ require 'shared_tools'
4
+ require 'shared_tools/tools/system_info_tool'
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "watir"
4
-
5
3
  module SharedTools
6
4
  module Tools
7
5
  module Browser
@@ -9,7 +7,7 @@ module SharedTools
9
7
  # class SeleniumTool < BaseTool
10
8
  # # ...
11
9
  # end
12
- class BaseTool
10
+ class BaseTool
13
11
  # @param logger [Logger]
14
12
  # @param driver [BaseDriver]
15
13
  def initialize(driver:, logger: Logger.new(IO::NULL))
@@ -20,15 +18,16 @@ module SharedTools
20
18
 
21
19
  protected
22
20
 
23
- def wait_for_element
21
+ def wait_for_element(timeout: 10)
24
22
  return yield if defined?(RSpec) # Skip waiting in tests
25
23
 
26
- Watir::Wait.until(timeout: 10) do
24
+ deadline = Time.now + timeout
25
+ loop do
27
26
  element = yield
28
- element if element && element_visible?(element)
27
+ return element if element && element_visible?(element)
28
+ break if Time.now >= deadline
29
+ sleep 0.2
29
30
  end
30
- rescue Watir::Wait::TimeoutError
31
- log_element_timeout
32
31
  nil
33
32
  end
34
33
 
@@ -39,10 +38,7 @@ module SharedTools
39
38
  end
40
39
 
41
40
  def log_element_timeout
42
- return unless @browser.respond_to?(:elements)
43
-
44
- visible_elements = @browser.elements.select(&:visible?).map(&:text).compact.first(10)
45
- @logger.error("Element not found after 10s. Sample visible elements: #{visible_elements}")
41
+ @logger.error("Element not found after timeout.")
46
42
  end
47
43
  end
48
44
  end
@@ -42,10 +42,12 @@ module SharedTools
42
42
  private
43
43
 
44
44
  def default_driver
45
- if defined?(Watir)
45
+ if defined?(Ferrum)
46
+ FerrumDriver.new(logger: @logger)
47
+ elsif defined?(Watir)
46
48
  WatirDriver.new(logger: @logger)
47
49
  else
48
- raise LoadError, "Browser tools require a driver. Either install the 'watir' gem or pass a driver: parameter"
50
+ raise LoadError, "Browser tools require a driver. Install the 'ferrum' gem or pass a driver: parameter"
49
51
  end
50
52
  end
51
53
  end