Skip to main content
Glama

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
NameRequiredDescriptionDefault
project_idNoProject ID to export (omit if only one project exists)

Implementation Reference

  • 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,
      };
    }
  • 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)',
            },
          },
        },
      },
  • Registration of the tracker_export handler in the tool handlers mapping.
    export const handlers: Record<string, ToolHandler> = {
      tracker_export: handleExport,
      tracker_import: handleImport,
    };

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/spranab/saga-mcp'

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