Skip to main content
Glama
README.md
# 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

A3.7/5.0

Scored across 5 tools

Disambiguation5/5

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.

Naming Consistency4/5

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.

Tool Count5/5

With 5 tools, the server is well-scoped and not overloaded. Each tool serves a clear, standalone purpose without redundancy.

Completeness3/5

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.

Maintenance

ActivityStale
ResponsivenessNo issues