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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +46 -16
- data/README.md +257 -262
- data/lib/shared_tools/browser_tool.rb +5 -0
- data/lib/shared_tools/calculator_tool.rb +4 -0
- data/lib/shared_tools/clipboard_tool.rb +4 -0
- data/lib/shared_tools/composite_analysis_tool.rb +4 -0
- data/lib/shared_tools/computer_tool.rb +5 -0
- data/lib/shared_tools/cron_tool.rb +4 -0
- data/lib/shared_tools/current_date_time_tool.rb +4 -0
- data/lib/shared_tools/data_science_kit.rb +4 -0
- data/lib/shared_tools/database.rb +4 -0
- data/lib/shared_tools/database_query_tool.rb +4 -0
- data/lib/shared_tools/database_tool.rb +5 -0
- data/lib/shared_tools/disk_tool.rb +5 -0
- data/lib/shared_tools/dns_tool.rb +4 -0
- data/lib/shared_tools/doc_tool.rb +5 -0
- data/lib/shared_tools/error_handling_tool.rb +4 -0
- data/lib/shared_tools/eval_tool.rb +5 -0
- data/lib/shared_tools/mcp/brave_search_client.rb +37 -0
- data/lib/shared_tools/mcp/chart_client.rb +32 -0
- data/lib/shared_tools/mcp/github_client.rb +38 -0
- data/lib/shared_tools/mcp/hugging_face_client.rb +43 -0
- data/lib/shared_tools/mcp/memory_client.rb +33 -0
- data/lib/shared_tools/mcp/notion_client.rb +40 -0
- data/lib/shared_tools/mcp/sequential_thinking_client.rb +33 -0
- data/lib/shared_tools/mcp/slack_client.rb +54 -0
- data/lib/shared_tools/mcp/streamable_http_patch.rb +42 -0
- data/lib/shared_tools/mcp/tavily_client.rb +41 -0
- data/lib/shared_tools/mcp.rb +45 -16
- data/lib/shared_tools/system_info_tool.rb +4 -0
- data/lib/shared_tools/tools/browser/base_tool.rb +8 -12
- data/lib/shared_tools/tools/browser/click_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/ferrum_driver.rb +119 -0
- data/lib/shared_tools/tools/browser/inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +19 -7
- data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +4 -2
- data/lib/shared_tools/tools/browser/visit_tool.rb +4 -2
- data/lib/shared_tools/tools/browser.rb +31 -2
- data/lib/shared_tools/tools/browser_tool.rb +6 -0
- data/lib/shared_tools/tools/clipboard_tool.rb +69 -144
- data/lib/shared_tools/tools/composite_analysis_tool.rb +60 -4
- data/lib/shared_tools/tools/computer/mac_driver.rb +37 -4
- data/lib/shared_tools/tools/cron_tool.rb +237 -379
- data/lib/shared_tools/tools/current_date_time_tool.rb +54 -120
- data/lib/shared_tools/tools/data_science_kit.rb +63 -13
- data/lib/shared_tools/tools/dns_tool.rb +335 -269
- data/lib/shared_tools/tools/doc/docx_reader_tool.rb +107 -0
- data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +171 -0
- data/lib/shared_tools/tools/doc/text_reader_tool.rb +57 -0
- data/lib/shared_tools/tools/doc.rb +3 -0
- data/lib/shared_tools/tools/doc_tool.rb +101 -6
- data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
- data/lib/shared_tools/tools/enabler.rb +42 -0
- data/lib/shared_tools/tools/error_handling_tool.rb +3 -1
- data/lib/shared_tools/tools/notification/base_driver.rb +51 -0
- data/lib/shared_tools/tools/notification/linux_driver.rb +115 -0
- data/lib/shared_tools/tools/notification/mac_driver.rb +66 -0
- data/lib/shared_tools/tools/notification/null_driver.rb +29 -0
- data/lib/shared_tools/tools/notification.rb +12 -0
- data/lib/shared_tools/tools/notification_tool.rb +99 -0
- data/lib/shared_tools/tools/system_info_tool.rb +130 -343
- data/lib/shared_tools/tools/workflow_manager_tool.rb +32 -0
- data/lib/shared_tools/utilities.rb +193 -0
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools/weather_tool.rb +4 -0
- data/lib/shared_tools/workflow_manager_tool.rb +4 -0
- data/lib/shared_tools.rb +28 -38
- metadata +74 -9
- data/lib/shared_tools/mcp/github_mcp_server.rb +0 -58
- data/lib/shared_tools/mcp/imcp.rb +0 -28
- data/lib/shared_tools/mcp/tavily_mcp_server.rb +0 -44
- data/lib/shared_tools/tools/devops_toolkit.rb +0 -420
|
@@ -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
|
+
)
|
data/lib/shared_tools/mcp.rb
CHANGED
|
@@ -1,24 +1,53 @@
|
|
|
1
1
|
# lib/shared_tools/mcp.rb
|
|
2
2
|
#
|
|
3
|
-
# MCP (Model Context Protocol) support for SharedTools
|
|
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
|
-
#
|
|
6
|
-
#
|
|
6
|
+
# @see https://github.com/patvice/ruby_llm-mcp
|
|
7
|
+
# @see https://www.rubyllm-mcp.com
|
|
7
8
|
#
|
|
8
|
-
#
|
|
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
|
-
#
|
|
12
|
-
#
|
|
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
|
-
#
|
|
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
|
-
#
|
|
21
|
-
#
|
|
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)
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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?(
|
|
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.
|
|
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
|