MCP server for AI Agents to manage Dida365 (TickTick) tasks and projects
Project description
dida365-agent-mcp
MCP server for AI agents to manage Dida365 / TickTick tasks and projects
A Python MCP server built on FastMCP that connects AI agents (Claude Code, Cursor, Windsurf, etc.) to the Dida365 / TickTick Open API. Manage tasks and projects through natural language.
Supports both Dida365 (China) and TickTick (International) — switch with a single env var.
Features
- 40 tools — 19 V1 (official Open API) + 21 V2 (private API: tags, search, habits, folders, parent tasks)
- Dual transport —
stdiofor local clients;streamable-httpfor remote agents - Dual platform — Dida365 and TickTick via
DIDA365_REGIONconfig - One-click OAuth — script auto-opens browser, receives callback, saves token
- Docker ready —
docker compose upto deploy - Agent-friendly — structured JSON responses, clear tool annotations, actionable error messages
Deployment
Option A — Local (stdio)
For use with Claude Code, Cursor, Windsurf, and other local MCP clients.
1. Install
git clone https://github.com/linhai0872/dida365-agent-mcp.git
cd dida365-agent-mcp
uv sync
Requires uv and Python 3.12+.
2. Get API Credentials
Go to the developer portal, create an app, and copy the credentials:
- Dida365: https://developer.dida365.com/manage
- TickTick: https://developer.ticktick.com/manage
Set Redirect URI to http://localhost:8000/oauth/callback.
3. Configure
cp .env.example .env
DIDA365_REGION=china # or "international" for TickTick
DIDA365_CLIENT_ID=your_client_id
DIDA365_CLIENT_SECRET=your_client_secret
4. Authorize
uv run python scripts/oauth_flow.py
Browser opens automatically. Token saved to ~/.dida365-agent-mcp/token.json (~180 days).
5. Connect your AI client
Claude Code
Edit ~/.claude/mcp.json:
{
"mcpServers": {
"dida365": {
"command": "uv",
"args": ["--directory", "/path/to/dida365-agent-mcp", "run", "dida365-mcp"]
}
}
}
Cursor
Go to Settings > MCP > Add new global MCP server:
{
"mcpServers": {
"dida365": {
"command": "uv",
"args": ["--directory", "/path/to/dida365-agent-mcp", "run", "dida365-mcp"]
}
}
}
Option B — Remote (Docker + HTTP)
For always-on or team deployments accessible via HTTP.
1. Complete OAuth locally first (Option A steps 1–4)
2. Configure
cp .env.example .env
# Set DIDA365_REGION, DIDA365_ACCESS_TOKEN (from OAuth output)
3. Deploy
docker compose up -d
Connect to http://your-host:8000/mcp.
The container cannot open a browser, so set
DIDA365_ACCESS_TOKENdirectly instead of running the OAuth script inside Docker.
6. (Optional) Enable V2 tools — tags, search, habits, folders, parent tasks
Pick one method in .env:
# Method 1: Session token (manual, most secure, 30-day expiry)
# Browser DevTools → Application → Cookies → copy 't' value
DIDA365_V2_SESSION_TOKEN=your_token
# Method 2: Auto-login (convenient, does not support 2FA)
DIDA365_USERNAME=your_email
DIDA365_PASSWORD=your_password
Without V2 config, you still get 19 V1 tools. With V2, you get all 40.
Try It
Talk to your AI agent naturally:
- "List all my projects"
- "Create a high-priority task 'Review PR' in Work, due tomorrow"
- "What tasks did I complete this week?"
- "Move 'Design review' to the Archive project"
- "Search tasks containing 'meeting'" (V2)
- "List all my tags" (V2)
- "Did I check in my reading habit today?" (V2)
Tools
Task Operations
| Tool | Description |
|---|---|
dida365_create_task |
Create a task with title, project, dates, priority, tags, reminders, recurrence |
dida365_update_task |
Update any task fields (partial update) |
dida365_complete_task |
Mark a task as completed |
dida365_delete_task |
Permanently delete a task |
dida365_get_task |
Get full details of a single task |
dida365_move_task |
Move a task between projects |
Query Operations
| Tool | Description |
|---|---|
dida365_get_project_tasks |
Get all uncompleted tasks in a project |
dida365_filter_tasks |
Filter by project, date range, priority, tags, status |
dida365_get_completed_tasks |
Get completed tasks within a time range |
Project Operations
| Tool | Description |
|---|---|
dida365_list_projects |
List all projects (call this first to get IDs) |
dida365_get_project |
Get project details |
dida365_create_project |
Create a project (list / kanban / timeline view) |
dida365_update_project |
Update project properties |
dida365_delete_project |
Permanently delete a project and all its tasks |
Batch Operations
| Tool | Description |
|---|---|
dida365_get_task_by_id |
Get a task by ID only (no project_id needed) |
dida365_list_undone_tasks |
List undone tasks by date range or project |
dida365_batch_create_tasks |
Batch create multiple tasks |
dida365_batch_update_tasks |
Batch update multiple tasks |
dida365_batch_complete_tasks |
Batch complete multiple tasks |
V2 Tools (optional — requires V2 auth)
| Tool | Description |
|---|---|
dida365_search_tasks |
Server-side full-text search with keyword, project, tag, status, date filters |
dida365_list_tags |
List all tags |
dida365_create_tags |
Batch create tags |
dida365_update_tags |
Batch update tags |
dida365_delete_tags |
Batch delete tags |
dida365_delete_tag |
Delete a single tag by name |
dida365_set_task_parent |
Set parent-child task relationship |
dida365_unset_task_parent |
Remove parent, make task top-level |
dida365_pin_task |
Pin or unpin a task |
dida365_list_habits |
List all habits |
dida365_create_habit |
Batch create habits |
dida365_update_habit |
Batch update habits |
dida365_delete_habit |
Batch delete habits |
dida365_checkin_habit |
Check in a habit for a date |
dida365_undo_checkin |
Undo a habit checkin |
dida365_list_habit_checkins |
List checkin records for a habit |
dida365_list_habit_sections |
List habit sections/groups |
dida365_list_folders |
List project folders |
dida365_create_folder |
Create a project folder |
dida365_update_folder |
Update a project folder |
dida365_delete_folder |
Delete a project folder |
Configuration
| Variable | Description | Default |
|---|---|---|
DIDA365_REGION |
china (dida365.com) or international (ticktick.com) |
china |
DIDA365_CLIENT_ID |
OAuth Client ID | — |
DIDA365_CLIENT_SECRET |
OAuth Client Secret | — |
DIDA365_ACCESS_TOKEN |
Access token (set directly to skip OAuth) | — |
DIDA365_REDIRECT_URI |
OAuth callback URL | http://localhost:8000/oauth/callback |
DIDA365_V2_SESSION_TOKEN |
V2 session token (browser cookie t, 30-day expiry) |
— |
DIDA365_USERNAME |
V2 auto-login email/phone (alternative to session token) | — |
DIDA365_PASSWORD |
V2 auto-login password (does not support 2FA) | — |
TRANSPORT |
stdio, streamable-http, or sse (legacy) |
stdio |
HOST |
Bind address (http / sse only) | 0.0.0.0 |
PORT |
Port (http / sse only) | 8000 |
Token Lifecycle
| Item | Detail |
|---|---|
| Validity | ~180 days |
| Auto-refresh | Not supported (API limitation) |
| Expiry detection | Built-in, warns 24h before expiry |
| Renewal | Re-run uv run python scripts/oauth_flow.py |
| Storage | ~/.dida365-agent-mcp/token.json (auto-loaded) |
Development
uv sync # Install dependencies
uv run pytest tests/ -v # Run tests
uv run ruff check src/ tests/ scripts/ # Lint
uv run ruff format src/ tests/ scripts/ # Format
uv run pyright src/ # Type check
uv run dida365-mcp # Run (stdio)
TRANSPORT=streamable-http uv run dida365-mcp # Run (Streamable HTTP)
Project Structure
dida365-agent-mcp/
├── src/dida365_agent_mcp/
│ ├── server.py # FastMCP server + 19 V1 tool definitions + MCP resource
│ ├── server_v2.py # 21 V2 tool definitions (tags, search, habits, folders)
│ ├── client.py # V1 async API client (httpx + OAuth)
│ ├── client_v2.py # V2 async API client (httpx + session cookie)
│ ├── auth.py # OAuth2 flow + token management
│ ├── models.py # Pydantic data models (V1 + V2)
│ └── config.py # Region-aware configuration
├── scripts/
│ └── oauth_flow.py # One-click OAuth script
├── tests/ # 62 unit tests (respx)
├── Dockerfile # Multi-stage build
└── docker-compose.yml
License
Acknowledgments
- Dida365 / TickTick Open API
- FastMCP — Python MCP framework
- Model Context Protocol
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dida365_agent_mcp-2.0.0.tar.gz.
File metadata
- Download URL: dida365_agent_mcp-2.0.0.tar.gz
- Upload date:
- Size: 15.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
39d37a45b1e43c72e9bef20175f07344e0f08a735a675b0d944a78d373997138
|
|
| MD5 |
d0e15ec362a232126ce87406a70d1fa1
|
|
| BLAKE2b-256 |
2297ab8b4574287a33c3eb70717b37edf273ec6dc16948f6618a5075eb8a8418
|
File details
Details for the file dida365_agent_mcp-2.0.0-py3-none-any.whl.
File metadata
- Download URL: dida365_agent_mcp-2.0.0-py3-none-any.whl
- Upload date:
- Size: 20.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.27 {"installer":{"name":"uv","version":"0.9.27","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9a0b4cbb460dfd9d8d8d3008a23de343ea633a103871fa8585e11c45c06b4ae9
|
|
| MD5 |
d08c14e141521e78091fe4d597ca7340
|
|
| BLAKE2b-256 |
8ac8e78e545ac670086563efc2cd78bfb8009f39887f017cc397b0933f523e7d
|