关于 Ticktick Mcp
ticktick-sdk 是一个面向 TickTick(及滴答清单)的异步 Python SDK,并内置 MCP 服务,可让 AI 助手管理任务、项目、标签和习惯。适合需要以编程方式或通过 AI 助手操作 TickTick 的用户。
主要功能
- 01异步 Python SDK,基于 httpx 构建
- 02通过 MCP 服务暴露 43 个工具
- 03批量创建、更新、完成与删除任务
- 04管理看板列、标签与文件夹
- 05完整的习惯追踪与打卡功能
- 06基于 Pydantic v2 的类型校验
适用场景
- 01让 AI 助手查询今日到期任务并创建新任务
- 02通过 AI 助手为习惯打卡并查看连续记录
- 03在 Python 应用中批量管理 TickTick 任务
MCP 相关
该项目提供 MCP 服务,向 MCP 客户端暴露任务、项目、看板列、标签、习惯、用户统计与专注数据等工具,并支持按模块或工具名过滤。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
ticktick-sdk:TickTick MCP 服务与完整 Python SDK
一款功能全面的异步 Python SDK,适用于 TickTick,并支持 MCP(Model Context Protocol)服务。
也完整支持 Dida365(滴答清单)。
通过 Python 以编程方式使用 TickTick,或让 AI 助手管理你的任务。
目录
功能
MCP 服务
- 43 个工具:精简覆盖 TickTick 的各项功能
- 批量操作:所有修改操作均接受列表(1–100 项)以进行批量处理
- 适用于 AI:可与 Claude、GPT 及其他兼容 MCP 的助手配合使用
- 双重输出:面向用户的 Markdown,面向机器的 JSON
Python 库
- 全面支持异步:基于
httpx构建,支持高性能异步操作 - 批量操作:单次调用最多可创建、更新、删除或完成 100 个任务
- 完整的任务管理:创建、读取、更新、删除、完成、移动和置顶任务
- 看板:全面的列管理(创建、更新、删除列,以及在列之间移动任务)
- 项目组织:项目、文件夹、看板
- 标签系统:支持颜色的分层标签
- 习惯追踪:完整支持习惯的增删改查,以及批量签到、连续记录和目标
- 专注/Pomodoro:访问专注时段数据和统计信息
- 用户分析:效率评分、等级、完成率
开发者体验
- 类型安全:使用 Pydantic v2 进行全面验证,并提供完善的类型提示
- 经过充分测试:包含 300 多项测试,覆盖模拟和实时 API 交互
- 文档齐全:提供详尽的文档字符串和示例
为什么选择这个库?
两套 API 难题
TickTick 有两套不同的 API:
| API | 类型 | 用途 |
|---|---|---|
| V1 (OAuth2) | 官方,提供文档 | 获取包含所有任务的项目、执行基本操作 |
| V2 (Session) | 非官方,通过逆向工程得出 | 标签、文件夹、习惯、专注、子任务等 |
官方 V1 API 的功能有限。TickTick 的大多数高级功能(标签、习惯、专注追踪)只能通过未公开文档的 V2 Web API 使用。本库结合了两套 API,会自动将每项操作路由到合适的 API。
与其他库的比较
根据对现有 TickTick Python 库实际源代码的分析:
| 功能 | ticktick-sdk | pyticktick | ticktick-py | tickthon | ticktick-python |
|---|---|---|---|---|---|
| I/O 模型 | 异步 | 异步 | 同步 | 同步 | 同步 |
| 类型系统 | Pydantic V2 | Pydantic V2 | 字典 | attrs | addict |
| MCP 服务 | 是 | 否 | 否 | 否 | 否 |
| 习惯 | 完整 CRUD | 否 | 基础 | 基础 | 否 |
| 专注/Pomo | 是 | 是 | 是 | 是 | 否 |
| 统一 V1+V2 | 智能路由 | 分离 | 两者 | 仅 V2 | 仅 V2 |
| 子任务 | 高级 | 批量 | 是 | 基础 | 基础 |
| 标签 | 完整(合并/重命名) | 是 | 是 | 是 | 否 |
主要区别:
- MCP 服务:只有 ticktick-sdk 提供基于 Model Context Protocol 的 AI 助手集成
- 统一 API 路由:根据功能需求,自动将操作路由到 V1 或 V2
- 完整的习惯 CRUD:完整的习惯管理,包括打卡、连续记录、归档/取消归档
- 异步优先:基于
httpx构建,支持高性能异步操作
安装
pip install ticktick-sdk
要求:
- Python 3.11+
- TickTick 账户(免费版或 Pro)
MCP 服务设置与使用
通过 Model Context Protocol,使用 Claude 等 AI 助手操作 TickTick。
第 1 步:注册应用
- 前往 TickTick Developer Portal
- 点击 "Create App"
- 填写:
- App Name:例如,"My TickTick MCP"
- Redirect URI:
http://127.0.0.1:8080/callback
- 保存你的 Client ID 和 Client Secret
第 2 步:获取 OAuth2 访问令牌
使用你的凭据运行身份验证命令:
TICKTICK_CLIENT_ID=your_client_id \
TICKTICK_CLIENT_SECRET=your_client_secret \
ticktick-sdk auth
此操作将:
- 打开浏览器,前往 TickTick 授权页面
- 授权应用——点击 "Authorize" 以授予访问权限
- 返回终端——授权后,你将看到类似以下内容的输出:
============================================================
SUCCESS! Here is your access token:
============================================================
a]234abc-5678-90de-f012-34567890abcd
============================================================
NEXT STEPS:
For Claude Code users:
Run (replace YOUR_* placeholders):
claude mcp add ticktick \
-e TICKTICK_CLIENT_ID=YOUR_CLIENT_ID \
...
- 复制此令牌——下一步需要用到它
注意:有时浏览器会显示“凭据无效”错误页面。只需刷新页面即可正常使用。
SSH/无头模式用户:添加
--manual标志,使用无需浏览器的文本流程。
第 3 步:配置 AI 助手
Claude Code(推荐)
claude mcp add ticktick \
-e TICKTICK_CLIENT_ID=your_client_id \
-e TICKTICK_CLIENT_SECRET=your_client_secret \
-e TICKTICK_ACCESS_TOKEN=your_access_token \
-e TICKTICK_USERNAME=your_email \
-e TICKTICK_PASSWORD=your_password \
-- ticktick-sdk
注意:对于
TICKTICK_ACCESS_TOKEN,粘贴从第 2 步复制的令牌。
验证是否正常工作:
claude mcp list # See all configured servers
/mcp # Within Claude Code, check server status
Claude Desktop
将以下内容添加到 Claude Desktop 配置中:
macOS:~/Library/Application Support/Claude/claude_desktop_config.json
Windows:%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"ticktick": {
"command": "ticktick-sdk",
"env": {
"TICKTICK_CLIENT_ID": "your_client_id",
"TICKTICK_CLIENT_SECRET": "your_client_secret",
"TICKTICK_ACCESS_TOKEN": "your_access_token",
"TICKTICK_USERNAME": "your_email",
"TICKTICK_PASSWORD": "your_password"
}
}
}
}
其他兼容 MCP 的工具
此服务适用于任何支持 Model Context Protocol 的工具,包括大多数现代 AI 助手和 IDE。配置方式类似——只需提供命令(ticktick-sdk)和上文所示的环境变量。
CLI 参考
ticktick-sdk 命令提供多个子命令:
| 命令 | 描述 |
|---|---|
ticktick-sdk | 启动 MCP 服务(默认) |
ticktick-sdk server | 启动 MCP 服务(显式指定) |
ticktick-sdk server --host HOST | 使用指定的 API 主机(ticktick.com 或 dida365.com) |
ticktick-sdk server --enabledModules MODULES | 仅启用指定的工具模块(逗号分隔) |
ticktick-sdk server --enabledTools TOOLS | 仅启用指定的工具(逗号分隔) |
ticktick-sdk auth | 获取 OAuth2 访问令牌(打开浏览器) |
ticktick-sdk auth --manual | 获取 OAuth2 访问令牌(适用于 SSH) |
ticktick-sdk --version | 显示版本信息 |
ticktick-sdk --help | 显示帮助信息 |
工具筛选(减少 AI 助手的上下文窗口用量):
# Enable only task and project tools
ticktick-sdk server --enabledModules tasks,projects
# Enable specific tools only
ticktick-sdk server --enabledTools ticktick_create_tasks,ticktick_list_tasks
# Available modules: tasks, projects, folders, columns, tags, habits, user, focus
示例对话
配置完成后,你可以向 Claude 提出以下问题或请求:
- “我今天有哪些到期任务?”
- “创建一个任务,明天下午 2 点给 John 打电话”
- “显示我的高优先级任务”
- “将购物任务标记为已完成”
- “我目前的「锻炼」习惯连续打卡了多少天?”
- “为我今天的冥想习惯打卡”
- “创建一个新习惯:每天喝 8 杯水”
可用的 MCP 工具(共 43 个)
所有变更类工具都支持以列表形式批量操作(1-100 项)。
任务工具(支持批量操作)
| 工具 | 描述 |
|---|---|
ticktick_create_tasks | 创建 1-50 个任务,并设置标题、日期、标签等 |
ticktick_get_task | 按 ID 获取任务详情 |
ticktick_list_tasks | 列出任务(可通过状态筛选器查看进行中/已完成/已放弃/已删除的任务) |
ticktick_update_tasks | 更新 1-100 个任务(包括列分配) |
ticktick_complete_tasks | 完成 1-100 个任务 |
ticktick_delete_tasks | 删除 1-100 个任务(移至回收站) |
ticktick_move_tasks | 在项目之间移动 1-50 个任务 |
ticktick_set_task_parents | 为 1-50 个任务设置父子关系 |
ticktick_unparent_tasks | 移除 1-50 个任务的父级关系 |
ticktick_search_tasks | 按文本搜索任务 |
ticktick_pin_tasks | 置顶或取消置顶 1-100 个任务 |
项目工具
| 工具 | 描述 |
|---|---|
ticktick_list_projects | 列出所有项目 |
ticktick_get_project | 获取项目详情及其任务 |
ticktick_create_project | 创建新项目 |
ticktick_update_project | 更新项目属性 |
ticktick_delete_project | 删除项目 |
文件夹工具
| 工具 | 描述 |
|---|---|
ticktick_list_folders | 列出所有文件夹 |
ticktick_create_folder | 创建文件夹 |
ticktick_rename_folder | 重命名文件夹 |
ticktick_delete_folder | 删除文件夹 |
看板列工具
| 工具 | 描述 |
|---|---|
ticktick_list_columns | 列出看板项目的列 |
ticktick_create_column | 创建看板列 |
ticktick_update_column | 更新列名称或顺序 |
ticktick_delete_column | 删除看板列 |
标签工具
| 工具 | 描述 |
|---|---|
ticktick_list_tags | 列出所有标签 |
ticktick_create_tag | 创建带颜色的标签 |
ticktick_update_tag | 更新标签属性(包括通过标签名称重命名) |
ticktick_delete_tag | 删除标签 |
ticktick_merge_tags | 合并两个标签 |
习惯工具(支持批量操作)
| 工具 | 描述 |
|---|---|
ticktick_habits | 列出所有习惯 |
ticktick_habit | 获取习惯详情 |
ticktick_habit_sections | 列出时段(早晨/下午/夜间) |
ticktick_create_habit | 创建新习惯 |
ticktick_update_habit | 更新习惯属性(包括归档/取消归档) |
ticktick_delete_habit | 删除习惯 |
ticktick_checkin_habits | 为 1-50 个习惯打卡(支持补打卡) |
ticktick_habit_checkins | 获取打卡历史记录 |
用户与分析工具
| 工具 | 描述 |
|---|---|
ticktick_get_profile | 获取用户个人资料 |
ticktick_get_status | 获取账户状态 |
ticktick_get_statistics | 获取效率统计数据 |
ticktick_get_preferences | 获取用户偏好设置 |
ticktick_focus_heatmap | 获取专注热力图数据 |
ticktick_focus_by_tag | 按标签获取专注时间 |
Python 库的设置与使用
在 Python 应用中以编程方式使用 TickTick。
设置
步骤 1:注册应用
与 MCP 设置相同,前往 TickTick 开发者门户 并创建应用。
步骤 2:创建 .env 文件
在项目目录中创建一个 .env 文件:
# V1 API (OAuth2)
TICKTICK_CLIENT_ID=your_client_id_here
TICKTICK_CLIENT_SECRET=your_client_secret_here
TICKTICK_REDIRECT_URI=http://127.0.0.1:8080/callback
TICKTICK_ACCESS_TOKEN= # Will be filled in Step 3
# V2 API (Session)
[email protected]
TICKTICK_PASSWORD=your_ticktick_password
# Optional
TICKTICK_TIMEOUT=30
步骤 3:获取 OAuth2 访问令牌
# Source your .env file first, or export the variables
ticktick-sdk auth
将访问令牌复制到 .env 文件中。
步骤 4:验证设置
import asyncio
from ticktick_sdk import TickTickClient
async def test():
async with TickTickClient.from_settings() as client:
profile = await client.get_profile()
print(f'Connected as: {profile.display_name}')
asyncio.run(test())
快速开始
import asyncio
from ticktick_sdk import TickTickClient
async def main():
async with TickTickClient.from_settings() as client:
# Create a task
task = await client.create_task(
title="Learn ticktick-sdk",
tags=["python", "productivity"],
)
print(f"Created: {task.title} (ID: {task.id})")
# List all tasks
tasks = await client.get_all_tasks()
print(f"You have {len(tasks)} active tasks")
# Complete the task
await client.complete_task(task.id, task.project_id)
print("Task completed!")
asyncio.run(main())
任务
创建任务
from datetime import datetime, timedelta
from ticktick_sdk import TickTickClient
async with TickTickClient.from_settings() as client:
# Simple task
task = await client.create_task(title="Buy groceries")
# Task with due date and priority
task = await client.create_task(
title="Submit report",
due_date=datetime.now() + timedelta(days=1),
priority="high", # none, low, medium, high
)
# Task with tags and content
task = await client.create_task(
title="Review PR #123",
content="Check for:\n- Code style\n- Tests\n- Documentation",
tags=["work", "code-review"],
)
# Recurring task (MUST include start_date!)
task = await client.create_task(
title="Daily standup",
start_date=datetime(2025, 1, 20, 9, 0),
recurrence="RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR",
)
# Task with reminder
task = await client.create_task(
title="Meeting with team",
due_date=datetime(2025, 1, 20, 14, 0),
reminders=["TRIGGER:-PT15M"], # 15 minutes before
)
# All-day task
task = await client.create_task(
title="Project deadline",
due_date=datetime(2025, 1, 31),
all_day=True,
)
管理任务
async with TickTickClient.from_settings() as client:
# Get a specific task
task = await client.get_task(task_id="...")
# Update a task
task.title = "Updated title"
task.priority = 5 # high priority
await client.update_task(task)
# Complete a task
await client.complete_task(task_id="...", project_id="...")
# Delete a task (moves to trash)
await client.delete_task(task_id="...", project_id="...")
# Move task to another project
await client.move_task(
task_id="...",
from_project_id="...",
to_project_id="...",
)
子任务
async with TickTickClient.from_settings() as client:
# Create parent task
parent = await client.create_task(title="Main task")
# Create child task
child = await client.create_task(title="Subtask")
# Make it a subtask (parent_id in create is ignored by API)
await client.make_subtask(
task_id=child.id,
parent_id=parent.id,
project_id=child.project_id,
)
# Remove parent relationship
await client.unparent_subtask(
task_id=child.id,
project_id=child.project_id,
)
查询任务
async with TickTickClient.from_settings() as client:
# All active tasks
all_tasks = await client.get_all_tasks()
# Tasks due today
today = await client.get_today_tasks()
# Overdue tasks
overdue = await client.get_overdue_tasks()
# Tasks by tag
work_tasks = await client.get_tasks_by_tag("work")
# Tasks by priority
urgent = await client.get_tasks_by_priority("high")
# Search tasks
results = await client.search_tasks("meeting")
# Recently completed
completed = await client.get_completed_tasks(days=7, limit=50)
# Abandoned tasks ("won't do")
abandoned = await client.get_abandoned_tasks(days=30)
# Deleted tasks (in trash)
deleted = await client.get_deleted_tasks(limit=50)
项目与文件夹
项目
async with TickTickClient.from_settings() as client:
# List all projects
projects = await client.get_all_projects()
for project in projects:
print(f"{project.name} ({project.id})")
# Get project with all its tasks
project_data = await client.get_project_tasks(project_id="...")
print(f"Project: {project_data.project.name}")
print(f"Tasks: {len(project_data.tasks)}")
# Create a project
project = await client.create_project(
name="Q1 Goals",
color="#4A90D9",
view_mode="kanban", # list, kanban, timeline
)
# Update a project
await client.update_project(
project_id=project.id,
name="Q1 Goals 2025",
color="#FF5500",
)
# Delete a project
await client.delete_project(project_id="...")
文件夹(项目组)
async with TickTickClient.from_settings() as client:
# List all folders
folders = await client.get_all_folders()
# Create a folder
folder = await client.create_folder(name="Work Projects")
# Create project in folder
project = await client.create_project(
name="Client A",
folder_id=folder.id,
)
# Rename a folder
await client.rename_folder(folder_id=folder.id, name="Work")
# Delete a folder
await client.delete_folder(folder_id="...")
标签
TickTick 标签支持层级关系(父子关系)和自定义颜色。
async with TickTickClient.from_settings() as client:
# List all tags
tags = await client.get_all_tags()
for tag in tags:
print(f"{tag.label} ({tag.name}) - {tag.color}")
# Create a tag
tag = await client.create_tag(
name="urgent",
color="#FF0000",
)
# Create nested tag
child_tag = await client.create_tag(
name="critical",
parent="urgent", # Parent tag name
)
# Rename a tag
await client.rename_tag(old_name="urgent", new_name="priority")
# Update tag color or parent
await client.update_tag(
name="priority",
color="#FF5500",
)
# Merge tags (move all tasks from source to target)
await client.merge_tags(source="old-tag", target="new-tag")
# Delete a tag
await client.delete_tag(name="obsolete")
习惯
TickTick 习惯是你想要每天追踪的重复活动。
习惯类型
| 类型 | 描述 | 示例 |
|---|---|---|
Boolean | 简单的“是”或“否” | “你今天锻炼了吗?” |
Real | 数值计数器 | “你读了多少页?” |
创建和管理习惯
async with TickTickClient.from_settings() as client:
# List all habits
habits = await client.get_all_habits()
# Boolean habit (yes/no)
exercise = await client.create_habit(
name="Exercise",
color="#4A90D9",
reminders=["07:00", "19:00"],
target_days=30,
encouragement="Stay strong!",
)
# Numeric habit
reading = await client.create_habit(
name="Read",
habit_type="Real",
goal=30, # 30 pages per day
step=5, # +5 button increment
unit="Pages",
)
# Check in a habit (today)
habit = await client.checkin_habit("habit_id")
print(f"Streak: {habit.current_streak} days!")
# Check in for a past date (backdate)
from datetime import date
habit = await client.checkin_habit("habit_id", checkin_date=date(2025, 12, 15))
# Archive/unarchive
await client.archive_habit("habit_id")
await client.unarchive_habit("habit_id")
习惯重复规则(RRULE 格式)
| 日程 | RRULE |
|---|---|
| 每天(每天) | RRULE:FREQ=WEEKLY;BYDAY=SU,MO,TU,WE,TH,FR,SA |
| 仅工作日 | RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR |
| 仅周末 | RRULE:FREQ=WEEKLY;BYDAY=SA,SU |
| 每周 X 次 | RRULE:FREQ=WEEKLY;TT_TIMES=5 |
| 指定日期 | RRULE:FREQ=WEEKLY;BYDAY=MO,WE,FR |
专注/番茄钟
from datetime import date, timedelta
async with TickTickClient.from_settings() as client:
# Focus heatmap (like GitHub contribution graph)
heatmap = await client.get_focus_heatmap(
start_date=date.today() - timedelta(days=90),
end_date=date.today(),
)
# Focus time by tag
by_tag = await client.get_focus_by_tag(days=30)
for tag, seconds in sorted(by_tag.items(), key=lambda x: -x[1]):
hours = seconds / 3600
print(f" {tag}: {hours:.1f} hours")
用户与统计信息
async with TickTickClient.from_settings() as client:
# User profile
profile = await client.get_profile()
print(f"Username: {profile.username}")
# Account status
status = await client.get_status()
print(f"Pro User: {status.is_pro}")
print(f"Inbox ID: {status.inbox_id}")
# Productivity statistics
stats = await client.get_statistics()
print(f"Level: {stats.level}")
print(f"Score: {stats.score}")
print(f"Tasks completed today: {stats.today_completed}")
错误处理
from ticktick_sdk import (
TickTickClient,
TickTickError,
TickTickNotFoundError,
TickTickAuthenticationError,
TickTickRateLimitError,
TickTickValidationError,
)
async with TickTickClient.from_settings() as client:
try:
task = await client.get_task("nonexistent-id")
except TickTickNotFoundError as e:
print(f"Task not found: {e}")
except TickTickAuthenticationError:
print("Authentication failed - check credentials")
except TickTickRateLimitError:
print("Rate limited - wait and retry")
except TickTickValidationError as e:
print(f"Invalid input: {e}")
except TickTickError as e:
print(f"TickTick error: {e}")
架构
┌─────────────────────────────────────────────────────────────┐
│ Your Application │
│ (or MCP Server for AI Assistants) │
└─────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ TickTickClient │
│ High-level, user-friendly async API │
│ (tasks, projects, tags, habits, focus, user methods) │
└─────────────────────────┬───────────────────────────────────┘
│
┌─────────────────────────▼───────────────────────────────────┐
│ UnifiedTickTickAPI │
│ Routes calls to V1 or V2, converts responses │
│ to unified Pydantic models │
└─────────────────────────┬───────────────────────────────────┘
│
┌──────────────┴──────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ V1 API │ │ V2 API │
│ (OAuth2) │ │ (Session) │
│ │ │ │
│ • Official API │ │ • Unofficial API │
│ • Project with tasks │ │ • Tags, folders │
│ • Limited features │ │ • Habits, focus │
│ │ │ • Full subtasks │
└──────────────────────┘ └──────────────────────┘
关键设计决策
- V2 优先:大多数操作使用 V2 API(功能更多),仅在必要时回退到 V1
- 统一模型:无论数据由哪个 API 提供,都使用同一套 Pydantic 模型
- 全程异步:所有 I/O 操作均为异步,以提升性能
- 类型安全:提供完整的类型提示和 Pydantic 验证
API 参考
模型
| 模型 | 描述 |
|---|---|
Task | 包含标题、日期、优先级、标签、子任务、重复规则等的任务 |
Project | 用于容纳任务的项目/列表 |
ProjectGroup | 用于整理项目的文件夹 |
ProjectData | 包含其任务的项目(来自 get_project_tasks) |
Column | 看板中用于整理任务的列 |
Tag | 包含名称、标签、颜色和可选父级的标签 |
Habit | 包含类型、目标、连续记录和签到的重复习惯 |
HabitSection | 按一天中的时段分组(上午/下午/晚上) |
HabitCheckin | 单条习惯签到记录 |
HabitPreferences | 用户习惯设置 |
User | 用户个人资料信息 |
UserStatus | 账户状态(Pro、收件箱 ID 等) |
UserStatistics | 生产力统计信息(等级、分数、计数) |
ChecklistItem | 任务中的子任务/清单项目 |
枚举
| 枚举 | 值 |
|---|---|
TaskStatus | ABANDONED (-1)、ACTIVE (0)、COMPLETED (2) |
TaskPriority | NONE (0)、LOW (1)、MEDIUM (3)、HIGH (5) |
TaskKind | TEXT、NOTE、CHECKLIST |
ProjectKind | TASK、NOTE |
ViewMode | LIST、KANBAN、TIMELINE |
异常
| 异常 | 描述 |
|---|---|
TickTickError | 所有错误的基类异常 |
TickTickAuthenticationError | 身份验证失败 |
TickTickNotFoundError | 未找到资源 |
TickTickValidationError | 输入数据无效 |
TickTickRateLimitError | 超出速率限制 |
TickTickConfigurationError | 缺少配置 |
TickTickForbiddenError | 访问被拒绝 |
TickTickServerError | 服务端错误 |
重要:TickTick API 的特殊行为
TickTick 的 API 有一些你需要了解的独特行为:
1. 重复任务需要 start_date
如果创建重复任务时未设置 start_date,TickTick 会悄无声息地忽略重复规则。
# WRONG - recurrence will be ignored!
task = await client.create_task(
title="Daily standup",
recurrence="RRULE:FREQ=DAILY",
)
# CORRECT
task = await client.create_task(
title="Daily standup",
start_date=datetime(2025, 1, 20, 9, 0),
recurrence="RRULE:FREQ=DAILY",
)
2. 子任务需要单独调用
在创建任务时设置 parent_id 会被 API 忽略:
# Create the child task first
child = await client.create_task(title="Subtask")
# Then make it a subtask
await client.make_subtask(
task_id=child.id,
parent_id="parent_task_id",
project_id=child.project_id,
)
3. 软删除
删除任务会将其移至垃圾箱(deleted=1),而非永久删除。
4. 清除日期
要清除任务的 due_date,还必须清除 start_date:
task.due_date = None
task.start_date = None
await client.update_task(task)
5. 标签顺序不会保留
API 不会保留标签顺序——返回的标签顺序可能不同。
6. 收件箱是特殊项目
收件箱是一个特殊项目,无法删除。通过 await client.get_status() 获取其 ID。
环境变量
| 变量 | 必填 | 描述 |
|---|---|---|
TICKTICK_CLIENT_ID | 是 | 开发者门户中的 OAuth2 客户端 ID |
TICKTICK_CLIENT_SECRET | 是 | OAuth2 客户端密钥 |
TICKTICK_ACCESS_TOKEN | 是 | OAuth2 访问令牌(来自 auth 命令) |
TICKTICK_USERNAME | 是 | 你的 TickTick 邮箱 |
TICKTICK_PASSWORD | 是 | 你的 TickTick 密码 |
TICKTICK_REDIRECT_URI | 否 | OAuth2 重定向 URI(默认值:http://127.0.0.1:8080/callback) |
TICKTICK_HOST | 否 | API 主机:ticktick.com(默认值)或 dida365.com(中文) |
TICKTICK_TIMEOUT | 否 | 请求超时时间(秒,默认值:30) |
TICKTICK_DEVICE_ID | 否 | V2 API 的设备 ID(自动生成) |
运行测试
# Install dev dependencies
pip install -e ".[dev]"
# All tests (mock mode - no API calls)
pytest
# With verbose output
pytest -v
# Live tests (requires credentials)
pytest --live
# With coverage
pytest --cov=ticktick_sdk --cov-report=term-missing
测试标记
| 标记 | 描述 |
|---|---|
unit | 单元测试(快速、隔离) |
tasks | 与任务相关的测试 |
projects | 与项目相关的测试 |
tags | 与标签相关的测试 |
habits | 与习惯相关的测试 |
focus | 专注/Pomodoro 测试 |
pinning | 任务置顶测试 |
columns | Kanban 列测试 |
mock_only | 仅适用于模拟对象的测试 |
live_only | 仅在使用 --live 时运行的测试 |
故障排除
“Token exchange failed”
- 确认 Client ID 和 Client Secret 正确无误
- 确保 Redirect URI 完全一致(包括末尾的斜杠)
- 检查你使用的是正确的 TickTick 开发者门户
“Authentication failed”
- 检查你的 TickTick 用户名(邮箱)和密码
- 尝试登录 ticktick.com 以验证凭据
“V2 initialization failed”
- 你的密码可能包含特殊字符,请尝试更改密码
- 检查是否启用了 2FA/MFA(目前不支持)
“Rate limit exceeded”
- 等待 30-60 秒后重试
- 降低 API 调用频率
贡献
欢迎贡献!请按以下步骤操作:
- Fork 此仓库
- 创建功能分支(
git checkout -b feature/amazing-feature) - 为新功能编写测试
- 确保所有测试均通过(
pytest) - 运行类型检查(
mypy src/) - 提交 pull request
开发环境设置
git clone https://github.com/dev-mirzabicer/ticktick-sdk.git
cd ticktick-sdk
python3 -m venv .venv
source .venv/bin/activate
pip install -e ".[dev]"
许可证
MIT License - 详见 LICENSE。
致谢
- 感谢 TickTick 提供出色的任务管理应用
- 感谢 Model Context Protocol 提供 AI 集成标准
- 感谢 FastMCP 提供 MCP 框架
- 感谢 Pydantic 提供数据验证功能
- 感谢 httpx 提供异步 HTTP 支持
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。