feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

feat: pluggable JSON schema validator providers - #1012

Merged
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker
Oct 25, 2025
Merged

feat: pluggable JSON schema validator providers#1012
bhosmer-ant merged 10 commits into
modelcontextprotocol:mainfrom
mattzcarey:fix/elliciation-broken-in-edge-worker

Conversation

@mattzcarey

@mattzcareymattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
Contributor

Fixes#689 by adding the concept of pluggable JSON schema validators.

Supports ajv and cfworker (works well on edge runtimes).
Also upgrades to ajv v8

Motivation and Context

AJV consistently causes issues in Workers due to its use of eval. You can see a repro of this using the latest v8 version and other related bugs here

How Has This Been Tested?

This is tested against a non exhaustive set of json schema 2020-12 spec (in prep for SEP-1330) and the limited set of features supported here

Breaking Changes

This is not breaking and should not be noticed by users unless it unblocks them on edge runtimes.
Users can opt in by declaring a jsonSchemaValidator in both Client and Server

{jsonSchemaValidator: ajvValidator// or cfWorkerValidator}

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

@mattzcarey
mattzcarey requested a review from a team as a code ownerOctober 7, 2025 10:52
@mattzcareymattzcarey changed the title Elicitation broken in Cloudflare Workerfix: elicitation json schema validation in Cloudflare WorkerOct 7, 2025
@paoloricciuti

Copy link
Copy Markdown

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

@mattzcarey

mattzcarey commented Oct 7, 2025

Copy link
Copy Markdown
ContributorAuthor

Elicitation can't work on cloudflare workers at all because it requires a second request from the client to resolve the first request and that's impossible in cloudflare architecture

Hey, I'm working on this at Cloudflare at the moment. We use a Durable Object to keep the state during the elicitation (and for all stateful MCP interactions). Multiple requests from the client can hit the same Durable Object :)

You can read more about it here

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey i used the same approach but was still stopped by cloudflare...would you be available for a quick chat some of those days?

Sure. I'm still very new (I started yesterday) but it's my job to get this working.

@paoloricciuti

Copy link
Copy Markdown

@mattzcarey do you have discord or where can I contact you?

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

@mattzcarey do you have discord or where can I contact you?

I'm in the cloudflare discord :)

@jake-danton

Copy link
Copy Markdown

I don't think it is the correct move to fully switch over to @cfworker/json-schema for everyone as it is significantly slower than Ajv. For validating the Tool schema in this library, it runs 40x - 80x slower and takes much longer to generate the validator as well. So while it unlocks using features like elicitInput in this library for those of us who use Cloudflare or other runtimes without eval, it will be a performance regression for most users.

I am pushing for the library to move away from these hard-coded dependencies (Ajv, Zod, etc) to a flexible core with the ability to inject them, allowing developers to choose what is best for their use case. I created a fork @enth/mcp-sdk that allows you to use the any JSON schema validator (including pre-compiled ones) or schema definition library. Would love to hear your thoughts on it and if that approach would work for your use case.

@mattzcarey

mattzcarey commented Oct 8, 2025

Copy link
Copy Markdown
ContributorAuthor

Hey @jake-danton awesome work on the fork. I ran some benchmarks on this change with some simple examples. I can imagine with deeply nested schemas this gap would widen.

Schema Type | AJV (M ops/sec) | CFWorker (M ops/sec) | Performance Ratio
----------------------------------------------------------------------------
String Email | 14.6M | 6.0M | 2.4x
String URI | 12.0M | 8.2M | 1.5x
String Date | 11.6M | 8.4M | 1.4x
String DateTime | 4.3M | 3.8M | 1.1x
Number | 34.9M | 19.5M | 1.8x
Integer | 34.7M | 19.3M | 1.8x

Slower sure, but I'm not sure this constitutes a completely new dev experience. There are other efficiencies that are lower hanging fruit in the SDK nevermind the fact that an LLM is always going to be the slowest part of this system.

There is ongoing chat about this internally, we could support AJV in its current form but the validator would have to be declared in the global scope for the eval to work. Maybe also an option.

I'll check out your repo and benchmarks more tomorrow, it is definitely cool work.

@felixweinberger

Copy link
Copy Markdown
Contributor

I believe this might resolve: #857

@felixweinbergerfelixweinberger linked an issue Oct 13, 2025 that may be closed by this pull request
@dsp-ant

Copy link
Copy Markdown
Member

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

I think the right approach here is to make the validator configurable and less of a hard-dependency as @jake-danton suggests. I am wary about moving to different validator given perf implications and since ajv seems to be the de-facto standard in the ecosystem. We must also consider that any validator must be able to support 2020-12 as a schema, since we are moving to this as the default schema.

Thanks @dsp-ant will make a pr for this direction.

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from 5dc06e3 to 05f23aaCompareOctober 17, 2025 15:33
@mattzcareymattzcarey changed the title fix: elicitation json schema validation in Cloudflare Workerfeat: pluggable JSON schema validator providersOct 17, 2025
@ihrprihrpr added this to the HPR milestone Oct 23, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @mattzcarey - thanks for working on this!

I believe there's an issue with removing ajv from dependencies though, since the SDK still needs it available for the default validator. (Adding it to devDependencies makes it available when working on the SDK itself, but e.g. apps that depend on the SDK won't bring it in.)

IIUC restoring the hard dependency should be fine w.r.t. the original problem, since the ajv's compile() will still never run when a custom validator is passed in.

fwiw here's what I did to confirm the problem:

  1. Build and pack the SDK:
    npm install
    npm run build
    npm pack # Creates modelcontextprotocol-sdk-1.20.1.tgz

  2. Create test dir and install sdk (doesn't bring in peer or dev deps):
    mkdir /tmp/mcp-test
    cd /tmp/mcp-test
    npm init -y
    npm install /path/to/modelcontextprotocol-sdk-1.20.1.tgz

  3. try to import:
    echo "const { Client } = require('@modelcontextprotocol/sdk/client')" > test.js
    node test.js

node:internal/modules/cjs/loader:1413
throw err;
^
Error: Cannot find module 'ajv'
Require stack:
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/validation/ajv-provider.js
- /private/tmp/mcp-test/node_modules/@modelcontextprotocol/sdk/dist/cjs/client/index.js
- /private/tmp/mcp-test/test.js
...

@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch 2 times, most recently from 6dfea9f to ca77e8bCompareOctober 24, 2025 09:39
@mattzcarey
mattzcareyforce-pushed the fix/elliciation-broken-in-edge-worker branch from ca77e8b to 1e75e04CompareOctober 24, 2025 09:44
@mattzcarey

Copy link
Copy Markdown
ContributorAuthor

Great spot thanks @bhosmer-ant I added ajv back as a dependancy :)

@felixweinbergerfelixweinberger added enhancement Request for a new feature that's not currently supported needs maintainer Potentially serious issue - needs proactive fix and maintainer attention labels Oct 24, 2025

@bhosmer-antbhosmer-ant left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Awesome! LGTM

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancementRequest for a new feature that's not currently supportedneeds maintainerPotentially serious issue - needs proactive fix and maintainer attention

Projects

None yet

7 participants

@mattzcarey@paoloricciuti@jake-danton@felixweinberger@dsp-ant@bhosmer-ant@ihrpr