Skip to main content
Glama
simple_client.py6.43 kB
#!/usr/bin/env python3 """ Simple TickTick Client - No OAuth Required! This uses an existing token file, so other projects can add tasks without going through the OAuth flow. Usage from another project: from simple_client import add_ticktick_task add_ticktick_task("Task title", content="Description", priority=5) """ import os import json import requests from pathlib import Path class SimpleTickTickClient: """Simple client that uses existing token - no OAuth flow needed""" BASE_URL = "https://api.ticktick.com/open/v1" def __init__(self, token_file=None): """ Initialize with existing token file Args: token_file: Path to .ticktick-token.json (default: looks in current dir and parent dirs) """ if token_file is None: token_file = self._find_token_file() if not token_file or not os.path.exists(token_file): raise FileNotFoundError( "No token file found. Please run authentication first:\n" " python ticktick_rest_api.py --auth\n" "This will create .ticktick-token.json" ) self.token_file = token_file self._load_token() def _find_token_file(self): """Search for token file in current dir and up to 3 parent directories""" current = Path.cwd() # Check current directory and up to 3 levels up for _ in range(4): token_path = current / '.ticktick-token.json' if token_path.exists(): return str(token_path) current = current.parent return None def _load_token(self): """Load access token from file""" with open(self.token_file, 'r') as f: data = json.load(f) self.access_token = data.get('access_token') if not self.access_token: raise ValueError("Invalid token file - no access_token found") def _request(self, method, endpoint, **kwargs): """Make authenticated API request""" url = f"{self.BASE_URL}/{endpoint}" headers = kwargs.pop('headers', {}) headers['Authorization'] = f"Bearer {self.access_token}" response = requests.request(method, url, headers=headers, **kwargs) if response.status_code == 401: raise Exception( "Token expired. Please re-authenticate:\n" " python ticktick_rest_api.py --auth" ) return response def get_projects(self): """Get all projects""" response = self._request('GET', 'project') response.raise_for_status() return response.json() def create_task(self, title, content=None, project=None, priority=0, due_date=None): """ Create a task in TickTick Args: title (str): Task title (required) content (str): Task description/notes project (str): Project name (e.g., "Health", "Work") priority (int): 0=None, 1=Low, 3=Medium, 5=High due_date (str): ISO format "2025-11-20T23:59:59+0000" or None Returns: dict: Created task data """ data = {'title': title} if content: data['content'] = content if priority: data['priority'] = priority if due_date: data['dueDate'] = due_date data['isAllDay'] = True # Look up project ID if project name provided if project: projects = self.get_projects() matching = [p for p in projects if p['name'].lower() == project.lower()] if matching: data['projectId'] = matching[0]['id'] else: raise ValueError( f"Project '{project}' not found. " f"Available: {', '.join([p['name'] for p in projects])}" ) response = self._request('POST', 'task', json=data) response.raise_for_status() return response.json() # Convenience function for easy importing def add_ticktick_task(title, content=None, project=None, priority=0, due_date=None, token_file=None): """ Simple function to add a task to TickTick Example: from simple_client import add_ticktick_task add_ticktick_task("Buy groceries", priority=3) add_ticktick_task("Exercise", project="Health", priority=5) Args: title (str): Task title content (str): Task description project (str): Project name (e.g., "Health") priority (int): 0, 1, 3, or 5 due_date (str): ISO format date or None token_file (str): Path to token file (auto-detected if None) Returns: dict: Created task data """ client = SimpleTickTickClient(token_file=token_file) return client.create_task(title, content, project, priority, due_date) def main(): """CLI interface for simple_client.py""" import argparse parser = argparse.ArgumentParser(description="Simple TickTick task creator (uses existing token)") parser.add_argument("title", help="Task title") parser.add_argument("--content", help="Task description") parser.add_argument("--project", help="Project name") parser.add_argument("--priority", type=int, choices=[0, 1, 3, 5], default=0, help="Priority (0-5)") parser.add_argument("--due", help="Due date (YYYY-MM-DD)") parser.add_argument("--token", help="Path to token file (default: auto-detect)") args = parser.parse_args() # Format due date if provided due_date = None if args.due: from datetime import datetime try: dt = datetime.strptime(args.due, '%Y-%m-%d') due_date = dt.strftime("%Y-%m-%dT23:59:59+0000") except ValueError: print(f"❌ Invalid date format: {args.due}. Use YYYY-MM-DD") return try: task = add_ticktick_task( title=args.title, content=args.content, project=args.project, priority=args.priority, due_date=due_date, token_file=args.token ) print(f"✅ Task created: {task['title']}") print(f" ID: {task['id']}") except FileNotFoundError as e: print(f"❌ {e}") except Exception as e: print(f"❌ Error: {e}") if __name__ == "__main__": main()

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kbadinger/ticktickmcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server