Skip to content

Cache compiled tool validators on v1.x lowlevel server - #3677

Closed
amariwan wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
amariwan:fix/v1x-cache-tool-validators
Closed

amariwan wants to merge 1 commit into
modelcontextprotocol:v1.xfrom
amariwan:fix/v1x-cache-tool-validators

Conversation

@amariwan

Copy link
Copy Markdown

Fixes #3676.

Problem

Server.call_tool() on v1.x calls one-shot jsonschema.validate() twice per tools/call (input at former line 536, structured output at former line 573). Each call re-selects the validator class, runs check_schema() against the meta-schema, and resolves $refs, then throws the result away. Tool schemas do not change between calls. This is the server-side counterpart of #3133, fixed for ClientSession in #3134.

Change (src/mcp/server/lowlevel/server.py)

  • Per-tool compiled input/output validators, built lazily once per tool listing and reused across calls (_tool_input_validators / _tool_output_validators, identity-bound to the cached Tool so re-listed schemas recompile; both cleared with _tool_cache).
  • Same construction as the merged Cache compiled output-schema validators on ClientSession #3134 fix: explicit empty referencing.Registry (resolution stays within the schema document and bundled meta-schemas) and invalid schemas surfaced as RuntimeError("Invalid schema for tool …"), which the existing generic handler turns into an error result.
  • validate_input=False still skips input validation (and its compile) entirely; unlisted tools still skip validation with the existing warning. Error message contract (Input validation error: … / Output validation error: …) unchanged.

Verification

  • New tests/server/test_lowlevel_validator_cache.py (5 tests): compile-once-reuse (spy on validator_for), preserved input/output error contracts, re-list recompiles and enforces the new schema, validate_input=False skips input compile.
  • tests/server/test_lowlevel_{validator_cache,input_validation,output_validation}.py: 20 passed.
  • Broader regression: test_lowlevel_exception_handling, test_session, test_stdio, tests/server/fastmcp: 353 passed.
  • ruff check + ruff format --check clean; pyright (strict) 0 errors on both touched files.

Note: drafted with AI assistance; I ran the suites above locally, reviewed the diff, and will answer questions on it.

One-shot jsonschema.validate() re-selects the validator class, checks the
schema against the meta-schema, and resolves $refs on every tools/call,
then discards the result. Compile per-tool input/output validators once per
tool listing and reuse them across calls, mirroring the client-side fix
from modelcontextprotocol#3134 (explicit empty referencing registry, SchemaError surfaced with
tool context).

Fixes modelcontextprotocol#3676
Copilot AI balanced review requested due to automatic review settings October 11, 2026 11:42
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@github-actions github-actions Bot added the missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md) label Oct 11, 2026
@github-actions

Copy link
Copy Markdown
Contributor

This PR has been closed automatically. This repo only keeps pull requests open when they come from a maintainer, or from a contributor a maintainer has assigned to the linked issue, and you aren't currently assigned to #3676.

If a maintainer assigns you to #3676, this PR reopens on its own and there's nothing more you need to do here. Assignment is a maintainer call based on capacity; comments that only ask to be assigned don't factor in. What does help is engaging on the issue itself by confirming the repro, explaining why it matters for your use case, or describing the approach you'd take.

You're welcome to keep pushing commits here (just avoid force-pushing, since GitHub can't reopen a rewritten branch), but that on its own won't get the PR reviewed or the issue assigned, and realistically most auto-closed PRs stay closed. There's no need to open a new PR either way.

CONTRIBUTING.md has the full reasoning, but in short:

  • We're a small team with very little capacity to review community PRs right now.
  • Many recent PRs are AI-generated with little human review, and reviewing one carefully still costs a maintainer as much time as it ever did. A well-described issue is usually more useful to us than the code.

Maintainers: reopen, remove missing-issue-link, or add bypass-issue-check to override.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Mutable schemas can retain stale validators, and unresolved references bypass the intended error contract.

2 open findings
What changed in this PR

Caches compiled server-side tool schema validators to avoid repeated JSON Schema compilation.

Changes:

  • Adds lazy input/output validator caches.
  • Invalidates caches when tool listings refresh.
  • Adds validation-cache regression tests.
File Description
src/​mcp/​server/​lowlevel/​server.py Implements compiled validator caching.
tests/​server/​test_lowlevel_validator_cache.py Tests reuse, relisting, errors, and disabled validation.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

validator_cls.check_schema(schema)
except jsonschema.SchemaError as e:
raise RuntimeError(f"Invalid schema for tool {tool_name}: {e}")
return validator_cls(schema, registry=Registry())
Comment on lines +532 to +534
cached = self._tool_input_validators.get(tool_name)
if cached is not None and cached[0] is tool:
return cached[1]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

missing-issue-link Auto-closed: PR needs a linked issue assigned to its author (see CONTRIBUTING.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants