Skip to content

Allow skipping validation for schema#362

Closed
dvisockas wants to merge 1 commit into
modelcontextprotocol:mainfrom
dvisockas:main
Closed

Allow skipping validation for schema#362
dvisockas wants to merge 1 commit into
modelcontextprotocol:mainfrom
dvisockas:main

Conversation

@dvisockas
Copy link
Copy Markdown

Motivation and Context

If you're building dynamic tool schemas - every time you build one it has to be validated as the Schema class calls validate_schema! in the initializer. This can get slow (taking up to ~100ms) for deeper schemas.

This PR allows the user of the Schema class to build the schema themselves and not pay the performance penalty of JSON.parse(JSON.dump(schema)) and validate_schema!.

How Has This Been Tested?

Added unit tests.

Breaking Changes

None.

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update

Checklist

  • I have read the MCP Documentation
  • My code follows the repository's style guidelines
  • New and existing tests pass locally
  • I have added appropriate error handling
  • I have added or updated documentation as needed

Additional context

An alternative

@koic
Copy link
Copy Markdown
Member

koic commented May 27, 2026

Thanks for the PR and the feedback. I agree that the issue here is the performance hit from validating the schema on every instantiation.

Adding an option like validate: false (validate = false) is one way to speed this up, but it also allows invalid schemas to be created. And once a validate option is exposed, it becomes part of the public interface and is not easy to remove later.

So rather than skipping validate_schema! entirely, it seems better to improve performance by caching validation results for identical schemas.

I've opened #363 with the caching approach. What do you think?

@dvisockas
Copy link
Copy Markdown
Author

Thanks for the PR and the feedback. I agree that the issue here is the performance hit from validating the schema on every instantiation.

Adding an option like validate: false (validate = false) is one way to speed this up, but it also allows invalid schemas to be created. And once a validate option is exposed, it becomes part of the public interface and is not easy to remove later.

So rather than skipping validate_schema! entirely, it seems better to improve performance by caching validation results for identical schemas.

I've opened #363 with the caching approach. What do you think?

Makes sense. Will leave comments on your opened PR if I see something

@dvisockas dvisockas closed this May 27, 2026
koic added a commit that referenced this pull request May 27, 2026
## Motivation and Context

`MCP::Tool::Schema#initialize` validated every instance against the draft-04 metaschema
via `JSON::Validator.fully_validate`, costing ~32ms for deep schemas (up to ~100ms).
When schemas are built dynamically and repeatedly, this work is wasted because
the result depends only on the schema content.

This caches successful validations by a content digest, so an identical schema is validated
once and subsequent constructions skip the traversal (~100ms to ~0.1ms on a cache hit).
The cache is bounded and thread-safe. It supersedes the `validate: false` escape hatch proposed
in #362, which would have added permanent public interface and allowed invalid schemas
to reach clients.

Python and TypeScript SDKs do not metaschema-validate schema definitions, so this cost is specific
to the Ruby SDK; caching reduces it without weakening the default or changing validation semantics.

## How Has This Been Tested?

Added regression tests in `test/mcp/tool/schema_test.rb`: identical schemas validate only once,
distinct schemas validate separately, a cache hit still yields a usable and correctly validated schema,
invalid schemas raise on every construction and are not cached, a schema at the normalization depth limit
is cached without a nesting error, and `ValidationCache` evicts the oldest entry beyond its max size.
The full suite (`rake test`) and `rake rubocop` pass.

## Breaking Changes

None. Validation safety is preserved: every distinct schema is still validated, and invalid schemas
continue to raise. The initializer signature and validation semantics are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants