tracker_export
Export complete project data including epics, tasks, dependencies, and comments as nested JSON for backup, migration, or sharing purposes.
Instructions
Export a full project as nested JSON. Includes all epics, tasks, subtasks, comments, dependencies, and related notes. Useful for backup, migration, or sharing.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | No | Project ID to export (omit if only one project exists) |
Implementation Reference
- src/tools/export-import.ts:40-169 (handler)The handleExport function performs the actual logic for the 'tracker_export' tool, querying the database for project data, epics, tasks, subtasks, comments, dependencies, and notes, and then restructuring them into a nested JSON export format.
function handleExport(args: Record<string, unknown>) { const db = getDb(); let projectId = args.project_id as number | undefined; if (!projectId) { const first = db.prepare('SELECT id FROM projects LIMIT 1').get() as { id: number } | undefined; if (!first) throw new Error('No projects found. Create a project first.'); projectId = first.id; } const project = db.prepare('SELECT * FROM projects WHERE id = ?').get(projectId) as Record<string, unknown>; if (!project) throw new Error(`Project ${projectId} not found`); const epics = db.prepare('SELECT * FROM epics WHERE project_id = ? ORDER BY sort_order, created_at') .all(projectId) as Array<Record<string, unknown>>; const epicData = epics.map((epic) => { const tasks = db.prepare('SELECT * FROM tasks WHERE epic_id = ? ORDER BY sort_order, created_at') .all(epic.id as number) as Array<Record<string, unknown>>; const taskData = tasks.map((task) => { const taskId = task.id as number; const subtasks = db.prepare('SELECT * FROM subtasks WHERE task_id = ? ORDER BY sort_order, created_at') .all(taskId) as Array<Record<string, unknown>>; const comments = db.prepare('SELECT * FROM comments WHERE task_id = ? ORDER BY created_at ASC') .all(taskId) as Array<Record<string, unknown>>; const deps = db.prepare('SELECT depends_on_task_id FROM task_dependencies WHERE task_id = ?') .all(taskId) as Array<{ depends_on_task_id: number }>; return { _original_id: task.id, title: task.title, description: task.description, status: task.status, priority: task.priority, sort_order: task.sort_order, assigned_to: task.assigned_to, estimated_hours: task.estimated_hours, actual_hours: task.actual_hours, due_date: task.due_date, source_ref: task.source_ref, tags: task.tags, metadata: task.metadata, depends_on: deps.map((d) => d.depends_on_task_id), subtasks: subtasks.map((s) => ({ title: s.title, status: s.status, sort_order: s.sort_order, })), comments: comments.map((c) => ({ author: c.author, content: c.content, created_at: c.created_at, })), }; }); return { _original_id: epic.id, name: epic.name, description: epic.description, status: epic.status, priority: epic.priority, sort_order: epic.sort_order, tags: epic.tags, metadata: epic.metadata, tasks: taskData, }; }); // Collect notes linked to this project, its epics, or its tasks const notes: Array<Record<string, unknown>> = []; notes.push(...db.prepare( `SELECT * FROM notes WHERE related_entity_type = 'project' AND related_entity_id = ?` ).all(projectId) as Array<Record<string, unknown>>); const epicIds = epics.map((e) => e.id as number); if (epicIds.length > 0) { const placeholders = epicIds.map(() => '?').join(','); notes.push(...db.prepare( `SELECT * FROM notes WHERE related_entity_type = 'epic' AND related_entity_id IN (${placeholders})` ).all(...epicIds) as Array<Record<string, unknown>>); } const allTaskIds: number[] = []; for (const epic of epics) { const tasks = db.prepare('SELECT id FROM tasks WHERE epic_id = ?') .all(epic.id as number) as Array<{ id: number }>; allTaskIds.push(...tasks.map((t) => t.id)); } if (allTaskIds.length > 0) { const placeholders = allTaskIds.map(() => '?').join(','); notes.push(...db.prepare( `SELECT * FROM notes WHERE related_entity_type = 'task' AND related_entity_id IN (${placeholders})` ).all(...allTaskIds) as Array<Record<string, unknown>>); } // Include unlinked notes notes.push(...db.prepare( 'SELECT * FROM notes WHERE related_entity_type IS NULL' ).all() as Array<Record<string, unknown>>); const noteData = notes.map((n) => ({ title: n.title, content: n.content, note_type: n.note_type, related_entity_type: n.related_entity_type, _original_related_entity_id: n.related_entity_id, tags: n.tags, metadata: n.metadata, })); return { format_version: '1.1', exported_at: new Date().toISOString(), project: { name: project.name, description: project.description, status: project.status, tags: project.tags, metadata: project.metadata, epics: epicData, }, notes: noteData, }; } - src/tools/export-import.ts:6-21 (schema)The definition of the 'tracker_export' tool including its name, description, and input schema.
export const definitions: Tool[] = [ { name: 'tracker_export', description: 'Export a full project as nested JSON. Includes all epics, tasks, subtasks, comments, dependencies, and related notes. Useful for backup, migration, or sharing.', annotations: { title: 'Export Project', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false }, inputSchema: { type: 'object', properties: { project_id: { type: 'integer', description: 'Project ID to export (omit if only one project exists)', }, }, }, }, - src/tools/export-import.ts:360-363 (registration)Registration of the tracker_export handler in the tool handlers mapping.
export const handlers: Record<string, ToolHandler> = { tracker_export: handleExport, tracker_import: handleImport, };