first-mcp-kit
by 0xJRP
README.md
# first-mcp-kit — Build Your First MCP
A real, runnable **MCP server** you run on your own machine. It exposes three
read-only tools — `search_inventory`, `read_pricelist`, `get_hours` — over a
sample data file you can swap for your own. Then a tiny CLI agent lets **Claude
discover those tools and fire them**, so you watch your own tool run because an
AI decided to call it.
This is the capstone of the [Build an MCP](https://rabbithole.consulting/learn/build-an-mcp)
lesson. Lesson 1 taught you what an MCP is (AI ⇢ MCP ⇢ your data). This kit has
you build one.
> **What's an MCP, in one line?** The Model Context Protocol is a standard way to
> hand an AI a set of *tools* it can call — like giving it a few buttons wired to
> your data. The AI reads each tool's description, decides which to press, and you
> stay in control of what those buttons actually do.
---
## Quickstart
```bash
git clone https://github.com/0xJRP/first-mcp-kit
cd first-mcp-kit
npm install
npm run setup # writes .env (asks for an optional Claude API key)
npm run dryrun # starts the server, fires every tool, sends NOTHING
```
The dry-run needs **no API key** — it never calls Claude. It just proves the
whole pipeline works on your machine: it starts the real server, connects a real
MCP client, lists the tools, calls each one, and shows you the exact
request/response shape.
To watch **Claude** drive the tools:
```bash
npm start # run the MCP server (leave it running)
# in a second terminal:
npm run agent # Claude answers a sample question using your tools
npm run agent -- "Do you sell painter's tape and is it in stock?"
```
---
## What it does
| Tool | What it answers | Example |
|------|-----------------|---------|
| `search_inventory` | "Do you have X? How many?" | `search_inventory({ query: "paint", inStockOnly: true })` |
| `read_pricelist` | "What does X cost?" | `read_pricelist({ category: "electrical" })` |
| `get_hours` | "When are you open?" | `get_hours({ day: "saturday" })` |
All three read from one JSON file. Out of the box that's a fictional shop
(`data/shop.sample.json`). **It's yours — edit it, or point `DATA_FILE` at your
own file** (same shape) to make the tools answer questions about your real
business. Nothing else changes.
### Make it about your business
1. Copy `data/shop.sample.json` to `data/my-shop.json`.
2. Replace the contents with your hours, inventory, and prices.
3. Run `npm run setup` again and give it the new path (or set `DATA_FILE` in `.env`).
4. `npm run dryrun` — same tools, now answering about *your* shop.
---
## The safety model — read-only / dry-run by default
This kit follows the rule that runs through everything we build: **anything that
could write, send, book, or charge shows a plan first and waits for a human to
say yes.** Here that rule is easy to keep, because every shipped tool is
read-only — the worst a read-only tool can do is read a stale price.
- **Everything is read-only.** No tool here writes, sends, or charges. Ever.
- **It runs entirely on your machine.** The server listens on `127.0.0.1` only.
No data leaves your computer. (The agent demo sends your *question* and the
*tool results* to Claude so it can answer — that's the only thing that talks
to the internet, and only when you run `npm run agent`.)
- **The dry-run sends nothing at all.** `npm run dryrun` exercises the full
pipeline with no API key and no network calls beyond localhost.
### Adding a write tool later — the confirm-before-send pattern
When you're ready to add a tool that *changes* something (places an order, sends
an email, books a slot), the kit shows you how to do it **safely** without
shipping a foot-gun. Open [`src/server.js`](src/server.js) and read the big
**"ADDING A WRITE TOOL SAFELY"** comment near the bottom. The one rule:
> A write tool **drafts** the action and returns it for review. It never performs
> the action itself. A human approves the plan through a separate, deliberate
> step that lives **outside the AI's reach**.
So `draft_order` returns *"here's the order I'd place: 3× PVC pipe, $14.37 total
— approve?"* and stops. Nothing is charged until a person clicks approve in your
own app. That commented example is **not enabled** — you copy it in deliberately,
on purpose, when you understand it.
The five questions from Lesson 1's safety panel, applied to any write tool:
1. **What can it touch?** Scope each tool to one narrow action.
2. **Can it spend money?** If yes — it drafts only; a human approves.
3. **Can it be undone?** Prefer reversible actions; log everything.
4. **Who sees the result?** Show the plan *before* it happens, not after.
5. **What if it's wrong?** A draft that's wrong costs nothing. Ship drafts.
---
## How it's wired (the Claude side)
The demo agent uses the official Anthropic SDK (`@anthropic-ai/sdk`) and defaults
to the model `claude-opus-4-8`. You can override it with `ANTHROPIC_MODEL` in
`.env` — `claude-haiku-4-5` is a cheaper, faster option for high-volume use.
The flow in [`src/agent.js`](src/agent.js): connect to your MCP server → ask it
for its tools → hand that tool list to Claude → send a question → when Claude
asks to call a tool, run it against the MCP server (read-only) and feed the
result back → Claude answers in plain English.
### Connecting Claude Desktop instead
Prefer to use the tool from the Claude desktop app rather than the CLI agent?
Run `npm start`, then add this MCP server to your Claude config. The endpoint is
`http://127.0.0.1:8765/mcp` (Streamable HTTP transport). Claude will list the
three tools and call them when your questions call for it — same server, same
read-only tools.
---
## Project layout
```
first-mcp-kit/
├── package.json # scripts: setup, dryrun, start/dev, agent
├── .env.example # copy to .env, or run `npm run setup`
├── LICENSE # MIT
├── data/
│ └── shop.sample.json # sample data — yours to edit or replace
└── src/
├── tools.js # the read-only tool logic (one place)
├── server.js # the real HTTP MCP server (+ how to add a write tool safely)
├── agent.js # tiny CLI agent: Claude discovers & fires the tools
├── setup.js # interactive wizard → writes .env
├── dryrun.js # full pipeline, sends nothing
└── env.js # tiny dependency-free .env loader
```
## Requirements
- Node.js 18 or newer.
- An Anthropic API key **only** for the `npm run agent` demo. Setup and dry-run
don't need one.
---
## You built this. Want the production version?
You now understand MCP better than most developers — you wrote a real server,
exposed real tools, and watched Claude call them. That's the whole idea: the kit
is genuinely yours, free and MIT-licensed, no neutered features.
Going from this toy MCP to one that's **secure, audited, and wired to your real
systems** — your inventory, your CRM, your bookings, behind auth and rate limits,
with write tools properly gated and logged across your whole stack — is real work.
That's the gap we close for clients.
**See what Rabbithole builds → [rabbithole.consulting](https://rabbithole.consulting)**
---
MIT © 2026 Rabbithole Consulting
This server cannot be deployed
Maintenance
ActivityInactive
ResponsivenessNo issues