Task / To-Do Manager — MCP Server
by zainabf07
README.md
# Task / To-Do Manager — MCP Server
A small MCP server that gives an AI assistant a menu of 5 things it can do
with a personal to-do list: add a task, list tasks, look one up, mark it
done, and delete it. Built for the "Building an MCP Server" beginner lab.
## What it does
The server exposes:
- **5 tools** — the actions the AI assistant can trigger
- **1 bonus resource** — a read-only view of pending tasks
- **Storage** — a single JSON file (`tasks.json`), created automatically the
first time you add a task. No database.
- **Transport** — stdio (standard input/output), the simplest option, as
used throughout this lab.
## Setup
```bash
# 1. Create and activate a virtual environment
python -m venv venv
source venv/bin/activate # Mac/Linux
venv\Scripts\activate # Windows
# 2. Install dependencies
pip install -r requirements.txt
```
> **Note on SDK version:** this server is written against **MCP Python SDK
> v2** (`mcp>=2.0.0`), which is what `pip install mcp` gives you today. If
> you followed an older guide that uses `from mcp.server.fastmcp import
> FastMCP`, that class was renamed — this server uses the current
> equivalent, `from mcp.server.mcpserver import MCPServer`. The tool and
> resource decorators (`@mcp.tool()`, `@mcp.resource()`) work the same way
> either version.
## Running it
```bash
python server.py
```
The server communicates over stdio, so it's meant to be launched by an MCP
client (like MCP Inspector or an AI assistant), not run and watched directly.
To test it by hand, point **MCP Inspector** at it:
```bash
npx @modelcontextprotocol/inspector python server.py
```
Then open the URL Inspector prints, and you'll see all 5 tools plus the
resource listed — click into any of them to call it and see the result.
## Tools
### 1. `add_task(title, notes="")`
Adds a new task. `title` is required and cannot be empty.
**Example call:**
```json
{ "title": "Buy milk", "notes": "Get 2% not whole" }
```
**Returns:**
```json
{ "status": "created", "task": { "id": "1", "title": "Buy milk", "notes": "Get 2% not whole", "done": false, "created_at": "..." } }
```
### 2. `list_tasks(only_pending=False)`
Lists all tasks, newest first. Set `only_pending: true` to see just the
tasks that aren't done yet.
**Example call:**
```json
{ "only_pending": true }
```
**Returns:**
```json
{ "count": 1, "tasks": [ { "id": "2", "title": "Write report", "...": "..." } ] }
```
### 3. `get_task(task_id)`
Fetches the full details of one task by its ID.
**Example call:**
```json
{ "task_id": "1" }
```
**Returns:** `{ "task": { ... } }` or `{ "error": "No task found with id '1'." }`
### 4. `complete_task(task_id)`
Marks a task as done and stamps it with `completed_at`.
**Example call:**
```json
{ "task_id": "1" }
```
**Returns:** `{ "status": "completed", "task": { ... } }`
### 5. `delete_task(task_id)`
Permanently removes a task.
**Example call:**
```json
{ "task_id": "1" }
```
**Returns:** `{ "status": "deleted", "task": { ... } }`
## Bonus resource
### `tasks://pending`
A read-only JSON snapshot of every task that hasn't been completed yet.
Unlike a tool, this doesn't *do* anything — it just hands over data.
## Error handling
- `add_task` with an empty/missing `title` → a clear JSON error, not a crash
(`{"error": "title is required and cannot be empty."}`).
- `get_task`, `complete_task`, `delete_task` with an unknown `task_id` → a
clear JSON error naming the missing ID.
- `complete_task` called twice on the same task → returns
`{"status": "already_done", ...}` instead of a duplicate-completion error.
- Missing required fields entirely (e.g. calling `add_task` with no `title`
key at all) are caught automatically by the tool's input schema before
the code even runs, and reported back as a validation error.
- If `tasks.json` is ever manually corrupted, the server treats it as an
empty list instead of crashing on startup.
## Example end-to-end walkthrough
A realistic request like *"add a task and then show me all my pending
tasks"* looks like this:
1. `add_task({"title": "Finish lab report"})` → task `1` created.
2. `list_tasks({"only_pending": true})` → returns `[task 1]`.
3. `complete_task({"task_id": "1"})` → task `1` marked done.
4. `list_tasks({"only_pending": true})` → returns `[]`.
## Project structure
```
todo-mcp-server/
├── server.py # the MCP server: tools, resource, entry point
├── tasks.json # created automatically on first add_task call
├── requirements.txt
└── README.md
```
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues