shared_tools 0.1.3 → 0.2.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 +12 -2
- data/README.md +36 -33
- data/lib/shared_tools/raix/what_is_the_weather.rb +3 -3
- data/lib/shared_tools/ruby_llm/edit_file.rb +1 -0
- data/lib/shared_tools/ruby_llm/incomplete/calculator_tool.rb +70 -0
- data/lib/shared_tools/ruby_llm/incomplete/composite_analysis_tool.rb +89 -0
- data/lib/shared_tools/ruby_llm/incomplete/data_science_kit.rb +128 -0
- data/lib/shared_tools/ruby_llm/incomplete/database_query_tool.rb +100 -0
- data/lib/shared_tools/ruby_llm/incomplete/devops_toolkit.rb +112 -0
- data/lib/shared_tools/ruby_llm/incomplete/error_handling_tool.rb +109 -0
- data/lib/shared_tools/ruby_llm/incomplete/secure_tool_template.rb +117 -0
- data/lib/shared_tools/ruby_llm/incomplete/weather_tool.rb +110 -0
- data/lib/shared_tools/ruby_llm/incomplete/workflow_manager_tool.rb +145 -0
- data/lib/shared_tools/ruby_llm/list_files.rb +1 -0
- data/lib/shared_tools/ruby_llm/mcp/github_mcp_server.rb +51 -0
- data/lib/shared_tools/ruby_llm/mcp/imcp.rb +33 -0
- data/lib/shared_tools/ruby_llm/mcp.rb +10 -0
- data/lib/shared_tools/ruby_llm/pdf_page_reader.rb +1 -0
- data/lib/shared_tools/ruby_llm/python_eval.rb +2 -1
- data/lib/shared_tools/ruby_llm/read_file.rb +1 -0
- data/lib/shared_tools/ruby_llm/ruby_eval.rb +2 -1
- data/lib/shared_tools/ruby_llm/run_shell_command.rb +2 -1
- data/lib/shared_tools/ruby_llm.rb +4 -1
- data/lib/shared_tools/version.rb +1 -1
- data/lib/shared_tools.rb +11 -2
- metadata +31 -5
@@ -0,0 +1,109 @@
|
|
1
|
+
# error_handling_tool.rb - Comprehensive error handling
|
2
|
+
require 'ruby_llm/tool'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module Tools
|
6
|
+
class RobustTool < RubyLLM::Tool
|
7
|
+
def self.name = 'robust_tool'
|
8
|
+
|
9
|
+
description <<~DESCRIPTION
|
10
|
+
Reference tool demonstrating comprehensive error handling patterns and resilience strategies
|
11
|
+
for robust tool development. This tool showcases best practices for handling different
|
12
|
+
types of errors including validation errors, network failures, authorization issues,
|
13
|
+
and general exceptions. It implements retry mechanisms with exponential backoff,
|
14
|
+
proper resource cleanup, detailed error categorization, and user-friendly error messages.
|
15
|
+
Perfect as a template for building production-ready tools that need to handle
|
16
|
+
various failure scenarios gracefully.
|
17
|
+
DESCRIPTION
|
18
|
+
|
19
|
+
def execute(**params)
|
20
|
+
begin
|
21
|
+
validate_preconditions(params)
|
22
|
+
result = perform_operation(params)
|
23
|
+
validate_postconditions(result)
|
24
|
+
|
25
|
+
{
|
26
|
+
success: true,
|
27
|
+
result: result,
|
28
|
+
metadata: operation_metadata
|
29
|
+
}
|
30
|
+
rescue ValidationError => e
|
31
|
+
handle_validation_error(e, params)
|
32
|
+
rescue NetworkError => e
|
33
|
+
handle_network_error(e, params)
|
34
|
+
rescue AuthorizationError => e
|
35
|
+
handle_authorization_error(e, params)
|
36
|
+
rescue StandardError => e
|
37
|
+
handle_general_error(e, params)
|
38
|
+
ensure
|
39
|
+
cleanup_resources
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def validate_preconditions(params)
|
46
|
+
# Check all preconditions before execution
|
47
|
+
end
|
48
|
+
|
49
|
+
def perform_operation(params)
|
50
|
+
# Main operation logic with retry mechanism
|
51
|
+
retry_count = 0
|
52
|
+
max_retries = 3
|
53
|
+
|
54
|
+
begin
|
55
|
+
# Operation implementation
|
56
|
+
rescue RetryableError => e
|
57
|
+
retry_count += 1
|
58
|
+
if retry_count <= max_retries
|
59
|
+
sleep(2 ** retry_count) # Exponential backoff
|
60
|
+
retry
|
61
|
+
else
|
62
|
+
raise e
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_validation_error(error, params)
|
68
|
+
{
|
69
|
+
success: false,
|
70
|
+
error_type: "validation",
|
71
|
+
error: error.message,
|
72
|
+
suggestions: error.suggestions,
|
73
|
+
provided_params: params.keys
|
74
|
+
}
|
75
|
+
end
|
76
|
+
|
77
|
+
def handle_network_error(error, params)
|
78
|
+
{
|
79
|
+
success: false,
|
80
|
+
error_type: "network",
|
81
|
+
error: "Network operation failed",
|
82
|
+
retry_suggested: true,
|
83
|
+
retry_after: 30
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
def handle_authorization_error(error, params)
|
88
|
+
{
|
89
|
+
success: false,
|
90
|
+
error_type: "authorization",
|
91
|
+
error: "Access denied",
|
92
|
+
documentation_url: "https://docs.example.com/auth"
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
def handle_general_error(error, params)
|
97
|
+
{
|
98
|
+
success: false,
|
99
|
+
error_type: "general",
|
100
|
+
error: error.message,
|
101
|
+
support_reference: SecureRandom.uuid
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def cleanup_resources
|
106
|
+
# Clean up any allocated resources
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# secure_tool_template.rb - Security best practices
|
2
|
+
require 'ruby_llm/tool'
|
3
|
+
require 'timeout'
|
4
|
+
|
5
|
+
module Tools
|
6
|
+
class SecureTool < RubyLLM::Tool
|
7
|
+
def self.name = 'secure_tool'
|
8
|
+
|
9
|
+
description <<~DESCRIPTION
|
10
|
+
Template tool demonstrating comprehensive security best practices for safe tool development.
|
11
|
+
This tool serves as a reference implementation for secure tool design, including input
|
12
|
+
validation, output sanitization, permission checks, rate limiting, audit logging,
|
13
|
+
timeout mechanisms, and proper error handling. It provides a complete security framework
|
14
|
+
that can be adapted for other tools that handle sensitive data or perform privileged
|
15
|
+
operations. All security violations are logged for monitoring and compliance purposes.
|
16
|
+
DESCRIPTION
|
17
|
+
|
18
|
+
# Input validation
|
19
|
+
param :user_input,
|
20
|
+
desc: <<~DESC,
|
21
|
+
User-provided input string that will be processed with comprehensive security validation.
|
22
|
+
Input is automatically sanitized and validated against multiple security criteria:
|
23
|
+
- Maximum length of 1000 characters to prevent buffer overflow attacks
|
24
|
+
- Character whitelist allowing only alphanumeric, spaces, hyphens, underscores, and dots
|
25
|
+
- Automatic removal of potentially dangerous characters and sequences
|
26
|
+
- Rate limiting to prevent abuse and denial-of-service attacks
|
27
|
+
All input validation failures are logged for security monitoring.
|
28
|
+
DESC
|
29
|
+
type: :string,
|
30
|
+
required: true,
|
31
|
+
validator: ->(value) {
|
32
|
+
# Custom validation logic
|
33
|
+
raise "Input too long" if value.length > 1000
|
34
|
+
raise "Invalid characters" unless value.match?(/\A[a-zA-Z0-9\s\-_\.]+\z/)
|
35
|
+
true
|
36
|
+
}
|
37
|
+
|
38
|
+
def execute(user_input:)
|
39
|
+
begin
|
40
|
+
# 1. Sanitize inputs
|
41
|
+
sanitized_input = sanitize_input(user_input)
|
42
|
+
|
43
|
+
# 2. Validate permissions
|
44
|
+
validate_permissions
|
45
|
+
|
46
|
+
# 3. Rate limiting
|
47
|
+
check_rate_limits
|
48
|
+
|
49
|
+
# 4. Audit logging
|
50
|
+
log_tool_usage(sanitized_input)
|
51
|
+
|
52
|
+
# 5. Execute with timeout
|
53
|
+
result = execute_with_timeout(sanitized_input)
|
54
|
+
|
55
|
+
# 6. Sanitize outputs
|
56
|
+
sanitized_result = sanitize_output(result)
|
57
|
+
|
58
|
+
{
|
59
|
+
success: true,
|
60
|
+
result: sanitized_result,
|
61
|
+
executed_at: Time.now.iso8601
|
62
|
+
}
|
63
|
+
rescue SecurityError => e
|
64
|
+
log_security_violation(e, user_input)
|
65
|
+
{
|
66
|
+
success: false,
|
67
|
+
error: "Security violation: Access denied",
|
68
|
+
violation_logged: true
|
69
|
+
}
|
70
|
+
rescue => e
|
71
|
+
{
|
72
|
+
success: false,
|
73
|
+
error: "Tool execution failed: #{e.message}"
|
74
|
+
}
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def sanitize_input(input)
|
81
|
+
# Remove potentially dangerous characters
|
82
|
+
# Validate against whitelist
|
83
|
+
input.gsub(/[^\w\s\-\.]/, '')
|
84
|
+
end
|
85
|
+
|
86
|
+
def validate_permissions
|
87
|
+
# Check user permissions
|
88
|
+
# Validate environment access
|
89
|
+
# Verify resource limits
|
90
|
+
end
|
91
|
+
|
92
|
+
def check_rate_limits
|
93
|
+
# Implement rate limiting logic
|
94
|
+
end
|
95
|
+
|
96
|
+
def log_tool_usage(input)
|
97
|
+
# Audit logging for compliance
|
98
|
+
end
|
99
|
+
|
100
|
+
def execute_with_timeout(input, timeout: 30)
|
101
|
+
# Implement timeout mechanism
|
102
|
+
Timeout::timeout(timeout) do
|
103
|
+
# Actual tool logic here
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def sanitize_output(output)
|
108
|
+
# Remove sensitive information from output
|
109
|
+
# Validate output format
|
110
|
+
output
|
111
|
+
end
|
112
|
+
|
113
|
+
def log_security_violation(error, input)
|
114
|
+
# Log security violations for monitoring
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
# weather_tool.rb - API integration example
|
2
|
+
require 'ruby_llm/tool'
|
3
|
+
require 'net/http'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Tools
|
7
|
+
class WeatherTool < RubyLLM::Tool
|
8
|
+
def self.name = 'weather_tool'
|
9
|
+
|
10
|
+
description <<~DESCRIPTION
|
11
|
+
Retrieve comprehensive current weather information for any city worldwide using the OpenWeatherMap API.
|
12
|
+
This tool provides real-time weather data including temperature, atmospheric conditions, humidity,
|
13
|
+
and wind information. It supports multiple temperature units and can optionally include extended
|
14
|
+
forecast data. The tool requires a valid OpenWeatherMap API key to be configured in the
|
15
|
+
OPENWEATHER_API_KEY environment variable. All weather data is fetched in real-time and includes
|
16
|
+
timestamps for accuracy verification.
|
17
|
+
DESCRIPTION
|
18
|
+
|
19
|
+
param :city,
|
20
|
+
desc: <<~DESC,
|
21
|
+
Name of the city for weather lookup. Can include city name only (e.g., 'London')
|
22
|
+
or city with country code for better accuracy (e.g., 'London,UK' or 'Paris,FR').
|
23
|
+
For cities with common names in multiple countries, including the country code
|
24
|
+
is recommended to ensure accurate results. The API will attempt to find the
|
25
|
+
closest match if an exact match is not found.
|
26
|
+
DESC
|
27
|
+
type: :string,
|
28
|
+
required: true
|
29
|
+
|
30
|
+
param :units,
|
31
|
+
desc: <<~DESC,
|
32
|
+
Temperature unit system for the weather data. Options are:
|
33
|
+
- 'metric': Temperature in Celsius, wind speed in m/s, pressure in hPa
|
34
|
+
- 'imperial': Temperature in Fahrenheit, wind speed in mph, pressure in hPa
|
35
|
+
- 'kelvin': Temperature in Kelvin (scientific standard), wind speed in m/s
|
36
|
+
Default is 'metric' which is most commonly used internationally.
|
37
|
+
DESC
|
38
|
+
type: :string,
|
39
|
+
default: "metric",
|
40
|
+
enum: ["metric", "imperial", "kelvin"]
|
41
|
+
|
42
|
+
param :include_forecast,
|
43
|
+
desc: <<~DESC,
|
44
|
+
Boolean flag to include a 3-day weather forecast in addition to current conditions.
|
45
|
+
When set to true, the response will include forecast data with daily high/low temperatures,
|
46
|
+
precipitation probability, and general weather conditions for the next three days.
|
47
|
+
This requires additional API calls and may increase response time slightly.
|
48
|
+
DESC
|
49
|
+
type: :boolean,
|
50
|
+
default: false
|
51
|
+
|
52
|
+
def execute(city:, units: "metric", include_forecast: false)
|
53
|
+
begin
|
54
|
+
api_key = ENV['OPENWEATHER_API_KEY']
|
55
|
+
raise "OpenWeather API key not configured" unless api_key
|
56
|
+
|
57
|
+
current_weather = fetch_current_weather(city, units, api_key)
|
58
|
+
result = {
|
59
|
+
success: true,
|
60
|
+
city: city,
|
61
|
+
current: current_weather,
|
62
|
+
units: units,
|
63
|
+
timestamp: Time.now.iso8601
|
64
|
+
}
|
65
|
+
|
66
|
+
if include_forecast
|
67
|
+
forecast_data = fetch_forecast(city, units, api_key)
|
68
|
+
result[:forecast] = forecast_data
|
69
|
+
end
|
70
|
+
|
71
|
+
result
|
72
|
+
rescue => e
|
73
|
+
{
|
74
|
+
success: false,
|
75
|
+
error: e.message,
|
76
|
+
city: city,
|
77
|
+
suggestion: "Verify city name and API key configuration"
|
78
|
+
}
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def fetch_current_weather(city, units, api_key)
|
85
|
+
uri = URI("https://api.openweathermap.org/data/2.5/weather")
|
86
|
+
params = {
|
87
|
+
q: city,
|
88
|
+
appid: api_key,
|
89
|
+
units: units
|
90
|
+
}
|
91
|
+
uri.query = URI.encode_www_form(params)
|
92
|
+
|
93
|
+
response = Net::HTTP.get_response(uri)
|
94
|
+
raise "Weather API error: #{response.code}" unless response.code == '200'
|
95
|
+
|
96
|
+
data = JSON.parse(response.body)
|
97
|
+
{
|
98
|
+
temperature: data['main']['temp'],
|
99
|
+
description: data['weather'][0]['description'],
|
100
|
+
humidity: data['main']['humidity'],
|
101
|
+
wind_speed: data['wind']['speed']
|
102
|
+
}
|
103
|
+
end
|
104
|
+
|
105
|
+
def fetch_forecast(city, units, api_key)
|
106
|
+
# Implementation for forecast data
|
107
|
+
# Similar pattern to current weather
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,145 @@
|
|
1
|
+
# workflow_manager_tool.rb - Managing state across tool invocations
|
2
|
+
require 'ruby_llm/tool'
|
3
|
+
require 'securerandom'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
module Tools
|
7
|
+
class WorkflowManager < RubyLLM::Tool
|
8
|
+
def self.name = 'workflow_manager'
|
9
|
+
|
10
|
+
description <<~DESCRIPTION
|
11
|
+
Manage complex multi-step workflows with persistent state tracking across tool invocations.
|
12
|
+
This tool enables the creation and management of stateful workflows that can span multiple
|
13
|
+
AI interactions and tool calls. It provides workflow initialization, step-by-step execution,
|
14
|
+
status monitoring, and completion tracking. Each workflow maintains its state in persistent
|
15
|
+
storage, allowing for resumption of long-running processes and coordination between
|
16
|
+
multiple tools and AI interactions. Perfect for complex automation tasks that require
|
17
|
+
multiple stages and decision points.
|
18
|
+
DESCRIPTION
|
19
|
+
|
20
|
+
param :action,
|
21
|
+
desc: <<~DESC,
|
22
|
+
Workflow management action to perform:
|
23
|
+
- 'start': Initialize a new workflow with initial data and return workflow ID
|
24
|
+
- 'step': Execute the next step in an existing workflow using provided step data
|
25
|
+
- 'status': Check the current status and progress of an existing workflow
|
26
|
+
- 'complete': Mark a workflow as finished and clean up associated resources
|
27
|
+
Each action requires different combinations of the other parameters.
|
28
|
+
DESC
|
29
|
+
type: :string,
|
30
|
+
required: true,
|
31
|
+
enum: ["start", "step", "status", "complete"]
|
32
|
+
|
33
|
+
param :workflow_id,
|
34
|
+
desc: <<~DESC,
|
35
|
+
Unique identifier for an existing workflow. Required for 'step', 'status', and 'complete'
|
36
|
+
actions. This ID is returned when starting a new workflow and should be used for all
|
37
|
+
subsequent operations on that workflow. The ID is a UUID string that ensures
|
38
|
+
uniqueness across all workflow instances.
|
39
|
+
DESC
|
40
|
+
type: :string
|
41
|
+
|
42
|
+
param :step_data,
|
43
|
+
desc: <<~DESC,
|
44
|
+
Hash containing data and parameters specific to the current workflow step.
|
45
|
+
For 'start' action: Initial configuration and parameters for the workflow.
|
46
|
+
For 'step' action: Input data, parameters, and context needed for the next step.
|
47
|
+
The structure depends on the specific workflow type and current step requirements.
|
48
|
+
Can include nested hashes, arrays, and any JSON-serializable data types.
|
49
|
+
DESC
|
50
|
+
type: :hash,
|
51
|
+
default: {}
|
52
|
+
|
53
|
+
def execute(action:, workflow_id: nil, step_data: {})
|
54
|
+
case action
|
55
|
+
when "start"
|
56
|
+
start_workflow(step_data)
|
57
|
+
when "step"
|
58
|
+
process_workflow_step(workflow_id, step_data)
|
59
|
+
when "status"
|
60
|
+
get_workflow_status(workflow_id)
|
61
|
+
when "complete"
|
62
|
+
complete_workflow(workflow_id)
|
63
|
+
else
|
64
|
+
{ success: false, error: "Unknown action: #{action}" }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
private
|
69
|
+
|
70
|
+
def start_workflow(initial_data)
|
71
|
+
workflow_id = SecureRandom.uuid
|
72
|
+
workflow_state = {
|
73
|
+
id: workflow_id,
|
74
|
+
status: "active",
|
75
|
+
steps: [],
|
76
|
+
created_at: Time.now.iso8601,
|
77
|
+
data: initial_data
|
78
|
+
}
|
79
|
+
|
80
|
+
save_workflow_state(workflow_id, workflow_state)
|
81
|
+
|
82
|
+
{
|
83
|
+
success: true,
|
84
|
+
workflow_id: workflow_id,
|
85
|
+
status: "started",
|
86
|
+
next_actions: suggested_next_actions(initial_data)
|
87
|
+
}
|
88
|
+
end
|
89
|
+
|
90
|
+
def process_workflow_step(workflow_id, step_data)
|
91
|
+
workflow_state = load_workflow_state(workflow_id)
|
92
|
+
return { success: false, error: "Workflow not found" } unless workflow_state
|
93
|
+
|
94
|
+
step = {
|
95
|
+
step_number: workflow_state[:steps].length + 1,
|
96
|
+
data: step_data,
|
97
|
+
processed_at: Time.now.iso8601,
|
98
|
+
result: process_step_logic(step_data, workflow_state)
|
99
|
+
}
|
100
|
+
|
101
|
+
workflow_state[:steps] << step
|
102
|
+
workflow_state[:updated_at] = Time.now.iso8601
|
103
|
+
|
104
|
+
save_workflow_state(workflow_id, workflow_state)
|
105
|
+
|
106
|
+
{
|
107
|
+
success: true,
|
108
|
+
workflow_id: workflow_id,
|
109
|
+
step_completed: step,
|
110
|
+
workflow_status: workflow_state[:status],
|
111
|
+
next_actions: suggested_next_actions(workflow_state)
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def save_workflow_state(workflow_id, state)
|
116
|
+
# Implementation for state persistence
|
117
|
+
# Could use files, database, or memory store
|
118
|
+
File.write(".workflow_#{workflow_id}.json", state.to_json)
|
119
|
+
end
|
120
|
+
|
121
|
+
def load_workflow_state(workflow_id)
|
122
|
+
# Implementation for state loading
|
123
|
+
file_path = ".workflow_#{workflow_id}.json"
|
124
|
+
return nil unless File.exist?(file_path)
|
125
|
+
|
126
|
+
JSON.parse(File.read(file_path), symbolize_names: true)
|
127
|
+
end
|
128
|
+
|
129
|
+
def get_workflow_status(workflow_id)
|
130
|
+
# Implementation for status retrieval
|
131
|
+
end
|
132
|
+
|
133
|
+
def complete_workflow(workflow_id)
|
134
|
+
# Implementation for workflow completion
|
135
|
+
end
|
136
|
+
|
137
|
+
def suggested_next_actions(workflow_state)
|
138
|
+
# Implementation for suggesting next actions
|
139
|
+
end
|
140
|
+
|
141
|
+
def process_step_logic(step_data, workflow_state)
|
142
|
+
# Implementation for processing step logic
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -6,6 +6,7 @@ module SharedTools
|
|
6
6
|
verify_gem :ruby_llm
|
7
7
|
|
8
8
|
class ListFiles < ::RubyLLM::Tool
|
9
|
+
def self.name = 'list_files'
|
9
10
|
|
10
11
|
description "List files and directories at a given path. If no path is provided, lists files in the current directory."
|
11
12
|
param :path, desc: "Optional relative path to list files from. Defaults to current directory if not provided."
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# shared_tools/ruby_llm/mcp/github_mcp_server.rb
|
2
|
+
# brew install github_mcp_server
|
3
|
+
|
4
|
+
require 'debug_me'
|
5
|
+
include DebugMe
|
6
|
+
|
7
|
+
require 'ruby_llm'
|
8
|
+
require 'ruby_llm/mcp'
|
9
|
+
|
10
|
+
require_relative '../../../shared_tools'
|
11
|
+
|
12
|
+
module SharedTools
|
13
|
+
verify_gem :ruby_llm
|
14
|
+
|
15
|
+
mcp_servers << RubyLLM::MCP.client(
|
16
|
+
name: "github-mcp-server",
|
17
|
+
transport_type: :stdio,
|
18
|
+
config: {
|
19
|
+
command: "/opt/homebrew/bin/github-mcp-server", # brew install github-mcp-server
|
20
|
+
args: %w[stdio],
|
21
|
+
env: { "GITHUB_PERSONAL_ACCESS_TOKEN" => ENV.fetch('GITHUB_PERSONAL_ACCESS_TOKEN') }
|
22
|
+
}
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
__END__
|
28
|
+
|
29
|
+
|
30
|
+
A GitHub MCP server that handles various tools and resources.
|
31
|
+
|
32
|
+
Usage:
|
33
|
+
server [command]
|
34
|
+
|
35
|
+
Available Commands:
|
36
|
+
completion Generate the autocompletion script for the specified shell
|
37
|
+
help Help about any command
|
38
|
+
stdio Start stdio server
|
39
|
+
|
40
|
+
Flags:
|
41
|
+
--dynamic-toolsets Enable dynamic toolsets
|
42
|
+
--enable-command-logging When enabled, the server will log all command requests and responses to the log file
|
43
|
+
--export-translations Save translations to a JSON file
|
44
|
+
--gh-host string Specify the GitHub hostname (for GitHub Enterprise etc.)
|
45
|
+
-h, --help help for server
|
46
|
+
--log-file string Path to log file
|
47
|
+
--read-only Restrict the server to read-only operations
|
48
|
+
--toolsets strings An optional comma separated list of groups of tools to allow, defaults to enabling all (default [all])
|
49
|
+
-v, --version version for server
|
50
|
+
|
51
|
+
Use "server [command] --help" for more information about a command.
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# shared_tools/ruby_llm/mcp/imcp.rb
|
2
|
+
# iMCP is a MacOS program that provides access to notes,calendar,contacts, etc.
|
3
|
+
# See: https://github.com/loopwork/iMCP
|
4
|
+
# brew install --cask loopwork/tap/iMCP
|
5
|
+
#
|
6
|
+
# CAUTION: AIA is getting an exception when trying to use this MCP client. Its returning to
|
7
|
+
# do a to_sym on a nil value. This is due to a lack of a nil guard in the
|
8
|
+
# version 0.3.1 of the ruby_llm-mpc Parameter#item_type method.
|
9
|
+
#
|
10
|
+
# NOTE: iMCP's server is a noisy little thing shooting all its log messages to STDERR.
|
11
|
+
# To silence it, redirect STDERR to /dev/null.
|
12
|
+
# If you messages then you might want to redirect STDERR to a file.
|
13
|
+
#
|
14
|
+
|
15
|
+
require 'debug_me'
|
16
|
+
include DebugMe
|
17
|
+
|
18
|
+
require 'ruby_llm'
|
19
|
+
require 'ruby_llm/mcp'
|
20
|
+
|
21
|
+
require_relative '../../../shared_tools'
|
22
|
+
|
23
|
+
module SharedTools
|
24
|
+
verify_gem :ruby_llm
|
25
|
+
|
26
|
+
mcp_servers << RubyLLM::MCP.client(
|
27
|
+
name: "imcp-server",
|
28
|
+
transport_type: :stdio,
|
29
|
+
config: {
|
30
|
+
command: "/Applications/iMCP.app/Contents/MacOS/imcp-server 2> /dev/null"
|
31
|
+
}
|
32
|
+
)
|
33
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# shared_tools/ruby_llm/mcp.rb
|
2
|
+
# This file loads all Ruby files in the mcp directory
|
3
|
+
|
4
|
+
# Get the directory path
|
5
|
+
mcp_dir = File.join(__dir__, 'mcp')
|
6
|
+
|
7
|
+
# Load all .rb files in the mcp directory
|
8
|
+
Dir.glob(File.join(mcp_dir, '*.rb')).each do |file|
|
9
|
+
require file
|
10
|
+
end
|
@@ -6,6 +6,7 @@ module SharedTools
|
|
6
6
|
verify_gem :ruby_llm
|
7
7
|
|
8
8
|
class PythonEval < ::RubyLLM::Tool
|
9
|
+
def self.name = 'python_eval'
|
9
10
|
|
10
11
|
description <<~DESCRIPTION
|
11
12
|
Execute Python source code safely and return the result.
|
@@ -29,7 +30,7 @@ module SharedTools
|
|
29
30
|
end
|
30
31
|
|
31
32
|
# Show user the code and ask for confirmation
|
32
|
-
allowed = SharedTools.execute?(tool: self.class.
|
33
|
+
allowed = SharedTools.execute?(tool: self.class.to_s, stuff: code)
|
33
34
|
|
34
35
|
unless allowed
|
35
36
|
RubyLLM.logger.warn("User declined to execute the Python code")
|
@@ -6,6 +6,7 @@ module SharedTools
|
|
6
6
|
verify_gem :ruby_llm
|
7
7
|
|
8
8
|
class ReadFile < ::RubyLLM::Tool
|
9
|
+
def self.name = 'read_file'
|
9
10
|
|
10
11
|
description "Read the contents of a given relative file path. Use this when you want to see what's inside a file. Do not use this with directory names."
|
11
12
|
param :path, desc: "The relative path of a file in the working directory."
|
@@ -6,6 +6,7 @@ module SharedTools
|
|
6
6
|
verify_gem :ruby_llm
|
7
7
|
|
8
8
|
class RubyEval < ::RubyLLM::Tool
|
9
|
+
def self.name = 'ruby_eval'
|
9
10
|
|
10
11
|
description <<~DESCRIPTION
|
11
12
|
Execute Ruby source code safely and return the result.
|
@@ -27,7 +28,7 @@ module SharedTools
|
|
27
28
|
end
|
28
29
|
|
29
30
|
# Show user the code and ask for confirmation
|
30
|
-
allowed = SharedTools.execute?(tool: self.class.
|
31
|
+
allowed = SharedTools.execute?(tool: self.class.to_s, stuff: code)
|
31
32
|
|
32
33
|
unless allowed
|
33
34
|
RubyLLM.logger.warn("User declined to execute the Ruby code")
|
@@ -6,6 +6,7 @@ module SharedTools
|
|
6
6
|
SharedTools.verify_gem :ruby_llm
|
7
7
|
|
8
8
|
class RunShellCommand < ::RubyLLM::Tool
|
9
|
+
def self.name = 'run_shell_command'
|
9
10
|
|
10
11
|
description "Execute a shell command"
|
11
12
|
param :command, desc: "The command to execute"
|
@@ -20,7 +21,7 @@ module SharedTools
|
|
20
21
|
end
|
21
22
|
|
22
23
|
# Show user the command and ask for confirmation
|
23
|
-
allowed = SharedTools.execute?(tool: self.class.
|
24
|
+
allowed = SharedTools.execute?(tool: self.class.to_s, stuff: command)
|
24
25
|
|
25
26
|
unless allowed
|
26
27
|
RubyLLM.logger.warn("User declined to execute the command: '#{command}'")
|
@@ -4,6 +4,9 @@ require_relative '../shared_tools'
|
|
4
4
|
|
5
5
|
SharedTools.verify_gem :ruby_llm
|
6
6
|
|
7
|
-
|
7
|
+
# This excludes the sub-directories and mcp.rb
|
8
|
+
Dir.glob(File.join(__dir__, "ruby_llm", "*.rb"))
|
9
|
+
.reject { |f| File.basename(f) == 'mcp.rb' }
|
10
|
+
.each do |file|
|
8
11
|
require file
|
9
12
|
end
|
data/lib/shared_tools/version.rb
CHANGED