Skip to content

MCP servers declaring draft-07 outputSchema are entirely unusable: "unsupported dialect" rejected client-side before dispatch #86142

Closed

Description

@amitfin

Preflight Checklist

  • I have searched existing issues and this hasn't been reported yet
  • This is a single bug report (please file separate reports for different bugs)
  • I am using the latest version of Claude Code

What's Wrong?

Every tool on an MCP server that declares outputSchema with the JSON Schema draft-07 dialect is now unusable. The call fails at schema registration, before any request reaches the server, with:

Tool '<name>' has an invalid outputSchema: JSON Schema declares an unsupported dialect
("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports
JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

This affects the entire server, not one tool: count, aggregate, find, and list-collections on mongodb-mcp-server all fail identically. The server process is never contacted — this is purely client-side.

The MCP specification does not require outputSchema to use 2020-12, and declaring $schema: draft-07 is valid. MongoDB has explicitly declined to remove the field (closed 2026-08-06, internal ref MCP-101), calling it "an internal field documented on the JsonSchema spec and it's standard." So this cannot be resolved server-side, and any MCP server using a draft-07 schema generator is affected — mongodb-mcp-server is likely just the most visible instance.

The error text itself (pass a pre-configured Ajv instance to AjvJs) reads like an internal Ajv message surfacing directly to users.

What Should Happen?

Claude Code should validate outputSchema documents that declare draft-07 (and ideally draft-04/06/2019-09), e.g. by registering the corresponding meta-schemas on the Ajv instance, or by falling back to skipping structured-output validation rather than disabling the tool entirely.

Disabling every tool on a server is a severe failure mode for what is a validator configuration gap — the previous behavior (accepting these servers) was strictly more useful.

Error Messages/Logs

Error: Tool 'count' has an invalid outputSchema: JSON Schema declares an unsupported dialect
("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports
JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

Identical for aggregate, find, list-collections.

Steps to Reproduce

  1. Configure any MCP server whose tools declare outputSchema with "$schema": "http://json-schema.org/draft-07/schema#". Minimal config:
{
  "mongodb": {
    "command": "npx",
    "args": ["-y", "mongodb-mcp-server@2.1.0", "--readOnly"],
    "env": { "MDB_MCP_CONNECTION_STRING": "mongodb://127.0.0.1:27017" }
  }
}
  1. Call any of its tools. All fail with the error above; the server logs show no incoming request.

To confirm the payload is draft-07 independently of Claude Code, this script speaks JSON-RPC to the server over stdio and prints the declared dialects. It needs no reachable database — tools/list responds regardless:

import json, subprocess, sys, os

def probe(ver):
    env = dict(os.environ, MDB_MCP_CONNECTION_STRING="mongodb://127.0.0.1:27099/?serverSelectionTimeoutMS=200")
    p = subprocess.Popen(["npx", "-y", f"mongodb-mcp-server@{ver}", "--readOnly"],
        stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.DEVNULL, text=True, env=env)
    def send(o): p.stdin.write(json.dumps(o) + "\n"); p.stdin.flush()
    send({"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"probe","version":"1"}}})
    send({"jsonrpc":"2.0","method":"notifications/initialized"})
    send({"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}})
    tools = None
    try:
        for _ in range(200):
            line = p.stdout.readline()
            if not line: break
            try: m = json.loads(line)
            except: continue
            if m.get("id") == 2:
                tools = m.get("result", {}).get("tools", []); break
    finally:
        p.kill()
    withos = [t for t in tools or [] if "outputSchema" in t]
    dialects = {t["outputSchema"].get("$schema", "<none>") for t in withos}
    return f"v{ver}: {len(tools or [])} tools, {len(withos)} with outputSchema, dialects={dialects or '-'}"

for v in sys.argv[1:]:
    print(probe(v))

Output — note that the version working before the regression emits the identical payload:

$ python3 probe.py 1.14.0 2.0.0 2.1.0
v1.14.0: 16 tools, 13 with outputSchema, dialects={'http://json-schema.org/draft-07/schema#'}
v2.0.0:  18 tools, 15 with outputSchema, dialects={'http://json-schema.org/draft-07/schema#'}
v2.1.0:  18 tools, 15 with outputSchema, dialects={'http://json-schema.org/draft-07/schema#'}

Is this a regression?

Yes — this worked before and broke recently.

Last Working Version

Last successful MCP call: 2026-08-04. First failure: 2026-08-12. Same machine, same MCP config, unchanged throughout.

The MongoDB server was upgraded from 1.14.0 to 2.1.0 on 2026-08-11 (the config pins @latest), which initially looked like the cause. The probe above rules it out: 1.14.0 — the version in use during the working period — declares the same draft-07 outputSchema. The server payload is unchanged; the client's handling of it changed.

Claude Code Version

Claude.app 1.28929.0

Platform

Claude Desktop (macOS)

Operating System

macOS (Darwin 25.6.0, Apple Silicon)

Additional Context

Possibly related, same subsystem:

The presence of both polarities (draft-07 rejected here, 2020-12 rejected in #80402) suggests inconsistent meta-schema registration across the different Ajv instances in the codebase, rather than one isolated bug.

Activity

  1. amitfin commented on Aug 12, 2026

    @amitfin
    Author

    Follow-up: I traced where the draft-07 dialect actually originates, and it significantly widens the scope of this bug.

    It is not a MongoDB-specific choice. It is the default in the MCP TypeScript SDK. From @modelcontextprotocol/sdk@1.30.0, dist/esm/server/zod-json-schema-compat.js:

    function mapMiniTarget(t) {
        if (!t)
            return 'draft-7';          // default when no target is passed
        if (t === 'jsonSchema7' || t === 'draft-7')
            return 'draft-7';
        if (t === 'jsonSchema2019-09' || t === 'draft-2020-12')
            return 'draft-2020-12';
        return 'draft-7';              // fallback
    }

    mongodb-mcp-server passes no target, so it inherits 'draft-7'. Under zod 4.4.3 that emits exactly the dialect this client now rejects:

    target: 'draft-7'        -> "http://json-schema.org/draft-07/schema#"
    target: 'draft-2020-12'  -> "https://json-schema.org/draft/2020-12/schema"
    

    Implication for severity: any MCP server built on the TypeScript SDK with zod v4 that declares outputSchema and does not explicitly pass target: 'draft-2020-12' will emit draft-07 and be entirely unusable in Claude Code. That is the SDK's default path, so the affected set is likely broad rather than one vendor — mongodb-mcp-server is simply where I hit it.

    This also means the fix cannot realistically be pushed onto server authors: it would require every SDK-based server to override an SDK default. Accepting draft-07 client-side (or registering the older meta-schemas on the Ajv instance) looks like the only fix with the right blast radius.

    Tracking the server-side workaround at mongodb-js/mongodb-mcp-server#1427.

  2. StableSoftwareDev commented on Aug 13, 2026

    @StableSoftwareDev

    Confirming this on Windows as well — it's labelled platform:macos, but it isn't platform-specific.

    Same error, different server: mssql-mcp-node@3.0.0 (@modelcontextprotocol/sdk ^1.29.0, zod ^3.24.2), launched via npx -y. All six tools fail identically — execute_read_query, list_databases, list_views, list_tables, describe_database, describe_table — and four separately-configured instances of that server broke simultaneously.

    Tool 'execute_read_query' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs
    

    Version data point in case it helps narrow the window: working on Claude Desktop 1.24012.9, broken on 1.28929.0, with no change to the MCP server, its version or its config in between — the app updated underneath it. Electron 42.7.0, app-bundled Node 24.18.0, Windows x64.

    Also ruled out on this end:

    • Restarting the desktop app.
    • Refreshing the MCP tool list — re-read all tools, zero added or removed, so the schema is what the server has always advertised.
    • Updating the server. mssql-mcp-node@3.0.0 is current, and @modelcontextprotocol/sdk@1.30.0 still depends on zod ^3.23.8 and zod-to-json-schema ^3.24.1, both of which emit draft-07. There is no server-side version available today that emits 2020-12, so users can't work around this by upgrading — which is worth weighing when prioritising.

    Agreed on the suggested fix: registering the draft-07 meta-schema on the Ajv instance, or skipping structured-output validation for an unrecognised dialect, both beat disabling the tool outright.

  3. Vantso commented on Aug 13, 2026

    @Vantso

    Reproducing with a different server family, which widens this beyond mongodb-mcp-server: an n8n instance-level MCP server (33 tools, built on the MCP TS SDK). All 33 tools fail identically — the message differs only by tool name. Verified on get_workflow_details, search_workflows, search_projects, get_sdk_reference, get_execution, execute_workflow. Same build 1.28929.0 (d1a6bc, built 2026-08-11T18:58:06Z), Windows x64, Electron 42.7.0, Node 24.18.0. Last known good on this end: 2026-08-11.

    Transport does not matter either: here the server runs locally and is proxied into a cloud session over the desktop bridge, and it fails the same way — the schema is rejected at registration, so nothing is dispatched.

    Three earlier reports that look like the same seam and are not linked here yet:

    Counting this one, outputSchema handling has now failed fatally in four different ways. Each was addressed (or not) as its own case, and the fixes have not generalised. The fallback requested in this issue — skip validation rather than disable the tool — is the one change that would have covered all four.

  4. SebCool commented on Aug 13, 2026

    @SebCool

    Another data point, and one that widens the blast radius: Anthropic's own reference server is affected.

    @modelcontextprotocol/server-filesystem@2026.7.10 (current), launched via npx -y, on Windows. All of its tools fail identically at registration:

    Tool 'list_allowed_directories' has an invalid outputSchema: JSON Schema declares an
    unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#").
    The default validator supports JSON Schema 2020-12 only; pass a pre-configured Ajv instance
    

    Confirms @amitfin's root cause: the package itself contains zero draft-07 strings — the dialect comes from the SDK default. mapMiniTarget() in @modelcontextprotocol/sdk@1.30.0 returns 'draft-7' when no target is passed, and server-filesystem passes none. Worth noting for anyone grepping to confirm this on their own setup: the SDK source uses 'draft-7' (no leading zero), so grepping for draft-07 in dist/ returns nothing and looks like a clean bill of health.

    Independent confirmation of @StableSoftwareDev's version boundary. Same transition observed here, with no change to any MCP server config:

    Date Claude Desktop server-filesystem
    2026-08-02 1.24012.9 working
    2026-08-13 1.28929.0 all tools rejected

    The app auto-updated in between. Also confirms this is not macOS-specific (label says platform:macos); this is the second Windows report in this thread.

    Ruled out on this end, in case it saves anyone time:

    • Updating the server — 2026.7.10 is current, and outputSchema has been declared since at least 2026.1.14, so there is no "fixed" version to move to.
    • Pinning an older server version — same reason.
    • Config or credentials — list_allowed_directories touches no filesystem path at all and still fails, so this is not a permissions issue.

    One impact angle not yet raised in this thread. For filesystem-type servers, the allowed-directories sandbox is a security boundary, not a convenience. When the server dies at registration, the natural workaround is to fall back to the client's built-in file tools — which have no path restriction at all. So the failure mode isn't just "a server is unavailable": it silently removes a containment guarantee and substitutes an unrestricted one, with nothing surfacing that the boundary is gone. In our case the sandbox was scoped to a single vault directory precisely to prevent cross-project writes.

    That makes the graceful-fallback behaviour referenced in #41361 (pre-2.1.88) meaningfully safer than a hard reject: a tool that still works under a relaxed output-validation is strictly better than a tool that disappears and gets replaced by an unsandboxed equivalent.

    Either suggested fix works from here — registering the draft-07 meta-schema on the Ajv instance, or skipping structured-output validation for an unrecognised dialect.

  5. ymeskini commented on Aug 14, 2026

    @ymeskini

    All my MCPs are no longer working with Claude Desktop and Claude Code

  6. Tigershawk commented on Aug 14, 2026

    @Tigershawk

    Confirming this same regression on a different MCP server — the IBM i connector (mcp__remote-devices__ibm-i__*) exposed through Cowork. So far, Chat seems to work so indicates that our MCP server is functioning at least correctly.

    Every tool on this connector fails identically before dispatch:

    Tool 'system_status' has an invalid outputSchema: JSON Schema declares an unsupported dialect
    ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports
    JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

    Confirmed affected tools: system-status, execute_sql (likely all tools on the connector, since it appears to be a connector-wide schema declaration). This makes the entire connector unusable, not just one tool.

    This matches the pattern described above — client-side rejection prior to any request reaching the server, and not caused by any change on the server/connector side. Adding as a second data point since it suggests this isn't isolated to mongodb-mcp-server but affects any MCP server declaring draft-07 outputSchema.

    Would strongly support the proposed fix of registering draft-07 (and ideally draft-04/06/2019-09) meta-schemas on the Ajv instance, or falling back to skipping output validation rather than disabling the tool entirely.

  7. bcherny commented on Aug 15, 2026

    @bcherny
    Collaborator

    Could not reproduce on Claude Code CLI v2.1.233 (macOS). Note the report and both confirmations are against Claude Desktop 1.28929.0, which we did not test here.

    Steps (CLI):

    1. Minimal stdio MCP server (plain Node) exposing one tool count whose outputSchema declares "$schema": "http://json-schema.org/draft-07/schema#" (plus type: object, properties, required), returning structuredContent: {"count": 42}.
    2. Config: {"mcpServers":{"draft07":{"command":"node","args":["server.mjs"]}}}
    3. claude -p --strict-mcp-config --mcp-config mcp.json --allowedTools mcp__draft07__count "Call mcp__draft07__count with what='apples' and report the exact result or error"

    Observed: the call reached the server and succeeded; the model reported {"count":42}, and no "unsupported dialect" error appeared anywhere, including the debug log. Same result with a draft-07 schema using $ref/definitions.

    Expected: same. The CLI tolerates draft-04/06/07/2019-09 $schema declarations on outputSchema (it drops the dialect marker and validates the structure under the default rules), so servers built on the MCP TypeScript SDK's default zod target work.

    Assessment: this looks like a genuine bug, but in Claude Desktop's MCP client rather than the Claude Code CLI. The error text is the MCP SDK's stock validator message, which is exactly what you get when a client uses that validator unmodified; the CLI ships a wrapper that avoids it. Based on the versions above it regressed in Desktop somewhere between 1.24012.9 and 1.28929.0. It should be routed to the Claude Desktop team; a longer-term upstream fix (a dialect-aware default validator in the MCP TypeScript SDK) would cover every client. If you do hit this in the CLI itself, please share claude --version and claude --debug output around the failing call.

    🤖 Generated with Claude Code

  8. added
    needs-reproBug needs reproduction steps before we can investigate
    on Aug 15, 2026
  9. github-actions commented on Aug 15, 2026

    @github-actions

    We weren't able to reproduce this. Could you provide steps to trigger the issue — what you ran, what happened, and what you expected? This issue will be closed automatically if there's no activity within 7 days.

  10. 37 remaining items

  11. m13v commented on Sep 2, 2026

    @m13v

    moving construction earlier costs the one signal in your own trace: the lazy 2.1.233 path let the first call round-trip a real -32602, proving the server parsed it. pair eager rejection with the skip-validation fallback and you bury the draft-07 mismatch before anything reaches the server at all, so the tool just looks empty instead of misconfigured.

  12. m13v commented on Sep 2, 2026

    @m13v

    the .mcpb path makes skip-validation the only lever left: an extension install gives you no config file to inject a pre-configured Ajv into, so the error's own remedy ("pass a pre-configured Ajv instance") is literally unreachable. and skip just green-lights unvalidated structuredContent, so a broken listDecks over the bridge reads identical to a clean one. the loud client-side reject you're hitting is the only thing currently telling you the draft-07 mismatch exists.

  13. m13v commented on Sep 2, 2026

    @m13v

    the CLI executing end-to-end isn't proof the schemas validated, only that nothing on that side checked them. so "make Desktop match CLI" is skip-validation wearing a parity argument: it green-lights unverified structuredContent instead of registering draft-07 so the dialect check keeps firing.

  14. m13v commented on Sep 2, 2026

    @m13v

    of your two fixes, dropping $schema is the quieter failure: it strips the draft-07 label, not the semantics, so Ajv grades constructs like tuple items under 2020-12 rules and silently passes them. the reject is the only thing flagging the dialect gap right now; registering draft-07 keeps that check firing instead of burying it downstream.

  15. m13v commented on Sep 2, 2026

    @m13v

    the server-side draft-2020-12 override you opened on kObsidian fixes those 66 tools but not the class: every other SDK-default server stays rejected client-side, so it's a per-server patch on a client-wide bug while the SDK keeps minting new casualties.

  16. m13v commented on Sep 2, 2026

    @m13v

    registering draft-07 on the Ajv you hit fixes this polarity, but the issue itself lists the inverse in #80402: 2020-12 rejected by a different instance. patching one meta-schema on one Ajv leaves the other one broken. the real gap is per-instance registration across the codebase, not the dialect.

  17. VolkerVV commented on Sep 2, 2026

    @VolkerVV

    Same failure with the Filesystem desktop extension on Windows, Claude Desktop (current build, Sept 2026):

    Error: Tool 'list_allowed_directories' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

    Only the tools that declare an outputSchema are affected — read_text_file, list_directory etc. on the same server keep working. So this is not limited to MongoDB/Perplexity; Anthropic's own Filesystem extension is hit as well.

  18. rsalvucci-git commented on Sep 2, 2026

    @rsalvucci-git

    Hitting this exact same error today (2026-09-01) with ClickUp's official remote MCP server (https://mcp.clickup.com/mcp), not just MongoDB — this issue shows as closed but the bug is still live in practice. Confirmed pattern: read tools work fine, write tools (create_task, update_task) fail client-side before any request reaches ClickUp's server, with the same "unsupported dialect" error. Reproduced both in Claude Code (CLI) and in the personal ClickUp connector on claude.ai/Claude Desktop.

  19. sorenharrison commented on Sep 8, 2026

    @sorenharrison

    Seeing this issue as well.

    "OmniFocus MCP read tools broken — outputSchema uses draft-07, Cowork validator requires 2020-12."

  20. EugeneKuvshinov commented on Sep 10, 2026

    @EugeneKuvshinov

    This is still live in Desktop/Cowork — closing it via the stale-bot after @bcherny's CLI-only "could not reproduce" seems to have been premature, since the CLI wrapper he tested isn't the client this report is about, and the thread has kept getting confirmations on Desktop/Cowork since the close (filesystem extension, ClickUp's remote server as recently as 2026-09-01, and an MCP connector "exposed through Cowork").

    Adding one more data point on the Cowork/desktop-bridge path specifically: SmartBear's official smartbear-mcp server (https://github.com/SmartBear/smartbear-mcp), run locally as a stdio server via node dist/index.js and proxied into a Cowork session over the desktop bridge. Every tool declaring outputSchema fails identically at registration — confirmed on zephyr_get_priorities, zephyr_get_test_cases, zephyr_get_projects — with valid credentials and config on our end, so this is purely the client-side dialect rejection described above, not anything server- or auth-related.

    Tool '' has an invalid outputSchema: JSON Schema declares an unsupported dialect
    ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports
    JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs

    Given the CLI/Desktop split @bcherny identified, and the continued reports since the close, could this be reopened and routed to the Desktop/Cowork client team specifically? Happy to provide more detail if useful.

  21. pawandprosper commented on Sep 13, 2026

    @pawandprosper

    Reopening this with a live, dated reproduction, this was closed based on Claude Code CLI testing, but CLI was never actually affected by this bug (it runs the older, lenient validator). Desktop and Cowork are still broken on the current build.

    Confirmed case: Obsidian vault connected via @modelcontextprotocol/server-filesystem (npx, local MCP server in Claude Desktop's Developer settings), affecting Cowork specifically.

    Every tool call fails with:
    Tool 'X' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema#"). The default validator supports JSON Schema 2020-12 only.

    Details:

    • Connector shows "Running" in Settings the entire time, no indication anything is wrong until a tool is actually called.
    • Deleted the server entry, manually re-added it via claude_desktop_config.json, fully quit and relaunched Claude Desktop. Reconnected cleanly, same error persists. Not a stale-connection issue.
    • The exact same vault connection works fine in regular Claude Desktop chat. Only Cowork hits this validator.
    • Confirmed today, September 13, 2026, on Claude Desktop 1.52386.3, so this is still broken as of the latest build.

    This is blocking a real daily workflow, not a hobby project. Would appreciate this being reopened and prioritized.

  22. michael-evrythngwrx commented on Sep 18, 2026

    @michael-evrythngwrx

    Easy fix is to add a shim that bypasses the $schema validation. You can implement the shim like so:
    `import { spawn } from "node:child_process";

    const [, , command, ...args] = process.argv;
    if (!command) {
    process.stderr.write("mcp-schema-shim: usage: node mcp-schema-shim.mjs [args...]\n");
    process.exit(2);
    }

    const child = spawn(command, args, { stdio: ["pipe", "pipe", "inherit"], env: process.env });

    // Client -> server: pass through untouched.
    process.stdin.pipe(child.stdin);
    process.stdin.on("end", () => child.stdin.end());

    // Server -> client: JSON-RPC over stdio is newline-delimited JSON, so buffer by line.
    let pending = "";
    child.stdout.setEncoding("utf8");
    child.stdout.on("data", (chunk) => {
    pending += chunk;
    let newline;
    while ((newline = pending.indexOf("\n")) !== -1) {
    const line = pending.slice(0, newline);
    pending = pending.slice(newline + 1);
    process.stdout.write(rewrite(line) + "\n");
    }
    });
    child.stdout.on("end", () => {
    if (pending) process.stdout.write(rewrite(pending));
    });

    function rewrite(line) {
    if (!line.includes("$schema")) return line; // fast path: nothing to strip
    let message;
    try {
    message = JSON.parse(line);
    } catch {
    return line; // not JSON; leave it alone
    }
    const tools = message?.result?.tools;
    if (!Array.isArray(tools)) return line; // only tools/list results are rewritten
    let changed = false;
    for (const tool of tools) {
    for (const key of ["inputSchema", "outputSchema"]) {
    const schema = tool?.[key];
    if (schema && typeof schema === "object" && "$schema" in schema) {
    delete schema.$schema;
    changed = true;
    }
    }
    }
    return changed ? JSON.stringify(message) : line;
    }

    // Lifecycle: mirror the child's exit status and forward termination signals.
    child.on("exit", (code, signal) => process.exit(code ?? (signal ? 1 : 0)));
    child.on("error", (err) => {
    process.stderr.write(mcp-schema-shim: failed to start "${command}": ${err.message}\n);
    process.exit(1);
    });
    for (const sig of ["SIGINT", "SIGTERM", "SIGHUP"]) {
    process.on(sig, () => child.kill(sig));
    }`

    name the file whatever you like and then shell copy to ~/.config/claude (mkdir -p ~/.config/claude && cp ~/Downloads/mcp-schema-shim.mjs ~/.config/claude/)

    check that the shim is functional:
    node --check ~/.config/claude/mcp-schema-shim.mjs && echo "shim OK" which node

    Add the MCP:
    "example_mcp_server": { "command": "/opt/homebrew/bin/node", "args": [ "/Users/YOUR-USERNAME/.config/claude/mcp-schema-shim.mjs", "npx", "-y", "mongodb-mcp-server@latest", "--readOnly" ], "env": { "MDB_MCP_CONNECTION_STRING": "keep your existing value" } }

  23. christophepub commented on Sep 20, 2026

    @christophepub

    Still reproducible on Claude Desktop and Cowork with @modelcontextprotocol/server-filesystem v2026.8.31 (checked 2026-09-20). The CLI was never affected, so the "could not reproduce" that closed this issue doesn't match what Desktop users see.

    A warning for anyone applying the $schema shim posted above: it fixes the rejection, but there is a second problem right behind it. Recent versions of the filesystem server replace all their allowed directories with the roots announced by the client (this is documented server behaviour). The client announces the session folder, so the directories from your config become unreachable. The tools load, and your vault is gone.

    The shim below handles both. It strips $schema from tools/list responses, and it removes capabilities.roots from the initialize message (and drops roots/list_changed notifications), so the server falls back to its command-line arguments.

    Tested today against v2026.8.31: 14 tools accepted, no dialect left in the schemas, and list_allowed_directories still returns the directory passed on the command line even though the client announced roots.

    claude_desktop_config.json:

    "filesystem": {
      "command": "node",
      "args": [
        "/path/to/strip-schema-dialect.mjs",
        "@modelcontextprotocol/server-filesystem",
        "/your/allowed/dir"
      ]
    }
    strip-schema-dialect.mjs
    #!/usr/bin/env node
    /**
     * MCP stdio shim.
     *
     * 1. Servers built on the MCP SDK (e.g. @modelcontextprotocol/server-filesystem)
     *    stamp their tool schemas with
     *      "$schema": "http://json-schema.org/draft-07/schema#"
     *    Claude Desktop / Cowork only validate JSON Schema 2020-12 and reject every
     *    tool of the server. This shim removes the "$schema" key from inputSchema /
     *    outputSchema in tools/list responses. With no dialect declared, the validator
     *    falls back to its default and accepts the schemas, whose structure is valid
     *    in both dialects.
     *
     * 2. Recent versions of the filesystem server replace ALL their allowed
     *    directories with the "roots" announced by the client (documented behaviour).
     *    The client announces the session folder, so the directories passed on the
     *    command line become unreachable. This shim removes `capabilities.roots`
     *    from the `initialize` message and drops `roots/list_changed` notifications,
     *    so the server falls back to its CLI arguments.
     *
     * Usage: node strip-schema-dialect.mjs <npm-package> [server args...]
     * Remove once the upstream incompatibility is fixed.
     */
    import { spawn } from "node:child_process";
    import readline from "node:readline";
    
    const [pkg, ...rest] = process.argv.slice(2);
    if (!pkg) {
      process.stderr.write("strip-schema-dialect: missing server package\n");
      process.exit(2);
    }
    
    const child = spawn("npx", ["-y", pkg, ...rest], {
      stdio: ["pipe", "pipe", "inherit"],
    });
    
    child.on("error", (e) => {
      process.stderr.write(`strip-schema-dialect: cannot start server: ${e.message}\n`);
      process.exit(1);
    });
    child.on("exit", (code, signal) => process.exit(signal ? 1 : code ?? 0));
    
    // client -> server: hide roots support so the CLI directories are kept
    readline.createInterface({ input: process.stdin }).on("line", (line) => {
      if (!line.trim()) return;
      let msg;
      try {
        msg = JSON.parse(line);
      } catch {
        child.stdin.write(line + "\n");
        return;
      }
      if (msg?.method === "initialize" && msg?.params?.capabilities?.roots) {
        delete msg.params.capabilities.roots;
      }
      if (msg?.method === "notifications/roots/list_changed") return; // not forwarded
      child.stdin.write(JSON.stringify(msg) + "\n");
    });
    
    const strip = (schema) => {
      if (schema && typeof schema === "object") delete schema.$schema;
    };
    
    // server -> client: clean tools/list responses
    readline.createInterface({ input: child.stdout }).on("line", (line) => {
      if (!line.trim()) return;
      let msg;
      try {
        msg = JSON.parse(line);
      } catch {
        process.stdout.write(line + "\n"); // non-JSON: passed through untouched
        return;
      }
      const tools = msg?.result?.tools;
      if (Array.isArray(tools)) {
        for (const t of tools) {
          strip(t.inputSchema);
          strip(t.outputSchema);
        }
      }
      process.stdout.write(JSON.stringify(msg) + "\n");
    });

    Could this be reopened? It was closed by the stale bot after a CLI-only test, and the affected clients are Desktop and Cowork.

  24. danielzarioiu commented on Sep 30, 2026

    @danielzarioiu

    Still reproducible on Desktop/Cowork — remote HTTP MCP variant, updater version 2.9939.4 (checked 2026-09-30)

    Adding a non-CLI data point since the issue was closed on a Claude Code CLI repro and the CLI was never affected (it runs the older, lenient validator). This reproduction is Claude Desktop in third-party managed deployment mode (deploymentMode: 3p), Cowork chat engine, updater version 2.9939.4, macOS ARM64, against a remote HTTP MCP server — MongoDB Atlas (https://mcp.mongodb.com), i.e. not a local stdio server and not the filesystem extension.

    What happens

    1. OAuth completes normally using the official Claude CIMD client identity (https://claude.ai/oauth/claude-code-client-metadata, the client id MongoDB ships in their mongodb-atlas plugin) — dynamic client registration is never attempted, tokens are stored, connector shows connected.
    2. tools/list succeeds: 21 tools discovered (atlas-list-orgs, atlas-list-projects, atlas-list-clusters, …). No ingestion-time rejection.
    3. Tool permission prompts render and are granted.
    4. Every tools/call fails identically, e.g. for all three read tools above (sanitized log, 2026-09-30):
    [custom3p-mcp] server connected { name: 'MongoDB Atlas', toolCount: 21 }
    Emitted tool permission request <redacted> for mcp__MongoDB_Atlas__atlas-list-orgs in session <redacted>
    Received permission response for <redacted>: always (tool: mcp__MongoDB_Atlas__atlas-list-orgs)
    [custom3p-mcp host] [directMcpHost:MongoDB Atlas] callTool failed: atlas-list-orgs: Tool 'atlas-list-orgs' has an invalid outputSchema: JSON Schema declares an unsupported dialect ("$schema": "http://json-schema.org/draft-07/schema The default validator supports JSON Schema 2020-12 only; pass a pre-configured Ajv instance to AjvJs
    

    (The line is truncated by the SDK's own .slice(0, 200) on the message; the reported tool id / permission uuids / session id are redacted.)

    tools/call is never sent to the MCP server

    Confirmed by locating the bundled MCP TypeScript SDK client's throw-site: output-schema validators are compiled lazily at the first callTool() against the cached tools/list entry, and an uncompilable schema throws ProtocolError(InvalidParams, "Tool 'X' has an invalid outputSchema: …") before the request is sent — the SDK's own changelog documents this exact ordering. Consistently, nothing shows up on the server side: no Atlas API call is ever made, and no org/project identifiers leave the client.

    Minimal reproducer

    Any tool whose outputSchema is:

    {
      "$schema": "http://json-schema.org/draft-07/schema#",
      "type": "object"
    }

    MongoDB's actual schemas are otherwise completely ordinary (trivial zod-derived objects — no $ref, no definitions/$defs, no oneOf/anyOf, no format). The dialect marker alone is sufficient to reproduce the failure; nothing else about the schema matters.

    Upstream SDK already fixed this class

    modelcontextprotocol/typescript-sdk#2534 — "fix(validators): honor declared draft-07/06 JSON Schema dialects instead of rejecting them" (merged 2026-07-27, released in @modelcontextprotocol/client 2.0.0) — made the default validator dispatch on the schema's declared dialect: 2020-12, 2019-09, draft-07 and draft-06 are all honored, with unknown dialects still erroring. That PR's description matches this failure byte-for-byte ("a client with default options cannot call tools on those servers … client.callTool fails with -32602 before the request reaches the wire").

    The error text observed on 2.9939.4 — "The default validator supports JSON Schema 2020-12 only" — matches the pre-#2534 code path (the current SDK message instead enumerates the four supported dialects). So the Desktop/Cowork host appears to bundle the older, restricted validator path. I'm not claiming a specific bundled SDK version — only the observable wording and behavior.

    What this is NOT

    • not an OAuth problem (auth completes, tokens persist, connector stays connected);
    • not a server or managed-deployment delivery problem (21 tools listed, permissions work);
    • not a malformed MCP request, and not a server response validation problem — response validation never happens, because schema compilation fails before the network call.

    Ask

    Please ship the #2534-era validator behavior in Desktop/Cowork (or pass a pre-configured multi-dialect Ajv instance to the SDK's validator provider). This also fully explains the still-open filesystem report #88988; the server-side counterpart is tracked by MongoDB at mongodb-js/mongodb-mcp-server#1427.

  25. digikittens commented on Sep 30, 2026

    @digikittens

    Hopefully this will help people!


    If you upgraded Claude Desktop recently and your MCP filesystem tools stopped working with an error like:

    unsupported dialect: http://json-schema.org/draft-07/schema

    Here's what happened and how to fix it.

    The cause: Claude Desktop's AJV validator was upgraded to require JSON Schema 2020-12 only. The MCP package but @modelcontextprotocol/server-filesystem (and many other MCP servers) output draft-07 schemas that include a $schema key. Claude now rejects those tools entirely on startup — silently, or with the above error.

    There are open issues on Anthropic's GitHub across IBM, n8n, Anki, Withings, Oura, and more. Still unpatched upstream.

    How I built the fix (with Claude Code): I used Claude Code to diagnose the AJV validator version conflict, write a lightweight Python stdio proxy that strips the $schema key before Claude Desktop sees it, and package the whole thing as a macOS .pkg installer — all in one session. No modifications to your MCP server required, no config changes beyond pointing Claude at the proxy instead of the tool directly.

    DigiKitten MCP Schema Bridge 🔗 https://github.com/digikittens/mcp-schema-bridge/releases

    macOS,pkg installer — double-click, restart Claude Desktop, done. Uninstaller included.

    One-command install (if you prefer terminal):

    curl -fsSL https://raw.githubusercontent.com/digikittens/mcp-schema-bridge/main/digikitten-install.sh | bash

    Free and open source.

    Irony note: the fix for a Claude Desktop bug was built by Claude itself, in one session, with one user. Still waiting on the official patch.

    Built by DigiKitten. For Amy. 💙

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions