Skip to content
mcp-tools-skill logo

MCP Tools Skill

mcp-tools-skill

Define and implement MCP tools for task CRUD operations

SKILL.md

Full skill instructions

MCP Tools Skill

Instructions

This skill provides guidance for defining and implementing Model Context Protocol (MCP) tools for task CRUD operations that AI agents can use.

Project Structure

backend/
├── mcp/
│   ├── __init__.py
│   ├── server.py              # MCP server implementation
│   ├── tools/
│   │   ├── __init__.py
│   │   ├── task_tools.py      # Task CRUD tools
│   │   └── user_tools.py      # User-related tools
│   └── schemas/
│       ├── __init__.py
│       └── tool_schemas.py    # JSON schemas for tools

MCP Server Setup

# backend/​mcp/​server.py
from mcp.server import Server
from mcp.types import Tool, TextContent
from contextlib import asynccontextmanager
import json

from database import get_db, AsyncSessionLocal
from tasks.service import TaskService
from auth.dependencies import get_current_user

app = Server("todo-mcp-server")

@asynccontextmanager
async def lifespan(app):
    # Startup: Initialize resources
    print("MCP Server starting...")
    yield
    # Shutdown: Cleanup resources
    print("MCP Server shutting down...")

# Dependency for getting database session
async def get_db_session():
    async with AsyncSessionLocal() as session:
        yield session

# Dependency for getting current user
async def get_current_user_context():
    # In production, this would extract user from context
    return {"id": 1, "email": "[email protected]"}

Task CRUD Tools

# backend/​mcp/​tools/​task_tools.py
from mcp.types import Tool, TextContent
from typing import Optional, List
from datetime import datetime
from pydantic import BaseModel

from database import AsyncSessionLocal
from tasks.service import TaskService
from auth.dependencies import get_current_user

class GetTasksInput(BaseModel):
    """Input for getting tasks"""
    completed: Optional[bool] = None
    limit: Optional[int] = 100
    offset: Optional[int] = 0

class CreateTaskInput(BaseModel):
    """Input for creating a task"""
    title: str
    description: Optional[str] = None
    due_date: Optional[datetime] = None
    priority: Optional[str] = "medium"

class UpdateTaskInput(BaseModel):
    """Input for updating a task"""
    task_id: int
    title: Optional[str] = None
    description: Optional[str] = None
    completed: Optional[bool] = None
    due_date: Optional[datetime] = None
    priority: Optional[str] = None

class DeleteTaskInput(BaseModel):
    """Input for deleting a task"""
    task_id: int

# Tool: Get Tasks
async def get_tasks(input_json: str) -> List[TextContent]:
    """Get all tasks for the current user, optionally filtered by completion status."""
    try:
        data = GetTasksInput.model_validate_json(input_json)
    except Exception as e:
        return [TextContent(type="text", text=f"Error parsing input: {e}")]

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)
        tasks = await service.get_tasks(
            owner_id=user.id,
            completed=data.completed,
            limit=data.limit,
            offset=data.offset
        )

        result = [
            {
                "id": task.id,
                "title": task.title,
                "description": task.description,
                "completed": task.completed,
                "due_date": task.due_date.isoformat() if task.due_date else None,
                "priority": task.priority,
                "created_at": task.created_at.isoformat(),
            }
            for task in tasks
        ]

        return [TextContent(
            type="text",
            text=json.dumps({"tasks": result, "count": len(result)}, indent=2)
        )]

# Tool: Create Task
async def create_task(input_json: str) -> List[TextContent]:
    """Create a new task for the current user."""
    try:
        data = CreateTaskInput.model_validate_json(input_json)
    except Exception as e:
        return [TextContent(type="text", text=f"Error parsing input: {e}")]

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)
        task = await service.create_task(
            task_data={
                "title": data.title,
                "description": data.description,
                "due_date": data.due_date,
                "priority": data.priority,
            },
            owner_id=user.id
        )

        return [TextContent(
            type="text",
            text=json.dumps({
                "message": f"Task '{task.title}' created successfully",
                "task": {
                    "id": task.id,
                    "title": task.title,
                    "created_at": task.created_at.isoformat(),
                }
            }, indent=2)
        )]

# Tool: Update Task
async def update_task(input_json: str) -> List[TextContent]:
    """Update an existing task."""
    try:
        data = UpdateTaskInput.model_validate_json(input_json)
    except Exception as e:
        return [TextContent(type="text", text=f"Error parsing input: {e}")]

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)

        update_data = {}
        if data.title is not None:
            update_data["title"] = data.title
        if data.description is not None:
            update_data["description"] = data.description
        if data.completed is not None:
            update_data["completed"] = data.completed
        if data.due_date is not None:
            update_data["due_date"] = data.due_date
        if data.priority is not None:
            update_data["priority"] = data.priority

        task = await service.update_task(
            task_id=data.task_id,
            update_data=update_data,
            owner_id=user.id
        )

        if not task:
            return [TextContent(
                type="text",
                text=f"Task {data.task_id} not found or you don't have permission to update it."
            )]

        return [TextContent(
            type="text",
            text=f"Task {task.id} updated successfully"
        )]

# Tool: Delete Task
async def delete_task(input_json: str) -> List[TextContent]:
    """Delete a task."""
    try:
        data = DeleteTaskInput.model_validate_json(input_json)
    except Exception as e:
        return [TextContent(type="text", text=f"Error parsing input: {e}")]

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)
        success = await service.delete_task(
            task_id=data.task_id,
            owner_id=user.id
        )

        if not success:
            return [TextContent(
                type="text",
                text=f"Task {data.task_id} not found or you don't have permission to delete it."
            )]

        return [TextContent(
            type="text",
            text=f"Task {data.task_id} deleted successfully"
        )]

Register Tools with Server

# backend/​mcp/​server.py (continued)
from mcp.tools.task_tools import (
    get_tasks,
    create_task,
    update_task,
    delete_task,
)

# Define tool definitions for discovery
TOOLS = [
    Tool(
        name="get_tasks",
        description="Get all tasks for the current user, optionally filtered by completion status",
        inputSchema={
            "type": "object",
            "properties": {
                "completed": {
                    "type": "boolean",
                    "description": "Filter by completion status"
                },
                "limit": {
                    "type": "integer",
                    "description": "Maximum number of tasks to return",
                    "default": 100
                },
                "offset": {
                    "type": "integer",
                    "description": "Number of tasks to skip",
                    "default": 0
                }
            }
        }
    ),
    Tool(
        name="create_task",
        description="Create a new task for the current user",
        inputSchema={
            "type": "object",
            "properties": {
                "title": {
                    "type": "string",
                    "description": "Task title (required)"
                },
                "description": {
                    "type": "string",
                    "description": "Optional task description"
                },
                "due_date": {
                    "type": "string",
                    "format": "date-time",
                    "description": "Optional due date in ISO format"
                },
                "priority": {
                    "type": "string",
                    "enum": ["low", "medium", "high"],
                    "description": "Task priority",
                    "default": "medium"
                }
            },
            "required": ["title"]
        }
    ),
    Tool(
        name="update_task",
        description="Update an existing task's properties",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {
                    "type": "integer",
                    "description": "ID of the task to update (required)"
                },
                "title": {
                    "type": "string",
                    "description": "New task title"
                },
                "description": {
                    "type": "string",
                    "description": "New task description"
                },
                "completed": {
                    "type": "boolean",
                    "description": "Completion status"
                },
                "due_date": {
                    "type": "string",
                    "format": "date-time",
                    "description": "New due date"
                },
                "priority": {
                    "type": "string",
                    "enum": ["low", "medium", "high"],
                    "description": "New priority"
                }
            },
            "required": ["task_id"]
        }
    ),
    Tool(
        name="delete_task",
        description="Delete a task",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {
                    "type": "integer",
                    "description": "ID of the task to delete (required)"
                }
            },
            "required": ["task_id"]
        }
    ),
]

@app.list_tools()
async def list_tools():
    return TOOLS

@app.call_tool()
async def call_tool(name: str, arguments: str) -> List[TextContent]:
    """Handle tool calls from the client."""
    if name == "get_tasks":
        return await get_tasks(arguments)
    elif name == "create_task":
        return await create_task(arguments)
    elif name == "update_task":
        return await update_task(arguments)
    elif name == "delete_task":
        return await delete_task(arguments)
    else:
        return [TextContent(
            type="text",
            text=f"Unknown tool: {name}"
        )]

Examples

User-Related MCP Tools

# backend/​mcp/​tools/​user_tools.py
from mcp.types import Tool, TextContent
from typing import Optional
from pydantic import BaseModel
import json

class GetUserProfileInput(BaseModel):
    include_tasks_count: bool = False

class UpdatePreferencesInput(BaseModel):
    theme: Optional[str] = None
    notifications_enabled: Optional[bool] = None
    default_priority: Optional[str] = None

async def get_user_profile(input_json: str) -> List[TextContent]:
    """Get the current user's profile information."""
    data = GetUserProfileInput.model_validate_json(input_json)

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        result = {
            "id": user.id,
            "email": user.email,
            "name": user.name,
            "created_at": user.created_at.isoformat(),
        }

        if data.include_tasks_count:
            service = TaskService(db)
            result["tasks_count"] = {
                "total": await service.count_tasks(user.id),
                "completed": await service.count_tasks(user.id, completed=True),
                "pending": await service.count_tasks(user.id, completed=False),
            }

        return [TextContent(type="text", text=json.dumps(result, indent=2))]

async def update_user_preferences(input_json: str) -> List[TextContent]:
    """Update user preferences."""
    data = UpdatePreferencesInput.model_validate_json(input_json)

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = UserService(db)

        update_data = {}
        if data.theme is not None:
            update_data["theme"] = data.theme
        if data.notifications_enabled is not None:
            update_data["notifications_enabled"] = data.notifications_enabled
        if data.default_priority is not None:
            update_data["default_priority"] = data.default_priority

        await service.update_preferences(user.id, update_data)

        return [TextContent(type="text", text="Preferences updated successfully")]

Search Tool

# backend/​mcp/​tools/​search_tools.py
from mcp.types import Tool, TextContent
from typing import Optional
from pydantic import BaseModel
import json

class SearchTasksInput(BaseModel):
    query: str
    completed: Optional[bool] = None
    limit: Optional[int] = 20

async def search_tasks(input_json: str) -> List[TextContent]:
    """Search tasks by title or description using keyword matching."""
    data = SearchTasksInput.model_validate_json(input_json)

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)
        tasks = await service.search_tasks(
            user.id,
            data.query,
            completed=data.completed,
            limit=data.limit
        )

        result = {
            "query": data.query,
            "results": [
                {
                    "id": task.id,
                    "title": task.title,
                    "description": task.description,
                    "completed": task.completed,
                    "match_type": "title" if data.query.lower() in task.title.lower() else "description"
                }
                for task in tasks
            ],
            "count": len(tasks)
        }

        return [TextContent(type="text", text=json.dumps(result, indent=2))]

Bulk Operations Tool

# backend/​mcp/​tools/​bulk_tools.py
from mcp.types import Tool, TextContent
from typing import List
from pydantic import BaseModel
import json

class BulkCompleteInput(BaseModel):
    task_ids: List[int]
    completed: bool = True

async def bulk_update_tasks(input_json: str) -> List[TextContent]:
    """Bulk update multiple tasks at once."""
    data = BulkCompleteInput.model_validate_json(input_json)

    async with AsyncSessionLocal() as db:
        user = await get_current_user(db)
        service = TaskService(db)

        updated = await service.bulk_update(
            user.id,
            data.task_ids,
            {"completed": data.completed}
        )

        return [TextContent(
            type="text",
            text=f"Updated {updated} tasks successfully"
        )]

MCP Server Entry Point

# backend/​mcp/​__main__.py
from mcp.server.stdio import stdio_server
import asyncio

async def main():
    async with stdio_server() as (read_stream, write_stream):
        await app.run(
            read_stream,
            write_stream,
            app.create_initialization_options()
        )

if __name__ == "__main__":
    asyncio.run(main())

Run MCP Server

# Start MCP server with stdio transport
python -m mcp

Connect from AI Client

// Example: Connecting to MCP server from AI agent
import { MCPClient } from 'mcp-client'

const client = new MCPClient()

async function setupTaskTools() {
  // Connect to MCP server via stdio
  const tools = await client.connect({
    command: 'python',
    args: ['-m', 'mcp'],
    env: {
      DATABASE_URL: process.env.DATABASE_URL,
      JWT_SECRET_KEY: process.env.JWT_SECRET_KEY,
    }
  })

  // Now the AI agent can call tools
  const tasks = await tools.get_tasks({ completed: false })
  return tasks
}

More Productivity & Planning skills

brainstorming logo
Productivity & Planning

brainstorming

Structured design dialogue that validates ideas before implementation begins.

295.4K 385.7K
View
ui-ux-pro-max logo
Productivity & Planning

ui-ux-pro-max

Comprehensive design intelligence for web and mobile UI/UX across 10 technology stacks.

133.1K 383.9K
View
writing-plans logo
Productivity & Planning

writing-plans

Comprehensive implementation plans for multi-step tasks, breaking down specs into bite-sized, testable steps.

295.4K 268K
View
using-superpowers logo
Productivity & Planning

using-superpowers

Introduction to the obra skills system with mandatory skill invocation rules and best practices.

295.4K 259.8K
View
executing-plans logo
Productivity & Planning

executing-plans

Execute a written implementation plan with critical review and task checkpoints.

295.4K 229.1K
View
dispatching-parallel-agents logo
Productivity & Planning

dispatching-parallel-agents

Delegate independent tasks to specialized agents working concurrently with isolated context.

295.4K 206.3K
View
using-git-worktrees logo
Productivity & Planning

using-git-worktrees

Isolated git worktrees with smart directory selection and safety verification.

295.4K 205.2K
View
webapp-testing logo
Productivity & Planning

webapp-testing

Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.

179.7K 170.6K
View
content-strategy logo
Productivity & Planning

content-strategy

Plan searchable and shareable content that drives traffic, builds authority, and generates leads.

53.3K 150.9K
View
repo-intake-and-plan logo
Productivity & Planning

repo-intake-and-plan

README-first repository scanner that extracts commands and classifies reproduction candidates without executing them.

497 139.6K
View
marketing-ideas logo
Productivity & Planning

marketing-ideas

Brainstorm and prioritize marketing strategies tailored to your SaaS stage, budget, and goals.

53.3K 137.2K
View
site-architecture logo
Productivity & Planning

site-architecture

Plan and optimize your website's page hierarchy, navigation, URL structure, and internal linking.

53.3K 112.8K
View

Productivity AI tools

Vimcal logo
Productivity

Vimcal

The world's fastest calendar for remote work

Free
View
SaveDay logo
Productivity

SaveDay

Capture, organize, and utilize your knowledge effortlessly.

Free
View
Any Summary logo
Productivity

Any Summary

Instant Summaries of Audio & Video Interviews with AnySummary

Freemium
View
M
Productivity

Map This

Transform PDFs into engaging mind maps.

Freemium
View
ChatPDF logo
Productivity

ChatPDF

Chat with any PDF instantly

Free
View
I
Productivity

intellisay

Create an optimal daily plan using your voice

Paid
View
A
Productivity

Aurora AI

A productivity platform to centralize organizational knowledge and workflows with contextual AI assistance.

Paid
View
AskYourPDF logo
Productivity

AskYourPDF

AskYourPDF Pricing Plans: Tailored to Your Needs

Free
View