FastAPI Todo MCP Server
by bensonaddo
README.md
# Todo Manager — FastAPI + MCP
A full-stack todo list application built with **FastAPI**, **SQLite**, and **fastapi-mcp**. It provides a REST API for CRUD operations, an interactive web UI, auto-generated OpenAPI documentation, and MCP tool exposure for AI assistants (e.g. Cursor).
**Live demo:** [https://fastapi-mcp-todo-ykjl.onrender.com](https://fastapi-mcp-todo-ykjl.onrender.com)
---
## Table of Contents
- [Features](#features)
- [Tech Stack](#tech-stack)
- [Project Structure](#project-structure)
- [Getting Started](#getting-started)
- [Running the Application](#running-the-application)
- [Web UI](#web-ui)
- [API Reference](#api-reference)
- [OpenAPI / Swagger Documentation](#openapi--swagger-documentation)
- [Data Models](#data-models)
- [MCP Integration](#mcp-integration)
- [Deployment](#deployment)
- [External Resources](#external-resources)
---
## Features
- **REST API** — Full CRUD for todo items with typed request/response schemas
- **Web frontend** — Responsive grid UI with add, edit, complete, delete, and filter actions
- **SQLite persistence** — Local file-based database (`todos.db`)
- **OpenAPI docs** — Interactive Swagger UI and ReDoc generated automatically by FastAPI
- **MCP server** — Exposes API operations as tools for AI clients via [fastapi-mcp](https://github.com/tadata-org/fastapi_mcp)
- **Production-ready** — Deployable to Render with a single `render.yaml` blueprint
---
## Tech Stack
| Layer | Technology |
| ------------ | ----------------------------------- |
| Backend | FastAPI, Uvicorn |
| ORM / DB | SQLAlchemy, SQLite |
| Validation | Pydantic v2 |
| Frontend | HTML, CSS, vanilla JavaScript |
| AI Tools | fastapi-mcp, MCP |
| Deployment | Render (Python web service) |
---
## Project Structure
```
fastapi-mcp-todo/
├── main.py # FastAPI app, routes, MCP mount
├── database.py # SQLite engine, session, init
├── models.py # SQLAlchemy Todo ORM model
├── schemas.py # Pydantic request/response schemas
├── static/
│ ├── index.html # Frontend shell
│ ├── css/style.css # UI styles
│ └── js/app.js # Client-side CRUD logic
├── requirements.txt # Python dependencies
├── render.yaml # Render deployment config
├── todos.db # SQLite database (created at runtime, gitignored)
└── readme.md
```
---
## Getting Started
### Prerequisites
- Python 3.9+
- `pip` (or `uv`)
### Installation
```bash
# Clone the repository and enter the project directory
cd fastapi-mcp-todo
# Create and activate a virtual environment
python -m venv .venv
source .venv/bin/activate # macOS / Linux
# .venv\Scripts\activate # Windows
# Install dependencies
pip install -r requirements.txt
```
---
## Running the Application
### Development (with auto-reload)
```bash
# Option A — FastAPI CLI
fastapi dev main.py
# Option B — Uvicorn directly
uvicorn main:app --reload
```
### Production
```bash
uvicorn main:app --host 0.0.0.0 --port 8000
```
The app starts on **http://127.0.0.1:8000** by default. The SQLite database and tables are created automatically on first startup.
---
## Web UI
| URL (local) | Description |
| ------------------------ | ------------------------------------ |
| http://127.0.0.1:8000/ | Todo Manager web interface |
| http://127.0.0.1:8000/static/ | Static assets (CSS, JS) |
The frontend communicates with the `/todos` API and supports:
- Adding todos (instant grid update)
- Marking todos complete / incomplete
- Editing todo content (modal)
- Deleting todos (with confirmation)
- Filtering by All / Active / Completed
- Live stats (total, active, completed)
---
## API Reference
Base URL (local): `http://127.0.0.1:8000`
Base URL (production): `https://fastapi-mcp-todo-ykjl.onrender.com`
All todo endpoints are tagged **Todos** in OpenAPI. Responses use `application/json`.
### Endpoints Summary
| Method | Path | Operation ID | Description | Success |
| ------ | ------------------ | ---------------- | ------------------------ | ------- |
| `GET` | `/todos` | `get_all_todos` | List all todos | `200` |
| `GET` | `/todos/{todo_id}` | `get_todo` | Get one todo by ID | `200` |
| `POST` | `/todos` | `create_todo` | Create a new todo | `201` |
| `PUT` | `/todos/{todo_id}` | `update_todo` | Update a todo | `200` |
| `DELETE` | `/todos/{todo_id}` | `delete_todo` | Delete a todo | `204` |
---
### `GET /todos` — List all todos
Returns every todo ordered by `todo_id`.
**Response `200`**
```json
[
{
"todo_id": 1,
"content": "Buy groceries",
"completed": false
},
{
"todo_id": 2,
"content": "Schedule dentist appointment",
"completed": true
}
]
```
**Example**
```bash
curl http://127.0.0.1:8000/todos
```
---
### `GET /todos/{todo_id}` — Get a single todo
**Path parameters**
| Name | Type | Description |
| --------- | ------- | ------------------ |
| `todo_id` | integer | Unique todo ID |
**Response `200`**
```json
{
"todo_id": 1,
"content": "Buy groceries",
"completed": false
}
```
**Response `404`** — Todo not found
```json
{
"detail": "Todo with id 99 not found"
}
```
**Example**
```bash
curl http://127.0.0.1:8000/todos/1
```
---
### `POST /todos` — Create a todo
**Request body**
```json
{
"content": "Finish reading chapter 5"
}
```
| Field | Type | Required | Description |
| --------- | ------ | -------- | ------------------------------------ |
| `content` | string | yes | Task description (min length: 1) |
`completed` defaults to `false` and cannot be set on create.
**Response `201`**
```json
{
"todo_id": 3,
"content": "Finish reading chapter 5",
"completed": false
}
```
**Example**
```bash
curl -X POST http://127.0.0.1:8000/todos \
-H "Content-Type: application/json" \
-d '{"content": "Finish reading chapter 5"}'
```
---
### `PUT /todos/{todo_id}` — Update a todo
Partial updates are supported — include only the fields you want to change.
**Request body** (all fields optional)
```json
{
"content": "Updated task text",
"completed": true
}
```
| Field | Type | Required | Description |
| ----------- | ------- | -------- | ------------------------ |
| `content` | string | no | Updated description |
| `completed` | boolean | no | Updated completion state |
**Response `200`**
```json
{
"todo_id": 1,
"content": "Updated task text",
"completed": true
}
```
**Response `404`** — Todo not found
**Example**
```bash
curl -X PUT http://127.0.0.1:8000/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed": true}'
```
---
### `DELETE /todos/{todo_id}` — Delete a todo
**Response `204`** — No content (success)
**Response `404`** — Todo not found
**Example**
```bash
curl -X DELETE http://127.0.0.1:8000/todos/1
```
---
## OpenAPI / Swagger Documentation
FastAPI auto-generates interactive API documentation from route decorators, type hints, and Pydantic schemas.
### Local
| Documentation | URL |
| ------------- | --- |
| **Swagger UI** | http://127.0.0.1:8000/docs |
| **ReDoc** | http://127.0.0.1:8000/redoc |
| **OpenAPI JSON** | http://127.0.0.1:8000/openapi.json |
### Production
| Documentation | URL |
| ------------- | --- |
| **Swagger UI** | https://fastapi-mcp-todo-ykjl.onrender.com/docs |
| **ReDoc** | https://fastapi-mcp-todo-ykjl.onrender.com/redoc |
| **OpenAPI JSON** | https://fastapi-mcp-todo-ykjl.onrender.com/openapi.json |
Use **Swagger UI** (`/docs`) to explore endpoints, view schemas, and send test requests directly from the browser.
---
## Data Models
### Todo (database)
| Column | Type | Constraints |
| ----------- | ------- | ------------------------ |
| `todo_id` | integer | Primary key, auto-increment |
| `content` | string | Not null |
| `completed` | boolean | Not null, default `false` |
### Pydantic schemas
| Schema | Purpose |
| -------------- | -------------------------------------------- |
| `TodoCreate` | Request body for `POST /todos` |
| `TodoUpdate` | Request body for `PUT /todos/{todo_id}` |
| `TodoResponse` | Response body for all todo read/write ops |
---
## MCP Integration
This app exposes selected API operations as **MCP tools** using [fastapi-mcp](https://github.com/tadata-org/fastapi_mcp), allowing AI assistants to manage todos programmatically.
### Exposed tools
| MCP Tool | Maps to API endpoint |
| ---------------- | ---------------------- |
| `get_all_todos` | `GET /todos` |
| `get_todo` | `GET /todos/{todo_id}` |
| `create_todo` | `POST /todos` |
| `update_todo` | `PUT /todos/{todo_id}` |
| `delete_todo` | `DELETE /todos/{todo_id}` |
Tools are registered via `operation_id` on each route and mounted at **`/mcp`**.
### Setup in `main.py`
```python
from fastapi_mcp import FastApiMCP
mcp = FastApiMCP(
app,
include_operations=[
"get_all_todos",
"get_todo",
"create_todo",
"update_todo",
"delete_todo",
],
)
mcp.mount()
```
### Connect from Cursor
1. Open **Cursor Settings → Tools & MCP → Add MCP Server**
2. Add the following to your MCP configuration:
```json
{
"fastapi-mcp-todo": {
"url": "http://127.0.0.1:8000/mcp"
}
}
```
For the deployed app, replace the URL with:
```json
{
"fastapi-mcp-todo": {
"url": "https://fastapi-mcp-todo-ykjl.onrender.com/mcp"
}
}
```
3. Restart Cursor if needed, then use natural language prompts such as:
- *"List all todos"*
- *"Add a todo to call mum today"*
- *"Mark todo 2 as completed"*
---
## Deployment
The project includes a [Render](https://render.com) blueprint (`render.yaml`):
```yaml
services:
- type: web
name: fastapi-mcp-todo
env: python
buildCommand: uv pip install -r requirements.txt
startCommand: uvicorn main:app --host 0.0.0.0 --port 8000
plan: free
```
> **Note:** On Render's free tier, the filesystem is ephemeral. SQLite data may reset on redeploy or spin-down. For persistent production storage, consider an external database (e.g. PostgreSQL).
---
## External Resources
- [FastAPI documentation](https://fastapi.tiangolo.com/)
- [FastAPI devdocs](https://devdocs.io/fastapi)
- [fastapi-mcp on GitHub](https://github.com/tadata-org/fastapi_mcp)
- [SQLAlchemy documentation](https://docs.sqlalchemy.org/)
- [Model Context Protocol (MCP)](https://modelcontextprotocol.io/)
---
## License
This project is for learning and demonstration purposes.
This server cannot be deployed
Maintenance
ActivityStale
ResponsivenessNo issues