Skip to main content
Glama
todos-tools.ts14.8 kB
import { Tool } from '@modelcontextprotocol/sdk/types.js' import { TodosApi } from '../api/todos.js' import { SidvyApiClient } from '../api/client.js' export class TodosTools { constructor( private todosApi: TodosApi, private client: SidvyApiClient, ) {} getTools(): Tool[] { return [ { name: 'list_todos', description: 'List todos with filtering options (by completion status, note, etc.)', inputSchema: { type: 'object', properties: { workspaceId: { type: 'string', description: 'Filter by workspace ID' }, noteId: { type: 'string', description: 'Filter by note ID' }, completed: { type: 'boolean', description: 'Filter by completion status' }, isDeleted: { type: 'boolean', description: 'Include deleted todos (default: false)' }, search: { type: 'string', description: 'Search todos by text' }, sort: { type: 'string', enum: [ 'text:asc', 'text:desc', 'completed:asc', 'completed:desc', 'createdAt:asc', 'createdAt:desc', 'updatedAt:asc', 'updatedAt:desc', 'completedAt:asc', 'completedAt:desc', ], description: 'Sort order for results', }, limit: { type: 'number', minimum: 1, maximum: 100, description: 'Number of todos to return', }, }, additionalProperties: false, }, }, { name: 'create_todo', description: 'Create a new todo associated with a note', inputSchema: { type: 'object', properties: { text: { type: 'string', minLength: 1, maxLength: 500, description: 'Todo text/description', }, noteId: { type: 'string', description: 'ID of the note this todo belongs to' }, lineNumber: { type: 'number', minimum: 1, description: 'Line number in the note where this todo appears', }, completed: { type: 'boolean', description: 'Initial completion status (default: false)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional, uses default)' }, }, required: ['text', 'noteId', 'lineNumber'], additionalProperties: false, }, }, { name: 'update_todo', description: "Update a todo's text, completion status, or line number", inputSchema: { type: 'object', properties: { todoId: { type: 'string', description: 'Todo ID to update' }, text: { type: 'string', minLength: 1, maxLength: 500, description: 'New todo text/description', }, completed: { type: 'boolean', description: 'Completion status' }, lineNumber: { type: 'number', minimum: 1, description: 'New line number in the note' }, }, required: ['todoId'], additionalProperties: false, }, }, { name: 'delete_todo', description: 'Delete a todo (soft delete)', inputSchema: { type: 'object', properties: { todoId: { type: 'string', description: 'Todo ID to delete' }, }, required: ['todoId'], additionalProperties: false, }, }, { name: 'toggle_todo', description: "Toggle a todo's completion status (complete <-> incomplete)", inputSchema: { type: 'object', properties: { todoId: { type: 'string', description: 'Todo ID to toggle' }, }, required: ['todoId'], additionalProperties: false, }, }, { name: 'complete_todo', description: 'Mark a todo as completed', inputSchema: { type: 'object', properties: { todoId: { type: 'string', description: 'Todo ID to complete' }, }, required: ['todoId'], additionalProperties: false, }, }, { name: 'uncomplete_todo', description: 'Mark a todo as incomplete', inputSchema: { type: 'object', properties: { todoId: { type: 'string', description: 'Todo ID to mark as incomplete' }, }, required: ['todoId'], additionalProperties: false, }, }, { name: 'get_pending_todos', description: 'Get all incomplete/pending todos', inputSchema: { type: 'object', properties: { workspaceId: { type: 'string', description: 'Workspace to get todos from (optional)' }, limit: { type: 'number', minimum: 1, maximum: 100, description: 'Number of todos to return', }, }, additionalProperties: false, }, }, { name: 'get_completed_todos', description: 'Get all completed todos', inputSchema: { type: 'object', properties: { workspaceId: { type: 'string', description: 'Workspace to get todos from (optional)' }, limit: { type: 'number', minimum: 1, maximum: 100, description: 'Number of todos to return', }, }, additionalProperties: false, }, }, { name: 'get_todos_for_note', description: 'Get all todos associated with a specific note', inputSchema: { type: 'object', properties: { noteId: { type: 'string', description: 'Note ID to get todos for' }, workspaceId: { type: 'string', description: 'Workspace to search in (optional)' }, }, required: ['noteId'], additionalProperties: false, }, }, { name: 'get_todo_stats', description: 'Get statistics about todos (total, completed, pending, completion rate)', inputSchema: { type: 'object', properties: { workspaceId: { type: 'string', description: 'Workspace to get stats from (optional)' }, }, additionalProperties: false, }, }, { name: 'search_todos', description: 'Search todos by text content', inputSchema: { type: 'object', properties: { query: { type: 'string', minLength: 1, description: 'Search query' }, workspaceId: { type: 'string', description: 'Workspace to search in (optional)' }, }, required: ['query'], additionalProperties: false, }, }, { name: 'create_todos_for_note', description: 'Create multiple todos for a note at once', inputSchema: { type: 'object', properties: { noteId: { type: 'string', description: 'Note ID to create todos for' }, todoTexts: { type: 'array', items: { type: 'string', minLength: 1, maxLength: 500 }, description: 'Array of todo text strings', }, startingLineNumber: { type: 'number', minimum: 1, description: 'Starting line number (default: 1)', }, workspaceId: { type: 'string', description: 'Workspace ID (optional)' }, }, required: ['noteId', 'todoTexts'], additionalProperties: false, }, }, ] } async handleToolCall(name: string, arguments_: any): Promise<any> { switch (name) { case 'list_todos': return this.handleListTodos(arguments_) case 'create_todo': return this.handleCreateTodo(arguments_) case 'update_todo': return this.handleUpdateTodo(arguments_) case 'delete_todo': return this.handleDeleteTodo(arguments_) case 'toggle_todo': return this.handleToggleTodo(arguments_) case 'complete_todo': return this.handleCompleteTodo(arguments_) case 'uncomplete_todo': return this.handleUncompleteTodo(arguments_) case 'get_pending_todos': return this.handleGetPendingTodos(arguments_) case 'get_completed_todos': return this.handleGetCompletedTodos(arguments_) case 'get_todos_for_note': return this.handleGetTodosForNote(arguments_) case 'get_todo_stats': return this.handleGetTodoStats(arguments_) case 'search_todos': return this.handleSearchTodos(arguments_) case 'create_todos_for_note': return this.handleCreateTodosForNote(arguments_) default: throw new Error(`Unknown tool: ${name}`) } } private async handleListTodos(args: any) { const response = await this.todosApi.listTodos(args) if (this.client.isSuccessResponse(response)) { return { success: true, todos: response.data, pagination: response.meta?.pagination, count: response.meta?.count, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleCreateTodo(args: any) { const response = await this.todosApi.createTodo(args) if (this.client.isSuccessResponse(response)) { return { success: true, todo: response.data, message: `Todo "${response.data.text}" created successfully`, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleUpdateTodo(args: any) { const response = await this.todosApi.updateTodo(args) if (this.client.isSuccessResponse(response)) { return { success: true, todo: response.data, message: `Todo "${response.data.text}" updated successfully`, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleDeleteTodo(args: any) { const response = await this.todosApi.deleteTodo(args) if (this.client.isSuccessResponse(response)) { return { success: true, deleted: response.data.deleted, todoId: response.data.todoId, softDeleted: response.data.softDeleted, message: 'Todo deleted successfully', } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleToggleTodo(args: any) { const response = await this.todosApi.toggleTodo(args.todoId) if (this.client.isSuccessResponse(response)) { const status = response.data.completed ? 'completed' : 'incomplete' return { success: true, todo: response.data, message: `Todo "${response.data.text}" marked as ${status}`, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleCompleteTodo(args: any) { const response = await this.todosApi.completeTodo(args.todoId) if (this.client.isSuccessResponse(response)) { return { success: true, todo: response.data, message: `Todo "${response.data.text}" marked as completed`, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleUncompleteTodo(args: any) { const response = await this.todosApi.uncompleteTodo(args.todoId) if (this.client.isSuccessResponse(response)) { return { success: true, todo: response.data, message: `Todo "${response.data.text}" marked as incomplete`, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleGetPendingTodos(args: any) { const response = await this.todosApi.getPendingTodos(args.workspaceId, args.limit) if (this.client.isSuccessResponse(response)) { return { success: true, todos: response.data, count: response.data.length, type: 'pending', } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleGetCompletedTodos(args: any) { const response = await this.todosApi.getCompletedTodos(args.workspaceId, args.limit) if (this.client.isSuccessResponse(response)) { return { success: true, todos: response.data, count: response.data.length, type: 'completed', } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleGetTodosForNote(args: any) { const response = await this.todosApi.getTodosForNote(args.noteId, args.workspaceId) if (this.client.isSuccessResponse(response)) { return { success: true, todos: response.data, noteId: args.noteId, count: response.data.length, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleGetTodoStats(args: any) { try { const stats = await this.todosApi.getTodoStats(args.workspaceId) return { success: true, stats, message: `Todo statistics: ${stats.completed}/${stats.total} completed (${stats.completionRate.toFixed(1)}%)`, } } catch (error: any) { throw new Error(error.message) } } private async handleSearchTodos(args: any) { const response = await this.todosApi.searchTodos(args.query, args.workspaceId) if (this.client.isSuccessResponse(response)) { return { success: true, todos: response.data, query: args.query, count: response.data.length, } } else { throw new Error(this.client.getErrorMessage(response)) } } private async handleCreateTodosForNote(args: any) { try { const responses = await this.todosApi.createTodosForNote( args.noteId, args.todoTexts, args.workspaceId, args.startingLineNumber || 1, ) const successful = responses.filter((r) => this.client.isSuccessResponse(r)) const failed = responses.filter((r) => !this.client.isSuccessResponse(r)) return { success: true, todos: successful.map((r) => (r as any).data), created: successful.length, failed: failed.length, message: `Created ${successful.length} todos for note (${failed.length} failed)`, } } catch (error: any) { throw new Error(error.message) } } }

Latest Blog Posts

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/martinhjartmyr/sidvy-mcp'

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