Module: VectorMCP::Util
- Defined in:
- lib/vector_mcp/util.rb
Overview
Provides utility functions for VectorMCP operations, such as data conversion and parsing.
Class Method Summary collapse
-
.all_content_items?(arr) ⇒ Boolean
Checks if all array items are pre-formatted MCP content items.
-
.array_content(arr, mime_type) ⇒ Array<Hash>
Converts an Array into MCP content items.
-
.binary_image_data?(str) ⇒ Boolean
Checks if a string contains binary image data.
-
.binary_image_to_content(binary_data) ⇒ Array<Hash>
Converts binary image data to MCP image content.
-
.convert_to_mcp_content(input, mime_type: "text/plain") ⇒ Array<Hash>
Converts a given Ruby object into an array of MCP content items.
-
.extract_id_from_invalid_json(json_string) ⇒ String?
Extracts an ID from a potentially malformed JSON string using regex.
-
.fallback_content(obj, mime_type) ⇒ Array<Hash>
Fallback conversion for any other object type to an MCP text content item.
-
.file_path_to_image_content(file_path) ⇒ Array<Hash>
Converts a file path string to image content.
-
.hash_content(hash) ⇒ Array<Hash>
Converts a Hash into an MCP content item.
-
.looks_like_image_file_path?(str) ⇒ Boolean
Checks if a string looks like a file path to an image.
-
.process_content_item(item) ⇒ Hash
Processes a single content item, normalizing and validating as needed.
-
.string_content(str, mime_type) ⇒ Array<Hash>
Converts a String into an MCP content item.
-
.validate_and_enhance_image_content(content) ⇒ Hash
Validates and enhances existing image content hash.
Class Method Details
.all_content_items?(arr) ⇒ Boolean
Checks if all array items are pre-formatted MCP content items.
105 106 107 |
# File 'lib/vector_mcp/util.rb', line 105 def all_content_items?(arr) arr.all? { |item| item.is_a?(Hash) && (item[:type] || item["type"]) } end |
.array_content(arr, mime_type) ⇒ Array<Hash>
Converts an Array into MCP content items. If all array elements are pre-formatted MCP content items, they are used directly. Otherwise, each item in the array is recursively converted using #convert_to_mcp_content.
94 95 96 97 98 99 100 |
# File 'lib/vector_mcp/util.rb', line 94 def array_content(arr, mime_type) if all_content_items?(arr) arr.map { |item| process_content_item(item) } else arr.flat_map { |item| convert_to_mcp_content(item, mime_type: mime_type) } end end |
.binary_image_data?(str) ⇒ Boolean
Checks if a string contains binary image data.
170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 |
# File 'lib/vector_mcp/util.rb', line 170 def binary_image_data?(str) return false if str.nil? || str.empty? # Check encoding first encoding = str.encoding is_binary = encoding == Encoding::ASCII_8BIT || !str.valid_encoding? return false unless is_binary # Use ImageUtil to detect if it's actually image data require_relative "image_util" !VectorMCP::ImageUtil.detect_image_format(str).nil? rescue StandardError false end |
.binary_image_to_content(binary_data) ⇒ Array<Hash>
Converts binary image data to MCP image content.
204 205 206 207 208 209 210 211 212 213 214 |
# File 'lib/vector_mcp/util.rb', line 204 def binary_image_to_content(binary_data) require_relative "image_util" begin image_content = VectorMCP::ImageUtil.to_mcp_image_content(binary_data) [image_content] rescue ArgumentError # If image processing fails, fall back to text content [{ type: "text", text: binary_data.to_s, mimeType: "application/octet-stream" }] end end |
.convert_to_mcp_content(input, mime_type: "text/plain") ⇒ Array<Hash>
Converts a given Ruby object into an array of MCP content items. This is the primary public helper for transforming arbitrary Ruby values into the wire-format expected by the MCP spec.
Keys present in each returned hash:
- :type –
"text"
or"image"
; automatic detection for binary data. - :text – UTF-8 encoded payload (for text content).
- :data – Base64 encoded payload (for image content).
- :mimeType – IANA media-type describing the content.
- :uri – Optional. Added downstream (e.g., by Handlers::Core.read_resource).
The method never returns nil
and always returns at least one element.
43 44 45 46 47 48 49 |
# File 'lib/vector_mcp/util.rb', line 43 def convert_to_mcp_content(input, mime_type: "text/plain") return string_content(input, mime_type) if input.is_a?(String) return hash_content(input) if input.is_a?(Hash) return array_content(input, mime_type) if input.is_a?(Array) fallback_content(input, mime_type) end |
.extract_id_from_invalid_json(json_string) ⇒ String?
Extracts an ID from a potentially malformed JSON string using regex.
This is a best-effort attempt, primarily for error reporting when full JSON parsing fails.
It looks for patterns like "id": 123
or "id": "abc"
.
137 138 139 140 141 142 143 144 145 146 147 |
# File 'lib/vector_mcp/util.rb', line 137 def extract_id_from_invalid_json(json_string) # Try to find id field with numeric value numeric_match = json_string.match(/"id"\s*:\s*(\d+)/) return numeric_match[1] if numeric_match # Try to find id field with string value, preserving escaped characters string_match = json_string.match(/"id"\s*:\s*"((?:\\.|[^"])*)"/) return string_match[1] if string_match nil end |
.fallback_content(obj, mime_type) ⇒ Array<Hash>
Fallback conversion for any other object type to an MCP text content item. Converts the object to its string representation.
124 125 126 |
# File 'lib/vector_mcp/util.rb', line 124 def fallback_content(obj, mime_type) [{ type: "text", text: obj.to_s, mimeType: mime_type }] end |
.file_path_to_image_content(file_path) ⇒ Array<Hash>
Converts a file path string to image content.
189 190 191 192 193 194 195 196 197 198 199 |
# File 'lib/vector_mcp/util.rb', line 189 def file_path_to_image_content(file_path) require_relative "image_util" begin image_content = VectorMCP::ImageUtil.file_to_mcp_image_content(file_path) [image_content] rescue ArgumentError => e # If image processing fails, fall back to text content with error message [{ type: "text", text: "Error loading image '#{file_path}': #{e.}", mimeType: "text/plain" }] end end |
.hash_content(hash) ⇒ Array<Hash>
Converts a Hash into an MCP content item.
If the hash appears to be a pre-formatted MCP content item, it's used directly.
Otherwise, it's converted to a JSON string with application/json
MIME type.
75 76 77 78 79 80 81 82 83 84 85 86 |
# File 'lib/vector_mcp/util.rb', line 75 def hash_content(hash) if hash[:type] || hash["type"] # Already in content format normalized = hash.transform_keys(&:to_sym) # Validate and enhance image content if needed return [validate_and_enhance_image_content(normalized)] if normalized[:type] == "image" [normalized] else [{ type: "text", text: hash.to_json, mimeType: "application/json" }] end end |
.looks_like_image_file_path?(str) ⇒ Boolean
Checks if a string looks like a file path to an image.
154 155 156 157 158 159 160 161 162 163 164 165 |
# File 'lib/vector_mcp/util.rb', line 154 def looks_like_image_file_path?(str) return false if str.nil? || str.empty? || str.length > 500 # Check for common image extensions image_extensions = %w[.jpg .jpeg .png .gif .webp .bmp .tiff .tif .svg] has_image_extension = image_extensions.any? { |ext| str.downcase.end_with?(ext) } # Check if it looks like a file path (contains / or \ or ends with image extension) looks_like_path = str.include?("/") || str.include?("\\") || has_image_extension has_image_extension && looks_like_path end |
.process_content_item(item) ⇒ Hash
Processes a single content item, normalizing and validating as needed.
112 113 114 115 116 117 |
# File 'lib/vector_mcp/util.rb', line 112 def process_content_item(item) normalized = item.transform_keys(&:to_sym) return validate_and_enhance_image_content(normalized) if normalized[:type] == "image" normalized end |
.string_content(str, mime_type) ⇒ Array<Hash>
Converts a String into an MCP content item. Intelligently detects if the string is binary image data, a file path to an image, or regular text content.
59 60 61 62 63 64 65 66 67 68 |
# File 'lib/vector_mcp/util.rb', line 59 def string_content(str, mime_type) # Check if this might be a file path to an image return file_path_to_image_content(str) if looks_like_image_file_path?(str) # Check if this is binary image data return binary_image_to_content(str) if binary_image_data?(str) # Default to text content [{ type: "text", text: str, mimeType: mime_type }] end |
.validate_and_enhance_image_content(content) ⇒ Hash
Validates and enhances existing image content hash.
219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/vector_mcp/util.rb', line 219 def validate_and_enhance_image_content(content) # Ensure required fields are present raise ArgumentError, "Image content must have both :data and :mimeType fields" unless content[:data] && content[:mimeType] # Validate the base64 data if possible begin require_relative "image_util" VectorMCP::ImageUtil.decode_base64(content[:data]) rescue ArgumentError => e raise ArgumentError, "Invalid base64 image data: #{e.}" end content end |