@delega-dev/mcp
Advanced tools
+53
-7
@@ -239,3 +239,3 @@ #!/usr/bin/env node | ||
| } | ||
| async releaseTask(taskId) { | ||
| async releaseTask(taskId, handoff) { | ||
| this.assertHostedClaiming("release_task"); | ||
@@ -245,3 +245,3 @@ return this.request( | ||
| `${this.pathPrefix}/tasks/${pathSegment(taskId)}/release`, | ||
| {} | ||
| handoff !== void 0 ? { handoff } : {} | ||
| ); | ||
@@ -265,2 +265,5 @@ } | ||
| } | ||
| async getFleetAttention() { | ||
| return this.request("GET", `${this.pathPrefix}/fleet/attention`); | ||
| } | ||
| // ── Agents ── | ||
@@ -384,2 +387,7 @@ async listAgents() { | ||
| } | ||
| if (typeof t.handoff_note === "string" && t.handoff_note) { | ||
| const by = formatAgentRef(t.handoff_by_agent, t.handoff_by_agent_id) || "previous holder"; | ||
| const st = typeof t.handoff_state === "string" && t.handoff_state ? ` [${t.handoff_state}]` : ""; | ||
| lines.push(` \u21A9 Resuming from ${by}${st}: "${t.handoff_note}"`); | ||
| } | ||
| const assignee = formatAgentRef(t.assigned_to_agent, t.assigned_to_agent_id); | ||
@@ -566,2 +574,26 @@ if (assignee) lines.push(` Assigned to: ${assignee}`); | ||
| } | ||
| function formatFleetAttention(resp) { | ||
| const buckets = resp.buckets ?? {}; | ||
| const count = resp.count ?? 0; | ||
| if (count === 0) return "Fleet attention: nothing needs attention right now."; | ||
| const labels = { | ||
| abandoned_claims: "Abandoned claims (agent went silent, lease expired)", | ||
| silent_holders: "Silent holders (claim held, agent gone quiet)", | ||
| errored: "Errored", | ||
| waiting_input: "Waiting on input", | ||
| overdue: "Overdue", | ||
| looping: "Looping (repeatedly reopened)" | ||
| }; | ||
| const lines = [`Fleet attention \u2014 ${count} item(s) need attention:`]; | ||
| for (const [key, label] of Object.entries(labels)) { | ||
| const items = buckets[key] ?? []; | ||
| if (!items.length) continue; | ||
| lines.push("", `${label} (${items.length}):`); | ||
| for (const t of items.slice(0, 20)) { | ||
| const detail = t.session_state_detail ? ` \u2014 "${t.session_state_detail}"` : ""; | ||
| lines.push(` #${t.id} ${t.content}${detail}`); | ||
| } | ||
| } | ||
| return lines.join("\n"); | ||
| } | ||
@@ -1203,11 +1235,12 @@ // src/index.ts | ||
| "release_task", | ||
| "Release a task you have claimed back to the queue without completing it. Use when you cannot finish the work or another agent should take over \u2014 the task returns to open status and becomes immediately claimable. Hosted API only.", | ||
| "Release a task you have claimed back to the queue without completing it. Use when you cannot finish the work or another agent should take over \u2014 the task returns to open status and becomes immediately claimable. Leave a `handoff` note so the next agent resumes instead of restarting. Hosted API only.", | ||
| { | ||
| task_id: z.union([z.string(), z.number()]).describe("The claimed task ID to release") | ||
| task_id: z.union([z.string(), z.number()]).describe("The claimed task ID to release"), | ||
| handoff: z.string().max(500).optional().describe("Optional handoff note: where you left off / why you stopped (e.g. 'migration written, blocked on prod DB creds'). The next agent to claim this task sees it as a 'Resuming from' line. If omitted, the task's last session-state detail is preserved as the note.") | ||
| }, | ||
| async ({ task_id }) => { | ||
| async ({ task_id, handoff }) => { | ||
| try { | ||
| await client.releaseTask(task_id); | ||
| await client.releaseTask(task_id, handoff); | ||
| return { | ||
| content: [{ type: "text", text: `Task #${task_id} released back to the queue.` }] | ||
| content: [{ type: "text", text: `Task #${task_id} released back to the queue${handoff ? " with a handoff note" : ""}.` }] | ||
| }; | ||
@@ -1220,2 +1253,15 @@ } catch (error) { | ||
| server.tool( | ||
| "fleet_attention", | ||
| "Triage board: one call returning everything across the account that needs a human or coordinator \u2014 abandoned claims (a crashed/silent agent's expired lease), silent holders, errored and input-blocked tasks, overdue, and looping (repeatedly reopened) tasks. Scoped like stats: coordinators/admins see the whole account, workers see their own involvement. Read-only. Hosted API only.", | ||
| {}, | ||
| async () => { | ||
| try { | ||
| const resp = await client.getFleetAttention(); | ||
| return { content: [{ type: "text", text: formatFleetAttention(resp) }] }; | ||
| } catch (error) { | ||
| return toolErrorResult(error); | ||
| } | ||
| } | ||
| ); | ||
| server.tool( | ||
| "delete_task", | ||
@@ -1222,0 +1268,0 @@ "Delete a task permanently", |
+1
-1
| { | ||
| "name": "@delega-dev/mcp", | ||
| "version": "1.13.2", | ||
| "version": "1.14.0", | ||
| "mcpName": "io.github.delega-dev/delega", | ||
@@ -5,0 +5,0 @@ "description": "MCP server for Delega — the task handoff layer for AI agents", |
+2
-1
@@ -77,3 +77,3 @@ # delega-mcp | ||
| | `heartbeat_task` | Extend the lease on a claimed task. Optionally report `working`, `waiting_input`, or `errored` plus detail while extending the lease. **Hosted API only.** | | ||
| | `release_task` | Release a claimed task back to the queue without completing it. **Hosted API only.** | | ||
| | `release_task` | Release a claimed task back to the queue without completing it. Pass an optional `handoff` note ("where I left off / why I stopped") that the next agent sees as a "Resuming from" line. **Hosted API only.** | | ||
| | `set_task_state` | Report `working`, `waiting_input`, or `errored` on a claimed task without extending the lease. **Hosted API only.** | | ||
@@ -85,2 +85,3 @@ | `complete_task` | Mark a task as completed | | ||
| | `get_stats` | Get task statistics | | ||
| | `fleet_attention` | Triage board of work needing a human: abandoned claims, silent holders, errored, waiting-on-input, overdue, and looping tasks. Scoped like stats. **Hosted API only.** | | ||
| | `list_agents` | List registered agents | | ||
@@ -87,0 +88,0 @@ | `register_agent` | Register a new agent (returns API key), optionally with a role preset | |
73439
4.08%1507
3.36%179
0.56%