def handle(request)
if request.is_a?(Array)
return JSONRPC.error_response(nil, JSONRPC::INVALID_REQUEST, "Invalid Request", data: "Request is an empty array") if request.empty?
responses = request.filter_map { |entry| handle(entry) }
return responses if responses.any?
return
end
unless request.is_a?(Hash)
return JSONRPC.error_response(nil, JSONRPC::INVALID_REQUEST, "Invalid Request", data: "Request must be a hash")
end
has_id = request.key?(:id)
id = request[:id]
method = request[:method]
params = request[:params]
unless request[:jsonrpc] == "2.0"
return JSONRPC.error_response(nil, JSONRPC::INVALID_REQUEST, "Invalid Request", data: "JSON-RPC version must be 2.0")
end
unless !has_id || id.is_a?(Integer) || (id.is_a?(String) && id.match?(/\A[a-zA-Z0-9_-]+\z/))
return JSONRPC.error_response(nil, JSONRPC::INVALID_REQUEST, "Invalid Request", data: "Request ID must be a string or integer")
end
unless method.is_a?(String) && !method.start_with?("rpc.")
return JSONRPC.error_response(nil, JSONRPC::INVALID_REQUEST, "Invalid Request", data: 'Method name must be a string and not start with "rpc."')
end
unless params.nil? || params.is_a?(Hash)
return JSONRPC.error_response(id, JSONRPC::INVALID_PARAMS, "Invalid params", data: "Method parameters must be an object or null")
end
result = case method
when "initialize"
{
protocolVersion: "2025-03-26",
capabilities: { tools: {} },
serverInfo: {
name: "rubydex_mcp",
version: Rubydex::VERSION,
},
instructions: SERVER_INSTRUCTIONS,
}
when "tools/list"
{ tools: Tool.tools.map(&:to_h) }
when "tools/call"
call_tool(params || {})
when "ping"
{}
when "notifications/initialized"
return
else
return has_id ? JSONRPC.error_response(id, JSONRPC::METHOD_NOT_FOUND, "Method not found", data: method) : nil
end
has_id ? { jsonrpc: "2.0", id: id, result: result } : nil
rescue KeyError => e
has_id ? JSONRPC.error_response(id, JSONRPC::INVALID_PARAMS, "Invalid params", data: e.message) : nil
rescue StandardError => e
has_id ? JSONRPC.error_response(id, JSONRPC::INTERNAL_ERROR, "Internal error", data: e.message) : nil
end