MCP tool schema loses type/validation info between server and Claude's tool-calling path

Status Open
Reported on v2.1.268
Maintainer reply None cached
Activity 0 comments · opened Sep 16, 2026

Bug Report: MCP tool schema loses type/validation info between server and Claude's tool-calling path

Summary

Calling the aicontextmcp MCP server's project.bootstrap tool from a Claude session in the affected integration path fails with a validation error (expected nonoptional, received undefined) when projectId and remote are omitted from the call — even though the server's own tools/list response marks both fields as optional (not present in any required array) with "type": ["string", "null"], "default": null.

A direct stdio probe against the deployed server binary (bypassing that integration path entirely) returned the correct, fully-typed schema. Comparing that raw schema against what the affected path exposes to the calling model (via its ToolSearch tool) shows several fields of the JSON Schema are missing in the exposed copy. This suggests information loss somewhere between the server's tools/list response and the schema used for validation — the exact component responsible has not been confirmed.

Environment

  • Client identity: Unconfirmed precisely. The server is configured in claude_desktop_config.json, which is read by the Claude desktop app, and this session was described to the model as running "inside the Claude desktop app (Code tab)." The 2.1.268 version below was obtained by running claude --version from this session's Bash tool — that reports the Claude Code CLI binary's version, which is not verified to be the same binary/build actually acting as the MCP client for this desktop session (the desktop app could bundle its own Claude Code build separately from any CLI install on PATH). Anyone filing this should confirm the exact client build from the desktop app's own version/about screen rather than relying on the CLI version below.
  • Claude Code CLI version (via claude --version, provenance caveat above): 2.1.268
  • MCP server: aicontextmcp
  • Server binary: D:\Astra\mcp\runtime\AIContextMCP\AIContextMCP.Server.exe
  • Server FileVersion: 1.0.0.0
  • Server ProductVersion: 1.0.0+381f7cc04ab40633102d3f39ba27c3d9bf986706
  • Launch config (claude_desktop_config.json):

``json
"aicontextmcp": {
"command": "D:\\Astra\\mcp\\runtime\\AIContextMCP\\AIContextMCP.Server.exe",
"args": [],
"env": {
"AIContextMCP_DatabasePath": "D:\\Astra\\mcp\\data\\AIContextMCP\\AIContextMCP.db",
"AIContextMCP_ArtifactRoot": "D:\\Astra\\mcp\\data\\AIContextMCP\\artifacts",
"AIContextMCP_ApprovedRepositoryRoots__0": "D:\\"
}
}
``

  • Transport: stdio, newline-delimited JSON-RPC (confirmed via direct probe — NOT Content-Length/LSP framing)

Schema comparison

Raw schema, project.bootstrap.inputSchema, from a direct stdio tools/list call against the server binary:

{
  "type": "object",
  "properties": {
    "requestId": {
      "type": ["string", "null"],
      "default": null,
      "description": "Nonempty replay ID; required when register=true."
    },
    "projectId": { "type": ["string", "null"], "default": null },
    "repositoryPath": {
      "type": ["string", "null"],
      "default": null,
      "description": "Repository path to inspect or register."
    },
    "remote": { "type": ["string", "null"], "default": null },
    "includeWorkingTree": {
      "type": "boolean",
      "default": true,
      "description": "Include current working-tree state; defaults to true."
    },
    "register": {
      "type": "boolean",
      "default": false,
      "description": "Register the path; defaults to false."
    }
  },
  "additionalProperties": false,
  "allOf": [
    {
      "if": {
        "required": ["register"],
        "properties": { "register": { "const": true } }
      },
      "then": {
        "required": ["requestId"],
        "properties": { "requestId": { "type": "string", "minLength": 1 } }
      }
    }
  ]
}

Note: projectId and remote are optional here specifically because they never appear in any required array (including the nested one inside allOf) — not merely because they carry a default. The schema's only required entries are conditional: register (to evaluate the if) and requestId (only then, when register === true).

Schema Claude Code exposes for the same tool (via ToolSearch, then confirmed by the live -32602 validation error):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "includeWorkingTree": { "default": true, "type": "boolean" },
    "projectId": { "default": null },
    "register": { "default": false, "type": "boolean" },
    "remote": { "default": null },
    "repositoryPath": { "default": null },
    "requestId": { "default": null }
  }
}

Fields present in the raw schema but absent from the exposed version:

  • "type": ["string", "null"] on requestId, projectId, repositoryPath, remote — a type constraint, not just documentation
  • "additionalProperties": false — a structural constraint on the whole object
  • The entire allOf conditional block (the conditional-requirement logic: requestId required and non-empty only when register: true)
  • description text on all four fields that had one in the raw schema: requestId, repositoryPath, includeWorkingTree, and register (projectId and remote never had a description in the raw schema to begin with)

Together, type, additionalProperties, and allOf are all constraints that shape what input is valid — not just the allOf block alone.

Reproduction

Call (omitting projectId and remote, which the raw schema allows):

requestId must be <current UTC epoch seconds>:<lowercase UUID> (e.g. 1789598061:52a8e167-7cd2-4302-a29a-a7efcd80a01d) — this stricter format is enforced by the server's storage layer and is not documented anywhere in the schema's description for requestId (which only says "Nonempty replay ID"). Generate the two parts separately (e.g. date +%s for epoch seconds, [guid]::NewGuid() for the UUID, lowercased) and join with :.

project_bootstrap({
  repositoryPath: "D:\\TheCertMaster",
  includeWorkingTree: true,
  register: true,
  requestId: "<current UTC epoch seconds>:<lowercase UUID>"
})

Result — rejected in the affected integration path; the responsible component is unconfirmed:

MCP error -32602: Input validation error: Invalid arguments for tool project.bootstrap: [
  {
    "code": "invalid_type",
    "expected": "nonoptional",
    "path": ["projectId"],
    "message": "Invalid input: expected nonoptional, received undefined"
  },
  {
    "code": "invalid_type",
    "expected": "nonoptional",
    "path": ["remote"],
    "message": "Invalid input: expected nonoptional, received undefined"
  }
]

This error's shape ("expected nonoptional, received undefined") does not correspond to anything in the raw JSON Schema shown above — there is no required entry for projectId or remote in the server's own schema. The wording is consistent with a Zod-style validator being reconstructed somewhere in the affected path from the (type-stripped) schema, which appears to treat "no type declared" as "key must be present" rather than as JSON Schema's actual meaning (unconstrained/any, including absence). Which component performs this reconstruction is not confirmed.

Workaround attempted (in the affected integration path): supplying projectId/remote with a value instead of omitting them. Empty string and the literal text null both pass the omission check described above, but then fail server-side (InvalidInput, "project id is invalid." / "remote is invalid.") — because neither is the JSON null the server actually wants, and no mechanism was found in this path for transmitting a literal JSON null for an untyped scalar parameter. No successful registration call has been made through this path as of this report — see the next section for a successful registration via a different path.

Independent verification

A direct stdio probe was run against the same deployed server binary (AIContextMCP.Server.exe), using an isolated temporary database, bypassing Claude Code's tool-calling layer entirely:

  • Confirmed transport: newline-delimited JSON-RPC (a prior attempt using Content-Length/LSP framing was rejected by the server with per-message -32600 Invalid JSON-RPC request errors)
  • Captured the full raw tools/list response, saved at the time to C:\Users\mez\AppData\Local\Temp\aicontext-schema-2c9dc8e6bd194db99f98b76d87f41d11\tools-list.json
  • This raw response is the source for the "raw schema" section above

No production data or configuration was changed by this probe (isolated temp database).

Additional evidence: successful registration via direct probe, with projectId/remote omitted or null

Separately from the schema capture above, a direct-probe test (also against the isolated temporary database, not the production AIContextMCP.db) was reported to have successfully exercised project.bootstrap with register: true and projectId/remote either omitted or sent as JSON null, where the correctly-formatted-path integration attempt above could not. This is included as evidence that the server itself accepts the omission the raw schema describes, and that the failure reproduced above is specific to the affected integration path rather than to the server's actual contract.

This specific test's raw request/response transcript was not captured into this report — only the outcome was relayed. Anyone filing this bug should reproduce and attach that transcript directly (same isolated-temp-database method as the tools/list capture above) rather than relying on this secondhand description.

Open questions / not yet established

  • Which component strips the schema fields? Not confirmed. Candidates include the MCP client/transport layer Claude Code uses, Claude Code's own tool-indexing/ToolSearch layer, or an intermediate cache — no component has been isolated as the specific cause.
  • Whether an alternate schema shape on the AIContextMCP side would avoid the loss (e.g., a differently structured nullable-field representation) is untested. This is a possible mitigation, not a confirmed fix, and doesn't address the underlying transformation issue if it turns out to be lossy for other schema shapes too.

Suggested filing

File against Claude Code: https://github.com/anthropics/claude-code/issues, including this report's schema diff, the reproduction steps, and the open question about which layer owns the transformation.

View original on GitHub ↗