tools-call-simple-text reports SUCCESS against a server that has no test_simple_text tool. The server's "not found" error result is accepted as "returns simple text content".
Reproduced on 0.1.16 and 0.2.0-alpha.11.
Server: a minimal Streamable HTTP server on the official TypeScript SDK with one tool, get_forecast (full source below).
npx @modelcontextprotocol/conformance server --url http://127.0.0.1:3932/mcp --scenario tools-call-simple-text
# Passed: 1/1 (0.1.16), Passed: 2/2 (0.2.0-alpha.11)
checks.json records the evidence for the pass:
{"id":"tools-call-simple-text","status":"SUCCESS",
"details":{"result":{"content":[{"type":"text","text":"MCP error -32602: Tool test_simple_text not found"}],"isError":true}}}
Expected: FAILURE (or not-applicable) when the result has isError: true, and ideally a check that the text is the fixture's expected text. As it stands, any server that answers an unknown tool with an isError text result passes. That is every server built on the TypeScript SDK.
This looks like the same class of bug as #505, where a scenario accepts tool-error results in place of the success it is meant to verify. The other tools-call-* scenarios may be worth auditing for the same thing.
minimal.mjs
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { z } from "zod";
function build() {
const s = new McpServer({ name: "weather", version: "1.0.0" });
s.tool("get_forecast", "Get the forecast for a city.", { city: z.string() },
{ readOnlyHint: true }, async ({ city }) => ({ content: [{ type: "text", text: `Sunny in ${city}` }] }));
return s;
}
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const server = build();
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
res.on("close", () => { transport.close(); server.close(); });
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.get("/mcp", (req, res) => res.status(405).end());
app.listen(Number(process.env.PORT), "127.0.0.1");
tools-call-simple-textreports SUCCESS against a server that has notest_simple_texttool. The server's "not found" error result is accepted as "returns simple text content".Reproduced on 0.1.16 and 0.2.0-alpha.11.
Server: a minimal Streamable HTTP server on the official TypeScript SDK with one tool,
get_forecast(full source below).npx @modelcontextprotocol/conformance server --url http://127.0.0.1:3932/mcp --scenario tools-call-simple-text # Passed: 1/1 (0.1.16), Passed: 2/2 (0.2.0-alpha.11)checks.jsonrecords the evidence for the pass:{"id":"tools-call-simple-text","status":"SUCCESS", "details":{"result":{"content":[{"type":"text","text":"MCP error -32602: Tool test_simple_text not found"}],"isError":true}}}Expected: FAILURE (or not-applicable) when the result has
isError: true, and ideally a check that the text is the fixture's expected text. As it stands, any server that answers an unknown tool with anisErrortext result passes. That is every server built on the TypeScript SDK.This looks like the same class of bug as #505, where a scenario accepts tool-error results in place of the success it is meant to verify. The other
tools-call-*scenarios may be worth auditing for the same thing.minimal.mjs