5 ms·
Show HN: Mcp-error-formatter – Cursor-style JSON errors for any MCP/LLM tool
Hi HN! Author here.
When an LLM-powered tool call blows up, the model usually sees an opaque stack trace. It can’t tell whether to retry, give up, or suggest a fix, so users get “Something went wrong” or infinite retry loops.
Cursor’s editor solved this by wrapping errors in a small, structured JSON envelope:
```
Request ID: c90e…
{"error":"ERROR_USER_ABORTED_REQUEST",
"details":{"title":"User aborted request.",
"isRetryable":false},
"isExpected":true}
```
That single structure lets the agent reason correctly (“don’t retry; the user cancelled”).
I wanted the same behavior in standalone MCP servers and LangChain tools, so I extracted it into a tiny package:
```
npm i @bjoaquinc/mcp-error-formatter
```
```ts
import { formatMCPError } from "@bjoaquinc/mcp-error-formatter";
export async function githubTool(args) {
try {
const data = await github.repos.get(args.repo);
return { content: [{ type: "text", text: JSON.stringify(data) }] };
} catch (err) {
return formatMCPError(err, {
title: "GitHub API failed",
isRetryable: true, // optional flags
});
}
}
```
* Supports both structured and unstructured content
* Zero deps (aside from `uuid`), \~3 kB min+gzip
* Adds `isRetryable`, `isExpected`, `errorType`, `requestId`, and free-form `additionalInfo`
* Returns a standard `CallToolResult`, so it slots into marimo, LangChain, FastMCP, or plain MCP SDK
* Apache-2.0 (OSS)
Repo: [https://github.com/bjoaquinc/mcp-error-formatter https://github.com/bjoaquinc/mcp-error-formatter](https://github.com/bjoaquinc/mcp-error-formatter https://github.com/bjoaquinc/mcp-error-formatter)
I’d love feedback on the format, naming, or edge-cases I’ve missed. PRs and issues welcome—happy to iterate.
- farkin88 1y agoI really like the tiny-lib approach. One thing that would make it even more useful in larger systems: OpenTelemetry hooks is if each call to formatMCPError() could optionally: start/finish a span (or attach events to the caller’s span), propagate requestId as the span ID / traceparent, and export errorType, isRetryable, etc. as span attributes. That way every tool failure would show up in our OTEL dashboards with perfect cross-service correlation. We could drill from an agent retry loop straight to the failing GitHub API call (or user-cancel event) in one click. I think OTEL-JS adds a few kB (need to double check that) and can be opt-in via a withTracing: true flag, so the “tiny by default” goal stays intact. What do you think?
- MutedEstate45 1y agoHi thanks for the suggestion! I think thats a great idea! I'll look into how to implement this