# ticktick-md-mcp
A Python tool for importing and exporting TickTick notes and tasks to Markdown and JSON formats.
**Three ways to use:**
1. π₯οΈ **Terminal CLI** - Interactive command-line interface
2. π **Python Library** - Import and use in your own code
3. π€ **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](#ticktick-oauth-setup))
### Install Dependencies
```bash
# 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:
```bash
cp .env.example .env
```
Edit `.env` and add your TickTick OAuth credentials:
```env
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:
```bash
python -m src.utils.get_token
```
Follow the prompts to:
1. Visit the authorization URL
2. Authorize the app
3. Copy the authorization code from the redirect URL
4. The script will save your access token to `.env`
### 3. Run the CLI
```bash
python ticktick_cli.py
```
The interactive menu provides all functionality:
1. **Import tasks from Markdown** - Create tasks from markdown files with tags
2. **Export notes by tag** - Filter and export tasks by tag
3. **Export entire projects** - Export all tasks from a project
4. **List tags in a project** - See available tags
5. **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:
```bash
python ticktick_cli.py
# Then select: 1. Import tasks from Markdown
```
You'll be prompted to:
1. Enter the markdown file path
2. Choose a default project (or leave empty for Inbox)
3. Preview (dry run) or import directly
#### Markdown Format
Tasks should be formatted as bullet lists with checkboxes:
```markdown
- [ ] 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:
```bash
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:
```python
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:
```python
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
1. **Install MCP dependency:**
```bash
pip install mcp>=1.0.0
```
2. **The `.mcp.json` file is already configured** in the project root - no setup needed!
3. **Set your access token:**
```bash
export TICKTICK_ACCESS_TOKEN=your_token_here
```
4. **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:
```json
{
"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`:
```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.json` exists in project root
- Check that `TICKTICK_ACCESS_TOKEN` is set in environment
- Restart Claude Code
**"Authentication failed":**
- Run `python -m src.utils.get_token` to get a fresh token
- Update `.env` file with new token
- Set environment variable: `export TICKTICK_ACCESS_TOKEN=token`
**"Import not working":**
- Set `dry_run=False` in the import command
- Default is `dry_run=True` for safety (preview mode)
## Export Formats
### Markdown
Exports tasks as a formatted Markdown document:
```markdown
# 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:
```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:
1. **From Title** - Extracts dates from titles like `2025-12-31 Task Name`
- Supports optional leading whitespace
- Format: `YYYY-MM-DD` at the start of the title
- Displays as: "December 31, 2025"
2. **From API** - Falls back to `modifiedTime` or `createdTime` if available
- Note: TickTick's Open API may not return these fields for notes
3. **No Date** - Shows "No date" if neither source is available
## Utilities
### Get Token Helper
Quickly get a new OAuth access token:
```bash
python -m src.utils.get_token
```
### Debug Tags
Inspect task structure and find tags:
```bash
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:**
1. **Terminal CLI**: Run `python ticktick_cli.py` for interactive interface
2. **Python Library**: Import and use `src/` modules in your own code
3. **MCP with Claude**: Open project in Claude Code - MCP auto-detected!
**Getting Started:**
1. Run `python -m src.utils.get_token` once to authenticate
2. **CLI**: Run `python ticktick_cli.py` for all import/export operations
3. **MCP**: Open project in Claude Code and ask Claude to interact with TickTick!
4. (Optional) Run `python -m src.utils.debug_tags` to inspect task structures
## TickTick OAuth Setup
### 1. Create a TickTick Developer App
1. Go to [TickTick Developer Portal](https://developer.ticktick.com/)
2. Sign in with your TickTick account
3. Create a new app
4. Set the **Redirect URI** to: `http://localhost:8000/auth/ticktick/callback`
5. Copy your **Client ID** and **Client Secret**
### 2. Configure Environment
Add your credentials to `.env`:
```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`/`modifiedTime` for 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:
```bash
pip install -r requirements.txt
```
## Development
### Testing
The project includes a comprehensive test suite using pytest.
#### Running Tests
```bash
# 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
```python
# 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 testing
- `mock_ticktick_api` - Mocked API instance
- `mock_env_token` - Mocked environment token
- `sample_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:
```python
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:
```python
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](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:
1. Check the [Troubleshooting](#troubleshooting) section
2. Review the [TickTick API Documentation](https://developer.ticktick.com/)
3. Open an issue (once repository is public)