board_get_handoff
Retrieve the full handoff context for a project mid-session to re-check pending tasks or provide context to background agents without starting a new session.
Instructions
Read the full handoff context for a project without starting a new session. board_create_session already returns this automatically at session start — use board_get_handoff mid-session when you need to re-check what was pending, or when a background agent needs context without claiming the session slot. Returns: project (id/name/status/description), last_session (progress_summary + handoff_notes + context_artifacts from the most recent completed/abandoned session, or null if none), active_tasks (all non-done tasks sorted critical → low priority, with id/title/status/priority/assigned_agent/riper_mode/depends_on), active_task_count, and recent_activity (last 20 activity_log entries, newest-first).
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| project_id | Yes | Project ID (from board_get_projects) to read handoff context for. |
Implementation Reference
- src/tools/sessions.ts:171-183 (handler)The board_get_handoff tool handler: calls buildHandoffContext(db, project_id) and returns the result as JSON.
async ({ project_id }) => { const handoff = await buildHandoffContext(db, project_id); return { content: [ { type: "text" as const, text: JSON.stringify(handoff, null, 2), }, ], }; } ); - src/tools/sessions.ts:186-284 (helper)buildHandoffContext helper: queries Firestore for project info, last completed/abandoned session, active non-done tasks (sorted by priority), and recent activity (last 20 entries).
async function buildHandoffContext(db: Firestore, project_id: string) { // Get project info const projectSnap = await db.collection("projects").doc(project_id).get(); const projectData = projectSnap.exists ? projectSnap.data() : null; // Get last completed or abandoned session const lastSessionSnap = await db .collection("sessions") .where("project_id", "==", project_id) .where("status", "in", ["completed", "abandoned"]) .orderBy("ended_at", "desc") .limit(1) .get(); const lastSession = lastSessionSnap.docs[0] ? (() => { const data = lastSessionSnap.docs[0].data(); return { id: lastSessionSnap.docs[0].id, status: data.status, progress_summary: data.progress_summary, handoff_notes: data.handoff_notes, context_artifacts: data.context_artifacts, started_at: data.started_at?.toDate?.()?.toISOString() ?? null, ended_at: data.ended_at?.toDate?.()?.toISOString() ?? null, }; })() : null; // Get all non-done tasks sorted by priority const tasksSnap = await db .collection("tasks") .where("project_id", "==", project_id) .where("status", "!=", "done") .get(); const priorityOrder: Record<string, number> = { critical: 0, high: 1, medium: 2, low: 3, }; const activeTasks = tasksSnap.docs .map((doc) => { const data = doc.data(); return { id: doc.id, title: data.title, status: data.status, priority: data.priority, assigned_agent: data.assigned_agent, riper_mode: data.riper_mode, depends_on: data.depends_on, }; }) .sort( (a, b) => (priorityOrder[a.priority] ?? 99) - (priorityOrder[b.priority] ?? 99) ); // Get recent activity const activitySnap = await db .collection("activity_log") .orderBy("created_at", "desc") .limit(20) .get(); // Filter to project-related activity (tasks in this project or sessions in this project) const taskIds = new Set(tasksSnap.docs.map((d) => d.id)); const recentActivity = activitySnap.docs .map((doc) => { const data = doc.data(); return { id: doc.id, action: data.action, agent_name: data.agent_name, details: data.details, task_id: data.task_id, session_id: data.session_id, created_at: data.created_at?.toDate?.()?.toISOString() ?? null, }; }); return { project: projectData ? { id: project_id, name: projectData.name, status: projectData.status, description: projectData.description, } : null, last_session: lastSession, active_tasks: activeTasks, active_task_count: activeTasks.length, recent_activity: recentActivity, }; } - src/tools/sessions.ts:165-183 (registration)Registration of the 'board_get_handoff' tool on the MCP server with its description and Zod schema for project_id.
server.tool( "board_get_handoff", "Read the full handoff context for a project without starting a new session. board_create_session already returns this automatically at session start — use board_get_handoff mid-session when you need to re-check what was pending, or when a background agent needs context without claiming the session slot. Returns: project (id/name/status/description), last_session (progress_summary + handoff_notes + context_artifacts from the most recent completed/abandoned session, or null if none), active_tasks (all non-done tasks sorted critical → low priority, with id/title/status/priority/assigned_agent/riper_mode/depends_on), active_task_count, and recent_activity (last 20 activity_log entries, newest-first).", { project_id: z.string().describe("Project ID (from board_get_projects) to read handoff context for."), }, async ({ project_id }) => { const handoff = await buildHandoffContext(db, project_id); return { content: [ { type: "text" as const, text: JSON.stringify(handoff, null, 2), }, ], }; } ); - src/index.ts:30-30 (registration)Call to registerSessionTools which registers board_get_handoff (and board_create_session) on the MCP server.
registerSessionTools(server, db); - src/tools/sessions.ts:169-170 (schema)Input schema: requires project_id (string) described as 'Project ID (from board_get_projects) to read handoff context for.'
project_id: z.string().describe("Project ID (from board_get_projects) to read handoff context for."), },