Skip to main content
Glama
gyannetics

MCP Utility Server

by gyannetics
README.md
# MCP Utility Server

A beginner-friendly **Model Context Protocol (MCP)** project in Python with two server implementations:

| File | Description |
|------|-------------|
| `server.py` | Core MVP — time, math, internet quotes, dad jokes |
| `server1.py` | Pro server — file tools, web search, optional LangChain agent (OpenAI / Groq) |

Includes a test client (`mcp-client.py`) and support for **stdio** (local) and **SSE** (remote) transports.

## What is MCP?

**Model Context Protocol** is an open standard that lets AI applications (Cursor, Claude Desktop, VS Code, custom agents, etc.) connect to external tools and data in a standardized way.

Your MCP server exposes **Tools** (actions the AI can call). This project focuses on tools.

## Tools

### `server.py` — Daily Utilities

| Tool | Description | Type |
|------|-------------|------|
| `get_current_datetime` | Current date and time, formatted | Sync |
| `add_numbers` | Adds two numbers | Sync |
| `multiply_numbers` | Multiplies two numbers | Sync |
| `safe_calculate` | Safely evaluates math expressions (no `eval`) | Sync |
| `get_motivational_quote` | Fetches a quote from the internet (with fallbacks) | Async |
| `get_dad_joke` | Fetches a dad joke from [icanhazdadjoke.com](https://icanhazdadjoke.com) | Async |

### `server1.py` — Daily Utilities Pro

Includes the basic tools above, plus:

| Tool | Description |
|------|-------------|
| `list_directory` | Lists files in allowed directories (project, Documents, Downloads) |
| `read_file` | Reads a text file (size-limited, sandboxed) |
| `web_search` | DuckDuckGo web search (requires `langchain` extra) |
| `enhance_prompt` | Simple prompt improvement helper |
| `ask_smart` | LangChain agent with session memory (requires API key + `langchain` extra) |

## Quick Start

### Prerequisites

- Python 3.10+
- [`uv`](https://docs.astral.sh/uv/) (recommended)

### Install

```bash
git clone https://github.com/gyannetics/mcp-utility-server.git
cd mcp-utility-server

# Core dependencies only (server.py)
uv sync

# All features (server1.py, SSE, LangChain)
uv sync --all-extras
```

### Optional dependency groups

| Extra | Packages | Used by |
|-------|----------|---------|
| *(core)* | `mcp`, `httpx`, `python-dotenv` | Both servers |
| `sse` | `fastapi`, `uvicorn` | Remote SSE mode |
| `langchain` | LangChain, OpenAI/Groq, DuckDuckGo search | `server1.py` agent & web search |
| `all` | Everything above | Full Pro setup |

```bash
uv sync --extra sse
uv sync --extra langchain
```

### Environment variables (`server1.py`)

Copy `.env` and add your keys (at least one for the smart agent):

```env
OPENAI_API_KEY=sk-...
GROQ_API_KEY=gsk-...
```

Groq is preferred when both keys are set. Basic tools work without any API key.

## Run the Server

### Stdio (local — Claude Desktop, Cursor)

```bash
uv run server.py
# or
uv run server1.py
```

The server waits for MCP connections over stdin/stdout.

### SSE (remote / Docker)

```bash
uv run server1.py sse
# MCP endpoint:  http://localhost:8000/sse
# Health check:  http://localhost:8000/health
```

Requires the `sse` extra (`uv sync --extra sse` or `--all-extras`).

## Docker

The container runs **`server1.py` in SSE mode** (the Pro server) with a built-in health check.

### Quick start

```bash
# Build and run with Docker Compose (loads .env if present)
docker compose up --build

# Or plain Docker
docker build -t mcp-utility-server .
docker run --rm -p 8000:8000 --env-file .env mcp-utility-server
```

### Endpoints

| URL | Purpose |
|-----|---------|
| `http://localhost:8000/health` | Liveness probe (JSON `{"status": "healthy", ...}`) |
| `http://localhost:8000/sse` | MCP SSE transport for remote clients |

### Connect a client to the container

```bash
uv run mcp-client.py --sse http://localhost:8000/sse
```

### Environment variables

| Variable | Default | Description |
|----------|---------|-------------|
| `HOST` | `0.0.0.0` | Bind address inside the container |
| `PORT` | `8000` | HTTP port |
| `OPENAI_API_KEY` | — | Enables the `ask_smart` LangChain tool |
| `GROQ_API_KEY` | — | Enables `ask_smart` via Groq (preferred if both set) |

### Run the core server instead

To containerize `server.py` instead of `server1.py`, change the `CMD` in the `Dockerfile`:

```dockerfile
CMD ["python", "server.py", "sse"]
```

## Test with the MCP Client

```bash
# Full demo via stdio (spawns server.py automatically)
uv run mcp-client.py

# Test a single tool
uv run mcp-client.py --tool get_motivational_quote

# Connect to a running SSE server (local or Docker)
uv run server1.py sse
uv run mcp-client.py --sse http://localhost:8000/sse
```

## Use with Cursor

1. Open **Cursor Settings** → **Tools & MCP** → **Add MCP Server**
2. Or edit `%USERPROFILE%\.cursor\mcp.json` (Windows) / `~/.cursor/mcp.json` (macOS/Linux):

```json
{
  "mcpServers": {
    "daily-utilities": {
      "command": "uv",
      "args": [
        "--directory",
        "C:\\ABSOLUTE\\PATH\\TO\\mcp-utility-server",
        "run",
        "server.py"
      ]
    }
  }
}
```

Use the **full absolute path** to this project. Reload Cursor after saving.

Example prompts:

- "What time is it?"
- "Calculate 15 * 7 + 22"
- "Tell me a dad joke"
- "Give me a motivational quote"

## Use with Claude Desktop

1. Open Claude Desktop → **Settings** → **Developer** → **Edit Config**
2. Add to `claude_desktop_config.json`:

```json
{
  "mcpServers": {
    "daily-utilities": {
      "command": "uv",
      "args": [
        "--directory",
        "/ABSOLUTE/PATH/TO/mcp-utility-server",
        "run",
        "server.py"
      ]
    }
  }
}
```

3. Fully quit and restart Claude Desktop.

## Project Structure

```
mcp-utility-server/
├── server.py           # Core MCP server
├── server1.py          # Pro server (default for Docker)
├── mcp-client.py       # Test client (stdio + SSE)
├── Dockerfile          # Container image (server1.py SSE mode)
├── docker-compose.yml  # Local container orchestration
├── pyproject.toml      # Dependencies and optional extras
├── .env                # API keys (not committed)
├── .dockerignore
├── .gitignore
└── README.md
```

## Troubleshooting

| Issue | Fix |
|-------|-----|
| Server not appearing in Cursor/Claude | Check absolute path in config; reload or restart the app |
| `ImportError` for `fastapi` / `langchain` | Run `uv sync --all-extras` |
| `Client` import error in `mcp-client.py` | Use the project venv: `uv run mcp-client.py` |
| Quotes/jokes time out | Network tools use a 30s timeout; check internet access |
| stdout errors in stdio mode | Never use `print()` — log to stderr with `logging` |
| pip conflicts in Anaconda | Use this project's `.venv` via `uv sync`, not global `pip` |

**Cursor MCP logs:** View → Output → select **MCP** from the dropdown.

**Claude Desktop logs (macOS):** `~/Library/Logs/Claude/mcp*.log`

## Learning & Resources

This project demonstrates:

- Building MCP servers with **FastMCP**
- Auto-generated tool schemas from type hints and docstrings
- Sync vs async tools
- Safe HTTP calls with fallbacks
- Optional LangChain agent integration
- Stdio and SSE transports
- A Python MCP client using `ClientSession`

### Extend it

1. Add **Resources** — expose files or data as readable context
2. Add **Prompts** — reusable prompt templates
3. Persist data — todo lists, notes, session history to disk
4. Deploy remotely — SSE on Railway, Fly.io, or similar
5. Connect more APIs — Notion, GitHub, databases, etc.

### Official links

- [Model Context Protocol](https://modelcontextprotocol.io/)
- [Build an MCP Server (tutorial)](https://modelcontextprotocol.io/docs/develop/build-server)
- [Python SDK](https://github.com/modelcontextprotocol/python-sdk)
- [Awesome MCP Servers](https://github.com/punkpeye/awesome-mcp-servers)

---

Built as an educational MCP starter. Experiment, extend, and have fun.