Module: MCPClient::SchemaValidator::UriReferences
- Included in:
- MCPClient::SchemaValidator
- Defined in:
- lib/mcp_client/schema_validator/uri_references.rb
Overview
URI reference resolution (RFC 3986 Section 5), used to decide which
schema resource a $ref names. A JSON Schema document may bundle the
resources it uses (JSON Schema 2020-12 Core Section 9.3.1): a reference
written as an absolute URI, as a URI relative to the base an enclosing
$id established, or as the empty reference then names a resource the
document already carries, and resolving it needs no retrieval. Nothing
here fetches or normalizes anything beyond what RFC 3986 defines, and
nothing here raises: a reference a peer wrote that cannot be parsed
simply names no local resource. Extended into SchemaValidator, so the
methods are its own.
Constant Summary collapse
- URI_REFERENCE =
RFC 3986 Appendix B: the components of a URI reference.
%r{ \A (?:(?<scheme>[A-Za-z][A-Za-z0-9+\-.]*):)? (?://(?<authority>[^/?\#]*))? (?<path>[^?\#]*) (?:\?(?<query>[^\#]*))? (?:\#(?<fragment>.*))? \z }mx
Instance Method Summary collapse
-
#compose_uri(scheme, authority, path, query) ⇒ String
RFC 3986 Section 5.3.
-
#dot_segment_step(input, output) ⇒ String, false
The RFC 3986 Section 5.2.4 C and D prefixes, which also pop an already-emitted segment for "..".
-
#merge_paths(origin, path) ⇒ String
RFC 3986 Section 5.2.3: a relative path is merged onto the base's.
-
#merge_relative_uri(origin, target) ⇒ String
The RFC 3986 Section 5.2.2 branches for a reference without a scheme.
-
#merge_uri(base, ref) ⇒ String?
Resolve a URI reference against a base URI (RFC 3986 Section 5.2.2), dropping the fragment: what comes back identifies a resource, which is what an
$iddeclares and what a$refselects before its fragment is applied. -
#parse_uri_reference(uri) ⇒ Hash?
:scheme, :authority, :query (each String or nil) and :path (always a String); nil when the text is not a URI reference (or is not readable text at all).
-
#remove_dot_segments(path) ⇒ String
RFC 3986 Section 5.2.4.
-
#strip_leading_dots(input) ⇒ String
The RFC 3986 Section 5.2.4 A and B prefixes, which are simply dropped.
Instance Method Details
#compose_uri(scheme, authority, path, query) ⇒ String
RFC 3986 Section 5.3.
82 83 84 85 86 87 88 89 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 82 def compose_uri(scheme, , path, query) composed = +'' composed << "#{scheme}:" if scheme composed << "//#{}" if composed << path composed << "?#{query}" if query composed end |
#dot_segment_step(input, output) ⇒ String, false
The RFC 3986 Section 5.2.4 C and D prefixes, which also pop an already-emitted segment for "..".
140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 140 def dot_segment_step(input, output) return "/#{input[3..]}" if input.start_with?('/./') return '/' if input == '/.' if input.start_with?('/../') || input == '/..' output.pop return input == '/..' ? '/' : "/#{input[4..]}" end false end |
#merge_paths(origin, path) ⇒ String
RFC 3986 Section 5.2.3: a relative path is merged onto the base's.
95 96 97 98 99 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 95 def merge_paths(origin, path) return "/#{path}" if origin[:authority] && origin[:path].empty? "#{origin[:path].sub(%r{[^/]*\z}, '')}#{path}" end |
#merge_relative_uri(origin, target) ⇒ String
The RFC 3986 Section 5.2.2 branches for a reference without a scheme.
49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 49 def merge_relative_uri(origin, target) if target[:authority] compose_uri(origin[:scheme], target[:authority], remove_dot_segments(target[:path]), target[:query]) elsif target[:path].empty? compose_uri(origin[:scheme], origin[:authority], origin[:path], target[:query] || origin[:query]) elsif target[:path].start_with?('/') compose_uri(origin[:scheme], origin[:authority], remove_dot_segments(target[:path]), target[:query]) else path = remove_dot_segments(merge_paths(origin, target[:path])) compose_uri(origin[:scheme], origin[:authority], path, target[:query]) end end |
#merge_uri(base, ref) ⇒ String?
Resolve a URI reference against a base URI (RFC 3986 Section 5.2.2),
dropping the fragment: what comes back identifies a resource, which
is what an $id declares and what a $ref selects before its
fragment is applied.
36 37 38 39 40 41 42 43 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 36 def merge_uri(base, ref) target = parse_uri_reference(ref) origin = parse_uri_reference(base) return nil unless target && origin return merge_relative_uri(origin, target) unless target[:scheme] compose_uri(target[:scheme], target[:authority], remove_dot_segments(target[:path]), target[:query]) end |
#parse_uri_reference(uri) ⇒ Hash?
Returns :scheme, :authority, :query (each String or nil) and :path (always a String); nil when the text is not a URI reference (or is not readable text at all).
66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 66 def parse_uri_reference(uri) return nil unless uri.is_a?(String) match = URI_REFERENCE.match(uri) return nil unless match # A scheme is case-insensitive (RFC 3986 Section 3.1), so two # spellings of one resource identify the same one. { scheme: match[:scheme]&.downcase, authority: match[:authority], path: match[:path].to_s, query: match[:query] } rescue ArgumentError nil end |
#remove_dot_segments(path) ⇒ String
RFC 3986 Section 5.2.4.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 104 def remove_dot_segments(path) input = path output = [] until input.empty? stripped = strip_leading_dots(input) unless stripped.equal?(input) input = stripped next end stepped = dot_segment_step(input, output) if stepped input = stepped next end segment = input[%r{\A/?[^/]*}] output << segment input = input[segment.length..] end output.join end |
#strip_leading_dots(input) ⇒ String
The RFC 3986 Section 5.2.4 A and B prefixes, which are simply dropped.
129 130 131 132 133 134 |
# File 'lib/mcp_client/schema_validator/uri_references.rb', line 129 def strip_leading_dots(input) return input.sub(%r{\A\.\.?/}, '') if input.start_with?('../', './') return '' if ['.', '..'].include?(input) input end |