Module: MCPClient::SchemaValidator::InputRequirements
- Included in:
- MCPClient::SchemaValidator
- Defined in:
- lib/mcp_client/schema_validator/input_requirements.rb
Overview
What an input schema requires of every instance, read through the
applicators that apply unconditionally: the root, what its $ref
chain reaches, and each allOf member (each of those recursively).
The tools specification says clients SHOULD follow $ref resolution
when validating tool inputs, and SEP-2106 made $ref and allOf legal
on an inputSchema: a required behind them is as required as one at
the root. A branch the instance may or may not satisfy (anyOf,
oneOf, if) decides nothing here and is left to the server, which
MUST validate the arguments anyway. Extended into SchemaValidator, so
the methods are its own.
Instance Method Summary collapse
-
#input_requirements(schema) ⇒ Array(Array<String>, Hash{String => Object})
The required property names, and the declared properties by name (a property declared nearer the root wins).
-
#position_dialect(node, root, scan) ⇒ String?
The dialect in force at a position: the one the anchor index recorded for its resource, else the root's.
-
#queue_referenced(node, ref, root, scan) ⇒ void
Queue what a local reference reaches (an external one is never dereferenced, and an unresolvable one is the preflight's to report).
-
#read_requirement_positions(root, scan) ⇒ void
Read every queued position, bounded like the preflight walk.
-
#read_requirements(node, root, scan) ⇒ void
One position's requirements, and what it applies next.
Instance Method Details
#input_requirements(schema) ⇒ Array(Array<String>, Hash{String => Object})
Returns the required property names, and the declared properties by name (a property declared nearer the root wins).
20 21 22 23 24 25 26 27 28 29 30 31 32 |
# File 'lib/mcp_client/schema_validator/input_requirements.rb', line 20 def input_requirements(schema) root = normalize_schema(schema) return [[], {}] unless root.is_a?(Hash) declared = dialect(root) scan = { count: 0, dialect: declared && canonical_dialect(declared), anchors: nil, walked: {}.compare_by_identity, required: [], properties: {}, pending: [root] } read_requirement_positions(root, scan) [scan[:required].uniq, scan[:properties]] rescue TooLarge # Unusable anyway: the preflight reports it. [[], {}] end |
#position_dialect(node, root, scan) ⇒ String?
The dialect in force at a position: the one the anchor index recorded for its resource, else the root's.
68 69 70 71 |
# File 'lib/mcp_client/schema_validator/input_requirements.rb', line 68 def position_dialect(node, root, scan) scan[:anchors] ||= anchor_index(root, scan[:dialect]) indexed_dialect(node, scan) || scan[:dialect] end |
#queue_referenced(node, ref, root, scan) ⇒ void
This method returns an undefined value.
Queue what a local reference reaches (an external one is never dereferenced, and an unresolvable one is the preflight's to report).
76 77 78 79 80 81 |
# File 'lib/mcp_client/schema_validator/input_requirements.rb', line 76 def queue_referenced(node, ref, root, scan) return if external_ref?(ref, root, scan[:dialect], scan, from: node) target = resolve_reference(root, ref, scan[:dialect], scan, from: node) scan[:pending] << target unless target.equal?(UNRESOLVED) end |
#read_requirement_positions(root, scan) ⇒ void
This method returns an undefined value.
Read every queued position, bounded like the preflight walk.
36 37 38 39 40 41 42 43 44 45 46 47 |
# File 'lib/mcp_client/schema_validator/input_requirements.rb', line 36 def read_requirement_positions(root, scan) until scan[:pending].empty? node = scan[:pending].pop next unless node.is_a?(Hash) && !scan[:walked].key?(node) scan[:walked][node] = true scan[:count] += 1 return if scan[:count] > MAX_SUBSCHEMAS read_requirements(node, root, scan) end end |
#read_requirements(node, root, scan) ⇒ void
This method returns an undefined value.
One position's requirements, and what it applies next. Under draft-07
nothing beside a $ref is applied (draft-07 Core Section 8.3) — the
draft-07 of the resource the position belongs to, which an embedded
resource declares for itself (2020-12 Core Section 9.3.2), not the
document root's.
55 56 57 58 59 60 61 62 63 |
# File 'lib/mcp_client/schema_validator/input_requirements.rb', line 55 def read_requirements(node, root, scan) ref = node['$ref'] queue_referenced(node, ref, root, scan) if ref.is_a?(String) return if position_dialect(node, root, scan) == DRAFT_07 && node.key?('$ref') scan[:required].concat(node['required'].map(&:to_s)) if node['required'].is_a?(Array) scan[:properties] = node['properties'].merge(scan[:properties]) if node['properties'].is_a?(Hash) scan[:pending].concat(node['allOf'].reverse) if node['allOf'].is_a?(Array) end |