@rendobar/mcp
April 30, 2026 · View on GitHub
Local stdio MCP server. Single ESM bundle. ~6 tools. Public, MIT.
CRITICAL RULES
1. NEVER write to stdout
stdout is reserved for JSON-RPC framing. Any byte kills the connection.
console.log,process.stdout.write,print— banned.- Pipe subprocess stdout to stderr or
/dev/null. - The boot-time global
console.*patch redirects to stderr (seesrc/logger.ts). Don't disable it.
--help and --version are the ONLY commands that write to stdout. Both exit before the MCP transport is wired.
2. NEVER silence Zod-version overload errors with as any
@modelcontextprotocol/sdk 1.x uses zod/v4 internally. Two Zod copies break registerTool overloads.
Fix: pnpm why zod shows one version. pnpm.overrides.zod pinned. Don't reach for as any.
3. Use registerTool, not .tool() shorthand
The legacy shorthand is removed in v2-alpha of the SDK. registerTool is the only one in v2.
4. Tool errors via isError: true, not throws
Recoverable failures (bad credits, rate limit, not found) → { isError: true, content: [...] }.
Programmer bugs → throw, surface in stderr as -32603 InternalError.
The withErrorMapping wrapper in src/errors.ts handles this. Use it for every tool.
5. Cold-start budget < 2 s
CI asserts time node dist/bin.js --version < 2000ms. No network calls at module load.
Lazy SDK init in tools, not at boot.
6. Bundle budget < 100 KB
CI asserts dist/bin.js < 100KB. If pino blows the budget, swap to the 30-line custom logger.
Build & test
pnpm install
pnpm typecheck
pnpm lint
pnpm test
pnpm build
pnpm inspector # interactive MCP testing
Layout
src/
├── bin.ts # entry: parse args, init logger + sdk, connect StdioServerTransport
├── server.ts # createRendobarMcpServer factory
├── config.ts # parseConfig: --flag → env → creds.json → fail
├── context.ts # RendobarContext type
├── logger.ts # JSON to stderr
├── errors.ts # withErrorMapping using ApiError from @rendobar/sdk
├── paths.ts # resolveSafe (realpath + roots check)
├── instructions.ts # SERVER_INSTRUCTIONS string
├── version.ts # built-time injected
└── tools/
├── index.ts # registerTools(server, ctx)
├── util.ts # defineTool helper
├── uploads.ts # upload_file
├── jobs.ts # submit_job, get_job, list_jobs, cancel_job
└── account.ts # get_account
Auth chain
--api-key flag → RENDOBAR_API_KEY env → ~/.config/rendobar/credentials.json (CLI-written) → fail.
Conventions
- Conventional commits — release-please reads them.
- TDD — test before implementation for new tools.
- One tool file per resource. Tools are pure objects via
defineTool(). - Reshape SDK responses to drop fields the LLM doesn't read. Token cost is real cost.
- Honest annotations — destructive tools must declare
destructiveHint: true. - Trust Zod-validated inputs. Don't double-validate inside handlers.
- Errors flow through
withErrorMapping. Nevertry/catchand swallow.
Don't
- Don't reach for
pinoif it blows the bundle budget. Use the 30-line custom logger. - Don't add
--debug-stdio,--toolsets,--read-onlyflags. Those are deferred until customers ask. - Don't add OAuth flow. The CLI handles auth (
rb loginwrites the creds file). - Don't bundle MCPB (
.mcpb) in v1. Deferred to v1.1. - Don't open PRs without running
pnpm typecheck && pnpm test && pnpm buildclean locally.