wellspring is a single Go binary (wsp) that gives you a consistent interface to six public APIs — Hacker News, Reddit, Open-Meteo, CoinGecko, Alpha Vantage, and World Bank — with normalized JSON output and an MCP server mode for AI agents.
The project is open source at github.com/yihan2099/wellspring.
Why I Built It
The public-apis repository catalogs 1,400+ public APIs, but they are just documentation — not callable tools. Every API has its own auth scheme, rate limits, pagination, and response format. I wanted one command that could query any of them with consistent output, and I wanted AI agents to be able to use the same adapters via MCP without any additional integration work.
The Two-Tier Adapter System
The core design insight: not all APIs are equal. Some are simple REST+JSON, others need custom auth or pagination logic. wellspring handles both through a single Adapter interface implemented two ways.
Declarative YAML adapters handle straightforward APIs. A YAML file describes endpoints, parameters, and field mappings using dot-notation paths (e.g., .data[0].title). The declarative engine (~470 lines of Go) handles the HTTP lifecycle, response parsing, and data normalization. Hacker News, Open-Meteo, CoinGecko, and World Bank all use this approach. Adding a new source means dropping a YAML file — no recompilation needed.
Coded Go adapters handle complex APIs that need custom logic. Reddit requires a specific User-Agent header and has a unique listing response structure. Alpha Vantage needs API key management, supports multiple actions (quote, daily, search), and enforces a 5 req/min rate limit. Both are implemented in Go but produce the same DataPoint output as YAML adapters.
Every adapter — YAML or Go — outputs the same normalized struct: Source, Category, Time, Title, Value, URL, and a Meta map for source-specific extras. The CLI auto-detects terminal vs. pipe and switches between table and JSON output accordingly.
MCP Server
Running wsp serve exposes all adapters as MCP tools on stdio transport. Each adapter-endpoint combination becomes a tool (e.g., reddit_hot, alphavantage_quote). The tool schemas reflect source-specific parameters — Reddit gets subreddit and time, Open-Meteo gets latitude and longitude. This was designed from day one, not added later: the same Adapter interface serves both CLI and MCP with zero translation.
What I Learned
YAML-driven systems scale well for the 80% of APIs that are simple REST+JSON. The declarative engine is built once, then each new YAML file adds a source without touching Go code. The remaining 20% — APIs with auth quirks, rate limits, or unusual response formats — justify custom code. Trying to force everything into YAML would have made the YAML spec fragile. The two-tier split keeps both sides clean.
Per-adapter rate limiting turned out to be essential. Each source defines its own limit (30 req/min for HN, 5 for Alpha Vantage) using a sliding window with file-based cache. Without this, users hit API quotas within minutes.
The biggest architectural takeaway: build the core abstraction once (the Adapter interface), then adapt it to different output layers (CLI, MCP, potentially HTTP). The MCP server required minimal extra code because the data model was already normalized.