What we're seeing
We're using the hosted Sanity MCP at https://mcp.sanity.io from a Claude Code skill that drives content-team workflows. We've been hitting a recurring failure on the first Sanity:* tool call per session: the LLM serializes the resource parameter as a JSON-encoded string rather than an object literal, and the server's validator rejects it.
Wanted to share the observation and a thought on possible mitigations, in case it's useful.
This is probably more of a DX observation rather than a bug — the server's current behavior is correct per its documented schema. But we're seeing it often enough that we thought it was worth flagging.
What the failure looks like
A Claude-driven call to get_schema went out like this:
{
"workspaceName": "administration",
"resource": "{\"projectId\": \"5abcdefg\", \"dataset\": \"production\"}",
"type": "resource"
}
Note resource's value is a string literal, not an object. Server response:
MCP error -32602: Input validation error: Invalid arguments for tool get_schema: [
{
"code": "invalid_type",
"expected": "object",
"received": "string",
"path": ["resource"],
"message": "Expected object, received string"
}
]
We've seen the same thing anywhere resource: { projectId, dataset } is required. The params argument on query_documents hits the same pattern.
What we've tried
From our side, we've tried mitigating in the prompt:
- Explicit instructions stating the expected shape ("
resource is a JSON object, not a stringified JSON")
- A dedicated "common mistakes" section in the skill
- Preloading the tool schema via the Claude harness's
ToolSearch so validation fires on the way out
None of these fully eliminate it. Our read is that the stringification is coming from the LLM's internal serialization layer, not from anything the prompt can deterministically control.
A thought on server-side mitigation
One pattern our debugging suggests is a lenient preprocessor that accepts either shape and parses the string form before validation — something like:
const resourceSchema = z.preprocess(
(val) => {
if (typeof val === "string") {
try { return JSON.parse(val); } catch { return val; }
}
return val;
},
z.object({
projectId: z.string(),
dataset: z.string(),
}),
);
This preserves strict validation for well-behaved clients while being forgiving of the stringification glitch. Just a thought — completely understand there may be reasons not to go this direction.
Error messaging
The error message is technically correct but doesn't give the LLM much to self-correct against. Something like "Expected object, received string. If you intended a JSON object, check that it isn't wrapped in quotes." might shorten the retry loop even if the strict validation stays in place.
Impact
For context on why we noticed this: our users are non-technical clinical content authors, and they hit this failure on the first Sanity:* call in most sessions. Each retry loop costs ~30–60s and surfaces a validation error in the UI. Not a blocker — the skill recovers — but it's consistent enough that we wanted to flag it.
What we're seeing
We're using the hosted Sanity MCP at
https://mcp.sanity.iofrom a Claude Code skill that drives content-team workflows. We've been hitting a recurring failure on the firstSanity:*tool call per session: the LLM serializes theresourceparameter as a JSON-encoded string rather than an object literal, and the server's validator rejects it.Wanted to share the observation and a thought on possible mitigations, in case it's useful.
This is probably more of a DX observation rather than a bug — the server's current behavior is correct per its documented schema. But we're seeing it often enough that we thought it was worth flagging.
What the failure looks like
A Claude-driven call to
get_schemawent out like this:{ "workspaceName": "administration", "resource": "{\"projectId\": \"5abcdefg\", \"dataset\": \"production\"}", "type": "resource" }Note
resource's value is a string literal, not an object. Server response:We've seen the same thing anywhere
resource: { projectId, dataset }is required. Theparamsargument onquery_documentshits the same pattern.What we've tried
From our side, we've tried mitigating in the prompt:
resourceis a JSON object, not a stringified JSON")ToolSearchso validation fires on the way outNone of these fully eliminate it. Our read is that the stringification is coming from the LLM's internal serialization layer, not from anything the prompt can deterministically control.
A thought on server-side mitigation
One pattern our debugging suggests is a lenient preprocessor that accepts either shape and parses the string form before validation — something like:
This preserves strict validation for well-behaved clients while being forgiving of the stringification glitch. Just a thought — completely understand there may be reasons not to go this direction.
Error messaging
The error message is technically correct but doesn't give the LLM much to self-correct against. Something like
"Expected object, received string. If you intended a JSON object, check that it isn't wrapped in quotes."might shorten the retry loop even if the strict validation stays in place.Impact
For context on why we noticed this: our users are non-technical clinical content authors, and they hit this failure on the first
Sanity:*call in most sessions. Each retry loop costs ~30–60s and surfaces a validation error in the UI. Not a blocker — the skill recovers — but it's consistent enough that we wanted to flag it.