active_mcp 0.5.1 → 0.7.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.
@@ -1,150 +0,0 @@
1
- require "json"
2
-
3
- module ActiveMcp
4
- class Server
5
- class ResourceManager
6
- attr_reader :resources
7
-
8
- def initialize(uri: nil, auth: nil)
9
- @resources = {}
10
- @base_uri = uri
11
-
12
- if auth
13
- @auth_header = "#{auth[:type] == :bearer ? "Bearer" : "Basic"} #{auth[:token]}"
14
- end
15
- end
16
-
17
- def load_registered_resources
18
- fetch_resources
19
- end
20
-
21
- def read_resource(uri)
22
- require "net/http"
23
-
24
- unless @base_uri.is_a?(URI) || @base_uri.is_a?(String)
25
- log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
26
- return {
27
- isError: true,
28
- content: [{type: "text", text: "Invalid URI configuration"}]
29
- }
30
- end
31
-
32
- begin
33
- base_uri = URI.parse(@base_uri.to_s)
34
-
35
- unless base_uri.scheme =~ /\Ahttps?\z/ && !base_uri.host.nil?
36
- log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
37
- return {
38
- isError: true,
39
- content: [{type: "text", text: "Invalid URI configuration"}]
40
- }
41
- end
42
-
43
- if defined?(Rails) && Rails.env.production? && base_uri.scheme != "https"
44
- return {
45
- isError: true,
46
- content: [{type: "text", text: "HTTPS is required in production environment"}]
47
- }
48
- end
49
- rescue URI::InvalidURIError => e
50
- log_error("Invalid URI format", e)
51
- return {
52
- isError: true,
53
- content: [{type: "text", text: "Invalid URI format"}]
54
- }
55
- end
56
-
57
- request = Net::HTTP::Post.new(base_uri)
58
- request.body = JSON.generate({
59
- method: Method::RESOURCES_READ,
60
- uri:,
61
- })
62
- request["Content-Type"] = "application/json"
63
- request["Authorization"] = @auth_header
64
-
65
- begin
66
- response = Net::HTTP.start(base_uri.hostname, base_uri.port) do |http|
67
- http.request(request)
68
- end
69
-
70
- if response.code == "200"
71
- JSON.parse(response.body, symbolize_names: true)
72
- else
73
- $stderr.puts(response.body)
74
- {
75
- isError: true,
76
- contents: []
77
- }
78
- end
79
- rescue => e
80
- log_error("Error calling tool", e)
81
- {
82
- isError: true,
83
- contents: []
84
- }
85
- end
86
- end
87
-
88
- private
89
-
90
- def fetch_resources
91
- return unless @base_uri
92
-
93
- require "net/http"
94
-
95
- unless @base_uri.is_a?(URI) || @base_uri.is_a?(String)
96
- log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
97
- return
98
- end
99
-
100
- begin
101
- uri = URI.parse(@base_uri.to_s)
102
-
103
- unless uri.scheme =~ /\Ahttps?\z/ && !uri.host.nil?
104
- log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
105
- return
106
- end
107
-
108
- if defined?(Rails) && Rails.env.production? && uri.scheme != "https"
109
- log_error("HTTPS is required in production environment", StandardError.new("Non-HTTPS URI in production"))
110
- return
111
- end
112
- rescue URI::InvalidURIError => e
113
- log_error("Invalid URI format", e)
114
- return
115
- end
116
-
117
- request = Net::HTTP::Post.new(uri)
118
- request.body = JSON.generate({
119
- method: "resources/list",
120
- arguments: "{}"
121
- })
122
- request["Content-Type"] = "application/json"
123
- request["Authorization"] = @auth_header
124
-
125
- begin
126
- response = Net::HTTP.start(uri.hostname, uri.port) do |http|
127
- http.request(request)
128
- end
129
-
130
- result = JSON.parse(response.body, symbolize_names: true)
131
- @resources = result[:result]
132
- rescue => e
133
- log_error("Error fetching resources", e)
134
- @resources = []
135
- end
136
- end
137
-
138
- def log_error(message, error)
139
- error_details = "#{message}: #{error.message}\n"
140
- error_details += error.backtrace.join("\n") if error.backtrace
141
-
142
- if defined?(Rails)
143
- Rails.logger.error(error_details)
144
- else
145
- $stderr.puts(error_details)
146
- end
147
- end
148
- end
149
- end
150
- end
@@ -1,163 +0,0 @@
1
- require "json"
2
-
3
- module ActiveMcp
4
- class Server
5
- class ToolManager
6
- attr_reader :tools
7
-
8
- def initialize(uri: nil, auth: nil)
9
- @tools = {}
10
- @uri = uri
11
-
12
- if auth
13
- @auth_header = "#{auth[:type] == :bearer ? "Bearer" : "Basic"} #{auth[:token]}"
14
- end
15
- end
16
-
17
- def call_tool(name, arguments = {})
18
- tool_info = @tools.find { _1[:name] == name }
19
-
20
- unless tool_info
21
- return {
22
- isError: true,
23
- content: [{type: "text", text: "Tool not found: #{name}"}]
24
- }
25
- end
26
-
27
- invoke_tool(name, arguments)
28
- end
29
-
30
- def load_registered_tools
31
- fetch_tools
32
- end
33
-
34
- private
35
-
36
- def invoke_tool(name, arguments)
37
- require "net/http"
38
-
39
- unless @uri.is_a?(URI) || @uri.is_a?(String)
40
- log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
41
- return {
42
- isError: true,
43
- content: [{type: "text", text: "Invalid URI configuration"}]
44
- }
45
- end
46
-
47
- begin
48
- uri = URI.parse(@uri.to_s)
49
-
50
- unless uri.scheme =~ /\Ahttps?\z/ && !uri.host.nil?
51
- log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
52
- return {
53
- isError: true,
54
- content: [{type: "text", text: "Invalid URI configuration"}]
55
- }
56
- end
57
-
58
- if defined?(Rails) && Rails.env.production? && uri.scheme != "https"
59
- return {
60
- isError: true,
61
- content: [{type: "text", text: "HTTPS is required in production environment"}]
62
- }
63
- end
64
- rescue URI::InvalidURIError => e
65
- log_error("Invalid URI format", e)
66
- return {
67
- isError: true,
68
- content: [{type: "text", text: "Invalid URI format"}]
69
- }
70
- end
71
-
72
- request = Net::HTTP::Post.new(uri)
73
- request.body = JSON.generate({
74
- method: "tools/call",
75
- name:,
76
- arguments: arguments
77
- })
78
- request["Content-Type"] = "application/json"
79
- request["Authorization"] = @auth_header
80
-
81
- begin
82
- response = Net::HTTP.start(uri.hostname, uri.port) do |http|
83
- http.request(request)
84
- end
85
-
86
- if response.code == "200"
87
- JSON.parse(response.body, symbolize_names: true)
88
- else
89
- {
90
- isError: true,
91
- content: [{type: "text", text: "HTTP Error: #{response.code}"}]
92
- }
93
- end
94
- rescue => e
95
- log_error("Error calling tool", e)
96
- {
97
- isError: true,
98
- content: [{type: "text", text: "Error calling tool"}]
99
- }
100
- end
101
- end
102
-
103
- def fetch_tools
104
- return unless @uri
105
-
106
- require "net/http"
107
-
108
- unless @uri.is_a?(URI) || @uri.is_a?(String)
109
- log_error("Invalid URI type", StandardError.new("URI must be a String or URI object"))
110
- return
111
- end
112
-
113
- begin
114
- uri = URI.parse(@uri.to_s)
115
-
116
- unless uri.scheme =~ /\Ahttps?\z/ && !uri.host.nil?
117
- log_error("Invalid URI", StandardError.new("URI must have a valid scheme and host"))
118
- return
119
- end
120
-
121
- if defined?(Rails) && Rails.env.production? && uri.scheme != "https"
122
- log_error("HTTPS is required in production environment", StandardError.new("Non-HTTPS URI in production"))
123
- return
124
- end
125
- rescue URI::InvalidURIError => e
126
- log_error("Invalid URI format", e)
127
- return
128
- end
129
-
130
- request = Net::HTTP::Post.new(uri)
131
- request.body = JSON.generate({
132
- method: "tools/list",
133
- arguments: "{}"
134
- })
135
- request["Content-Type"] = "application/json"
136
- request["Authorization"] = @auth_header
137
-
138
- begin
139
- response = Net::HTTP.start(uri.hostname, uri.port) do |http|
140
- http.request(request)
141
- end
142
-
143
- result = JSON.parse(response.body, symbolize_names: true)
144
- @tools = result[:result]
145
- rescue => e
146
- log_error("Error fetching tools", e)
147
- @tools = []
148
- end
149
- end
150
-
151
- def log_error(message, error)
152
- error_details = "#{message}: #{error.message}\n"
153
- error_details += error.backtrace.join("\n") if error.backtrace
154
-
155
- if defined?(Rails)
156
- Rails.logger.error(error_details)
157
- else
158
- $stderr.puts(error_details)
159
- end
160
- end
161
- end
162
- end
163
- end
@@ -1,82 +0,0 @@
1
- require "json-schema"
2
-
3
- module ActiveMcp
4
- class Tool
5
- class << self
6
- attr_reader :desc, :schema
7
-
8
- def tool_name
9
- name ? name.underscore.sub(/_tool$/, "") : ""
10
- end
11
-
12
- def description(value)
13
- @desc = value
14
- end
15
-
16
- def property(name, type, required: false, description: nil)
17
- @schema ||= default_schema
18
-
19
- @schema["properties"][name.to_s] = {"type" => type.to_s}
20
- @schema["properties"][name.to_s]["description"] = description if description
21
- @schema["required"] << name.to_s if required
22
- end
23
-
24
- def argument(...)
25
- property(...)
26
- end
27
-
28
- def registered_tools
29
- @registered_tools ||= []
30
- end
31
-
32
- attr_writer :registered_tools
33
-
34
- def inherited(subclass)
35
- registered_tools << subclass
36
- end
37
-
38
- def visible?(auth_info)
39
- if respond_to?(:authorized?)
40
- authorized?(auth_info)
41
- else
42
- true
43
- end
44
- end
45
-
46
- def authorized_tools(auth_info = nil)
47
- registered_tools.select do |tool_class|
48
- tool_class.visible?(auth_info)
49
- end.map do |tool_class|
50
- {
51
- name: tool_class.tool_name,
52
- description: tool_class.desc,
53
- inputSchema: tool_class.schema || default_schema
54
- }
55
- end
56
- end
57
-
58
- def default_schema
59
- {
60
- "type" => "object",
61
- "properties" => {},
62
- "required" => []
63
- }
64
- end
65
- end
66
-
67
- def initialize
68
- end
69
-
70
- def call(**args)
71
- raise NotImplementedError, "#{self.class.name}#call must be implemented"
72
- end
73
-
74
- def validate_arguments(args)
75
- return true unless self.class.schema
76
-
77
- JSON::Validator.validate!(self.class.schema, args)
78
- rescue JSON::Schema::ValidationError => e
79
- {error: e.message}
80
- end
81
- end
82
- end