MCP Learning Server
# MCP Learning Server
A beginner-friendly **Model Context Protocol (MCP) server**, built in plain
Node.js/JavaScript. This project exists purely to teach you, step by step,
how an MCP server actually works under the hood.
---
## 1. What is MCP?
**MCP (Model Context Protocol)** is an open standard that lets an AI model
(like Claude) talk to external programs called **MCP servers**. An MCP
server exposes a set of **tools** — real pieces of functionality such as
"do math", "read a file", or "call a weather API" — that the AI model can
discover and call on demand.
Without MCP, an AI model can only generate text based on what it already
knows. With MCP, the AI can:
- Discover what tools are available and what input each one needs.
- Call a tool with real arguments.
- Receive a real, structured result back and use it in its response.
Think of the AI as a "brain" and this server as a "toolbox" the brain can
reach into whenever it needs to do something it can't do on its own.
---
## 2. What is this project?
This project is a single MCP server exposing **five independent tools**:
| Tool name | What it does |
|-----------------------|-----------------------------------------------------------|
| `calculator` | Add, subtract, multiply, or divide two numbers |
| `uuid_generator` | Generate 1–20 random UUIDs |
| `read_notes` | Read the contents of `data/notes.txt` |
| `get_weather` | Fetch live weather for a city via the OpenWeatherMap API |
| `password_generator` | Generate a random password (6–32 characters) |
Every tool follows the exact same pattern: **name → description → input
schema → validation → try/catch → standardized response**. Once you
understand one tool deeply, you understand all five.
The code is intentionally simple: small functions, descriptive names, no
clever one-liners, and heavy comments explaining *why*, not just *what*.
---
## 3. Installation
You need [Node.js](https://nodejs.org/) version 18 or later installed.
```bash
# 1. Move into the project folder
cd mcp-learning-server
# 2. Install dependencies
npm install
```
---
## 4. Environment Variables
Only the weather tool needs a secret: a free API key from
[OpenWeatherMap](https://openweathermap.org/api).
```bash
# Copy the example file
cp .env.example .env
```
Then open `.env` and paste your key:
```
WEATHER_API_KEY=your_real_key_here
```
`.env` is listed in `.gitignore`, so your real key is never committed to
version control. If you skip this step, every tool **except** `get_weather`
will still work perfectly fine — `get_weather` will just return a friendly
error explaining the key is missing.
---
## 5. How to Run
```bash
npm start
```
You should see this line printed to your terminal:
```
mcp-learning-server is running and ready for requests.
```
The process will keep running — it is now waiting for an MCP client to
connect to it over stdin/stdout. This is normal; it is not supposed to
exit on its own.
### Testing it interactively
The easiest way to try the tools by hand, without setting up a full AI
client, is the official MCP Inspector:
```bash
npx @modelcontextprotocol/inspector node src/server.js
```
This opens a browser UI listing all five registered tools, where you can
fill in inputs and see exactly what each tool returns.
### Connecting it to Claude Desktop
Add an entry to Claude Desktop's MCP configuration file pointing at
`node` and the absolute path to `src/server.js`. Restart Claude Desktop,
and the five tools will appear as available capabilities in a
conversation.
---
## 6. Folder Structure
```
mcp-learning-server/
├── data/
│ └── notes.txt # Sample file used by the read_notes tool
├── src/
│ ├── server.js # Entry point: creates & starts the MCP server
│ ├── tools/
│ │ ├── calculator.js
│ │ ├── uuidGenerator.js
│ │ ├── fileReader.js
│ │ ├── weather.js
│ │ └── passwordGenerator.js
│ └── utils/
│ └── response.js # Shared success/error response helpers
├── .env.example # Template for required environment variables
├── .gitignore
├── package.json
└── README.md
```
**Why each folder exists:**
- **`data/`** — holds real, static local data that a tool can access.
It exists to prove that MCP tools can touch the filesystem, not just
compute in memory.
- **`src/tools/`** — one file per tool. Keeping every tool in its own
file means each one can be read, understood, and tested in isolation,
without needing to understand the other four.
- **`src/utils/`** — shared code used by *every* tool (like our response
formatting helpers). Anything more than one tool needs belongs here,
instead of being copy-pasted into each tool file.
- **`src/server.js`** — the single place where the server is created and
every tool is registered. This file's only job is "wiring", not logic.
---
## 7. Tool Reference
### `calculator`
**Input:**
```json
{ "operation": "add", "a": 20, "b": 10 }
```
**Success output:**
```json
{ "success": true, "result": 30 }
```
**Error example (divide by zero):**
```json
{ "success": false, "message": "Division by zero is not allowed." }
```
### `uuid_generator`
**Input:**
```json
{ "count": 5 }
```
**Success output:**
```json
{ "success": true, "uuids": ["...", "...", "...", "...", "..."] }
```
Valid range: `count` must be between 1 and 20.
### `read_notes`
**Input:** none (`{}`)
**Success output:**
```json
{ "success": true, "content": "Learning MCP Server\nNode.js is awesome.\n..." }
```
**Error example:**
```json
{ "success": false, "message": "notes.txt was not found. Make sure data/notes.txt exists." }
```
### `get_weather`
**Input:**
```json
{ "city": "Lucknow" }
```
**Success output:**
```json
{ "success": true, "city": "Lucknow", "temperature": 32, "humidity": 65, "condition": "haze" }
```
**Error example:**
```json
{ "success": false, "message": "City \"Notacity123\" was not found." }
```
### `password_generator`
**Input:**
```json
{ "length": 12, "symbols": true }
```
**Success output:**
```json
{ "success": true, "password": "Ab@12Lk#98Pq" }
```
Valid range: `length` must be between 6 and 32.
---
## 8. Common Errors
| Error message | Cause |
|----------------------------------------------------------|----------------------------------------------------------------|
| `"Division by zero is not allowed."` | `calculator` was called with `operation: "divide"` and `b: 0` |
| `"notes.txt was not found. Make sure data/notes.txt exists."` | `data/notes.txt` was deleted or moved |
| `"WEATHER_API_KEY is missing. Add it to your .env file."` | You never created a `.env` file or left the key blank |
| `"City \"X\" was not found."` | The city name sent to `get_weather` doesn't exist per the API |
| A zod validation error before your handler even runs | Input didn't match the tool's schema (e.g. `count: 50` when max is 20) |
Every tool in this project returns errors as **plain JSON objects**
(`{ "success": false, "message": "..." }`) instead of throwing raw
JavaScript exceptions. This is deliberate — see the "Error Handling"
section in `src/utils/response.js` for the full reasoning.
---
## 9. Learning Summary
By working through this project you should now understand:
- **MCP architecture** — an AI model (client) talks to a local process
(server) over a shared protocol, most simply via stdin/stdout.
- **Tool registration** — calling `server.tool(name, description, schema, handler)`
once per capability, during server startup.
- **Tool discovery** — the AI reads each tool's name, description, and
schema to decide when and how to call it; it never sees your source code.
- **The request lifecycle** — client sends a "call tool" message → SDK
validates arguments against the schema → your handler runs → your
return value is wrapped and sent back.
- **JSON schema & input validation** — using `zod` to describe exactly
what shape of input a tool accepts, so bad input never reaches your logic.
- **File handling** — reading local files safely with `fs/promises` and
`async/await`.
- **External API calls** — using `axios` plus environment variables to
call a real third-party API without hard-coding secrets.
- **Standardized error handling** — never throwing raw errors back to a
client; always responding with a predictable `{ success, message }`
or `{ success, ...data }` shape.
- **Best practices** — small single-responsibility functions, descriptive
names, and heavy comments, all of which make a codebase easier to trust
and extend as it grows.
From here, a natural next step is adding a sixth tool of your own —
try building one that combines two ideas from this project (for example,
a tool that reads a file *and* calls an API).
TDQS
Scored across 5 tools
All five tools perform entirely distinct and unrelated functions: arithmetic, weather lookup, password generation, note reading, and UUID generation. There is no overlap or confusion between them.
Most tool names follow a clear 'verb_noun' pattern (get_weather, read_notes, uuid_generator, password_generator). 'calculator' is a noun rather than verb_noun, but it is still unambiguous and fits the style.
With 5 tools, the server is well-scoped and not overloaded. Each tool serves a clear, standalone purpose without redundancy.
Each tool is individually complete for its specific function; for example, calculator covers basic arithmetic. However, the tools are disconnected and do not form a coherent domain, so there is no sense of a full surface.