@thehammer/danx-dashboard-mcp 0.1.33 → 0.1.35
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/handlers.js +5 -0
- package/dist/index.js +12 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ All exposed as `mcp__danx_dashboard__<name>` once wired through the workspace `m
|
|
|
26
26
|
| `issue_create` | `POST /api/issues` | Epic REQUIRES non-empty `phase_children[]` (atomic insert) |
|
|
27
27
|
| `issue_edit` | `PATCH /api/issues/:id/edit` | Prose + structured keys (`title`, `description`, `ac`, `checklists`, `effort_level`, `parent_id`, `priority`, `list_id`); semantic keys refused with 400 + pointer to dedicated handler. `priority` (DX-1532) takes a tier word (`low`/`high`/…) or a number in `[0,6)` — the ONLY way to set the numeric column the Trello label + dashboard badge read; never set priority via description prose |
|
|
28
28
|
| `issue_transition` | `POST /api/issues/:id/transition` | Actions: ready, pickup, rollback_pickup, complete, cancel, block, unblock, archive, reopen |
|
|
29
|
-
| `issue_triage` | `POST /api/issues/:id/triage` |
|
|
29
|
+
| `issue_triage` | `POST /api/issues/:id/triage` | Send `{confidence, reason}` — an integer 0-5 score; the server computes the verdict (approve/cancel/keep/defer) against the board's configured thresholds (DX-2086). `keep`/`defer` now block the card. None of these are a cross-card ordering gate; use `issue_dependency` to sequence cards |
|
|
30
30
|
| `issue_comment` | `POST/PATCH/DELETE /api/issues/:id/comments[/:cid]` | Author server-stamped, soft-delete preserved |
|
|
31
31
|
| `issue_dependency` | `POST/DELETE /api/issues/:id/dependencies[/:did]` | `depends_on` cycle-checked; remove hardcodes `reason: "recorded_in_error"`. The only mechanism the dispatch picker enforces to sequence one card after another — status alone is not a substitute |
|
|
32
32
|
| `issue_requires_human` | `POST/DELETE /api/issues/:id/requires-human` | Set replaces step rows atomically; clear soft-deletes them |
|
package/dist/handlers.js
CHANGED
|
@@ -120,6 +120,11 @@ export async function issueCreate(client, args, defaultBoard) {
|
|
|
120
120
|
body.list_id = args.list_id;
|
|
121
121
|
if (args.gate_decisions !== undefined)
|
|
122
122
|
body.gate_decisions = args.gate_decisions;
|
|
123
|
+
// DX-1895 (explicit-only) — forward the caller's explicit auto-triage
|
|
124
|
+
// decision; the server stamps false when absent (never auto-triaged).
|
|
125
|
+
// Per-child flags ride along inside phase_children entries verbatim.
|
|
126
|
+
if (args.triage_enabled !== undefined)
|
|
127
|
+
body.triage_enabled = args.triage_enabled;
|
|
123
128
|
if (args.phase_children !== undefined)
|
|
124
129
|
body.phase_children = args.phase_children;
|
|
125
130
|
return client.request({ method: "POST", path: "", body, board });
|
package/dist/index.js
CHANGED
|
@@ -175,7 +175,6 @@ const TRANSITION_ACTIONS = [
|
|
|
175
175
|
"archive",
|
|
176
176
|
"reopen",
|
|
177
177
|
];
|
|
178
|
-
const TRIAGE_VERDICTS = ["approve", "cancel", "keep", "defer"];
|
|
179
178
|
// Optional per-call board override shared by every tool. Omitted → the
|
|
180
179
|
// dispatch's env-derived board (`<repo>:<slug>`) is used; provided → that
|
|
181
180
|
// board is targeted instead (the dashboard owns board→repo and 404s an
|
|
@@ -226,7 +225,7 @@ server.tool("issue_get", "Fetch a single issue via GET /api/issues/:id. Board-sc
|
|
|
226
225
|
...boardField,
|
|
227
226
|
}, async (args) => jsonResult(await issueGet(client, args)));
|
|
228
227
|
// ---------------- issue_create ----------------
|
|
229
|
-
server.tool("issue_create", 'Create a fresh card via POST /api/issues. Board-scoped; defaults to the dispatch\'s board. Pass `board` (a qualified id `<repo>:<slug>`) to create the card on another board (forwarded into body.board + ?board=; unknown board → 404). INVARIANT: type=Epic REQUIRES non-empty phase_children[] (epic-with-phases atomicity per DX-575) and the route atomically inserts the epic + every phase in ONE transaction. Non-Epic types REFUSE phase_children[] with 400. Status defaults to Review (no lifecycle timestamps stamped on create). parent_id optional. ac items take {title}; phase children inherit the new epic\'s id as parent_id. Optional list_id PLACES the card directly into a column in ONE call. **Pass EITHER a board_lists id OR the list\'s display NAME (case-insensitive, emoji-tolerant — e.g. a queue name like "⚙️ Fulfillment Queue" or just "Fulfillment Queue") — the server resolves a name to its id.** The card lands DIRECTLY in that column with the matching lifecycle stamped automatically — a `ready`-type queue → ToDo, a `completed` list → Done, etc. **You do NOT need a separate issue_transition(ready) + issue_edit(list_id) afterward — just pass the queue name here and the card is created already in that column.** Omit list_id for the default (Review). NOT valid on type=Epic (Epic status derives from children) → 400. Unknown name/id → 400. **gate_decisions is REQUIRED whenever the board has any OPTIONAL quality gate for the card\'s type** (DX-1594): supply one `{gate, enabled, note}` per board-optional gate. The create FAILS CLOSED — a missing decision returns 400 `{error, required_gate_decisions:[...]}` enumerating exactly which gates to answer, so just retry with a decision for each listed gate. `required`/`disabled` board gates take no decision; a board with no optional gates needs no gate_decisions at all.', {
|
|
228
|
+
server.tool("issue_create", 'Create a fresh card via POST /api/issues. Board-scoped; defaults to the dispatch\'s board. Pass `board` (a qualified id `<repo>:<slug>`) to create the card on another board (forwarded into body.board + ?board=; unknown board → 404). INVARIANT: type=Epic REQUIRES non-empty phase_children[] (epic-with-phases atomicity per DX-575) and the route atomically inserts the epic + every phase in ONE transaction. Non-Epic types REFUSE phase_children[] with 400. Status defaults to Review (no lifecycle timestamps stamped on create). parent_id optional. ac items take {title}; phase children inherit the new epic\'s id as parent_id. Optional list_id PLACES the card directly into a column in ONE call. **Pass EITHER a board_lists id OR the list\'s display NAME (case-insensitive, emoji-tolerant — e.g. a queue name like "⚙️ Fulfillment Queue" or just "Fulfillment Queue") — the server resolves a name to its id.** The card lands DIRECTLY in that column with the matching lifecycle stamped automatically — a `ready`-type queue → ToDo, a `completed` list → Done, etc. **You do NOT need a separate issue_transition(ready) + issue_edit(list_id) afterward — just pass the queue name here and the card is created already in that column.** Omit list_id for the default (Review). NOT valid on type=Epic (Epic status derives from children) → 400. Unknown name/id → 400. **gate_decisions is REQUIRED whenever the board has any OPTIONAL quality gate for the card\'s type** (DX-1594): supply one `{gate, enabled, note}` per board-optional gate. The create FAILS CLOSED — a missing decision returns 400 `{error, required_gate_decisions:[...]}` enumerating exactly which gates to answer, so just retry with a decision for each listed gate. `required`/`disabled` board gates take no decision; a board with no optional gates needs no gate_decisions at all. **ALWAYS pass `triage_enabled` explicitly** (root card AND every phase_children[] entry): decide per card whether it should enter the automatic triage/dispatch pipeline — `true` only when auto-triage is expected without further human review; absent → false, the card is NEVER auto-triaged (explicit-only, reverting DX-1928 — auto-created cards must never silently enter the dispatch pipeline).', {
|
|
230
229
|
type: z.enum(ISSUE_TYPES),
|
|
231
230
|
title: z.string().min(1),
|
|
232
231
|
description: z.string(),
|
|
@@ -259,8 +258,16 @@ server.tool("issue_create", 'Create a fresh card via POST /api/issues. Board-sco
|
|
|
259
258
|
}))
|
|
260
259
|
.optional()
|
|
261
260
|
.describe("Per-child fail-closed gate decisions — same shape + rule as the root gate_decisions (incl. the optional per-gate effort_level, DX-1760), resolved against THIS child's own type. Required when the child's type has board-optional gates."),
|
|
261
|
+
triage_enabled: z
|
|
262
|
+
.boolean()
|
|
263
|
+
.optional()
|
|
264
|
+
.describe("Per-child auto-triage opt-in (explicit-only, reverting DX-1928). ALWAYS pass explicitly per child — decide whether THIS child should enter the automatic triage pipeline. Absent → false (never auto-triaged). Never inherited from the root card."),
|
|
262
265
|
}))
|
|
263
266
|
.optional(),
|
|
267
|
+
triage_enabled: z
|
|
268
|
+
.boolean()
|
|
269
|
+
.optional()
|
|
270
|
+
.describe("DX-1895 (explicit-only — reverting DX-1928): per-card opt-in for the automatic triage dispatcher. ALWAYS pass this explicitly — decide per card whether auto-triage is expected. true = the card enters the automatic triage/dispatch pipeline without further human review; absent → false, the card is NEVER auto-triaged. Operator-directed POST /api/triage and direct issue_triage calls are NOT gated by this flag."),
|
|
264
271
|
...boardField,
|
|
265
272
|
}, async (args) => jsonResult(await issueCreate(client, args, config.board)));
|
|
266
273
|
// ---------------- issue_edit ----------------
|
|
@@ -294,7 +301,7 @@ server.tool("issue_edit", 'Patch prose + structured fields via PATCH /api/issues
|
|
|
294
301
|
triage_enabled: z
|
|
295
302
|
.boolean()
|
|
296
303
|
.optional()
|
|
297
|
-
.describe("DX-1895 — per-card opt-in for the DX-1886 auto-triage dispatcher. false
|
|
304
|
+
.describe("DX-1895 (explicit-only — reverting DX-1928): per-card opt-in for the DX-1886 auto-triage dispatcher. Default false — the create path stamps false unless the creator explicitly set it, so this edit field is the only post-create way to opt a card in or out. false = the automatic dispatcher trigger never selects this card, even when every other eligibility condition holds. This flag gates ONLY the automatic dispatcher; operator-directed POST /api/triage and direct issue_triage calls remain flag-independent."),
|
|
298
305
|
...boardField,
|
|
299
306
|
}, async (args) => jsonResult(await issueEdit(client, args)));
|
|
300
307
|
// ---------------- issue_transition ----------------
|
|
@@ -308,18 +315,10 @@ server.tool("issue_transition", "Stamp a lifecycle transition via POST /api/issu
|
|
|
308
315
|
...boardField,
|
|
309
316
|
}, async (args) => jsonResult(await issueTransition(client, args)));
|
|
310
317
|
// ---------------- issue_triage ----------------
|
|
311
|
-
server.tool("issue_triage", "Record a triage
|
|
318
|
+
server.tool("issue_triage", "Record a triage confidence score via POST /api/issues/:id/triage (DX-2086). Caller sends a single `confidence` integer 0-5 plus a required non-empty `reason` — the server computes the verdict by comparing `confidence` against the board's configured thresholds (all band edges inclusive on the low side): confidence <= cancelThreshold -> cancel (stamps cancelled_at, terminal); cancelThreshold < confidence <= archiveThreshold -> defer (stamps archived_at AND blocked_at/blocked_reason); archiveThreshold < confidence <= reviewThreshold -> keep (stamps blocked_at/blocked_reason, stays derived-Review); confidence > reviewThreshold -> approve (stamps ready_at). REFUSES 409 on terminal cards. A keep/defer verdict now BLOCKS the card (blocked_at set) — it is not a cross-card ordering gate; use issue_dependency (kind: depends_on) to sequence one card after another.", {
|
|
312
319
|
id: z.string().min(1),
|
|
313
|
-
|
|
320
|
+
confidence: z.number().int().min(0).max(5),
|
|
314
321
|
reason: z.string().min(1),
|
|
315
|
-
ice: z
|
|
316
|
-
.object({
|
|
317
|
-
i: z.number().finite(),
|
|
318
|
-
c: z.number().finite(),
|
|
319
|
-
e: z.number().finite(),
|
|
320
|
-
})
|
|
321
|
-
.optional(),
|
|
322
|
-
ttl_seconds: z.number().positive().optional(),
|
|
323
322
|
...boardField,
|
|
324
323
|
}, async (args) => jsonResult(await issueTriage(client, args)));
|
|
325
324
|
// ---------------- issue_comment ----------------
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.35",
|
|
4
4
|
"description": "Stdio MCP server wrapping danxbot's dashboard /api/issues/* normalized DB-backed HTTP routes for dispatched agents (DX-704 Phase 2).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|