Module: RailsMcpServer::Extensions::ServerTemplating::InstanceMethods
- Defined in:
- lib/rails-mcp-server/extensions/server_templating.rb
Overview
Instance methods to be prepended
Instance Method Summary collapse
-
#handle_request(*args) ⇒ Object
Override handle_request to ensure resources/templates/list endpoint is available.
-
#handle_resources_list(id) ⇒ Object
The target server already has these methods, but we can add defensive checks.
-
#handle_resources_read(params, id) ⇒ Object
Add some defensive programming to handle_resources_read.
- #handle_resources_templates_list(id) ⇒ Object
-
#read_resource(uri) ⇒ Object
The target server already has most functionality, but we can add defensive checks.
Instance Method Details
#handle_request(*args) ⇒ Object
Override handle_request to ensure resources/templates/list endpoint is available
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 |
# File 'lib/rails-mcp-server/extensions/server_templating.rb', line 110 def handle_request(*args) # Extract arguments - handle different signatures if args.length == 2 json_str, headers = args headers ||= {} else json_str = args[0] headers = {} end begin request = JSON.parse(json_str) rescue JSON::ParserError, TypeError return send_error(-32_600, "Invalid Request", nil) end @logger.debug("Received request: #{request.inspect}") # Check if it's a valid JSON-RPC 2.0 request unless request["jsonrpc"] == "2.0" && request["method"] return send_error(-32_600, "Invalid Request", request["id"]) end method = request["method"] params = request["params"] || {} id = request["id"] # Handle the resources/templates/list endpoint specifically since it might not exist in original if method == "resources/templates/list" @logger.debug("Handling resources/templates/list via extension") return handle_resources_templates_list(id) end # For all other methods, call the original implementation begin super rescue NoMethodError => e # If super doesn't work, provide our own fallback @logger.debug("Original handle_request not available, using fallback: #{e.}") handle_request_fallback(method, params, id, headers) end rescue => e @logger.error("Error handling request: #{e.}, #{e.backtrace.join("\n")}") send_error(-32_600, "Internal error: #{e.}", id) end |
#handle_resources_list(id) ⇒ Object
The target server already has these methods, but we can add defensive checks
80 81 82 83 84 85 86 87 88 89 |
# File 'lib/rails-mcp-server/extensions/server_templating.rb', line 80 def handle_resources_list(id) # Handle both hash-based and array-based resource storage resources_collection = @resources.is_a?(Hash) ? @resources.values : @resources resources_list = resources_collection.select do |resource| !resource.respond_to?(:templated?) || resource.non_templated? end.map(&:metadata) # rubocop:disable Performance/ChainArrayAllocation send_result({resources: resources_list}, id) end |
#handle_resources_read(params, id) ⇒ Object
Add some defensive programming to handle_resources_read
30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'lib/rails-mcp-server/extensions/server_templating.rb', line 30 def handle_resources_read(params, id) uri = params["uri"] return send_error(-32_602, "Invalid params: missing resource URI", id) unless uri @logger.debug("Looking for resource with URI: #{uri}") begin resource = read_resource(uri) return send_error(-32_602, "Resource not found: #{uri}", id) unless resource # Defensive check for templated method is_templated = resource.respond_to?(:templated?) ? resource.templated? : false @logger.debug("Found resource: #{resource.respond_to?(:resource_name) ? resource.resource_name : resource.name}, templated: #{is_templated}") base_content = {uri: uri} base_content[:mimeType] = resource.mime_type if resource.mime_type # Handle both templated and non-templated resources resource_instance = if is_templated && resource.respond_to?(:instance) resource.instance(uri) else # Fallback for non-templated resources or resources without instance method resource.respond_to?(:instance) ? resource.instance : resource end # Defensive check for params method if resource_instance.respond_to?(:params) @logger.debug("Resource instance params: #{resource_instance.params.inspect}") end result = if resource_instance.respond_to?(:binary?) && resource_instance.binary? { contents: [base_content.merge(blob: Base64.strict_encode64(resource_instance.content))] } else { contents: [base_content.merge(text: resource_instance.content)] } end send_result(result, id) rescue => e @logger.error("Error reading resource: #{e.}") @logger.error(e.backtrace.join("\n")) send_error(-32_600, "Internal error reading resource: #{e.}", id) end end |
#handle_resources_templates_list(id) ⇒ Object
91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/rails-mcp-server/extensions/server_templating.rb', line 91 def handle_resources_templates_list(id) @logger.debug("Handling resources/templates/list request") # Handle both hash-based and array-based resource storage resources_collection = @resources.is_a?(Hash) ? @resources.values : @resources templated_resources_list = resources_collection.select do |resource| resource.respond_to?(:templated?) && resource.templated? end.map do |resource| # rubocop:disable Performance/ChainArrayAllocation = resource. @logger.debug("Template resource metadata: #{}") end @logger.info("Returning #{templated_resources_list.length} templated resources") send_result({resourceTemplates: templated_resources_list}, id) end |
#read_resource(uri) ⇒ Object
The target server already has most functionality, but we can add defensive checks
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'lib/rails-mcp-server/extensions/server_templating.rb', line 9 def read_resource(uri) # Handle both hash-based and array-based resource storage if @resources.is_a?(Hash) # First try exact match (hash lookup) exact_match = @resources[uri] return exact_match if exact_match # Then try templated resource matching @resources.values.find { |r| r.respond_to?(:match) && r.match(uri) } else # Array-based storage (original target server behavior) resource = @resources.find { |r| r.respond_to?(:match) && r.match(uri) } # Fallback: if no templated match, try exact URI match for backward compatibility resource ||= @resources.find { |r| r.respond_to?(:uri) && r.uri == uri } resource end end |