shared_tools 0.4.4 → 0.6.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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +211 -7
  4. data/lib/shared_tools/apply_patch_tool.rb +4 -0
  5. data/lib/shared_tools/bash_tool.rb +4 -0
  6. data/lib/shared_tools/bundle_tool.rb +4 -0
  7. data/lib/shared_tools/command_guard.rb +35 -0
  8. data/lib/shared_tools/csv_read_tool.rb +4 -0
  9. data/lib/shared_tools/csv_write_tool.rb +4 -0
  10. data/lib/shared_tools/data_path.rb +52 -0
  11. data/lib/shared_tools/diff_tool.rb +4 -0
  12. data/lib/shared_tools/download_file_tool.rb +4 -0
  13. data/lib/shared_tools/gem_tool.rb +4 -0
  14. data/lib/shared_tools/git_tool.rb +5 -0
  15. data/lib/shared_tools/glob_tool.rb +4 -0
  16. data/lib/shared_tools/http_request_tool.rb +4 -0
  17. data/lib/shared_tools/json_query_tool.rb +4 -0
  18. data/lib/shared_tools/lint_tool.rb +4 -0
  19. data/lib/shared_tools/mcp/brave_search_client.rb +18 -16
  20. data/lib/shared_tools/mcp/chart_client.rb +18 -15
  21. data/lib/shared_tools/mcp/client_support.rb +103 -0
  22. data/lib/shared_tools/mcp/github_client.rb +23 -19
  23. data/lib/shared_tools/mcp/hugging_face_client.rb +22 -22
  24. data/lib/shared_tools/mcp/memory_client.rb +20 -15
  25. data/lib/shared_tools/mcp/notion_client.rb +21 -19
  26. data/lib/shared_tools/mcp/playwright_client.rb +19 -15
  27. data/lib/shared_tools/mcp/sequential_thinking_client.rb +17 -14
  28. data/lib/shared_tools/mcp/slack_client.rb +30 -25
  29. data/lib/shared_tools/mcp/tavily_client.rb +21 -21
  30. data/lib/shared_tools/mcp.rb +32 -41
  31. data/lib/shared_tools/multi_edit_tool.rb +4 -0
  32. data/lib/shared_tools/parse_ruby_tool.rb +4 -0
  33. data/lib/shared_tools/process_kill_tool.rb +4 -0
  34. data/lib/shared_tools/process_list_tool.rb +4 -0
  35. data/lib/shared_tools/process_output_tool.rb +4 -0
  36. data/lib/shared_tools/process_registry.rb +224 -0
  37. data/lib/shared_tools/process_runner.rb +69 -0
  38. data/lib/shared_tools/process_start_tool.rb +4 -0
  39. data/lib/shared_tools/python_tests_tool.rb +4 -0
  40. data/lib/shared_tools/replace_in_files_tool.rb +4 -0
  41. data/lib/shared_tools/ruby_outline.rb +207 -0
  42. data/lib/shared_tools/run_tests_tool.rb +4 -0
  43. data/lib/shared_tools/text_diff.rb +80 -0
  44. data/lib/shared_tools/todo_write_tool.rb +4 -0
  45. data/lib/shared_tools/toml_parser.rb +409 -0
  46. data/lib/shared_tools/toml_query_tool.rb +4 -0
  47. data/lib/shared_tools/tools/apply_patch_tool.rb +122 -0
  48. data/lib/shared_tools/tools/bash_tool.rb +102 -0
  49. data/lib/shared_tools/tools/browser/click_tool.rb +1 -1
  50. data/lib/shared_tools/tools/browser/inspect_tool.rb +1 -1
  51. data/lib/shared_tools/tools/browser/page_inspect_tool.rb +1 -1
  52. data/lib/shared_tools/tools/browser/page_screenshot_tool.rb +1 -1
  53. data/lib/shared_tools/tools/browser/selector_inspect_tool.rb +1 -1
  54. data/lib/shared_tools/tools/browser/text_field_area_set_tool.rb +1 -1
  55. data/lib/shared_tools/tools/browser/visit_tool.rb +1 -1
  56. data/lib/shared_tools/tools/browser_tool.rb +1 -1
  57. data/lib/shared_tools/tools/bundle_tool.rb +96 -0
  58. data/lib/shared_tools/tools/calculator_tool.rb +1 -1
  59. data/lib/shared_tools/tools/clipboard_tool.rb +1 -1
  60. data/lib/shared_tools/tools/composite_analysis_tool.rb +1 -1
  61. data/lib/shared_tools/tools/computer_tool.rb +1 -1
  62. data/lib/shared_tools/tools/cron_tool.rb +1 -1
  63. data/lib/shared_tools/tools/csv_read_tool.rb +82 -0
  64. data/lib/shared_tools/tools/csv_write_tool.rb +67 -0
  65. data/lib/shared_tools/tools/current_date_time_tool.rb +1 -1
  66. data/lib/shared_tools/tools/data_science_kit.rb +1 -1
  67. data/lib/shared_tools/tools/database_query_tool.rb +1 -1
  68. data/lib/shared_tools/tools/database_tool.rb +1 -1
  69. data/lib/shared_tools/tools/diff_tool.rb +48 -0
  70. data/lib/shared_tools/tools/disk/directory_create_tool.rb +1 -1
  71. data/lib/shared_tools/tools/disk/directory_delete_tool.rb +1 -1
  72. data/lib/shared_tools/tools/disk/directory_list_tool.rb +1 -1
  73. data/lib/shared_tools/tools/disk/directory_move_tool.rb +1 -1
  74. data/lib/shared_tools/tools/disk/file_create_tool.rb +1 -1
  75. data/lib/shared_tools/tools/disk/file_delete_tool.rb +1 -1
  76. data/lib/shared_tools/tools/disk/file_move_tool.rb +1 -1
  77. data/lib/shared_tools/tools/disk/file_read_tool.rb +1 -1
  78. data/lib/shared_tools/tools/disk/file_replace_tool.rb +1 -1
  79. data/lib/shared_tools/tools/disk/file_write_tool.rb +1 -1
  80. data/lib/shared_tools/tools/disk_tool.rb +1 -1
  81. data/lib/shared_tools/tools/dns_tool.rb +1 -1
  82. data/lib/shared_tools/tools/doc/docx_reader_tool.rb +1 -1
  83. data/lib/shared_tools/tools/doc/pdf_reader_tool.rb +1 -1
  84. data/lib/shared_tools/tools/doc/spreadsheet_reader_tool.rb +1 -1
  85. data/lib/shared_tools/tools/doc/text_reader_tool.rb +1 -1
  86. data/lib/shared_tools/tools/doc_tool.rb +1 -1
  87. data/lib/shared_tools/tools/docker/compose_run_tool.rb +1 -1
  88. data/lib/shared_tools/tools/download_file_tool.rb +87 -0
  89. data/lib/shared_tools/tools/enabler.rb +2 -2
  90. data/lib/shared_tools/tools/error_handling_tool.rb +1 -1
  91. data/lib/shared_tools/tools/eval/python_eval_tool.rb +1 -1
  92. data/lib/shared_tools/tools/eval/ruby_eval_tool.rb +1 -1
  93. data/lib/shared_tools/tools/eval/shell_eval_tool.rb +1 -1
  94. data/lib/shared_tools/tools/eval_tool.rb +1 -1
  95. data/lib/shared_tools/tools/gem_tool.rb +157 -0
  96. data/lib/shared_tools/tools/git/add_tool.rb +73 -0
  97. data/lib/shared_tools/tools/git/blame_tool.rb +77 -0
  98. data/lib/shared_tools/tools/git/branch_tool.rb +55 -0
  99. data/lib/shared_tools/tools/git/checkout_tool.rb +69 -0
  100. data/lib/shared_tools/tools/git/commit_tool.rb +73 -0
  101. data/lib/shared_tools/tools/git/diff_tool.rb +69 -0
  102. data/lib/shared_tools/tools/git/grep_tool.rb +82 -0
  103. data/lib/shared_tools/tools/git/helpers.rb +70 -0
  104. data/lib/shared_tools/tools/git/log_tool.rb +71 -0
  105. data/lib/shared_tools/tools/git/show_tool.rb +66 -0
  106. data/lib/shared_tools/tools/git/status_tool.rb +46 -0
  107. data/lib/shared_tools/tools/git.rb +18 -0
  108. data/lib/shared_tools/tools/git_tool.rb +148 -0
  109. data/lib/shared_tools/tools/glob_tool.rb +75 -0
  110. data/lib/shared_tools/tools/http_helpers.rb +119 -0
  111. data/lib/shared_tools/tools/http_request_tool.rb +91 -0
  112. data/lib/shared_tools/tools/json_query_tool.rb +81 -0
  113. data/lib/shared_tools/tools/lint_tool.rb +93 -0
  114. data/lib/shared_tools/tools/multi_edit_tool.rb +134 -0
  115. data/lib/shared_tools/tools/notification_tool.rb +1 -1
  116. data/lib/shared_tools/tools/parse_ruby_tool.rb +127 -0
  117. data/lib/shared_tools/tools/process_kill_tool.rb +55 -0
  118. data/lib/shared_tools/tools/process_list_tool.rb +41 -0
  119. data/lib/shared_tools/tools/process_output_tool.rb +69 -0
  120. data/lib/shared_tools/tools/process_start_tool.rb +0 -0
  121. data/lib/shared_tools/tools/python_tests_tool.rb +105 -0
  122. data/lib/shared_tools/tools/replace_in_files_tool.rb +166 -0
  123. data/lib/shared_tools/tools/run_tests_tool.rb +108 -0
  124. data/lib/shared_tools/tools/search_codebase_tool.rb +178 -47
  125. data/lib/shared_tools/tools/secure_tool_template.rb +1 -1
  126. data/lib/shared_tools/tools/system_info_tool.rb +1 -1
  127. data/lib/shared_tools/tools/todo_write_tool.rb +81 -0
  128. data/lib/shared_tools/tools/toml_query_tool.rb +81 -0
  129. data/lib/shared_tools/tools/toolchain_helpers.rb +64 -0
  130. data/lib/shared_tools/tools/tree_tool.rb +100 -0
  131. data/lib/shared_tools/tools/weather_tool.rb +1 -1
  132. data/lib/shared_tools/tools/web_fetch_tool.rb +85 -0
  133. data/lib/shared_tools/tools/workflow_manager_tool.rb +1 -1
  134. data/lib/shared_tools/tools/yaml_query_tool.rb +86 -0
  135. data/lib/shared_tools/tree_tool.rb +4 -0
  136. data/lib/shared_tools/url_guard.rb +106 -0
  137. data/lib/shared_tools/utilities.rb +2 -2
  138. data/lib/shared_tools/version.rb +1 -1
  139. data/lib/shared_tools/web_fetch_tool.rb +4 -0
  140. data/lib/shared_tools/yaml_query_tool.rb +4 -0
  141. data/lib/shared_tools.rb +1 -1
  142. metadata +516 -409
  143. data/lib/shared_tools/mcp/streamable_http_patch.rb +0 -42
  144. data/lib/shared_tools/tools/version.rb +0 -7
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # lib/shared_tools/mcp/client_support.rb
4
+ #
5
+ # Class-level helpers shared by the SharedTools MCP server classes.
6
+ #
7
+ # Each client in this directory is a plain RubyLLM::MCP subclass (ruby_llm >= 2.1)
8
+ # that describes ONE server with the class-level DSL (command / env / url /
9
+ # bearer_token / timeout ...). Defining the class has no side effects: no process
10
+ # is started, nothing is downloaded and no network connection is made until a
11
+ # chat or robot first asks the server for its tools.
12
+ #
13
+ # This module adds a little SharedTools-specific metadata on top of that DSL:
14
+ #
15
+ # class SharedTools::MCP::GitHub < RubyLLM::MCP
16
+ # extend SharedTools::MCP::ClientSupport
17
+ # requires_envars "GITHUB_PERSONAL_ACCESS_TOKEN"
18
+ # requires_packages "github-mcp-server"
19
+ # command "github-mcp-server", "stdio"
20
+ # end
21
+ #
22
+ # SharedTools::MCP::GitHub.available? #=> false when the token is unset
23
+ # SharedTools::MCP::GitHub.missing_envars #=> ["GITHUB_PERSONAL_ACCESS_TOKEN"]
24
+ # SharedTools::MCP::GitHub.install_packages! # brew/apt/dnf install, on demand only
25
+
26
+ require "ruby_llm"
27
+ require_relative "../utilities"
28
+
29
+ module SharedTools
30
+ module MCP
31
+ @clients = []
32
+
33
+ class << self
34
+ # Every SharedTools MCP server class that has been loaded.
35
+ def clients
36
+ @clients.dup
37
+ end
38
+
39
+ # The loaded server classes whose required environment variables are set.
40
+ #
41
+ # RobotLab.build(mcp: SharedTools::MCP.available)
42
+ def available
43
+ @clients.select(&:available?)
44
+ end
45
+
46
+ def register(klass) # :nodoc:
47
+ @clients << klass unless @clients.include?(klass)
48
+ klass
49
+ end
50
+ end
51
+
52
+ module ClientSupport
53
+ def self.extended(klass)
54
+ SharedTools::MCP.register(klass)
55
+ end
56
+
57
+ # Declares (or, with no arguments, returns) the environment variables
58
+ # the server needs. All of them must be set for #available? to be true.
59
+ def requires_envars(*names)
60
+ return @required_envars || [] if names.empty?
61
+
62
+ @required_envars = names.flatten.map(&:to_s)
63
+ end
64
+
65
+ # Declares (or returns) the OS packages that provide the server binary.
66
+ # They are only installed when #install_packages! is called.
67
+ def requires_packages(*names)
68
+ return @required_packages || [] if names.empty?
69
+
70
+ @required_packages = names.flatten.map(&:to_s)
71
+ end
72
+
73
+ def missing_envars
74
+ requires_envars.select { |name| ENV.fetch(name, "").empty? }
75
+ end
76
+
77
+ def available?
78
+ missing_envars.empty?
79
+ end
80
+
81
+ # Raises LoadError (like SharedTools.verify_envars) when a required
82
+ # environment variable is missing; returns the class otherwise.
83
+ def verify!
84
+ missing = missing_envars
85
+ raise LoadError, "Missing envars: #{missing.join(', ')}" unless missing.empty?
86
+
87
+ self
88
+ end
89
+
90
+ # Installs the server's OS packages via SharedTools.package_install.
91
+ def install_packages!
92
+ SharedTools.package_install(*requires_packages) unless requires_packages.empty?
93
+ self
94
+ end
95
+
96
+ # An environment variable read when the server process starts rather
97
+ # than when the class is defined. Missing variables resolve to "".
98
+ def env_from(name)
99
+ -> { ENV.fetch(name, "") }
100
+ end
101
+ end
102
+ end
103
+ end
@@ -1,38 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/github_client.rb
2
4
  #
3
- # GitHub MCP Server Client — requires brew installation
5
+ # GitHub MCP Server — local binary over stdio
4
6
  #
5
7
  # Provides full GitHub API access: repositories, issues, pull requests,
6
8
  # code search, commits, branches, releases, and more.
7
9
  #
8
10
  # Prerequisites:
9
- # - Homebrew (https://brew.sh)
11
+ # - The github-mcp-server binary on PATH. Install it with
12
+ # SharedTools::MCP::GitHub.install_packages! (Homebrew/apt/dnf) or by hand.
10
13
  # - A GitHub Personal Access Token
11
- # The github-mcp-server binary is installed automatically via brew if missing.
12
14
  #
13
15
  # Configuration:
14
16
  # export GITHUB_PERSONAL_ACCESS_TOKEN=your_token_here
15
17
  #
16
18
  # Usage:
17
19
  # require 'shared_tools/mcp/github_client'
18
- # client = RubyLLM::MCP.clients["github"]
19
- # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::GitHub)
21
+ # robot = RobotLab.build(name: "dev", mcp: [SharedTools::MCP::GitHub])
20
22
  #
21
- # Compatible with ruby_llm-mcp >= 0.7.0
23
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
24
+ # server's tools are first requested.
25
+
26
+ require_relative "client_support"
22
27
 
23
- require_relative "../utilities"
28
+ module SharedTools
29
+ module MCP
30
+ class GitHub < ::RubyLLM::MCP
31
+ extend ClientSupport
24
32
 
25
- SharedTools.verify_envars("GITHUB_PERSONAL_ACCESS_TOKEN")
26
- SharedTools.package_install("github-mcp-server")
33
+ self.default_name = "github"
27
34
 
28
- require "ruby_llm/mcp"
35
+ requires_envars "GITHUB_PERSONAL_ACCESS_TOKEN"
36
+ requires_packages "github-mcp-server"
29
37
 
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
- )
38
+ command "github-mcp-server", "stdio"
39
+ env "GITHUB_PERSONAL_ACCESS_TOKEN" => env_from("GITHUB_PERSONAL_ACCESS_TOKEN")
40
+ end
41
+ end
42
+ end
@@ -1,43 +1,43 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/hugging_face_client.rb
2
4
  #
3
- # Hugging Face MCP Server Client — requires brew installation
5
+ # Hugging Face MCP Server — local binary over stdio
4
6
  #
5
7
  # Provides access to the Hugging Face Hub: search and inspect models,
6
8
  # datasets, and Spaces; read model cards; run inference; browse
7
9
  # trending repositories.
8
10
  #
9
11
  # Prerequisites:
10
- # - Homebrew (https://brew.sh)
12
+ # - The hf-mcp-server binary on PATH. Install it with
13
+ # SharedTools::MCP::HuggingFace.install_packages! (Homebrew/apt/dnf) or by hand.
11
14
  # - A Hugging Face user access token (free)
12
15
  # Create one at https://huggingface.co/settings/tokens
13
- # The hf-mcp-server binary is installed automatically via brew if missing.
14
16
  #
15
17
  # Configuration:
16
18
  # export HF_TOKEN=hf_your_access_token_here
17
19
  #
18
20
  # Usage:
19
21
  # require 'shared_tools/mcp/hugging_face_client'
20
- # client = RubyLLM::MCP.clients["hugging-face"]
21
- # chat = RubyLLM.chat.with_tools(*client.tools)
22
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::HuggingFace)
23
+ # robot = RobotLab.build(name: "ml", mcp: [SharedTools::MCP::HuggingFace])
22
24
  #
23
- # Compatible with ruby_llm-mcp >= 0.7.0
25
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
26
+ # server's tools are first requested.
24
27
 
25
- require_relative "../utilities"
28
+ require_relative "client_support"
26
29
 
27
- SharedTools.verify_envars("HF_TOKEN")
28
- SharedTools.package_install("hf-mcp-server")
30
+ module SharedTools
31
+ module MCP
32
+ class HuggingFace < ::RubyLLM::MCP
33
+ extend ClientSupport
29
34
 
30
- require "ruby_llm/mcp"
35
+ requires_envars "HF_TOKEN"
36
+ requires_packages "hf-mcp-server"
31
37
 
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
- )
38
+ command "hf-mcp-server"
39
+ env "TRANSPORT" => "stdio",
40
+ "DEFAULT_HF_TOKEN" => env_from("HF_TOKEN")
41
+ end
42
+ end
43
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/memory_client.rb
2
4
  #
3
5
  # MCP Memory Server — npx auto-download (no pre-installation required)
@@ -8,26 +10,29 @@
8
10
  # Prerequisites:
9
11
  # - Node.js and npx (https://nodejs.org)
10
12
  # The @modelcontextprotocol/server-memory package is downloaded automatically
11
- # on first use via `npx -y`.
13
+ # the first time the server is started, via `npx -y`.
12
14
  #
13
15
  # Configuration (all optional):
14
- # export MEMORY_FILE_PATH=/path/to/memory.jsonl # default: memory.jsonl in cwd
16
+ # export MEMORY_FILE_PATH=/path/to/memory.jsonl # default: the server's own default
15
17
  #
16
18
  # Usage:
17
19
  # require 'shared_tools/mcp/memory_client'
18
- # client = RubyLLM::MCP.clients["memory"]
19
- # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::Memory)
21
+ # robot = RobotLab.build(name: "assistant", mcp: [SharedTools::MCP::Memory])
20
22
  #
21
- # Compatible with ruby_llm-mcp >= 0.7.0
23
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
24
+ # server's tools are first requested.
25
+
26
+ require_relative "client_support"
22
27
 
23
- require "ruby_llm/mcp"
28
+ module SharedTools
29
+ module MCP
30
+ class Memory < ::RubyLLM::MCP
31
+ extend ClientSupport
24
32
 
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
- )
33
+ command "npx", "-y", "@modelcontextprotocol/server-memory"
34
+ # An unset MEMORY_FILE_PATH resolves to "", which the server treats as unset.
35
+ env "MEMORY_FILE_PATH" => env_from("MEMORY_FILE_PATH")
36
+ end
37
+ end
38
+ end
@@ -1,40 +1,42 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/notion_client.rb
2
4
  #
3
- # Notion MCP Server Client — requires brew installation
5
+ # Notion MCP Server — local binary over stdio
4
6
  #
5
7
  # Provides full Notion workspace access: search pages and databases,
6
8
  # read and update content, create pages, and query databases.
7
9
  #
8
10
  # Prerequisites:
9
- # - Homebrew (https://brew.sh)
11
+ # - The notion-mcp-server binary on PATH. Install it with
12
+ # SharedTools::MCP::Notion.install_packages! (Homebrew/apt/dnf) or by hand.
10
13
  # - A Notion internal integration token
11
14
  # Create one at https://www.notion.so/profile/integrations
12
15
  # then share the relevant pages/databases with the integration.
13
- # The notion-mcp-server binary is installed automatically via brew if missing.
14
16
  #
15
17
  # Configuration:
16
18
  # export NOTION_TOKEN=ntn_your_integration_token_here
17
19
  #
18
20
  # Usage:
19
21
  # require 'shared_tools/mcp/notion_client'
20
- # client = RubyLLM::MCP.clients["notion"]
21
- # chat = RubyLLM.chat.with_tools(*client.tools)
22
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::Notion)
23
+ # robot = RobotLab.build(name: "notes", mcp: [SharedTools::MCP::Notion])
22
24
  #
23
- # Compatible with ruby_llm-mcp >= 0.7.0
25
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
26
+ # server's tools are first requested.
24
27
 
25
- require_relative "../utilities"
28
+ require_relative "client_support"
26
29
 
27
- SharedTools.verify_envars("NOTION_TOKEN")
28
- SharedTools.package_install("notion-mcp-server")
30
+ module SharedTools
31
+ module MCP
32
+ class Notion < ::RubyLLM::MCP
33
+ extend ClientSupport
29
34
 
30
- require "ruby_llm/mcp"
35
+ requires_envars "NOTION_TOKEN"
36
+ requires_packages "notion-mcp-server"
31
37
 
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
- )
38
+ command "notion-mcp-server"
39
+ env "NOTION_TOKEN" => env_from("NOTION_TOKEN")
40
+ end
41
+ end
42
+ end
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/playwright_client.rb
2
4
  #
3
- # Playwright MCP Server Client — npx auto-download (no pre-installation required)
5
+ # Playwright MCP Server — npx auto-download (no pre-installation required)
4
6
  #
5
7
  # Provides browser automation via Playwright: navigate pages, click elements,
6
8
  # fill forms, take screenshots, extract text/HTML, and interact with web apps.
@@ -9,26 +11,28 @@
9
11
  #
10
12
  # Prerequisites:
11
13
  # - Node.js and npx (https://nodejs.org)
12
- # The @playwright/mcp package is downloaded automatically on first use via `npx -y`.
14
+ # The @playwright/mcp package is downloaded automatically the first time the
15
+ # server is started, via `npx -y`.
13
16
  #
14
17
  # Configuration:
15
18
  # No environment variables required.
16
19
  #
17
20
  # Usage:
18
21
  # require 'shared_tools/mcp/playwright_client'
19
- # client = RubyLLM::MCP.clients["playwright"]
20
- # chat = RubyLLM.chat.with_tools(*client.tools)
22
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::Playwright)
23
+ # robot = RobotLab.build(name: "browser", mcp: [SharedTools::MCP::Playwright])
21
24
  #
22
- # Compatible with ruby_llm-mcp >= 0.7.0
25
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
26
+ # server's tools are first requested.
27
+
28
+ require_relative "client_support"
23
29
 
24
- require "ruby_llm/mcp"
30
+ module SharedTools
31
+ module MCP
32
+ class Playwright < ::RubyLLM::MCP
33
+ extend ClientSupport
25
34
 
26
- RubyLLM::MCP.add_client(
27
- name: "playwright",
28
- transport_type: :stdio,
29
- config: {
30
- command: "npx",
31
- args: ["-y", "@playwright/mcp"],
32
- env: {},
33
- },
34
- )
35
+ command "npx", "-y", "@playwright/mcp"
36
+ end
37
+ end
38
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/sequential_thinking_client.rb
2
4
  #
3
5
  # MCP Sequential Thinking Server — npx auto-download (no pre-installation required)
@@ -9,25 +11,26 @@
9
11
  # Prerequisites:
10
12
  # - Node.js and npx (https://nodejs.org)
11
13
  # The @modelcontextprotocol/server-sequential-thinking package is downloaded
12
- # automatically on first use via `npx -y`.
14
+ # automatically the first time the server is started, via `npx -y`.
13
15
  #
14
16
  # No API key required.
15
17
  #
16
18
  # Usage:
17
19
  # require 'shared_tools/mcp/sequential_thinking_client'
18
- # client = RubyLLM::MCP.clients["sequential-thinking"]
19
- # chat = RubyLLM.chat.with_tools(*client.tools)
20
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::SequentialThinking)
21
+ # robot = RobotLab.build(name: "thinker", mcp: [SharedTools::MCP::SequentialThinking])
20
22
  #
21
- # Compatible with ruby_llm-mcp >= 0.7.0
23
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
24
+ # server's tools are first requested.
25
+
26
+ require_relative "client_support"
22
27
 
23
- require "ruby_llm/mcp"
28
+ module SharedTools
29
+ module MCP
30
+ class SequentialThinking < ::RubyLLM::MCP
31
+ extend ClientSupport
24
32
 
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
- )
33
+ command "npx", "-y", "@modelcontextprotocol/server-sequential-thinking"
34
+ end
35
+ end
36
+ end
@@ -1,16 +1,18 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/slack_client.rb
2
4
  #
3
- # Slack MCP Server Client — requires brew installation
5
+ # Slack MCP Server — local binary over stdio
4
6
  #
5
7
  # Provides Slack workspace access: read channels, messages, and threads,
6
8
  # search message history, list users, and (optionally) post messages.
7
9
  #
8
10
  # Prerequisites:
9
- # - Homebrew (https://brew.sh)
11
+ # - The slack-mcp-server binary on PATH. Install it with
12
+ # SharedTools::MCP::Slack.install_packages! (Homebrew/apt/dnf) or by hand.
10
13
  # - A Slack token — at least one of the following must be set:
11
14
  # SLACK_MCP_XOXP_TOKEN — user OAuth token (xoxp-...) — full access
12
15
  # 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
16
  #
15
17
  # Configuration:
16
18
  # export SLACK_MCP_XOXP_TOKEN=xoxp-your-user-token # recommended
@@ -23,32 +25,35 @@
23
25
  #
24
26
  # Usage:
25
27
  # require 'shared_tools/mcp/slack_client'
26
- # client = RubyLLM::MCP.clients["slack"]
27
- # chat = RubyLLM.chat.with_tools(*client.tools)
28
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::Slack)
29
+ # robot = RobotLab.build(name: "slacker", mcp: [SharedTools::MCP::Slack])
28
30
  #
29
- # Compatible with ruby_llm-mcp >= 0.7.0
31
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). Nothing starts until the
32
+ # server's tools are first requested. Variables are read when the server
33
+ # process starts; unset ones are passed as "", which the server treats as unset.
34
+
35
+ require_relative "client_support"
30
36
 
31
- require_relative "../utilities"
37
+ module SharedTools
38
+ module MCP
39
+ class Slack < ::RubyLLM::MCP
40
+ extend ClientSupport
32
41
 
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?
42
+ TOKEN_ENVARS = %w[SLACK_MCP_XOXP_TOKEN SLACK_MCP_XOXB_TOKEN].freeze
36
43
 
37
- SharedTools.package_install("slack-mcp-server")
44
+ requires_packages "slack-mcp-server"
38
45
 
39
- require "ruby_llm/mcp"
46
+ command "slack-mcp-server"
47
+ env "SLACK_MCP_XOXP_TOKEN" => env_from("SLACK_MCP_XOXP_TOKEN"),
48
+ "SLACK_MCP_XOXB_TOKEN" => env_from("SLACK_MCP_XOXB_TOKEN"),
49
+ "SLACK_MCP_ADD_MESSAGE_TOOL" => env_from("SLACK_MCP_ADD_MESSAGE_TOOL")
40
50
 
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"]
51
+ # Either token is enough, so this replaces the all-of requires_envars check.
52
+ def self.missing_envars
53
+ return [] if TOKEN_ENVARS.any? { |name| !ENV.fetch(name, "").empty? }
45
54
 
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
- )
55
+ ["SLACK_MCP_XOXP_TOKEN or SLACK_MCP_XOXB_TOKEN"]
56
+ end
57
+ end
58
+ end
59
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # shared_tools/mcp/tavily_client.rb
2
4
  #
3
- # Tavily MCP Server Client — Remote HTTP (no local installation required)
5
+ # Tavily MCP Server — Remote Streamable HTTP (no local installation required)
4
6
  #
5
- # Connects directly to Tavily's hosted MCP endpoint via Streamable HTTP transport.
7
+ # Connects directly to Tavily's hosted MCP endpoint.
6
8
  # No npx, no Node.js, no local binary required — only a Tavily API key.
7
9
  #
8
10
  # Provides:
@@ -16,26 +18,24 @@
16
18
  #
17
19
  # Usage:
18
20
  # require 'shared_tools/mcp/tavily_client'
19
- # client = RubyLLM::MCP.clients["tavily"]
20
- # chat = RubyLLM.chat.with_tools(*client.tools)
21
+ # chat = RubyLLM.chat.with_mcp(SharedTools::MCP::Tavily)
22
+ # robot = RobotLab.build(name: "researcher", mcp: [SharedTools::MCP::Tavily])
21
23
  #
22
- # Compatible with ruby_llm-mcp >= 0.7.0
24
+ # Requires ruby_llm >= 2.1 (built-in RubyLLM::MCP). No request is made until
25
+ # the server's tools are first requested.
26
+
27
+ require_relative "client_support"
23
28
 
24
- require_relative "../utilities"
25
- require_relative "streamable_http_patch"
29
+ module SharedTools
30
+ module MCP
31
+ class Tavily < ::RubyLLM::MCP
32
+ extend ClientSupport
26
33
 
27
- SharedTools.verify_envars("TAVILY_API_KEY")
34
+ requires_envars "TAVILY_API_KEY"
28
35
 
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
- )
36
+ url "https://mcp.tavily.com/mcp/"
37
+ bearer_token { ENV.fetch("TAVILY_API_KEY", "") }
38
+ timeout 30
39
+ end
40
+ end
41
+ end