Commit graph ruby-sdk/lib/mcp/tool/schema.rb
Author SHA1 Message Date
Koichi ITO
1fc698bb8e Advertise JSON Schema 2020-12 dialect on emitted tool schemas
## Motivation and Context

MCP 2025-11-25 (SEP-1613) establishes JSON Schema 2020-12 as the default dialect
for MCP schema definitions.
 https://github.com/modelcontextprotocol/modelcontextprotocol/issues/1613

The Ruby SDK was emitting tool input and output schemas with no `$schema` field,
leaving the dialect implicit and inconsistent with the TypeScript SDK
(`packages/core/src/util/standardSchema.ts`, which emits with `target: 'draft-2020-12'`)
and the Python SDK (which preserves `$schema` in its types and conformance fixtures).

`Tool::Schema#to_h` now includes `"$schema": "https://json-schema.org/draft/2020-12/schema"`
unless the user supplied a different `$schema` value, so clients see the canonical dialect URI
on the wire.

Runtime metaschema and argument validation remain on draft-04 because the `json-schema` gem
does not yet support 2020-12. Switching the runtime validator is left as a separate change
because it requires either replacing the gem or waiting for upstream 2020-12 support.

## How Has This Been Tested?

Updated `Tool::Schema#to_h` expectations across `input_schema_test`, `output_schema_test`,
`tool_test`, and the `tools/list` server tests to reflect the new `$schema` field.
Added focused regression tests:

- `to_h` preserves a user-supplied `$schema` URI rather than overwriting it
  (covered for both symbol-keyed input and the string-keyed Hash form that
  goes through the initializer's `symbolize_names` normalization)
- `validate_arguments` / `validate_result` continue to work when the user supplies
  a 2020-12 `$schema` URI on the schema (the validator strips the unknown top-level
  dialect URI before invoking the draft-04 validator, instead of raising `JSON::Schema::SchemaError`)
- `tools/list` JSON-RPC response includes the 2020-12 `$schema` on both `inputSchema`
  and `outputSchema` (transport-boundary assertion)

`bundle exec rake test` and `bundle exec rake rubocop` both pass.

## Breaking Changes

Additive change. Tolerant consumers of the `tools/list` payload are unaffected.
Consumers that strictly assert hash equality on the emitted `inputSchema` / `outputSchema`
need to account for the new `$schema` key, but no schema semantics change.

Schemas using 2020-12-only keywords (e.g. `prefixItems`, nested `$schema` on subschemas)
are still rejected by the draft-04 metaschema validator, as before.
Lifting that restriction depends on adopting a 2020-12-capable validator and is out of scope
for this change.
2026-05-11 13:44:30 +09:00
Koichi ITO
3a68937bb7 Use symbolize_names: true in JSON.parse calls
Replace custom `deep_transform_keys` in `Tool::Schema` and
string key access in `StreamableHTTPTransport` with
the `symbolize_names: true` option of `JSON.parse`.

This aligns `StreamableHTTPTransport` with `StdioTransport`,
which already uses `symbolize_names: true`.
2026-03-31 04:15:42 +09:00
Koichi ITO
99cbefc610 Allow $ref in Tool::Schema for protocol version 2025-11-25
`Tool::Schema` rejected `$ref` with an `ArgumentError`.
Earlier MCP spec versions (2024-11-05 through 2025-06-18) did not define
a JSON Schema dialect or reference the `$ref` keyword. `inputSchema` was described
only as "JSON Schema defining expected parameters" with no further constraints:

- https://modelcontextprotocol.io/specification/2024-11-05/server/tools
- https://modelcontextprotocol.io/specification/2025-06-18/server/tools

The MCP spec 2025-11-25 introduced a "JSON Schema Usage" section that adopts
JSON Schema 2020-12 as the default dialect. Since `$ref` is a core keyword in 2020-12,
it is now allowed for protocol version 2025-11-25 and later.

- https://modelcontextprotocol.io/specification/2025-11-25/basic#json-schema-usage
- https://modelcontextprotocol.io/specification/2025-11-25/server/tools
- https://json-schema.org/draft/2020-12/release-notes

For backward compatibility with older protocol versions (2025-06-18 and earlier),
`Server#validate!` continues to raise `ArgumentError` when a tool input schema contains `$ref`.
This preserves the existing behavior for servers targeting those spec versions.
2026-02-27 14:33:36 +09:00
Edgars Beigarts
a3c24d2bcc
Refactor schema validation to reduce duplication and add additionalProperties support
This refactoring extracts common schema validation logic into a base Schema
class, eliminating ~100 lines of duplicate code between InputSchema and
OutputSchema. It also adds support for the JSON Schema `additionalProperties`
keyword to allow fine-grained control over extra properties in tool arguments.

Previously `additionalParameters` could be set only on OutputSchema.

Key changes:

- Created MCP::Tool::Schema base class consolidating:
  - Schema validation against JSON Schema Draft 4 metaschema
  - Deep key transformation (string to symbol conversion)
  - $ref disallowance checking
  - Common validation methods

- Refactored InputSchema to inherit from Schema:
  - Simplified constructor to accept full schema hash
  - Changed required fields storage from symbols to strings
  - Removed ~40 lines of duplicated validation logic

- Refactored OutputSchema to inherit from Schema:
  - Removed ~54 lines of duplicated validation logic
  - Now shares all common functionality with InputSchema

- Updated Tool.input_schema setter:
  - Simplified to pass full hash directly to InputSchema

- Added additionalProperties support:
  - By default, tools now allow additional properties (JSON Schema default)
  - Can explicitly set `additionalProperties: false` to disallow extras
  - Added comprehensive test coverage for both behaviors
2025-12-12 12:49:52 +02:00