I Built My First MCP Server in 30 Minutes — Here's Exactly How
Overview
I Built My First MCP Server in 30 Minutes — Here's Exactly How I’d seen Model Context Protocol (MCP) pop up everywhere for weeks, but I assumed it was some fancy enterprise tool for teams building AI agents, not something I could spin up locally over a lunch b
Key Concepts
- • MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- • Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- • **"Server disconnected" error on launch**: 9 times out of 10, this is the relative path mistake I mentioned earlier. To confirm, check Claude’s logs at `~/Library/Logs/Claude` on Mac — you’ll see a "no such file or directory" error for your `index.js` if this is the problem. Swap in the absolute path, restart Claude, and it will work.
- • **"Cannot use import statement outside a module"**: This is the missing `"type": "module"` line in your `package.json`. Add that line and you’re done.
- • **Tool doesn’t show up in the hammer menu**: You didn’t fully restart Claude after changing the config. Quit completely and reopen. If that doesn’t work, validate your config JSON — I missed a comma once that broke the entire config, so a quick lint with an online JSON validator will catch that.
- • Modify the coffee calculator to fit your own habits. Change it to use ounces instead of milliliters, add your default ratio as the new default, or add an option to calculate for multiple cups — it’s 5 minutes of code change max.
I’d seen Model Context Protocol (MCP) pop up everywhere for weeks, but I assumed it was some fancy enterprise tool for teams building AI agents, not something I could spin up locally over a lunch break. I was wrong. Last week, I built my first working MCP server to fix a tiny but annoying problem I had with Claude, and it took me 32 minutes total — 10 of which were wasted on a dumb mistake I’ll help you avoid.
I don’t work for Anthropic, I’m just a regular developer who wanted to extend Claude with a custom tool, and I was surprised how straightforward it was once I cut through the vague hype. Here’s exactly what I did, step by step, with working code and all the errors I hit along the way.
Exact Local Setup (5 Minutes Max)
You only need two things to start: Node.js 18 or higher, and Claude Desktop installed on your local machine. Claude Desktop is free to download from Anthropic’s site, and works with any paid Claude plan.
First, confirm your Node version:
```bash
node -v
```
If you’re below version 18, head to nodejs.org and grab the LTS release — that’s all you need to update.
Next, create a new project folder and initialize it:
```bash
mkdir mcp-coffee-calculator
cd mcp-coffee-calculator
npm init -y
```
Open your new `package.json` and add `"type": "module"` right after the `name` field. I forgot this step on my first try and hit an immediate import error, so don’t skip it. Your `package.json` should look something like this after the change:
```json
{
"name": "mcp-coffee-calculator",
"type": "module",
"version": "1.0.0",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"description": ""
}
```
Install the only two dependencies you’ll need for this project:
```bash
npm install @modelcontextprotocol/sdk zod
```
That’s it for setup. You’re ready to build your server.
Build a Minimal MCP Server (5 Minutes)
MCP servers for Claude Desktop communicate over standard input/output (stdio), which means no ports to open, no networking config, nothing complicated. Your first minimal server just verifies everything connects correctly before you add a useful tool.
Create an `index.js` file and paste this runnable code:
```javascript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// Create a basic MCP server with tool support
const server = new Server({
name: "my-first-mcp",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Minimal MCP server running on stdio");
}
main().catch(err => console.error("Server error:", err));
```
This is the smallest working MCP server you can build. It doesn’t do anything yet, but it will connect to Claude Desktop just fine. Let’s add something useful next.
Add A Useful Tool (10 Minutes)
The problem I wanted to fix: I drink pour-over coffee every morning, and I kept asking Claude how much coffee to use for a given amount of water, and it almost always messed up the ratio. Half the time it inverted the ratio, giving me 4800 grams of coffee instead of 18. Other times it rounded so aggressively my coffee came out over-extracted and bitter.
I built a simple coffee calculator tool that gets the math right every time. Replace the code in `index.js` with this full working version:
```javascript
#!/usr/bin/env node
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
// Create MCP server with tool capability
const server = new Server({
name: "coffee-calculator",
version: "1.0.0"
}, {
capabilities: {
tools: {}
}
});
// Register the coffee ratio calculator tool
server.tool(
"calculate_coffee",
"Calculate coffee or water amount for a given brew ratio. Provide either waterMl OR coffeeGrams, not both.",
{
waterMl: z.number().optional().describe("Total volume of water you want to brew, in milliliters"),
coffeeGrams: z.number().optional().describe("Amount of coffee you have, in grams"),
ratio: z.number().default(16).describe("Brew ratio (1:ratio, e.g. 16 = 1 gram coffee : 16ml water)")
},
async ({ waterMl, coffeeGrams, ratio }) => {
if (waterMl === undefined && coffeeGrams === undefined) {
return {
content: [{ type: "text", text: "Error: You must provide either waterMl or coffeeGrams to calculate." }]
};
}
if (waterMl !== undefined && coffeeGrams !== undefined) {
return {
content: [{ type: "text", text: "Error: Provide only one of waterMl or coffeeGrams, I'll calculate the other." }]
};
}
let result;
if (waterMl) {
const coffee = waterMl / ratio;
result = `For ${waterMl}ml of water at a 1:${ratio} ratio, you need ${coffee.toFixed(1)} grams of coffee.`;
} else {
const water = coffeeGrams * ratio;
result = `For ${coffeeGrams}g of coffee at a 1:${ratio} ratio, you need ${water.toFixed(0)}ml of water.`;
}
return {
content: [{ type: "text", text: result }]
};
}
);
// Start the server
async function main() {
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("Coffee Calculator MCP running on stdio");
}
main().catch(err => console.error("Fatal error:", err));
```
That’s the entire server. No extra code, no APIs to sign up for, no keys to manage. It just runs locally and does one thing well.
Test It With Claude Desktop (5 Minutes)
Now you just need to tell Claude Desktop about your new server. First, find Claude’s config file:
- MacOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
If the file doesn’t exist yet, just create a new one. Add your server to the `mcpServers` object. The critical mistake I made here: use the full absolute path to your `index.js` file, not a relative path. Claude runs from its own application directory, so relative paths will almost always break.
My config looks like this (swap the path for your own absolute path):
```json
{
"mcpServers": {
"coffee-calculator": {
"command": "node",
"args": ["/Users/andrew/projects/mcp-coffee-calculator/index.js"]
}
}
}
```
If you already have other MCP servers configured, just add the `coffee-calculator` entry to your existing `mcpServers` object — don’t duplicate the top-level key.
Once you save the config, fully restart Claude Desktop. On Mac, just closing the window isn’t enough — right-click the Claude icon in your dock and select Quit, then reopen it. That’s another common mistake that took me a few minutes to figure out.
When Claude reopens, you’ll see a small hammer icon in the bottom right corner of the input box. Click it, and you should see your `calculate_coffee` tool listed. That means it worked! Test it by asking something like “I’m making pour over for two people, 300ml of water at 1:16 ratio, how much coffee do I need?” Claude will call your tool, and you’ll get the correct 18.8g answer every time.
Common Errors I Hit (And How I Fixed Them)
I ran into three issues while building this that were easy to fix once I knew what to look for:
- **"Server disconnected" error on launch**: 9 times out of 10, this is the relative path mistake I mentioned earlier. To confirm, check Claude’s logs at `~/Library/Logs/Claude` on Mac — you’ll see a "no such file or directory" error for your `index.js` if this is the problem. Swap in the absolute path, restart Claude, and it will work.
- **"Cannot use import statement outside a module"**: This is the missing `"type": "module"` line in your `package.json`. Add that line and you’re done.
- **Tool doesn’t show up in the hammer menu**: You didn’t fully restart Claude after changing the config. Quit completely and reopen. If that doesn’t work, validate your config JSON — I missed a comma once that broke the entire config, so a quick lint with an online JSON validator will catch that.
This local approach is perfect for personal tools: it’s free, no hosting required, all data stays on your machine, and it takes almost no time to build. The tradeoff is it only works on your local machine with Claude Desktop — if you want to share it or use it with remote clients, you’ll need to add HTTP transport and authentication, which adds more work. But for personal use, this setup can’t be beat for speed and simplicity.
Actionable Next Steps For The Next 24 Hours
Now that you’ve got a working MCP server, try these small steps to keep going:
- Modify the coffee calculator to fit your own habits. Change it to use ounces instead of milliliters, add your default ratio as the new default, or add an option to calculate for multiple cups — it’s 5 minutes of code change max.
- Build a second tiny tool that solves one small annoying problem you have with Claude. I built mine to pull my uncompleted tasks from my local `todo.txt` file after that, so Claude can prioritize my day without me pasting the whole file every time. Other easy ideas: a word counter for local text files, a tool that calculates tip for a restaurant bill, or a simple random number generator for D&D.
- Add a custom resource to your server, not just a tool. MCP supports more than tools — you can add resources that let Claude pull static context like your personal notes or recipe collection automatically. Spend an hour adding a resource that loads your favorite recipes so Claude never messes up the measurements again.
- Push your finished server to GitHub and share it. All someone needs to use your MCP server is Node.js and one entry added to their Claude config, so it’s trivial to share small tools with friends or the community.
Related Guides In This Intent
These pages cover nearby scope with different focus, helping reduce overlap and choose the right guide.
Related MCP Tools
fastmcp
FastMCP is a Python framework for building high-performance MCP servers with minimal boilerplate. It emphasizes speed and simplicity, providing decorators and utilities that let developers create MCP servers from existing Python functions without understanding the full MCP protocol details. Editor's Review: FastMCP is the fastest path from Python function to MCP server. If you have existing Python code that you want to expose as MCP tools, FastMCP lets you do that with minimal additional code. The framework handles the protocol overhead, letting you focus on your tool's logic rather than MCP implementation details. Performance is a key design goal—FastMCP servers have lower latency than naive implementations, which matters for production deployments where tools are called frequently. For Python developers building MCP integrations, FastMCP is the recommended starting point.
@upstash/context7-mcp
Context7 is a specialized MCP server that provides extended context management for AI assistants. It maintains conversation context across long sessions, enabling AI models to reason about complex, multi-turn interactions without losing track of earlier exchanges. Editor's Review: Context7 solves a fundamental problem with LLM-based AI assistants—limited context windows. By intelligently managing what context to retain and how to retrieve it, Context7 enables AI assistants to maintain coherence over much longer interactions than would otherwise be possible. This is particularly valuable for complex debugging sessions, architectural design discussions, or any workflow where earlier decisions inform later ones. The server is well-documented and straightforward to configure. If you find that AI assistants lose track of your project details in long sessions, Context7 is one of the most practical solutions available.
Related Workflows
Related Skills
API Integration Mapping
Use MCP-enabled tooling to handle api integration mapping tasks with repeatable inputs and outputs. Built from observed capabilities in @upstash/context7-mcp, fastmcp, mcp-use.
Workspace Knowledge Routing
Use MCP-enabled tooling to handle workspace knowledge routing tasks with repeatable inputs and outputs. Built from observed capabilities in google_workspace_mcp, @notionhq/notion-mcp-server.
What To Do Next
Move from this guide to a concrete workflow and a matching tool page to apply the concepts.
References
- Model Context Protocol (MCP) — Official Documentation
- MCP Specification & Quick Start
- MCP GitHub Organization
Last updated: April 5, 2026