How I Turned a REST API into an MCP Server

RDRajesh Dhiman
5 min read

🛠️ How I Turned a REST API into an MCP Server (And Why You Should Too)

🍔 Introduction: Turning a Fast Food Joint into Fine Dining

Let’s say you walk into a fast food restaurant. You shout, “One burger!” and they toss it to you in under 30 seconds. Quick, dirty, and done. That’s your typical REST API: fast, unstructured, and a little unpredictable.

But what if you wanted to open a high-end place — where every dish has a printed menu, the ingredients are verified, and the chef follows a consistent recipe? That’s what Modular Contract Protocols (MCPs) are like. They don’t just serve data; they serve it with structure, clarity, and a smile.

In this post, I’ll show you exactly how I wrapped the public Hacker News API in an MCP — turning a chaotic data stream into a clean, LLM-friendly microservice.


🧠 Wait... What’s an MCP Again?

Let me break it down:

MCP stands for Modular Contract Protocol. It’s a way of writing API logic where every endpoint:

  • Has a clear input and output contract
  • Is modular and self-contained
  • Can be called by humans, machines, or LLMs
  • Plays nicely in a function-calling world (like OpenAI or Claude)

In simple terms: it's like giving every API endpoint its own resume. It knows who it is, what it accepts, and what it gives back.


🔥 Why Wrap an Existing API?

I wanted to showcase how powerful MCPs are by not building everything from scratch. Instead, I took the Hacker News API — a basic REST interface with zero documentation, no validation, and no structure — and made it:

  • 🌱 Type-safe with Zod
  • 🤖 Callable by LLM agents
  • 📜 OpenAPI-exportable via zod-openapi
  • 🌍 Deployable & reusable

You get all the benefits of modern, clean backend architecture — without rebuilding the wheel.


🚧 Step 1: Picking the API

I went with Hacker News API. Why?

  • It’s public and requires no authentication
  • It returns JSON (yay!)
  • It’s a little raw — making it perfect for a glow-up

Hacker News gives you endpoints like:

  • /topstories.json → returns an array of story IDs
  • /item/{id}.json → returns the details for a story or comment

But there’s no validation. No input schema. No docs. Just vibes.


🧱 Step 2: MCP Project Structure

I set up the project with this simple folder structure:

src/
├── contracts/                // Zod + OpenAPI metadata
├── resolvers/                // Business logic
├── handlers/                 // Express routes
├── utils/                    // Hacker News client
├── docs/openapi.ts           // OpenAPI spec generator (zod-openapi)
├── setup/zod-openapi-init.ts // Shared zod setup with OpenAPI support
└── server.ts                 // Main entry point 

Think of it like building with LEGO blocks — every piece does one thing, and snaps into place without duct tape.


🧪 Step 3: Creating the First Contract

Let’s start with the endpoint to list top stories.

✅ Zod Contract

export const listTopStoriesOutput = z.array(z.number()).openapi({
  description: "Array of Hacker News story IDs",
}); 

It’s like saying: “Hey, this endpoint gives back an array of numbers. No more, no less.”


⚙️ Step 4: Writing the Resolver

The resolver is the actual brain. It connects to Hacker News, fetches the data, and validates it.

import { fetchTopStoryIds } from "../utils/hnClient";
import { listTopStoriesOutput } from "../contracts/listTopStories.contract";

export const listTopStoriesHandler = async (req, res) => {
  try {
    const storyIds = await fetchTopStoryIds();
    res.json(listTopStoriesOutput.parse(storyIds));
  } catch (e) {
    res.status(500).json({ error: "Something went wrong!" });
  }
}; 

📖 Step 5: Adding the getStory Endpoint

This one lets you fetch details about any story by ID.

✅ Input Contract

export const getStoryInput = z
  .object({
    id: z
      .string()
      .regex(/^[0-9]+$/)
      .openapi({ description: "Story ID" }),
  })
  .openapi({ title: "GetStoryInput" }); 

✅ Output Contract

export const getStoryOutput = z
  .object({
    id: z.number().openapi({ description: "ID of the story" }),
    title: z.string().openapi({ description: "Title of the story" }),
    by: z.string().openapi({ description: "Author" }),
    score: z.number().openapi({ description: "Score or points of the story" }),
    url: z.string().optional().openapi({ description: "URL (if any)" }),
    time: z.number().openapi({ description: "Unix timestamp" }),
    type: z.string().openapi({ description: "Item type (story/comment)" }),
  })
  .openapi({ title: "GetStoryOutput" }); 

🚀 Step 6: Hosting + OpenAPI + LLM Ready

After writing the resolvers, I hosted the whole thing here:

🔗 https://mcp-news-server.onrender.com

Test it:

  • /api/listTopStories
  • /api/getStory/8863
  • /openapi.json

And yes, it works with LangChain, Claude, OpenAI, or any custom LLM runner!


🎁 What You Can Do Next

  • Wrap any REST API you love in an MCP
  • Add contracts, deploy, and share with LLMs
  • Use zod-openapi to create swagger-compatible specs
  • Register it in an agent-aware toolchain or build your own GPT plugin

💬 TL;DR

Modular Contract Protocols give your API structure and meaning. By wrapping a basic REST API like Hacker News with Zod contracts and generating OpenAPI with zod-openapi, you can:

  • Build more robust backend tools
  • Make them compatible with LLMs
  • Reduce guesswork and increase composability

Let’s stop building brittle REST services — and start building smart, structured, machine-readable APIs.

💡 Try it yourself — and let me know what you wrap next!

Free field guide

Ship AI features that survive real traffic

Start from the failure modes, choose the smallest architecture that holds up, and instrument the path before you scale it.

No spam, unsubscribe anytime. You will also get instant access on the next screen.

Share this article

Buy Me a Coffee
Support my work

If you found this article helpful, consider buying me a coffee to support more content like this.

Related Articles

AI Agent Skills vs Tools: The Distinction Every Developer Gets Wrong

Most developers build agents with tools. That's not enough. Here's the architectural difference between agent tools and agent skills — and why getting this wrong burns tokens, degrades accuracy, and makes your agents brittle in production.

Why You Keep Hitting Claude's Usage Limits (And 5 Token Tricks That Actually Fix It)

Claude doesn't count messages like ChatGPT — it counts tokens, and one conversation can burn 10x more than another. Here are 5 tricks that cut your token usage by 80% or more.

A2A vs MCP: Agent Chatter vs Contract Clarity

Explore the key differences between Agent-to-Agent (A2A) communication and Modular Contract Protocols (MCP) — and why you need both to build powerful AI systems.