Module: RailVerdict::MCP::Serializers
- Defined in:
- lib/rail_verdict/mcp/serializers.rb
Constant Summary collapse
- MAX_TOOL_RESPONSE_BYTES =
256 * 1024
Class Method Summary collapse
- .bounded_findings(findings, limit, offset) ⇒ Object
- .error_response(message, code: "invalid_arguments") ⇒ Object
- .gate_result_to_structured(outcome) ⇒ Object
- .scrub(value) ⇒ Object
- .text_content(structured) ⇒ Object
- .tool_response(structured, error: false) ⇒ Object
- .truncate_structured(structured) ⇒ Object
Class Method Details
.bounded_findings(findings, limit, offset) ⇒ Object
85 86 87 88 89 90 91 92 93 94 95 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 85 def self.bounded_findings(findings, limit, offset) total = findings.length slice = findings.slice(offset, limit) || [] { "findings" => slice.map(&:to_schema_h), "total" => total, "limit" => limit, "offset" => offset, "truncated" => (offset + limit) < total } end |
.error_response(message, code: "invalid_arguments") ⇒ Object
79 80 81 82 83 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 79 def self.error_response(, code: "invalid_arguments") scrubbed = scrub() payload = { "code" => code, "message" => scrubbed } ::MCP::Tool::Response.new([{ type: "text", text: JSON.generate(payload) }], error: true, structured_content: payload) end |
.gate_result_to_structured(outcome) ⇒ Object
21 22 23 24 25 26 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 21 def self.gate_result_to_structured(outcome) result = outcome.result h = result.to_schema_h h["findings"] = h["findings"] || [] h end |
.scrub(value) ⇒ Object
8 9 10 11 12 13 14 15 16 17 18 19 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 8 def self.scrub(value) case value when String value.encode(Encoding::UTF_8, invalid: :replace, undef: :replace, replace: "\uFFFD").gsub(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/, "?") when Hash value.transform_keys { |k| scrub(k) }.transform_values { |v| scrub(v) } when Array value.map { |v| scrub(v) } else value end end |
.text_content(structured) ⇒ Object
28 29 30 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 28 def self.text_content(structured) scrub(JSON.generate(structured)) end |
.tool_response(structured, error: false) ⇒ Object
34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/rail_verdict/mcp/serializers.rb', line 34 def self.tool_response(structured, error: false) text = text_content(structured) if text.bytesize > MAX_TOOL_RESPONSE_BYTES truncated = truncate_structured(structured) if truncated structured = truncated text = text_content(structured) else payload = { "code" => "response_too_large", "message" => "response exceeds #{MAX_TOOL_RESPONSE_BYTES} bytes; use list_findings/get_finding with pagination" } return ::MCP::Tool::Response.new([{ type: "text", text: JSON.generate(payload) }], error: true, structured_content: payload) end end ::MCP::Tool::Response.new([{ type: "text", text: text }], error: error, structured_content: structured) end |
.truncate_structured(structured) ⇒ Object
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/rail_verdict/mcp/serializers.rb', line 49 def self.truncate_structured(structured) return nil unless structured.is_a?(Hash) if structured.key?("findings") && structured["findings"].is_a?(Array) && structured["findings"].length > 10 total = structured["findings"].length copy = structured.dup copy["findings"] = structured["findings"].first(20) copy["findings_truncated"] = true copy["total_findings"] = total copy["returned_findings"] = 20 copy["truncated_due_to_size"] = true return copy if JSON.generate(copy).bytesize <= MAX_TOOL_RESPONSE_BYTES end if structured.key?("gate_result") && structured["gate_result"].is_a?(Hash) gr = structured["gate_result"] if gr["findings"] && gr["findings"].is_a?(Array) && gr["findings"].length > 10 total = gr["findings"].length copy = structured.dup copy["gate_result"] = gr.dup copy["gate_result"]["findings"] = gr["findings"].first(20) copy["gate_result"]["findings_truncated"] = true copy["gate_result"]["total_findings"] = total copy["gate_result"]["returned_findings"] = 20 copy["gate_result"]["truncated_due_to_size"] = true return copy if JSON.generate(copy).bytesize <= MAX_TOOL_RESPONSE_BYTES end end nil end |