Coding Todo Server
by dennismartis
import asyncio
from datetime import datetime
from enum import Enum
from typing import Dict, List, Optional
from mcp.server.fastmcp import FastMCP, Context
from pydantic import BaseModel
# Todo status enum
class TodoStatus(str, Enum):
PENDING = "pending"
IN_PROGRESS = "in_progress"
COMPLETED = "completed"
# Todo model with coding project specific fields
class Todo(BaseModel):
id: str
title: str
description: str
status: TodoStatus = TodoStatus.PENDING
created_at: datetime = datetime.now()
updated_at: Optional[datetime] = None
project: Optional[str] = None
priority: int = 1 # 1 (lowest) to 5 (highest)
tags: List[str] = []
# Store todos in memory
todos: Dict[str, Todo] = {}
# Create an MCP server using FastMCP
mcp = FastMCP("coding-todo-server")
# Resource to list all todos
@mcp.resource("todo://list")
def get_todo_list() -> str:
"""List of all coding project todos"""
if not todos:
return "No todos found."
result = "# Coding Project Todos\n\n"
for todo_id, todo in todos.items():
status_marker = "[ ]" if todo.status != TodoStatus.COMPLETED else "[x]"
result += f"{status_marker} **{todo.title}** (ID: {todo_id}, Priority: {todo.priority})\n"
if todo.tags:
result += f" Tags: {', '.join(todo.tags)}\n"
return result
# Resource to view a specific todo
@mcp.resource("todo://item/{todo_id}")
def get_todo_item(todo_id: str) -> str:
"""Get details of a specific todo item"""
if todo_id not in todos:
raise ValueError(f"Todo not found: {todo_id}")
todo = todos[todo_id]
result = f"# Todo: {todo.title}\n\n"
result += f"**Status:** {todo.status.value}\n"
result += f"**Priority:** {todo.priority}/5\n"
result += f"**Created:** {todo.created_at.strftime('%Y-%m-%d %H:%M')}\n"
if todo.updated_at:
result += f"**Updated:** {todo.updated_at.strftime('%Y-%m-%d %H:%M')}\n"
if todo.project:
result += f"**Project:** {todo.project}\n"
if todo.tags:
result += f"**Tags:** {', '.join(todo.tags)}\n"
result += f"\n**Description:**\n{todo.description}\n"
return result
# Prompt to summarize todos
@mcp.prompt()
def summarize_todos(status: str = "pending", project: str = None) -> str:
"""Creates a summary of todos with optional filtering
Args:
status: Filter by status (pending/in_progress/completed/all)
project: Filter by project name
"""
filtered_todos = list(todos.values())
if status != "all":
filtered_todos = [t for t in filtered_todos if t.status.value == status]
if project:
filtered_todos = [t for t in filtered_todos if t.project == project]
status_text = f"{status} " if status != "all" else ""
project_text = f"for project {project} " if project else ""
return f"""Here are the current {status_text}todos {project_text}to summarize:
{chr(10).join(f"- {t.title} (Priority: {t.priority}): {t.description}" for t in filtered_todos)}
Please provide a concise summary of these todos and suggest an approach to tackle them efficiently."""
# Prompt to suggest which todo to tackle next
@mcp.prompt()
def suggest_next_todo() -> str:
"""Suggests which todo to tackle next based on priority and status"""
pending_todos = [t for t in todos.values() if t.status != TodoStatus.COMPLETED]
if not pending_todos:
return "There are no pending todos. Would you like suggestions for new coding tasks to add?"
# Sort by priority (highest first)
sorted_todos = sorted(pending_todos, key=lambda t: (-t.priority, t.created_at))
return f"""Here are the current pending todos in order of priority:
{chr(10).join(f"- {t.title} (Priority: {t.priority}, Status: {t.status.value}): {t.description}" for t in sorted_todos)}
Based on these todos, which one should I tackle next and why? Please provide a brief recommendation."""
# Tool to add a new todo
@mcp.tool()
def add_todo(title: str, description: str, project: str = None, priority: int = 1, tags: List[str] = None) -> str:
"""Add a new todo item
Args:
title: Title of the todo item
description: Detailed description of the todo item
project: Project name (optional)
priority: Priority from 1 (lowest) to 5 (highest)
tags: List of tags related to the todo
"""
# Generate a new todo ID
todo_id = f"todo{len(todos) + 1}"
new_todo = Todo(
id=todo_id,
title=title,
description=description,
project=project,
priority=priority,
tags=tags or [],
)
todos[todo_id] = new_todo
return f"Added todo '{title}' with ID: {todo_id}"
# Tool to update the status of a todo
@mcp.tool()
def update_todo_status(id: str, status: str) -> str:
"""Update the status of a todo item
Args:
id: The ID of the todo item
status: New status (pending/in_progress/completed)
"""
if id not in todos:
raise ValueError(f"Todo not found: {id}")
try:
todos[id].status = TodoStatus(status)
todos[id].updated_at = datetime.now()
except ValueError:
raise ValueError(f"Invalid status: {status}. Must be one of: pending, in_progress, completed")
return f"Updated todo '{todos[id].title}' status to {status}"
# Tool to delete a todo
@mcp.tool()
def delete_todo(id: str) -> str:
"""Delete a todo item
Args:
id: The ID of the todo item to delete
"""
if id not in todos:
raise ValueError(f"Todo not found: {id}")
title = todos[id].title
del todos[id]
return f"Deleted todo '{title}' (ID: {id})"
# Tool to update a todo's details
@mcp.tool()
def update_todo(id: str, title: str = None, description: str = None, project: str = None,
priority: int = None, tags: List[str] = None) -> str:
"""Update a todo item's details
Args:
id: The ID of the todo item
title: New title (optional)
description: New description (optional)
project: New project name (optional)
priority: New priority from 1 (lowest) to 5 (highest) (optional)
tags: New list of tags (optional)
"""
if id not in todos:
raise ValueError(f"Todo not found: {id}")
todo = todos[id]
if title is not None:
todo.title = title
if description is not None:
todo.description = description
if project is not None:
todo.project = project
if priority is not None:
if not 1 <= priority <= 5:
raise ValueError("Priority must be between 1 and 5")
todo.priority = priority
if tags is not None:
todo.tags = tags
# Update the timestamp
todo.updated_at = datetime.now()
return f"Updated todo '{todo.title}' (ID: {id})"
# Initialize with example todos
def initialize_example_todos():
example_todos = [
Todo(
id="todo1",
title="Implement user authentication",
description="Add JWT-based authentication to the API endpoints",
priority=4,
project="Backend API",
tags=["backend", "security"],
),
Todo(
id="todo2",
title="Fix CSS responsiveness",
description="The dashboard layout breaks on mobile devices",
priority=3,
project="Frontend UI",
tags=["frontend", "css", "bugfix"],
),
Todo(
id="todo3",
title="Write unit tests",
description="Create tests for the new data processing module",
priority=2,
project="Backend API",
tags=["testing", "quality"],
),
]
for todo in example_todos:
todos[todo.id] = todo
# Initialize and run server
if __name__ == "__main__":
initialize_example_todos()
mcp.run()