ticktick-md-mcp
A Python tool for importing and exporting TickTick notes and tasks to Markdown and JSON formats.
Three ways to use:
- 🖥️ Terminal CLI - Interactive command-line interface
- 🐍 Python Library - Import and use in your own code
- 🤖 MCP Server - AI integration with Claude (via Model Context Protocol)
Features
- OAuth 2.0 authentication with TickTick
- Import tasks from Markdown files with tags and project assignment
- Export tasks filtered by tag or entire projects
- Multiple export formats (Markdown, JSON)
- Automatic date extraction from task titles
- Extensible exporter architecture
- 🆕 MCP Server - Expose functionality to Claude AI via Model Context Protocol
Installation
Prerequisites
- Python 3.11 or higher
- TickTick account
- TickTick OAuth app credentials (Setup Guide)
Install Dependencies
# Using pip
pip install -r requirements.txt
# Or using uv (recommended)
uv pip install -r requirements.txt
Quick Start
1. Set Up Environment Variables
Create a .env file in the project root:
cp .env.example .env
Edit .env and add your TickTick OAuth credentials:
TICKTICK_CLIENT_ID=your_client_id_here
TICKTICK_CLIENT_SECRET=your_client_secret_here
TICKTICK_REDIRECT_URI=http://localhost:8000/auth/ticktick/callback
TICKTICK_ACCESS_TOKEN=your_access_token_here
2. Get Access Token
Run the token helper to authenticate:
python -m src.utils.get_token
Follow the prompts to:
- Visit the authorization URL
- Authorize the app
- Copy the authorization code from the redirect URL
- The script will save your access token to
.env
3. Run the CLI
python ticktick_cli.py
The interactive menu provides all functionality:
- Import tasks from Markdown - Create tasks from markdown files with tags
- Export notes by tag - Filter and export tasks by tag
- Export entire projects - Export all tasks from a project
- List tags in a project - See available tags
- Exit
Choose your output format (Markdown or JSON) when exporting.
Usage
The CLI provides a unified interface for all operations:
Importing Tasks from Markdown
Run the CLI and select option 1:
python ticktick_cli.py
# Then select: 1. Import tasks from Markdown
You'll be prompted to:
- Enter the markdown file path
- Choose a default project (or leave empty for Inbox)
- Preview (dry run) or import directly
Markdown Format
Tasks should be formatted as bullet lists with checkboxes:
- [ ] Review pull request
Tags: code-review, urgent
Project: Work Tasks
Check for performance issues and security
- [ ] Buy groceries
Tags: shopping, personal
Milk, bread, eggs
- [x] Completed task
Tags: done
Metadata Fields:
- Tags: Comma-separated list of tags (e.g.,
Tags: work, urgent) - Project: TickTick project/list name (e.g.,
Project: Work Tasks) - Description: Multi-line task description (any non-metadata text after the title)
Task States:
- [ ]- Unchecked task (will be imported)- [x]- Completed task (skipped during import)
Exporting Tasks
Run the CLI and select option 2 or 3:
python ticktick_cli.py
# Then select:
# 2. Export notes by tag
# 3. Export entire project
Export options:
- Choose between Markdown or JSON format
- Specify output filename
Programmatic Usage
If you need to use the functionality in your own Python scripts:
from src.exporters.notes_exporter import NotesManager, MarkdownExporter, JSONExporter
from src.importers.task_importer import TaskImporter
# Initialize
manager = NotesManager()
# Export by tag (Markdown)
manager.export_by_tag(
tag_name="journal",
output_file="journal.md",
project_name="On My Mind"
)
# Export by tag (JSON)
manager.export_by_tag(
tag_name="work",
output_file="work_notes.json",
project_name="Work Tasks",
exporter=JSONExporter()
)
# Export entire project
manager.export_project(
project_name="Personal",
output_file="personal_notes.md"
)
# List all projects
manager.list_projects()
# List tags in a project
manager.list_tags(project_name="On My Mind")
API Usage
Use the TickTick API wrapper directly:
from src.api.ticktick_api import TickTickAPI, TickTickOAuth
import os
# Initialize API
api = TickTickAPI(os.getenv('TICKTICK_ACCESS_TOKEN'))
# Get user info
user = api.get_user_info()
# Get all projects
projects = api.get_projects()
# Get project data with tasks
project_data = api.get_projects_data(project_id)
# Create a task
task = api.create_task(
title="New Task",
content="Task description",
project_id="project_id_here",
tags=["work", "urgent"]
)
MCP Server - AI Integration
The MCP (Model Context Protocol) server allows Claude AI to directly interact with your TickTick account for importing and exporting tasks.
What is MCP?
MCP is a protocol that lets AI assistants like Claude access external tools and data sources. With the TickTick MCP server, you can ask Claude to:
- "Export my work tasks to markdown"
- "Import these tasks to my Home project"
- "List all my TickTick projects"
- "Create a task in my Work project"
Quick Start
-
Install MCP dependency:
pip install mcp>=1.0.0 -
The
.mcp.jsonfile is already configured in the project root - no setup needed! -
Set your access token:
export TICKTICK_ACCESS_TOKEN=your_token_here -
Use with Claude Code:
- Open this project in Claude Code
- The MCP server will be automatically detected
- Ask Claude to interact with TickTick!
Available MCP Tools
The server exposes these tools to Claude:
list-projects
List all your TickTick projects/lists.
Example: "Show me all my TickTick projects"
list-tags
List all tags in a specific project.
Parameters:
- project_name: Name of the project
Example: "What tags are in my Work Tasks project?"
export-by-tag
Export tasks filtered by a specific tag.
Parameters:
- tag_name: Tag to filter by
- project_name: Project to export from
- format: 'markdown' or 'json' (default: markdown)
Example: "Export all tasks tagged 'urgent' from Work Tasks"
export-project
Export all tasks from an entire project.
Parameters:
- project_name: Project to export
- format: 'markdown' or 'json' (default: markdown)
Example: "Export my Personal project as markdown"
import-from-markdown
Import tasks from markdown content into TickTick.
Parameters:
- markdown_content: Markdown formatted task list
- project_name: Target project (defaults to Inbox)
- dry_run: Preview without creating (default: true)
Example: "Import these tasks to my Work project:
- [ ] Review documentation
Tags: work, urgent
- [ ] Schedule meeting
Tags: work"
create-task
Create a single task in TickTick.
Parameters:
- title: Task title
- project_name: Project to add to (optional)
- content: Task description (optional)
- tags: List of tags (optional)
Example: "Create a task 'Buy groceries' with tags shopping and personal"
Example Claude Conversations
Export tasks:
You: "Export all my work tasks tagged 'urgent' as markdown"
Claude: *uses export-by-tag tool*
Claude: "Here are your urgent work tasks: [markdown content]"
Import tasks:
You: "Here's a list of tasks, import them to my Home project:
- [ ] Fix leaky faucet
Tags: home, maintenance
- [ ] Plant garden
Tags: home, weekend"
Claude: *uses import-from-markdown tool with dry_run=false*
Claude: "I've imported 2 tasks to your Home project"
List and organize:
You: "What projects do I have in TickTick?"
Claude: *uses list-projects tool*
Claude: "You have 5 projects: Work Tasks, Personal, Home Tasks, Shopping, Ideas"
MCP Configuration
The .mcp.json file in the project root is pre-configured:
{
"mcpServers": {
"ticktick": {
"command": "python3",
"args": ["ticktick_mcp_server.py"]
}
}
}
Make sure TICKTICK_ACCESS_TOKEN is set in your environment or .env file.
For Claude Desktop (if you use it), add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"ticktick": {
"command": "python3",
"args": ["/absolute/path/to/pyTickTick/ticktick_mcp_server.py"],
"env": {
"TICKTICK_ACCESS_TOKEN": "your_token_here"
}
}
}
}
Architecture
The MCP server reuses all existing code:
- Same API wrapper (
src/api/ticktick_api.py) - Same import logic (
src/importers/) - Same export logic (
src/exporters/)
No duplication - the MCP server is a thin wrapper that exposes existing functionality to Claude via the Model Context Protocol.
Troubleshooting MCP
"Server not detected":
- Ensure
.mcp.jsonexists in project root - Check that
TICKTICK_ACCESS_TOKENis set in environment - Restart Claude Code
"Authentication failed":
- Run
python -m src.utils.get_tokento get a fresh token - Update
.envfile with new token - Set environment variable:
export TICKTICK_ACCESS_TOKEN=token
"Import not working":
- Set
dry_run=Falsein the import command - Default is
dry_run=Truefor safety (preview mode)
Export Formats
Markdown
Exports tasks as a formatted Markdown document:
# Journal Notes
Exported on January 03, 2026 at 07:31 PM
Total notes: 7
---
## 2025-12-31 My Note Title
**Date:** December 31, 2025
Note content here...
---
Features:
- Tasks sorted by date (most recent first)
- Dates extracted from task titles (format:
YYYY-MM-DD) - Fallback to API dates if available
- Metadata header with export info
JSON
Exports tasks as structured JSON:
{
"metadata": {
"exported_at": "2026-01-03T19:31:00.123456",
"total_tasks": 7,
"tag_name": "journal",
"project_name": "On My Mind"
},
"tasks": [
{
"id": "...",
"title": "2025-12-31 My Note",
"content": "Note content...",
"tags": ["journal"],
...
}
]
}
Date Handling
The exporter intelligently handles dates:
-
From Title - Extracts dates from titles like
2025-12-31 Task Name- Supports optional leading whitespace
- Format:
YYYY-MM-DDat the start of the title - Displays as: "December 31, 2025"
-
From API - Falls back to
modifiedTimeorcreatedTimeif available- Note: TickTick's Open API may not return these fields for notes
-
No Date - Shows "No date" if neither source is available
Utilities
Get Token Helper
Quickly get a new OAuth access token:
python -m src.utils.get_token
Debug Tags
Inspect task structure and find tags:
python -m src.utils.debug_tags
Shows:
- All projects in your account
- Sample task structures
- Tasks containing specific keywords
- Available tags
Project Structure
pyTickTick/
├── 🚀 ticktick_cli.py # Main CLI - Interactive terminal interface
├── 🤖 ticktick_mcp_server.py # MCP Server - AI integration with Claude
│
├── src/ # Source code (organized by function)
│ ├── api/
│ │ └── ticktick_api.py # TickTick API wrapper (OAuth + API client)
│ │
│ ├── exporters/
│ │ └── notes_exporter.py # Export tasks to Markdown/JSON
│ │
│ ├── importers/
│ │ ├── task_importer.py # Import tasks from Markdown
│ │ └── markdown_parser.py# Parse markdown task format
│ │
│ └── utils/
│ ├── get_token.py # OAuth token helper
│ └── debug_tags.py # Task structure debugger
│
├── tests/ # Comprehensive test suite
│ ├── conftest.py # Pytest fixtures and configuration
│ ├── test_markdown_parser.py
│ ├── test_task_importer.py
│ ├── test_notes_exporter.py
│ └── test_ticktick_api.py
│
├── 📝 Config:
│ ├── .env.example # Template for credentials (copy to .env)
│ ├── .mcp.json # MCP server configuration for Claude
│ ├── requirements.txt # Python dependencies
│ ├── pyproject.toml # Project metadata
│ └── pytest.ini # Pytest configuration
│
├── LICENSE # MIT License
└── README.md # This file
Three Ways to Use:
- Terminal CLI: Run
python ticktick_cli.pyfor interactive interface - Python Library: Import and use
src/modules in your own code - MCP with Claude: Open project in Claude Code - MCP auto-detected!
Getting Started:
- Run
python -m src.utils.get_tokenonce to authenticate - CLI: Run
python ticktick_cli.pyfor all import/export operations - MCP: Open project in Claude Code and ask Claude to interact with TickTick!
- (Optional) Run
python -m src.utils.debug_tagsto inspect task structures
TickTick OAuth Setup
1. Create a TickTick Developer App
- Go to TickTick Developer Portal
- Sign in with your TickTick account
- Create a new app
- Set the Redirect URI to:
http://localhost:8000/auth/ticktick/callback - Copy your Client ID and Client Secret
2. Configure Environment
Add your credentials to .env:
TICKTICK_CLIENT_ID=your_client_id_from_developer_portal
TICKTICK_CLIENT_SECRET=your_client_secret_from_developer_portal
TICKTICK_REDIRECT_URI=http://localhost:8000/auth/ticktick/callback
3. Get Access Token
Run python -m src.utils.get_token and follow the prompts.
Troubleshooting
"No access token found"
Run python -m src.utils.get_token to authenticate and get a new token.
"Failed to fetch projects"
- Verify your access token is valid
- Check that your OAuth app has the correct scopes:
tasks:read tasks:write - Try getting a new token with
python -m src.utils.get_token
"No date" showing for all tasks
- The TickTick Open API doesn't return
createdTime/modifiedTimefor notes - Add dates to your task titles in format
YYYY-MM-DD Task Name - The exporter will automatically extract and format these dates
Import errors
Make sure all dependencies are installed:
pip install -r requirements.txt
Development
Testing
The project includes a comprehensive test suite using pytest.
Running Tests
# Install test dependencies
pip install -r requirements.txt
# Run all tests
pytest
# Run with coverage report
pytest --cov=src --cov-report=html
# Run specific test file
pytest tests/test_markdown_parser.py
# Run tests with specific marker
pytest -m unit # Run only unit tests
pytest -m integration # Run only integration tests
pytest -m api # Run only API tests
# Run with verbose output
pytest -v
Test Structure
- tests/conftest.py - Shared fixtures and test configuration
- tests/test_markdown_parser.py - Tests for markdown parsing (40+ tests)
- tests/test_task_importer.py - Tests for task import functionality
- tests/test_notes_exporter.py - Tests for export functionality
- tests/test_ticktick_api.py - Tests for API wrapper
Test Coverage
The test suite covers:
- Markdown parsing with various formats
- Task import with dry-run and actual creation
- Export to Markdown and JSON formats
- API wrapper with mocked responses
- Edge cases and error handling
- Project and tag filtering
Writing New Tests
# tests/test_example.py
import pytest
@pytest.mark.unit
def test_example(sample_markdown_content):
"""Test description"""
# Your test code here
assert True
Available fixtures:
sample_markdown_content- Sample markdown for testingmock_ticktick_api- Mocked API instancemock_env_token- Mocked environment tokensample_tasks- Sample task data
File Responsibilities
- src/api/ticktick_api.py - Pure API wrapper, no business logic
- src/exporters/notes_exporter.py - Export logic and functionality
- src/importers/task_importer.py - Import logic and functionality
- src/utils/get_token.py - Simple OAuth flow helper
- src/utils/debug_tags.py - Debugging and inspection utility
- ticktick_cli.py - Main unified CLI interface
Adding New Export Formats
Extend the BaseExporter class:
from src.exporters.notes_exporter import BaseExporter
class CSVExporter(BaseExporter):
def export(self, tasks, output_file, metadata):
# Your CSV export logic here
pass
Then use it:
manager.export_by_tag(
tag_name="notes",
output_file="notes.csv",
exporter=CSVExporter()
)
Requirements
- python-dotenv >= 1.1.1
- requests >= 2.26.0
See requirements.txt for complete list.
License
MIT License - see LICENSE for details.
Contributing
Contributions are welcome! Please feel free to submit issues and pull requests.
Roadmap
Future ideas under consideration:
- Full CLI interface with Click
- Additional export formats (CSV, HTML, PDF)
- Task management features (update, delete, complete)
- Configuration system
- Package distribution on PyPI
Support
For issues or questions:
- Check the Troubleshooting section
- Review the TickTick API Documentation
- Open an issue (once repository is public)