Boolean JSON Schema at a named property breaks MCP proxy construction — all tools of the server are dropped (#50194 still reproduces, wrong root cause)

Status Open
Reported on v2.1.220
Maintainer reply None cached
Activity 0 comments · opened Jul 31, 2026

Follow-up to #50194, which was closed as completed but still reproduces. That issue is locked, and its closing bot comment asks to file a new one referencing it.

More importantly, #50194's stated cause is wrong, which may be why a fix did not resolve it. It blamed additionalProperties: true. I enumerated all 57 tool schemas from the server's live tools/list — there are zero boolean values at additionalProperties (or items, not, propertyNames, contains, unevaluatedProperties, additionalItems). A guard placed only around additionalProperties would not help here. The actual offender is a boolean schema sitting at a named property.

What's Wrong?

With the Grafana MCP Server extension (v0.10.0, ant.dir.gh.grafana.grafana-mcp) installed and enabled, the extension connects fine and reports 57 tools, but Claude Desktop throws while building the SDK proxy. The result: none of the 57 tools reach the Claude Code session. The extension shows as installed and enabled and silently delivers nothing — ToolSearch returns no matches for any Grafana keyword.

Root cause

Two tools declare a property whose schema is the boolean true:

create_alert_rule  ->  inputSchema.properties.data.items.properties.model = true
update_alert_rule  ->  inputSchema.properties.data.items.properties.model = true

true is a valid JSON Schema from draft-6 onward — it means "any value is allowed" and is equivalent to {}. jsonSchemaToZodShape recurses into properties and evaluates 'properties' in node on each property's schema without first checking that the node is an object, so a boolean node throws TypeError: Cannot use 'in' operator to search for 'properties' in true.

Blast radius

Per the stack, createSdkServer maps over the tool list with Array.map inside a Promise.all, with no per-tool isolation. So two unconvertible schemas drop all 57 tools of that server. Even after the boolean case is handled, a per-tool try/catch that skips only the offending tool would keep a single odd schema from taking down an entire server.

What Should Happen?

A boolean schema node should be accepted (treated as {} for true, and as "never" / an always-failing schema for false), and the remaining 55 tools should be exposed regardless of whether these two convert.

Steps to Reproduce

  1. Install the Grafana MCP Server extension v0.10.0 in Claude Desktop.
  2. Configure it with any grafana_url and service_account_token (the failure happens after tools/list, so the credentials need not be valid — the schema conversion is purely local).
  3. Start any Claude Code session in the desktop app.
  4. No Grafana tools are available; ~/Library/Logs/Claude/main.log shows the error below.

Generalized — this does not require Grafana. Any MCP server exposing a tool whose input schema nests a boolean schema at a named property should trigger it:

{
  "type": "object",
  "properties": {
    "data": {
      "type": "array",
      "items": { "type": "object", "properties": { "model": true } }
    }
  }
}

Error Messages/Logs

~/Library/Logs/Claude/main.log (hostname redacted):

[info]  [LocalMcpServerManager] Connecting to Grafana MCP Server
[info]  Using basic execution for extension Grafana MCP Server: server.type is "binary" (not node/python/uv)
[info]  [LocalMcpServerManager] Connected to Grafana MCP Server (57 tools)
[error] [LocalMcpServerManager] Failed to create proxy for Grafana MCP Server:
        Cannot use 'in' operator to search for 'properties' in true
TypeError: Cannot use 'in' operator to search for 'properties' in true
    at a (.../index.chunk-6k1UHY_-.js:1:1480)
    at a (.../index.chunk-6k1UHY_-.js:1:1359)
    at a (.../index.chunk-6k1UHY_-.js:1:1220)
    at Object.p [as jsonSchemaToZodShape] (.../index.chunk-6k1UHY_-.js:1:2119)
    at .../index.chunk-y08W5C7k.js:1:10761
    at Array.map (<anonymous>)
    at R.createSdkServer (.../index.chunk-y08W5C7k.js:1:10716)
    at R.createProxyServers (.../index.chunk-y08W5C7k.js:1:4402)
    at async Promise.all (index 0)
    at async pi.createAllServers (.../index.chunk-CzaHV0Pg.js:171:18577)
    at async j.setupMcpAndPlugins (.../index.chunk-DT0P6tKR.js:347:4990)
    at async j.buildStartSdkOptions (.../index.chunk-DT0P6tKR.js:361:2717)
    at async j.warmSession (.../index.chunk-DT0P6tKR.js:372:7987)

The extension itself is healthy — mcp-server-Grafana MCP Server.log shows a successful initialize and a tools/list result. Driving the same bundled binary directly over stdio outside the app returns 57 tools and real data from a tools/call, so the failure is entirely in the app's proxy construction.

Unrelated but visible in the same log, the extension's env template is not fully substituted — GRAFANA_ORG_ID arrives as the literal string ${user_config.org_id}, and the server logs Invalid GRAFANA_ORG_ID value, ignoring. It also reports basic_auth_set=true because GRAFANA_USERNAME / GRAFANA_PASSWORD are passed as unsubstituted placeholders rather than omitted. Neither is fatal; filing here only as a note, happy to split it out.

Suggested Fix

In the schema walker, handle boolean schemas before any in / property access:

if (typeof node === 'boolean') return node ? z.any() : z.never();

and wrap per-tool conversion in createSdkServer so one unconvertible tool is skipped and logged rather than discarding the whole server's tool list.

Environment

  • Claude Code version: 2.1.220
  • Claude Desktop: 1.24012.9
  • Grafana MCP Server extension: v0.10.0 (ant.dir.gh.grafana.grafana-mcp), server.type: binary, darwin-arm64
  • OS: macOS 26.5.2 (Darwin 25.5.0), Apple Silicon
  • Platform: Anthropic API
  • Regression: I don't know — first install of this extension here; #50194 reports the same failure in April 2026 on Claude Code 2.1.111.
  • Other extensions (Figma, Chrome Control) and other MCP servers load fine in the same sessions, so the proxy failure is isolated to the affected server.

View original on GitHub ↗