Ravenight13/simple-task-tracker-mcp
If you are the rightful owner of simple-task-tracker-mcp and would like to certify it and/or have it hosted online, please leave a comment on the right or send an email to dayong@mcphub.com.
The Task MCP Server is a lightweight Model Context Protocol server designed for efficient task and subtask tracking during agentic AI development.
Task MCP Server
A lightweight Model Context Protocol (MCP) server for task and subtask tracking during agentic AI development. Provides isolated SQLite databases per project workspace for Claude Code and Claude Desktop integration.
What's New in v0.4.0 🎉
Released: 2025-11-02
- 🛡️ Workspace Metadata Tracking: Automatic capture of workspace context on task creation
- 🔍 Workspace Validation:
validate_task_workspacetool to detect cross-project contamination - 📋 Comprehensive Audit:
audit_workspace_integritytool for full workspace health checks - 📖 Audit Guide: Complete documentation in
- 🔐 Contamination Prevention: Early detection and cleanup of misplaced tasks
All changes are backward compatible with v0.3.0. See for full details.
Previous Releases
v0.3.0 (2025-10-29)
- 🏢 Entity System: Track files, vendors, and other entities with many-to-many task relationships
- 🔗 Entity Links: 7 new MCP tools for entity CRUD and linking to tasks
- 📊 Generic Metadata: Flexible JSON storage for entity-specific data
- 🗂️ Tag-based Discovery: Filter entities by type and tags for quick discovery
v0.2.0 (2025-10-27)
- ✨ Auto-capture conversation ID in
created_byfield - 🕐 ISO 8601 timestamp format
- 🔧 True partial updates for tasks
Features
🎯 Core Capabilities
- Project Isolation: Separate SQLite database per workspace (auto-detected)
- Task Hierarchy: Parent/child task relationships with unlimited nesting
- Dependencies: Explicit task dependencies with validation
- Status Tracking: State machine workflow (todo → in_progress → blocked → done → cancelled)
- Soft Delete: 30-day retention before permanent deletion
- Full-Text Search: Search tasks by title and description
- Tag Organization: Space-separated tags with normalization
- Concurrent Access: SQLite WAL mode for simultaneous Claude Code + Desktop reads
- Entity Tracking: Track files, vendors, and other entities with many-to-many task relationships
- Generic Metadata: Flexible JSON storage for entity-specific data
- Entity Links: Many-to-many relationships between tasks and entities with soft delete cascade
- Workspace Validation: Automatic metadata capture and cross-project contamination detection
- Integrity Audits: Comprehensive workspace health checks with actionable recommendations
🔒 Data Validation
- Description length limit (10,000 characters)
- Status/Priority enum validation
- Blocker reason requirement when status='blocked'
- Dependency resolution before task completion
- Tag normalization (lowercase, single spaces)
📊 Database Architecture
- Per-project databases:
~/.task-mcp/databases/project_{hash}.db - Master registry:
~/.task-mcp/master.db(cross-client discovery) - Workspace detection: Explicit parameter → TASK_MCP_WORKSPACE env → current directory
- Path hashing: SHA256 truncated to 8 characters for safe filenames
Installation
Prerequisites
- Python 3.9 or higher
- uv package manager (recommended)
Install from Source
# Clone repository
git clone https://github.com/Ravenight13/simple-task-tracker-mcp.git
cd simple-task-tracker-mcp
# Install dependencies
uv sync --dev
# Verify installation
uv run task-mcp --help
Install as Package
# Install with uv
uv pip install git+https://github.com/Ravenight13/simple-task-tracker-mcp.git
# Or with pip
pip install git+https://github.com/Ravenight13/simple-task-tracker-mcp.git
Configuration
Claude Desktop
Add to claude_desktop_config.json:
{
"mcpServers": {
"task-mcp": {
"command": "uv",
"args": ["run", "task-mcp"],
"env": {
"TASK_MCP_WORKSPACE": "/path/to/your/project"
}
}
}
}
Location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json - Linux:
~/.config/Claude/claude_desktop_config.json
Claude Code
For Claude Code, the server auto-detects the workspace from the current working directory. Add to .claude/config.json:
{
"mcpServers": {
"task-mcp": {
"command": "uv",
"args": ["run", "task-mcp"]
}
}
}
The TASK_MCP_WORKSPACE environment variable is automatically set by Claude Code based on the active project.
Quick Start
Create a Task
# Via MCP tool
create_task(
title="Implement authentication API",
description="Add JWT-based authentication to the REST API",
status="todo",
priority="high",
tags="backend api authentication"
)
List Tasks
# Get all tasks
list_tasks()
# Filter by status
list_tasks(status="in_progress")
# Filter by priority and tags
list_tasks(priority="high", tags="backend")
Search Tasks
# Full-text search
search_tasks("authentication")
Update Task Status
update_task(
task_id=1,
status="in_progress"
)
Create Subtasks
# Create parent task
parent = create_task(title="Build Authentication System")
# Create subtasks
create_task(
title="Design database schema",
parent_task_id=parent['id']
)
create_task(
title="Implement JWT generation",
parent_task_id=parent['id']
)
# Get full task tree
get_task_tree(parent['id'])
Task Dependencies
# Create tasks with dependencies
task1 = create_task(title="Set up database")
task2 = create_task(title="Create API endpoints", depends_on=[task1['id']])
# Get next actionable tasks (no unresolved dependencies)
get_next_tasks()
MCP Tools Reference
Core CRUD Operations
create_task
Create a new task with validation.
Parameters:
title(str, required): Task titleworkspace_path(str | None): Optional workspace path (auto-detected)description(str | None): Task description (max 10k chars)status(str): Task status (default: "todo")priority(str): Priority level (default: "medium")parent_task_id(int | None): Parent task ID for subtasksdepends_on(list[int] | None): List of task IDs this depends ontags(str | None): Space-separated tagsfile_references(list[str] | None): List of file pathscreated_by(str | None): Conversation ID
Returns: Task object with all fields
Example:
task = create_task(
title="Implement user registration",
description="Create registration endpoint with email validation",
priority="high",
tags="backend api user-management",
file_references=["src/api/auth.py", "tests/test_auth.py"]
)
get_task
Fetch a single task by ID.
Parameters:
task_id(int, required): Task IDworkspace_path(str | None): Optional workspace path
Returns: Task object
Example:
task = get_task(task_id=1)
print(task['title'], task['status'])
update_task
Update an existing task with validation.
Parameters:
task_id(int, required): Task IDworkspace_path(str | None): Optional workspace pathtitle(str | None): Updated titledescription(str | None): Updated descriptionstatus(str | None): Updated statuspriority(str | None): Updated priorityblocker_reason(str | None): Required when status='blocked'- Other fields as needed
Returns: Updated task object
Example:
# Mark task as blocked
update_task(
task_id=1,
status="blocked",
blocker_reason="Waiting for API key from infrastructure team"
)
# Complete task
update_task(task_id=1, status="done")
list_tasks
List tasks with optional filters.
Parameters:
workspace_path(str | None): Optional workspace pathstatus(str | None): Filter by statuspriority(str | None): Filter by priorityparent_task_id(int | None): Filter by parent tasktags(str | None): Filter by tags (partial match)
Returns: List of task objects
Example:
# Get all high-priority in-progress tasks
tasks = list_tasks(status="in_progress", priority="high")
# Get all subtasks of a parent
subtasks = list_tasks(parent_task_id=5)
# Get tasks by tag
backend_tasks = list_tasks(tags="backend")
search_tasks
Full-text search on task title and description.
Parameters:
search_term(str, required): Search termworkspace_path(str | None): Optional workspace path
Returns: List of matching tasks
Example:
results = search_tasks("authentication")
delete_task
Soft delete a task (sets deleted_at timestamp).
Parameters:
task_id(int, required): Task IDworkspace_path(str | None): Optional workspace pathcascade(bool): If True, also delete all subtasks (default: False)
Returns: Success confirmation with deleted count
Example:
# Delete single task
delete_task(task_id=1)
# Delete task and all subtasks
delete_task(task_id=1, cascade=True)
Advanced Query Tools
get_task_tree
Get task with all descendant subtasks (recursive).
Parameters:
task_id(int, required): Root task IDworkspace_path(str | None): Optional workspace path
Returns: Task object with nested 'subtasks' field
Example:
tree = get_task_tree(task_id=1)
print(tree['title'])
for subtask in tree['subtasks']:
print(f" - {subtask['title']}")
get_blocked_tasks
Get all tasks with status='blocked'.
Parameters:
workspace_path(str | None): Optional workspace path
Returns: List of blocked tasks with blocker_reason
Example:
blocked = get_blocked_tasks()
for task in blocked:
print(f"{task['title']}: {task['blocker_reason']}")
get_next_tasks
Get actionable tasks (status='todo', no unresolved dependencies).
Parameters:
workspace_path(str | None): Optional workspace path
Returns: List of actionable tasks sorted by priority DESC, created_at ASC
Example:
next_tasks = get_next_tasks()
if next_tasks:
print(f"Next task to work on: {next_tasks[0]['title']}")
Maintenance Tools
cleanup_deleted_tasks
Permanently delete tasks soft-deleted more than N days ago.
Parameters:
workspace_path(str | None): Optional workspace pathdays(int): Retention days (default: 30)
Returns: Count of purged tasks
Example:
# Purge tasks deleted >30 days ago
result = cleanup_deleted_tasks()
print(f"Purged {result['purged_count']} tasks")
# Custom retention period
cleanup_deleted_tasks(days=60)
Project Management Tools
list_projects
List all known projects from master database.
Returns: List of projects sorted by last_accessed DESC
Example:
projects = list_projects()
for project in projects:
print(f"{project['friendly_name']}: {project['workspace_path']}")
get_project_info
Get project metadata and task statistics.
Parameters:
workspace_path(str, required): Project workspace path
Returns: Project info with task counts by status and priority
Example:
info = get_project_info("/path/to/project")
print(f"Total tasks: {info['total_tasks']}")
print(f"By status: {info['by_status']}")
print(f"Blocked: {info['blocked_count']}")
set_project_name
Set friendly name for a project.
Parameters:
workspace_path(str, required): Project workspace pathfriendly_name(str, required): Human-readable project name
Returns: Success confirmation
Example:
set_project_name("/path/to/project", "My Awesome Project")
Entity Tools
create_entity
Create a new entity (file, vendor, or other type).
Parameters:
entity_type(str, required): Entity type ("file" or "other")name(str, required): Human-readable entity nameidentifier(str | None): Unique identifier (e.g., file path, vendor code)workspace_path(str | None): Optional workspace path (auto-detected)description(str | None): Entity descriptionmetadata(dict | str | None): Generic JSON metadatatags(str | None): Space-separated tagscreated_by(str | None): Conversation ID (auto-captured)
Returns: Entity object with all fields
Example:
# Create vendor entity
vendor = create_entity(
entity_type="other",
name="ABC Insurance Company",
identifier="ABC-INS",
metadata={"phase": "active", "formats": ["xlsx", "pdf"]},
tags="vendor insurance active"
)
# Create file entity
file_entity = create_entity(
entity_type="file",
name="Authentication Controller",
identifier="/src/auth/login.py",
metadata={"language": "python", "line_count": 250},
tags="backend auth"
)
update_entity
Update an existing entity with partial updates.
Parameters:
entity_id(int, required): Entity IDworkspace_path(str | None): Optional workspace pathname(str | None): Updated nameidentifier(str | None): Updated identifierdescription(str | None): Updated descriptionmetadata(dict | str | None): Updated metadatatags(str | None): Updated tags
Returns: Updated entity object
Example:
# Update vendor metadata
update_entity(
entity_id=1,
metadata={"phase": "inactive", "formats": ["xlsx"]},
tags="vendor insurance inactive"
)
get_entity
Retrieve a single entity by ID.
Parameters:
entity_id(int, required): Entity IDworkspace_path(str | None): Optional workspace path
Returns: Entity object with all fields
Example:
entity = get_entity(entity_id=1)
print(f"{entity['name']}: {entity['entity_type']}")
list_entities
List entities with optional filtering.
Parameters:
workspace_path(str | None): Optional workspace pathentity_type(str | None): Filter by entity type ("file" or "other")tags(str | None): Filter by tags (partial match)
Returns: List of entity objects
Example:
# Get all vendor entities
vendors = list_entities(entity_type="other", tags="vendor")
# Get all file entities with auth tag
auth_files = list_entities(entity_type="file", tags="auth")
delete_entity
Soft delete an entity (cascades to links).
Parameters:
entity_id(int, required): Entity IDworkspace_path(str | None): Optional workspace path
Returns: Success confirmation
Example:
delete_entity(entity_id=1)
link_entity_to_task
Create a many-to-many relationship between a task and an entity.
Parameters:
task_id(int, required): Task IDentity_id(int, required): Entity IDworkspace_path(str | None): Optional workspace pathcreated_by(str | None): Conversation ID (auto-captured)
Returns: Link object with link_id, task_id, entity_id, created_at
Example:
# Link vendor to commission processing task
link_entity_to_task(task_id=5, entity_id=1)
get_task_entities
Get all entities linked to a task.
Parameters:
task_id(int, required): Task IDworkspace_path(str | None): Optional workspace path
Returns: List of entity objects with link metadata (link_created_at, link_created_by)
Example:
# Get all vendors for a task
entities = get_task_entities(task_id=5)
for entity in entities:
print(f"{entity['name']} ({entity['entity_type']})")
print(f" Linked at: {entity['link_created_at']}")
get_entity_tasks
Get all tasks linked to an entity (reverse query).
Parameters:
entity_id(int, required): Entity IDworkspace_path(str | None): Optional workspace pathstatus(str | None): Filter by task status (todo, in_progress, done, etc.)priority(str | None): Filter by task priority (low, medium, high)
Returns: List of task objects with link metadata (link_created_at, link_created_by)
Example:
# Get all tasks for ABC Insurance vendor
tasks = get_entity_tasks(entity_id=7)
for task in tasks:
print(f"{task['title']} - {task['status']}")
print(f" Linked at: {task['link_created_at']}")
# Get only high-priority tasks for a vendor
high_priority_tasks = get_entity_tasks(
entity_id=7,
priority="high"
)
# Get only in-progress tasks for a vendor
in_progress_tasks = get_entity_tasks(
entity_id=7,
status="in_progress"
)
Example: Vendor Entity Management
Track insurance vendors and link them to commission processing tasks:
# Create vendor entity
vendor = create_entity(
entity_type="other",
name="ABC Insurance Company",
identifier="ABC-INS",
metadata={"phase": "active", "formats": ["xlsx", "pdf"]},
tags="vendor insurance active"
)
# Create commission processing task
task = create_task(
title="Process ABC Insurance Q4 commissions",
priority="high",
tags="commission q4 vendor"
)
# Link vendor to task
link_entity_to_task(task_id=task["id"], entity_id=vendor["id"])
# Get all vendors for task (forward query)
vendors = get_task_entities(task_id=task["id"])
for v in vendors:
metadata = json.loads(v['metadata'])
print(f"{v['name']}: Phase {metadata['phase']}, Formats: {metadata['formats']}")
# Get all tasks for ABC Insurance vendor (reverse query)
vendor_tasks = get_entity_tasks(entity_id=vendor["id"])
for t in vendor_tasks:
print(f"Task: {t['title']} ({t['status']}) - Priority: {t['priority']}")
Development
Running Tests
# Run all tests
uv run pytest
# Run with coverage
uv run pytest --cov=task_mcp --cov-report=html
# Run specific test file
uv run pytest tests/test_database.py -v
# Run specific test
uv run pytest tests/test_database.py::TestDatabase::test_get_connection_creates_database
Quality Gates
# Linting
uv run ruff check src/ tests/
# Type checking
uv run mypy src/
# Format code
uv run black src/ tests/
Project Structure
task-mcp/
├── src/task_mcp/
│ ├── __init__.py
│ ├── server.py # FastMCP server + tool registration
│ ├── database.py # Project database operations
│ ├── master.py # Master database (project registry)
│ ├── models.py # Pydantic data models
│ └── utils.py # Utilities (workspace detection, hashing)
├── tests/
│ ├── test_database.py # Database integration tests
│ ├── test_models.py # Model validation tests
│ └── test_mcp_tools.py # MCP tools integration tests
├── pyproject.toml # Project configuration
└── README.md
Database Schema
Tasks Table (tasks):
CREATE TABLE tasks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
title TEXT NOT NULL,
description TEXT,
status TEXT NOT NULL CHECK(status IN ('todo', 'in_progress', 'blocked', 'done', 'cancelled')),
priority TEXT DEFAULT 'medium' CHECK(priority IN ('low', 'medium', 'high')),
parent_task_id INTEGER,
depends_on TEXT, -- JSON array of task IDs
tags TEXT, -- Space-separated tags
blocker_reason TEXT,
file_references TEXT, -- JSON array of file paths
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP,
deleted_at TIMESTAMP,
FOREIGN KEY (parent_task_id) REFERENCES tasks(id)
);
-- Indexes
CREATE INDEX idx_status ON tasks(status);
CREATE INDEX idx_parent ON tasks(parent_task_id);
CREATE INDEX idx_deleted ON tasks(deleted_at);
CREATE INDEX idx_tags ON tasks(tags);
Entities Table (entities):
CREATE TABLE entities (
id INTEGER PRIMARY KEY AUTOINCREMENT,
entity_type TEXT NOT NULL CHECK(entity_type IN ('file', 'other')),
name TEXT NOT NULL,
identifier TEXT,
description TEXT,
metadata TEXT, -- JSON storage for flexible data
tags TEXT, -- Space-separated tags
created_by TEXT, -- Audit: who created
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_by TEXT, -- Audit: who last updated
deleted_at TIMESTAMP
);
-- Indexes
CREATE INDEX idx_entity_type ON entities(entity_type);
CREATE INDEX idx_entity_deleted ON entities(deleted_at);
CREATE INDEX idx_entity_tags ON entities(tags);
CREATE UNIQUE INDEX idx_entity_identifier ON entities(identifier, entity_type)
WHERE deleted_at IS NULL; -- Partial unique index
Task-Entity Links Table (task_entity_links):
CREATE TABLE task_entity_links (
id INTEGER PRIMARY KEY AUTOINCREMENT,
task_id INTEGER NOT NULL,
entity_id INTEGER NOT NULL,
created_by TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (task_id) REFERENCES tasks(id),
FOREIGN KEY (entity_id) REFERENCES entities(id),
UNIQUE(task_id, entity_id)
);
-- Indexes for bidirectional queries
CREATE INDEX idx_link_task ON task_entity_links(task_id);
CREATE INDEX idx_link_entity ON task_entity_links(entity_id);
Projects Table (master.db):
CREATE TABLE projects (
id TEXT PRIMARY KEY, -- 8-char hash
workspace_path TEXT UNIQUE NOT NULL,
friendly_name TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
last_accessed TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_last_accessed ON projects(last_accessed);
Troubleshooting
Database Location
Databases are stored at:
- Project databases:
~/.task-mcp/databases/project_{hash}.db - Master database:
~/.task-mcp/master.db
To inspect manually:
sqlite3 ~/.task-mcp/master.db "SELECT * FROM projects;"
Workspace Detection Issues
Check workspace resolution priority:
- Explicit
workspace_pathparameter (highest priority) TASK_MCP_WORKSPACEenvironment variable- Current working directory (fallback)
Verify workspace detection:
from task_mcp.utils import resolve_workspace
print(resolve_workspace())
Reset Project Database
# Find project hash
python -c "from task_mcp.utils import hash_workspace_path; print(hash_workspace_path('/path/to/project'))"
# Remove database
rm ~/.task-mcp/databases/project_{hash}.db
License
MIT License - see LICENSE file
Contributing
Contributions welcome! Please:
- Fork the repository
- Create a feature branch
- Add tests for new functionality
- Ensure all quality gates pass (ruff, mypy, pytest)
- Submit a pull request
Acknowledgments
Built with:
- FastMCP - MCP server framework
- Pydantic v2 - Data validation
- SQLite - Embedded database