Skip to main content
Glama

MCP Todoist

by kentaroh7777
todoist-client.ts5.42 kB
import axios, { AxiosResponse, AxiosError } from 'axios'; import { TodoistTask, TodoistProject, GetTasksParams, CreateTaskParams, UpdateTaskParams, CreateProjectParams, UpdateProjectParams } from '../types/todoist'; export class TodoistApiError extends Error { constructor( public status: number, public message: string, public description?: string ) { super(message); this.name = 'TodoistApiError'; } } export class TodoistClient { private baseURL: string; private apiToken: string; private timeout: number; private maxRetries: number; constructor(apiToken: string, timeout: number = 3000, maxRetries: number = 2) { if (!apiToken || apiToken.trim() === '') { throw new Error('Todoist API token is required'); } this.apiToken = apiToken; this.baseURL = 'https://api.todoist.com/rest/v2'; this.timeout = timeout; this.maxRetries = maxRetries; } private async makeRequest<T>( method: 'GET' | 'POST' | 'DELETE', endpoint: string, data?: any, params?: any ): Promise<T> { let lastError: any; for (let attempt = 0; attempt <= this.maxRetries; attempt++) { try { const response: AxiosResponse<T> = await axios({ method, url: `${this.baseURL}${endpoint}`, headers: { 'Authorization': `Bearer ${this.apiToken}`, 'Content-Type': 'application/json', }, data, params, timeout: this.timeout, }); return response.data; } catch (error) { lastError = error; if (error instanceof AxiosError && error.response) { const { status, data } = error.response; console.error(`[TodoistClient] API Error - Status: ${status}, Data:`, JSON.stringify(data, null, 2)); let message = 'Unknown error'; let description = ''; if (data && typeof data === 'object') { if (data.error) { message = data.error; } else if (status === 401) { message = 'Unauthorized'; description = 'Invalid API token'; } else if (status === 429) { message = 'Too Many Requests'; description = 'Rate limit exceeded'; } else if (status === 500) { message = 'Internal Server Error'; description = ''; } if (data.error_description) { description = data.error_description; } } // Don't retry for client errors (4xx) except 429 if (status >= 400 && status < 500 && status !== 429) { throw new TodoistApiError(status, description ? `${message}: ${description}` : message); } // Retry for server errors (5xx) and rate limiting (429) if (attempt < this.maxRetries && (status >= 500 || status === 429)) { await this.delay(Math.pow(2, attempt) * 1000); // Exponential backoff continue; } throw new TodoistApiError(status, description ? `${message}: ${description}` : message); } // Retry for network errors if (attempt < this.maxRetries) { await this.delay(Math.pow(2, attempt) * 1000); continue; } throw error; } } throw lastError; } private delay(ms: number): Promise<void> { return new Promise(resolve => setTimeout(resolve, ms)); } async getTasks(params?: GetTasksParams): Promise<TodoistTask[]> { return this.makeRequest<TodoistTask[]>('GET', '/tasks', undefined, params); } async createTask(taskData: CreateTaskParams): Promise<TodoistTask> { if (!taskData.content || taskData.content.trim() === '') { throw new Error('Task content is required'); } return this.makeRequest<TodoistTask>('POST', '/tasks', taskData); } async updateTask(taskId: string, updateData: UpdateTaskParams): Promise<TodoistTask> { return this.makeRequest<TodoistTask>('POST', `/tasks/${taskId}`, updateData); } async deleteTask(taskId: string): Promise<void> { await this.makeRequest<void>('DELETE', `/tasks/${taskId}`); } async getProjects(): Promise<TodoistProject[]> { return this.makeRequest<TodoistProject[]>('GET', '/projects'); } async getProject(projectId: string): Promise<TodoistProject> { return this.makeRequest<TodoistProject>('GET', `/projects/${projectId}`); } async createProject(projectData: CreateProjectParams): Promise<TodoistProject> { return this.makeRequest<TodoistProject>('POST', '/projects', projectData); } async updateProject(projectId: string, updateData: UpdateProjectParams): Promise<TodoistProject> { return this.makeRequest<TodoistProject>('POST', `/projects/${projectId}`, updateData); } async deleteProject(projectId: string): Promise<void> { await this.makeRequest<void>('DELETE', `/projects/${projectId}`); } async closeTask(taskId: string): Promise<void> { await this.makeRequest<void>('POST', `/tasks/${taskId}/close`); } async reopenTask(taskId: string): Promise<void> { await this.makeRequest<void>('POST', `/tasks/${taskId}/reopen`); } async getTask(taskId: string): Promise<TodoistTask> { return this.makeRequest<TodoistTask>('GET', `/tasks/${taskId}`); } }

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/kentaroh7777/mcp-todoist'

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