Class: Tina4::McpServer
- Inherits:
-
Object
- Object
- Tina4::McpServer
- Defined in:
- lib/tina4/mcp.rb
Overview
── McpServer ─────────────────────────────────────────────────────
Class Attribute Summary collapse
-
.instances ⇒ Object
readonly
Returns the value of attribute instances.
Instance Attribute Summary collapse
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#version ⇒ Object
readonly
Returns the value of attribute version.
Instance Method Summary collapse
-
#handle_message(raw_data) ⇒ Object
Process an incoming JSON-RPC message and return the response string.
-
#initialize(path, name: "Tina4 MCP", version: "1.0.0") ⇒ McpServer
constructor
A new instance of McpServer.
-
#register_resource(uri, handler, description = "", mime_type = "application/json") ⇒ Object
Register a resource URI.
-
#register_routes(router = nil) ⇒ Object
Register HTTP routes for this MCP server on the Tina4 router.
-
#register_tool(name, handler, description = "", schema = nil) ⇒ Object
Register a tool callable.
-
#resources ⇒ Object
Access registered resources (for testing).
-
#tools ⇒ Object
Access registered tools (for testing).
-
#write_claude_config(port = 7145) ⇒ Object
Write/update .claude/settings.json with this MCP server config.
Constructor Details
#initialize(path, name: "Tina4 MCP", version: "1.0.0") ⇒ McpServer
Returns a new instance of McpServer.
174 175 176 177 178 179 180 181 182 |
# File 'lib/tina4/mcp.rb', line 174 def initialize(path, name: "Tina4 MCP", version: "1.0.0") @path = path.chomp("/") @name = name @version = version @tools = {} @resources = {} @initialized = false self.class.instances << self end |
Class Attribute Details
.instances ⇒ Object (readonly)
Returns the value of attribute instances.
171 172 173 |
# File 'lib/tina4/mcp.rb', line 171 def instances @instances end |
Instance Attribute Details
#name ⇒ Object (readonly)
Returns the value of attribute name.
166 167 168 |
# File 'lib/tina4/mcp.rb', line 166 def name @name end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
166 167 168 |
# File 'lib/tina4/mcp.rb', line 166 def path @path end |
#version ⇒ Object (readonly)
Returns the value of attribute version.
166 167 168 |
# File 'lib/tina4/mcp.rb', line 166 def version @version end |
Instance Method Details
#handle_message(raw_data) ⇒ Object
Process an incoming JSON-RPC message and return the response string.
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/tina4/mcp.rb', line 212 def (raw_data) begin method, params, request_id = McpProtocol.decode_request(raw_data) rescue ArgumentError => e return McpProtocol.encode_error(nil, McpProtocol::PARSE_ERROR, e.) end handler_method = { "initialize" => :_handle_initialize, "notifications/initialized" => :_handle_initialized, "tools/list" => :_handle_tools_list, "tools/call" => :_handle_tools_call, "resources/list" => :_handle_resources_list, "resources/read" => :_handle_resources_read, "ping" => :_handle_ping }[method] if handler_method.nil? return McpProtocol.encode_error(request_id, McpProtocol::METHOD_NOT_FOUND, "Method not found: #{method}") end begin result = send(handler_method, params) return "" if request_id.nil? # Notification -- no response McpProtocol.encode_response(request_id, result) rescue => e McpProtocol.encode_error(request_id, McpProtocol::INTERNAL_ERROR, e.) end end |
#register_resource(uri, handler, description = "", mime_type = "application/json") ⇒ Object
Register a resource URI.
201 202 203 204 205 206 207 208 209 |
# File 'lib/tina4/mcp.rb', line 201 def register_resource(uri, handler, description = "", mime_type = "application/json") @resources[uri] = { "uri" => uri, "name" => description.empty? ? uri : description, "description" => description.empty? ? uri : description, "mimeType" => mime_type, "handler" => handler } end |
#register_routes(router = nil) ⇒ Object
Register HTTP routes for this MCP server on the Tina4 router.
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 |
# File 'lib/tina4/mcp.rb', line 243 def register_routes(router = nil) server = self msg_path = "#{@path}/message" sse_path = "#{@path}/sse" Tina4::Router.post(msg_path) do |request, response| body = request.body raw = body.is_a?(Hash) ? body : (body.is_a?(String) ? body : body.to_s) result = server.(raw) if result.nil? || result.empty? response.call("", 204) else response.call(JSON.parse(result)) end end Tina4::Router.get(sse_path) do |request, response| endpoint_url = "#{request.url.sub(%r{/sse\z}, "")}/message" sse_data = "event: endpoint\ndata: #{endpoint_url}\n\n" response.call(sse_data, 200, "text/event-stream") end end |
#register_tool(name, handler, description = "", schema = nil) ⇒ Object
Register a tool callable.
190 191 192 193 194 195 196 197 198 |
# File 'lib/tina4/mcp.rb', line 190 def register_tool(name, handler, description = "", schema = nil) schema ||= Tina4.schema_from_method(handler) @tools[name] = { "name" => name, "description" => description.empty? ? name : description, "inputSchema" => schema, "handler" => handler } end |
#resources ⇒ Object
Access registered resources (for testing)
296 297 298 |
# File 'lib/tina4/mcp.rb', line 296 def resources @resources end |
#tools ⇒ Object
Access registered tools (for testing)
291 292 293 |
# File 'lib/tina4/mcp.rb', line 291 def tools @tools end |
#write_claude_config(port = 7145) ⇒ Object
Write/update .claude/settings.json with this MCP server config.
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/tina4/mcp.rb', line 267 def write_claude_config(port = 7145) config_dir = File.join(Dir.pwd, ".claude") FileUtils.mkdir_p(config_dir) config_file = File.join(config_dir, "settings.json") config = {} if File.exist?(config_file) begin config = JSON.parse(File.read(config_file)) rescue JSON::ParserError, IOError # ignore corrupt file end end config["mcpServers"] ||= {} server_key = @name.downcase.gsub(" ", "-") config["mcpServers"][server_key] = { "url" => "http://localhost:#{port}#{@path}/sse" } File.write(config_file, JSON.pretty_generate(config) + "\n") end |