# Directory Structure ``` ├── .gitignore ├── coding_todo.py ├── minimal_server.py ├── README.md └── requirements.txt ``` # Files -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- ``` 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | *.dylib 9 | 10 | # Distribution / packaging 11 | .Python 12 | env/ 13 | venv/ 14 | .venv 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | MANIFEST 33 | 34 | # PyInstaller 35 | # Usually these files are written into a _MEIPASS folder, but if PyInstaller 36 | # creates a self-extracting executable, it will use a temp folder instead. 37 | _MEIPASS/ 38 | _MEI*/ 39 | 40 | # Environments 41 | .env 42 | .venv 43 | env/ 44 | venv/ 45 | ENV/ 46 | env.bak/ 47 | venv.bak/ 48 | 49 | # Spyder project settings 50 | .spyderproject 51 | .spyproject 52 | 53 | # Rope project settings 54 | .ropeproject 55 | 56 | # mypy 57 | .mypy_cache/ 58 | .dmypy.json 59 | dmypy.sock 60 | 61 | # Environments 62 | .env 63 | .venv 64 | env/ 65 | venv/ 66 | ENV/ 67 | env.bak/ 68 | venv.bak/ 69 | 70 | # General ignores 71 | tmp/ 72 | temp/ 73 | *.tmp 74 | *.log 75 | *.DS_Store 76 | 77 | # MCP directory 78 | mcp/ 79 | ``` -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- ```markdown 1 | # Coding Todo Server 2 | 3 | This MCP server provides tools and resources for managing a coding project's todo list. 4 | 5 | ## Overview 6 | 7 | This server allows you to: 8 | 9 | - View the current todo list 10 | - View details of specific todo items 11 | - Add new todo items 12 | - Update the status of todo items 13 | - Delete todo items 14 | - Update todo item details 15 | 16 | ## Resources 17 | 18 | - `todo://list`: Provides a list of all todo items with their status, title, priority and tags. 19 | - `todo://item/{todo_id}`: Provides detailed information about a specific todo item, including status, priority, creation date, project, tags, and description. 20 | 21 | ## Tools 22 | 23 | - `add_todo`: Adds a new todo item to the list. 24 | - Arguments: 25 | - `title`: Title of the todo item (required) 26 | - `description`: Detailed description of the todo item (required) 27 | - `project`: Project name (optional) 28 | - `priority`: Priority from 1 (lowest) to 5 (highest) (optional, default: 1) 29 | - `tags`: List of tags related to the todo (optional) 30 | 31 | - `update_todo_status`: Updates the status of an existing todo item. 32 | - Arguments: 33 | - `id`: The ID of the todo item to update (required) 34 | - `status`: New status (pending/in_progress/completed) (required) 35 | 36 | - `delete_todo`: Deletes a todo item from the list. 37 | - Arguments: 38 | - `id`: The ID of the todo item to delete (required) 39 | 40 | - `update_todo`: Updates the details of an existing todo item. 41 | - Arguments: 42 | - `id`: The ID of the todo item to update (required) 43 | - `title`: New title (optional) 44 | - `description`: New description (optional) 45 | - `project`: New project name (optional) 46 | - `priority`: New priority from 1 (lowest) to 5 (highest) (optional) 47 | - `tags`: New list of tags (optional) 48 | 49 | ## Installation 50 | 51 | Before running the server, you need to install the required Python packages. You can do this using pip: 52 | 53 | ```bash 54 | pip install -r requirements.txt 55 | ``` 56 | 57 | ## Usage 58 | 59 | To run the server, execute the `coding_todo.py` script. 60 | 61 | ```bash 62 | python coding_todo.py 63 | ``` 64 | 65 | This will start the MCP server, making its tools and resources available to MCP clients. 66 | ``` -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- ``` 1 | mcp-sdk 2 | pydantic 3 | ``` -------------------------------------------------------------------------------- /minimal_server.py: -------------------------------------------------------------------------------- ```python 1 | #!/usr/bin/env python 2 | import asyncio 3 | from mcp.server.fastmcp import FastMCP 4 | 5 | mcp = FastMCP("minimal-server") 6 | 7 | @mcp.tool() 8 | def hello_world() -> str: 9 | """A simple hello world tool""" 10 | return "Hello, world from minimal MCP server!" 11 | 12 | if __name__ == "__main__": 13 | print("Starting MCP server...") 14 | mcp.run() 15 | ``` -------------------------------------------------------------------------------- /coding_todo.py: -------------------------------------------------------------------------------- ```python 1 | import asyncio 2 | from datetime import datetime 3 | from enum import Enum 4 | from typing import Dict, List, Optional 5 | 6 | from mcp.server.fastmcp import FastMCP, Context 7 | from pydantic import BaseModel 8 | 9 | # Todo status enum 10 | class TodoStatus(str, Enum): 11 | PENDING = "pending" 12 | IN_PROGRESS = "in_progress" 13 | COMPLETED = "completed" 14 | 15 | # Todo model with coding project specific fields 16 | class Todo(BaseModel): 17 | id: str 18 | title: str 19 | description: str 20 | status: TodoStatus = TodoStatus.PENDING 21 | created_at: datetime = datetime.now() 22 | updated_at: Optional[datetime] = None 23 | project: Optional[str] = None 24 | priority: int = 1 # 1 (lowest) to 5 (highest) 25 | tags: List[str] = [] 26 | 27 | # Store todos in memory 28 | todos: Dict[str, Todo] = {} 29 | 30 | # Create an MCP server using FastMCP 31 | mcp = FastMCP("coding-todo-server") 32 | 33 | # Resource to list all todos 34 | @mcp.resource("todo://list") 35 | def get_todo_list() -> str: 36 | """List of all coding project todos""" 37 | if not todos: 38 | return "No todos found." 39 | 40 | result = "# Coding Project Todos\n\n" 41 | for todo_id, todo in todos.items(): 42 | status_marker = "[ ]" if todo.status != TodoStatus.COMPLETED else "[x]" 43 | result += f"{status_marker} **{todo.title}** (ID: {todo_id}, Priority: {todo.priority})\n" 44 | if todo.tags: 45 | result += f" Tags: {', '.join(todo.tags)}\n" 46 | return result 47 | 48 | # Resource to view a specific todo 49 | @mcp.resource("todo://item/{todo_id}") 50 | def get_todo_item(todo_id: str) -> str: 51 | """Get details of a specific todo item""" 52 | if todo_id not in todos: 53 | raise ValueError(f"Todo not found: {todo_id}") 54 | 55 | todo = todos[todo_id] 56 | result = f"# Todo: {todo.title}\n\n" 57 | result += f"**Status:** {todo.status.value}\n" 58 | result += f"**Priority:** {todo.priority}/5\n" 59 | result += f"**Created:** {todo.created_at.strftime('%Y-%m-%d %H:%M')}\n" 60 | if todo.updated_at: 61 | result += f"**Updated:** {todo.updated_at.strftime('%Y-%m-%d %H:%M')}\n" 62 | if todo.project: 63 | result += f"**Project:** {todo.project}\n" 64 | if todo.tags: 65 | result += f"**Tags:** {', '.join(todo.tags)}\n" 66 | result += f"\n**Description:**\n{todo.description}\n" 67 | return result 68 | 69 | # Prompt to summarize todos 70 | @mcp.prompt() 71 | def summarize_todos(status: str = "pending", project: str = None) -> str: 72 | """Creates a summary of todos with optional filtering 73 | 74 | Args: 75 | status: Filter by status (pending/in_progress/completed/all) 76 | project: Filter by project name 77 | """ 78 | filtered_todos = list(todos.values()) 79 | if status != "all": 80 | filtered_todos = [t for t in filtered_todos if t.status.value == status] 81 | if project: 82 | filtered_todos = [t for t in filtered_todos if t.project == project] 83 | 84 | status_text = f"{status} " if status != "all" else "" 85 | project_text = f"for project {project} " if project else "" 86 | 87 | return f"""Here are the current {status_text}todos {project_text}to summarize: 88 | 89 | {chr(10).join(f"- {t.title} (Priority: {t.priority}): {t.description}" for t in filtered_todos)} 90 | 91 | Please provide a concise summary of these todos and suggest an approach to tackle them efficiently.""" 92 | 93 | # Prompt to suggest which todo to tackle next 94 | @mcp.prompt() 95 | def suggest_next_todo() -> str: 96 | """Suggests which todo to tackle next based on priority and status""" 97 | pending_todos = [t for t in todos.values() if t.status != TodoStatus.COMPLETED] 98 | 99 | if not pending_todos: 100 | return "There are no pending todos. Would you like suggestions for new coding tasks to add?" 101 | 102 | # Sort by priority (highest first) 103 | sorted_todos = sorted(pending_todos, key=lambda t: (-t.priority, t.created_at)) 104 | 105 | return f"""Here are the current pending todos in order of priority: 106 | 107 | {chr(10).join(f"- {t.title} (Priority: {t.priority}, Status: {t.status.value}): {t.description}" for t in sorted_todos)} 108 | 109 | Based on these todos, which one should I tackle next and why? Please provide a brief recommendation.""" 110 | 111 | # Tool to add a new todo 112 | @mcp.tool() 113 | def add_todo(title: str, description: str, project: str = None, priority: int = 1, tags: List[str] = None) -> str: 114 | """Add a new todo item 115 | 116 | Args: 117 | title: Title of the todo item 118 | description: Detailed description of the todo item 119 | project: Project name (optional) 120 | priority: Priority from 1 (lowest) to 5 (highest) 121 | tags: List of tags related to the todo 122 | """ 123 | # Generate a new todo ID 124 | todo_id = f"todo{len(todos) + 1}" 125 | 126 | new_todo = Todo( 127 | id=todo_id, 128 | title=title, 129 | description=description, 130 | project=project, 131 | priority=priority, 132 | tags=tags or [], 133 | ) 134 | 135 | todos[todo_id] = new_todo 136 | 137 | return f"Added todo '{title}' with ID: {todo_id}" 138 | 139 | # Tool to update the status of a todo 140 | @mcp.tool() 141 | def update_todo_status(id: str, status: str) -> str: 142 | """Update the status of a todo item 143 | 144 | Args: 145 | id: The ID of the todo item 146 | status: New status (pending/in_progress/completed) 147 | """ 148 | if id not in todos: 149 | raise ValueError(f"Todo not found: {id}") 150 | 151 | try: 152 | todos[id].status = TodoStatus(status) 153 | todos[id].updated_at = datetime.now() 154 | except ValueError: 155 | raise ValueError(f"Invalid status: {status}. Must be one of: pending, in_progress, completed") 156 | 157 | return f"Updated todo '{todos[id].title}' status to {status}" 158 | 159 | # Tool to delete a todo 160 | @mcp.tool() 161 | def delete_todo(id: str) -> str: 162 | """Delete a todo item 163 | 164 | Args: 165 | id: The ID of the todo item to delete 166 | """ 167 | if id not in todos: 168 | raise ValueError(f"Todo not found: {id}") 169 | 170 | title = todos[id].title 171 | del todos[id] 172 | 173 | return f"Deleted todo '{title}' (ID: {id})" 174 | 175 | # Tool to update a todo's details 176 | @mcp.tool() 177 | def update_todo(id: str, title: str = None, description: str = None, project: str = None, 178 | priority: int = None, tags: List[str] = None) -> str: 179 | """Update a todo item's details 180 | 181 | Args: 182 | id: The ID of the todo item 183 | title: New title (optional) 184 | description: New description (optional) 185 | project: New project name (optional) 186 | priority: New priority from 1 (lowest) to 5 (highest) (optional) 187 | tags: New list of tags (optional) 188 | """ 189 | if id not in todos: 190 | raise ValueError(f"Todo not found: {id}") 191 | 192 | todo = todos[id] 193 | 194 | if title is not None: 195 | todo.title = title 196 | 197 | if description is not None: 198 | todo.description = description 199 | 200 | if project is not None: 201 | todo.project = project 202 | 203 | if priority is not None: 204 | if not 1 <= priority <= 5: 205 | raise ValueError("Priority must be between 1 and 5") 206 | todo.priority = priority 207 | 208 | if tags is not None: 209 | todo.tags = tags 210 | 211 | # Update the timestamp 212 | todo.updated_at = datetime.now() 213 | 214 | return f"Updated todo '{todo.title}' (ID: {id})" 215 | 216 | # Initialize with example todos 217 | def initialize_example_todos(): 218 | example_todos = [ 219 | Todo( 220 | id="todo1", 221 | title="Implement user authentication", 222 | description="Add JWT-based authentication to the API endpoints", 223 | priority=4, 224 | project="Backend API", 225 | tags=["backend", "security"], 226 | ), 227 | Todo( 228 | id="todo2", 229 | title="Fix CSS responsiveness", 230 | description="The dashboard layout breaks on mobile devices", 231 | priority=3, 232 | project="Frontend UI", 233 | tags=["frontend", "css", "bugfix"], 234 | ), 235 | Todo( 236 | id="todo3", 237 | title="Write unit tests", 238 | description="Create tests for the new data processing module", 239 | priority=2, 240 | project="Backend API", 241 | tags=["testing", "quality"], 242 | ), 243 | ] 244 | 245 | for todo in example_todos: 246 | todos[todo.id] = todo 247 | 248 | # Initialize and run server 249 | if __name__ == "__main__": 250 | initialize_example_todos() 251 | mcp.run() ```