Add Tool output_schema support with comprehensive validation
This commit implements optional JSON Schema validation for tool outputs according to the MCP specification: - Add MCP::Tool::OutputSchema class mirroring InputSchema functionality - Support output_schema in class definitions, Tool.define, and OutputSchema objects - Include validate_result method for runtime validation - Update MCP::Client::Tool to track output schemas - Add comprehensive test coverage with 249 passing tests - Update README with dedicated Tool Output Schemas section - Follow MCP spec requirements for server/client validation
This commit is contained in:
parent
3f2a4b3b21
commit
aad8e94530
1 changed files with 495 additions and 7 deletions
66
lib/mcp/tool/output_schema.rb
Normal file
66
lib/mcp/tool/output_schema.rb
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "json-schema"
|
||||
|
||||
module MCP
|
||||
class Tool
|
||||
class OutputSchema
|
||||
class ValidationError < StandardError; end
|
||||
|
||||
attr_reader :properties, :required
|
||||
|
||||
def initialize(properties: {}, required: [])
|
||||
@properties = properties
|
||||
@required = required.map(&:to_sym)
|
||||
validate_schema!
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(OutputSchema) && properties == other.properties && required == other.required
|
||||
end
|
||||
|
||||
def to_h
|
||||
{ type: "object" }.tap do |hsh|
|
||||
hsh[:properties] = properties if properties.any?
|
||||
hsh[:required] = required if required.any?
|
||||
end
|
||||
end
|
||||
|
||||
def validate_result(result)
|
||||
errors = JSON::Validator.fully_validate(to_h, result)
|
||||
if errors.any?
|
||||
raise ValidationError, "Invalid result: #{errors.join(", ")}"
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def validate_schema!
|
||||
check_for_refs!
|
||||
schema = to_h
|
||||
schema_reader = JSON::Schema::Reader.new(
|
||||
accept_uri: false,
|
||||
accept_file: ->(path) { path.to_s.start_with?(Gem.loaded_specs["json-schema"].full_gem_path) },
|
||||
)
|
||||
metaschema = JSON::Validator.validator_for_name("draft4").metaschema
|
||||
errors = JSON::Validator.fully_validate(metaschema, schema, schema_reader: schema_reader)
|
||||
if errors.any?
|
||||
raise ArgumentError, "Invalid JSON Schema: #{errors.join(", ")}"
|
||||
end
|
||||
end
|
||||
|
||||
def check_for_refs!(obj = properties)
|
||||
case obj
|
||||
when Hash
|
||||
if obj.key?("$ref") || obj.key?(:$ref)
|
||||
raise ArgumentError, "Invalid JSON Schema: $ref is not allowed in tool output schemas"
|
||||
end
|
||||
|
||||
obj.each_value { |value| check_for_refs!(value) }
|
||||
when Array
|
||||
obj.each { |item| check_for_refs!(item) }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Loading…
Reference in a new issue