Skip to main content
Glama
TodoistClient.ts4.87 kB
/* eslint-disable @typescript-eslint/no-explicit-any */ import { v4 as uuidv4 } from 'uuid'; import { log } from './helpers.js'; import { SyncCommand } from './types.js'; const API_BASE_URL = 'https://api.todoist.com/rest/v2'; const API_SYNC_BASE_URL = 'https://api.todoist.com/sync/v9'; export class TodoistClient { private readonly apiToken: string; constructor(apiToken: string) { this.apiToken = apiToken; } private getHeaders(includeContentType = false): HeadersInit { const headers: HeadersInit = { Authorization: `Bearer ${this.apiToken}`, Accept: 'application/json', 'X-Request-Id': uuidv4(), }; if (includeContentType) { headers['Content-Type'] = 'application/json'; } return headers; } private async handleResponse(response: Response) { if (!response.ok) { const errorText = await response.text(); throw new Error(`Todoist API error (${response.status}): ${errorText}`); } // For 204 No Content responses if (response.status === 204) { return null; } return response.json(); } /** * Make a GET request to Todoist API * @param endpoint - API endpoint path (without base URL) * @param params - Query parameters object * @returns API response data */ async get(endpoint: string, params: Record<string, string> = {}): Promise<any> { let url = `${API_BASE_URL}${endpoint}`; const queryParams = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value) { queryParams.append(key, value); } } const queryString = queryParams.toString(); if (queryString) { url += `?${queryString}`; } log(`Making GET request to: ${url}`); const response = await fetch(url, { method: 'GET', headers: this.getHeaders(), }); return this.handleResponse(response); } /** * Make a POST request to Todoist API * @param endpoint - API endpoint path (without base URL) * @param data - Request body data * @returns API response data */ async post(endpoint: string, data: Record<string, any> = {}): Promise<any> { const url = `${API_BASE_URL}${endpoint}`; log(`Making POST request to: ${url} with data:`, JSON.stringify(data, null, 2)); const response = await fetch(url, { method: 'POST', headers: this.getHeaders(true), body: JSON.stringify(data), }); return this.handleResponse(response); } /** * Make a DELETE request to Todoist API * @param endpoint - API endpoint path (without base URL) * @returns API response data */ async delete(endpoint: string): Promise<any> { const url = `${API_BASE_URL}${endpoint}`; log(`Making DELETE request to: ${url}`); const response = await fetch(url, { method: 'DELETE', headers: this.getHeaders(), }); return this.handleResponse(response); } /** * Make a Sync API request to Todoist * @param commands - Array of command objects to execute * @returns API response data */ async sync(commands: Array<SyncCommand>): Promise<any> { const url = `${API_SYNC_BASE_URL}/sync`; log(`Making SYNC request to: ${url} with commands:`, JSON.stringify(commands, null, 2)); const response = await fetch(url, { method: 'POST', headers: this.getHeaders(true), body: JSON.stringify({ commands }), }); return this.handleResponse(response); } /** * Get completed tasks using Sync API * @param params - Query parameters for filtering completed tasks * @returns API response data with completed tasks */ async getCompletedTasks(params: Record<string, string> = {}): Promise<any> { const url = `${API_SYNC_BASE_URL}/completed/get_all`; log( `Making completed tasks request to: ${url} with params:`, JSON.stringify(params, null, 2) ); const formData = new URLSearchParams(); for (const [key, value] of Object.entries(params)) { if (value) { formData.append(key, value); } } const response = await fetch(url, { method: 'POST', headers: { Authorization: `Bearer ${this.apiToken}`, 'Content-Type': 'application/x-www-form-urlencoded', 'X-Request-Id': uuidv4(), }, body: formData.toString(), }); return this.handleResponse(response); } }

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

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