anomalyco / anomalyco/opencode
MCP: "Failed to get tools" when a tool inputSchema contains a boolean sub-schema in properties
@neriousy is already working on this.
Since Sep 17, 2026.
- Dominant language
- TypeScript
- Stars
- 209k
- Forks
- 27.5k
- PR merge metrics
- PR metrics pending
Description
Summary
OpenCode marks an entire MCP server as failed with "Failed to get tools" when any tool's inputSchema contains a boolean sub-schema inside properties (e.g. "properties": { "raw_input": true }). Boolean sub-schemas are valid JSON Schema (2020-12 permits true/false where a schema is expected), and the same server works in clients that tolerate them. A single such tool takes down every tool on the server.
This is the same failure path as #21591 / #26529 (a schema-processing error in listTools() collapsing to "Failed to get tools"), but a different trigger: those were outputSchema $refs and were fixed by the tolerant retry keyed off isOutputSchemaValidationError(). Boolean inputSchema sub-schemas are not covered by that retry.
Related: #47644 (the underlying error is discarded), #48196 (silent failure), and the same class in the MCP Python SDK surface types — modelcontextprotocol/python-sdk#3353 / PR #3354.
Steps to reproduce
repro-mcp.mjs — dependency-free stdio MCP server with one offending tool:
let buffer = "";
const send = (message) => process.stdout.write(JSON.stringify(message) + "\n");
process.stdin.on("data", (chunk) => {
buffer += chunk;
let newline;
while ((newline = buffer.indexOf("\n")) >= 0) {
const line = buffer.slice(0, newline).trim();
buffer = buffer.slice(newline + 1);
if (!line) continue;
const message = JSON.parse(line);
if (message.method === "initialize") {
send({ jsonrpc: "2.0", id: message.id, result: {
protocolVersion: "2024-11-05",
capabilities: { tools: {} },
serverInfo: { name: "repro", version: "1.0.0" },
}});
} else if (message.method === "tools/list") {
send({ jsonrpc: "2.0", id: message.id, result: { tools: [{
name: "read_everything",
description: "Example tool.",
inputSchema: {
type: "object",
additionalProperties: false,
properties: {
reason: { type: "string" },
raw_input: true, // <-- boolean sub-schema
input: {
type: "object",
properties: { input: true }, // <-- boolean sub-schema (nested)
},
},
},
}]}});
} else if (message.id !== undefined) {
send({ jsonrpc: "2.0", id: message.id, result: {} });
}
}
});
opencode.json beside it:
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"repro": { "type": "local", "command": ["node", "repro-mcp.mjs"], "enabled": true }
}
}
Then:
opencode mcp list --print-logs --log-level DEBUG
Observed
- The server process starts, the
initializehandshake completes, andtools/listreturns a valid response over stdio (verifiable with any raw MCP client). - OpenCode closes the connection and reports the whole server as failed:
● ✗ repro failed
│ Failed to get tools
│ node repro-mcp.mjs
- No underlying schema error is surfaced, even with
--print-logs --log-level DEBUG— only the constant"Failed to get tools"(#47644).
Expected
One of:
- Boolean sub-schemas in
inputSchema/outputSchemapropertiesare accepted (they are valid JSON Schema 2020-12), so the tools load; or - A schema-processing failure on a single tool is skipped/downgraded and the remaining tools still load, with a visible warning; and
- The real underlying error is surfaced instead of the constant string.
Workaround
Because MCP tool definitions skip the tool.definition hook (#46628), a plugin cannot fix this. The only user-space workaround is to proxy the server and rewrite the tools/list response — rewriting boolean sub-schemas (true -> {}, false -> {"not": {}}) and dropping outputSchema. I verified this against a real server with 45 tools carrying "raw_input": true: after sanitizing, opencode mcp list reports connected and all 45 tools are preserved.
Environment
- OpenCode version: 1.18.31
- OS: macOS (darwin) x86_64
- MCP transport: stdio (local), server protocol
2024-11-05 - Also reproduced against a Go-based stdio MCP server; the same class is reported for third-party servers against Claude Code (anthropics/claude-code#25081) and the MCP Python SDK (modelcontextprotocol/python-sdk#3353).
Plugins
None — reproduced with the server alone; MCP tools do not pass through tool.definition.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.