Summary
For STDIO MCP servers, MetaMCP logs every line a child process writes to stderr at level error. Per the MCP specification, stderr is the designated channel for all logging from a stdio server (not just errors), so well-behaved servers routinely emit info/debug lines there. The result is a flood of false [ERROR] log entries for perfectly healthy servers.
What the spec says
From the MCP specification (2025-11-25), Transports → stdio:
Servers may use standard error for logging purposes, which clients may choose to capture or ignore.
And the changelog for that revision clarifies this exact point under "Minor changes":
Servers using stdio transport are permitted to use stderr for all types of logging, not exclusively for error messages.
This is by design: for a stdio server, stdout is reserved exclusively for newline-delimited JSON-RPC messages (a stray log line there corrupts the stream), so stderr is the only place a server can log — at any level. A server emitting INFO ... on stderr is therefore spec-compliant, and a client should not assume those lines are errors.
Current behavior
apps/backend/src/lib/metamcp/client.ts, the stderr data handler (lines ~67–73) hardcodes the level to error:
stderrStream?.on("data", (chunk: Buffer) => {
metamcpLogStore.addLog(
serverParams.name,
"error", // <-- every stderr line tagged as error
chunk.toString().trim(),
);
});
stderr is an unstructured byte stream with no severity attached, so tagging all of it error misrepresents the severity the spec deliberately leaves open.
Reproduction
- Configure any stdio MCP server that logs at info level to stderr (e.g. a Python server using the standard SDK /
logging, such as uvx some-mcp-server).
- Watch MetaMCP's logs.
- Observe routine info lines surfaced as
[ERROR], e.g.:
[ERROR] ... [MetaMCP][some-mcp] INFO Processing request of type ...
These are healthy request traces, not errors.
Suggested fix
Log captured stderr data at a non-error level (info is the minimum correct choice; MetaMcpLogEntry.level is "error" | "info" | "warn"):
stderrStream?.on("data", (chunk: Buffer) => {
metamcpLogStore.addLog(serverParams.name, "info", chunk.toString().trim());
});
Note: the adjacent stream error event handler (lines ~75–82) should stay at error — that's a genuine failure of the stderr stream itself, not server log output.
(A fancier version could parse common level prefixes like INFO/WARN/ERROR from the line, but defaulting captured stderr to info/neutral is the spec-correct baseline.)
🤖 Filed with Claude Code
Summary
For STDIO MCP servers, MetaMCP logs every line a child process writes to
stderrat levelerror. Per the MCP specification,stderris the designated channel for all logging from a stdio server (not just errors), so well-behaved servers routinely emitinfo/debuglines there. The result is a flood of false[ERROR]log entries for perfectly healthy servers.What the spec says
From the MCP specification (2025-11-25), Transports → stdio:
And the changelog for that revision clarifies this exact point under "Minor changes":
This is by design: for a stdio server,
stdoutis reserved exclusively for newline-delimited JSON-RPC messages (a stray log line there corrupts the stream), sostderris the only place a server can log — at any level. A server emittingINFO ...on stderr is therefore spec-compliant, and a client should not assume those lines are errors.Current behavior
apps/backend/src/lib/metamcp/client.ts, the stderrdatahandler (lines ~67–73) hardcodes the level toerror:stderris an unstructured byte stream with no severity attached, so tagging all of iterrormisrepresents the severity the spec deliberately leaves open.Reproduction
logging, such asuvx some-mcp-server).[ERROR], e.g.:Suggested fix
Log captured stderr
dataat a non-error level (infois the minimum correct choice;MetaMcpLogEntry.levelis"error" | "info" | "warn"):Note: the adjacent stream
errorevent handler (lines ~75–82) should stay aterror— that's a genuine failure of the stderr stream itself, not server log output.(A fancier version could parse common level prefixes like
INFO/WARN/ERRORfrom the line, but defaulting captured stderr toinfo/neutral is the spec-correct baseline.)🤖 Filed with Claude Code