/**
* Project-related MCP tools
*/
import { z } from 'zod';
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { TodoistService } from '../services/todoist-service.js';
export function registerProjectTools(server: McpServer, todoist: TodoistService) {
// ─────────────────────────────────────────────────────────────
// list_projects
// ─────────────────────────────────────────────────────────────
server.tool(
'list_projects',
'List all projects in the Todoist account',
{},
async () => {
try {
const projects = await todoist.listProjects();
return {
content: [
{
type: 'text',
text: JSON.stringify({ projects }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// create_project
// ─────────────────────────────────────────────────────────────
server.tool(
'create_project',
'Create a new project',
{
name: z.string().describe('Project name'),
color: z.string().optional().describe('Color name (e.g., "berry_red", "blue") or hex code'),
parentId: z.string().optional().describe('Parent project ID for nested projects'),
isFavorite: z.boolean().optional().describe('Mark as favorite'),
viewStyle: z.enum(['list', 'board']).optional().describe('View style (list or board)'),
},
async (params) => {
try {
const project = await todoist.createProject({
name: params.name,
color: params.color,
parentId: params.parentId,
isFavorite: params.isFavorite,
viewStyle: params.viewStyle,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({ project }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// list_sections
// ─────────────────────────────────────────────────────────────
server.tool(
'list_sections',
'List sections within a project',
{
projectId: z.string().optional().describe('Project ID (if not provided, lists all sections)'),
},
async (params) => {
try {
const sections = await todoist.listSections(params.projectId);
return {
content: [
{
type: 'text',
text: JSON.stringify({ sections }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// create_section
// ─────────────────────────────────────────────────────────────
server.tool(
'create_section',
'Create a new section within a project',
{
projectId: z.string().describe('Project ID'),
name: z.string().describe('Section name'),
},
async (params) => {
try {
const section = await todoist.createSection(params.projectId, params.name);
return {
content: [
{
type: 'text',
text: JSON.stringify({ section }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// update_project
// ─────────────────────────────────────────────────────────────
server.tool(
'update_project',
'Update an existing project (name, color, favorite status, view style)',
{
projectId: z.string().describe('Project ID to update'),
name: z.string().optional().describe('New project name'),
color: z.string().optional().describe('New color name (e.g., "berry_red", "blue") or hex code'),
isFavorite: z.boolean().optional().describe('Mark/unmark as favorite'),
viewStyle: z.enum(['list', 'board']).optional().describe('View style (list or board)'),
},
async (params) => {
try {
const { projectId, ...updateData } = params;
const project = await todoist.updateProject(projectId, {
name: updateData.name,
color: updateData.color,
isFavorite: updateData.isFavorite,
viewStyle: updateData.viewStyle,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({ project }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// delete_project
// ─────────────────────────────────────────────────────────────
server.tool(
'delete_project',
'Permanently delete a project and all its tasks',
{
projectId: z.string().describe('Project ID to delete'),
},
async (params) => {
try {
const result = await todoist.deleteProject(params.projectId);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// update_section
// ─────────────────────────────────────────────────────────────
server.tool(
'update_section',
'Update a section (rename or collapse/expand)',
{
sectionId: z.string().describe('Section ID to update'),
name: z.string().optional().describe('New section name'),
collapsed: z.boolean().optional().describe('Collapse (true) or expand (false) the section'),
},
async (params) => {
try {
const section = await todoist.updateSection(params.sectionId, {
name: params.name,
collapsed: params.collapsed,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({ section }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// move_section
// ─────────────────────────────────────────────────────────────
server.tool(
'move_section',
'Move a section to a different project',
{
sectionId: z.string().describe('Section ID to move'),
projectId: z.string().describe('Destination project ID'),
},
async (params) => {
try {
const section = await todoist.moveSection(params.sectionId, {
projectId: params.projectId,
});
return {
content: [
{
type: 'text',
text: JSON.stringify({ section }, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
// ─────────────────────────────────────────────────────────────
// delete_section
// ─────────────────────────────────────────────────────────────
server.tool(
'delete_section',
'Permanently delete a section (tasks will be moved to project root)',
{
sectionId: z.string().describe('Section ID to delete'),
},
async (params) => {
try {
const result = await todoist.deleteSection(params.sectionId);
return {
content: [
{
type: 'text',
text: JSON.stringify(result, null, 2),
},
],
};
} catch (error) {
return {
content: [{ type: 'text', text: `Error: ${(error as Error).message}` }],
isError: true,
};
}
}
);
}