Remora
Remora is my config-driven MCP server — a single Go binary that turns a YAML file into a set of tools Claude can call, with no Go written per integration.
Every service I add is one I eventually want Claude to use, and a bespoke MCP server each time is the same boilerplate: register a tool, validate arguments, translate them into a request, call the backend. Remora lets me describe that mapping once — a tool’s name, its inputs, and which endpoint answers it — and dispatch to REST, GraphQL, or ConnectRPC backends. It serves over stdio, Streamable HTTP, or SSE, applies per-tool auth from the environment, and ships a validate subcommand so a bad config fails in CI rather than at runtime.
Remora is my config-driven MCP (Model Context Protocol) server — a single Go binary that turns a YAML file into a set of tools Claude can call, without writing a line of Go for each new integration.
Why I Built It
Every self-hosted service I add to my platform is a service I eventually want Claude to be able to use — search my index, look up a conversation, kick off a job. Writing a bespoke MCP server per service means the same boilerplate every time: register a tool, validate its arguments, translate them into a request, call the backend, shovel the response back. Remora exists so I describe that mapping once, in YAML — a tool’s name, its inputs, and which backend endpoint answers it — and get an MCP tool for free. Add a new backend method to expose to Claude, and it’s a few lines of config, not a new Go package.
The name fits: a remora attaches itself to something bigger and rides along, picking up whatever surfaces. That’s what this does with the rest of my platform — it doesn’t own any data itself, it just exposes what’s already there.
What It Does
- Reads a YAML config file and registers each entry under
tools:as an MCP tool, with typed, optionally-required input parameters. - Supports an
includedirective so tool definitions can be split across files (e.g.tools/shrike.yaml,tools/grey-seal.yaml) and merged at load time instead of living in one giant config. - Serves over stdio (the default — spawned as a subprocess by Claude Desktop or Claude Code), Streamable HTTP (
transport: http, for an HTTP-reachable deployment — the current MCP remote transport), or SSE (transport: sse, legacy), selected viatransportin the config. - Dispatches each tool call to a
rest,graphql, orgrpcbackend declared per tool. “grpc” backends are actually plain JSON over HTTP to a ConnectRPC service (Content-Type: application/json,Connect-Protocol-Version: 1) — no generated stubs or.protofiles needed on Remora’s side. - Maps MCP tool arguments into the backend request via
request_mapping: each field is either a literal or a$.input.<field>expression evaluated withgjson. - Applies per-tool auth — bearer token, API key header, or basic auth — sourced from an environment variable at call time.
- Ships a
remora validatesubcommand that checks a config file (duplicate tool names, missing descriptions, missing required backend fields, unknown transport/backend types) without starting the server, so a bad config fails fast in CI rather than at runtime.
Tech Stack
- Language: Go
- MCP protocol:
mark3labs/mcp-go - Config: YAML (
gopkg.in/yaml.v3) - Request mapping:
tidwall/gjson - CLI:
spf13/cobra
For a deeper look at how the pieces fit together, see Architecture.