@thehammer/danx-dashboard-mcp 0.1.97 → 0.1.99
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/dist/index.js +8 -8
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -815,7 +815,7 @@ async (args) => {
|
|
|
815
815
|
}),
|
|
816
816
|
});
|
|
817
817
|
});
|
|
818
|
-
server.tool("plan_add_record", "Add a goal, rule or caveat to your connected plan (POST /api/plans/mine/records). A GOAL is an outcome the work is measured against. A RULE is a constraint that must hold while it is worked. A CAVEAT is a lasting trade-off or limitation of the ARCHITECTURE — never progress, status or a session note (those are comments on the card). `body` is ONE plain statement of at most 250 characters; the evidence, history and detail go in `context` (markdown). An overlong body is refused with a 400 naming its length. The server allocates a permanent reference (`G-1`, `R-4`, `CAV-12`). Takes no plan id; `session_not_connected` → `plan_connect` first.
|
|
818
|
+
server.tool("plan_add_record", "Add a goal, rule or caveat to your connected plan (POST /api/plans/mine/records). A GOAL is an outcome the work is measured against. A RULE is a constraint that must hold while it is worked. A CAVEAT is a lasting trade-off or limitation of the ARCHITECTURE — never progress, status or a session note (those are comments on the card). `body` is ONE plain statement of at most 250 characters; the evidence, history and detail go in `context` (markdown). An overlong body is refused with a 400 naming its length. The server allocates a permanent reference (`G-1`, `R-4`, `CAV-12`). Takes no plan id; `session_not_connected` → `plan_connect` first. DX-3072 — returns the created record plus `records_count` (that kind's live count, not the whole list, which can grow unboundedly over a plan's life); read the list itself with `plan_get({fields:[\"records:<kind>\"]})` or the dedicated GET.", {
|
|
819
819
|
kind: z.enum(["goal", "rule", "caveat"]).describe("goal = outcome, rule = constraint, caveat = architecture trade-off."),
|
|
820
820
|
body: z.string().min(1).describe("One plain statement, at most 250 characters. Details go in `context`."),
|
|
821
821
|
context: z.string().optional().describe("Markdown detail behind the statement: evidence, history, examples."),
|
|
@@ -823,7 +823,7 @@ server.tool("plan_add_record", "Add a goal, rule or caveat to your connected pla
|
|
|
823
823
|
server.tool("plan_get_record", "Read one goal/rule/caveat of your connected plan (GET /api/plans/mine/records/:rid) without pulling the whole plan. Takes no plan id; an unknown or another plan's record id → 404. Returns `{record: {id, planId, kind, refNum, ref, body, context, contentHash, createdAt, updatedAt}}` — `context` is the markdown detail, or null.", {
|
|
824
824
|
record_id: z.number().int().positive().describe("A record id, from `plan_add_record`, `plan_get` or `plan_get_record` itself."),
|
|
825
825
|
}, async (args) => jsonResult(await planGetRecord(client, args)));
|
|
826
|
-
server.tool("plan_update_record", 'Edit a goal/rule/caveat of your connected plan (PATCH /api/plans/mine/records/:rid). The reference never moves. `body` stays ONE plain statement of at most 250 characters (400 otherwise); detail belongs in markdown `context`. `content_hash` must be the record\'s `contentHash` from your last read, and covers body AND context. On a mismatch nothing is written and you get `{error: "stale_plan_record", currentHash, currentBody, currentContext}`: merge into those and retry with `content_hash: currentHash`, never blindly. Takes no plan id.
|
|
826
|
+
server.tool("plan_update_record", 'Edit a goal/rule/caveat of your connected plan (PATCH /api/plans/mine/records/:rid). The reference never moves. `body` stays ONE plain statement of at most 250 characters (400 otherwise); detail belongs in markdown `context`. `content_hash` must be the record\'s `contentHash` from your last read, and covers body AND context. On a mismatch nothing is written and you get `{error: "stale_plan_record", currentHash, currentBody, currentContext}`: merge into those and retry with `content_hash: currentHash`, never blindly. Takes no plan id. DX-3072 — returns the edited record plus `records_count` (that kind\'s live count, not the whole list — see `plan_add_record`).', {
|
|
827
827
|
record_id: z.number().int().positive().describe("The record id to edit."),
|
|
828
828
|
content_hash: z.string().describe("The record's `contentHash` from your last read. Required."),
|
|
829
829
|
body: z.string().min(1).describe("The new statement, at most 250 characters. Plain text."),
|
|
@@ -833,11 +833,11 @@ server.tool("plan_update_record", 'Edit a goal/rule/caveat of your connected pla
|
|
|
833
833
|
.optional()
|
|
834
834
|
.describe("New markdown detail. Omit to keep the stored context; null clears it."),
|
|
835
835
|
}, async (args) => jsonResult(await planUpdateRecord(client, args)));
|
|
836
|
-
server.tool("plan_delete_record", 'Soft-delete a goal/rule/caveat of your connected plan (DELETE /api/plans/mine/records/:rid). Its reference is retired permanently, never reused. `content_hash` must be the record\'s `contentHash` from your last read; a stale hash deletes nothing and returns `{error: "stale_plan_record", currentHash, currentBody, currentContext}`. Takes no plan id. Unknown or already-deleted id → 404.
|
|
836
|
+
server.tool("plan_delete_record", 'Soft-delete a goal/rule/caveat of your connected plan (DELETE /api/plans/mine/records/:rid). Its reference is retired permanently, never reused. `content_hash` must be the record\'s `contentHash` from your last read; a stale hash deletes nothing and returns `{error: "stale_plan_record", currentHash, currentBody, currentContext}`. Takes no plan id. Unknown or already-deleted id → 404. DX-3072 — returns `{records_count}`, that kind\'s remaining live count, not the whole list (see `plan_add_record`).', {
|
|
837
837
|
record_id: z.number().int().positive().describe("The record id to delete."),
|
|
838
838
|
content_hash: z.string().describe("The record's `contentHash` from your last read. Required."),
|
|
839
839
|
}, async (args) => jsonResult(await planDeleteRecord(client, args)));
|
|
840
|
-
server.tool("plan_add_note", "Write a milestone note to a plan's timeline, via POST /api/plans/:plan_id/notes (DX-2915). A note is a MILESTONE, not a log — write one for a card (or related group of cards) finishing, an important decision, or a meaningful goal/rule/caveat/architecture-section change; routine step progress stays a card comment, never a note. Terse tone: `title` at most 60 characters, `body` (the wrap-up) at most 250 (both 400 if too long, naming the limit and actual length). Links resolve on read into what they point at (a card's title, a record's ref+body, a section's title): an unknown card, an unparseable or foreign record ref, or an unknown or foreign section id is refused 400 naming exactly which one. A card link does NOT require the card to be a member of this plan; a record/section link MUST belong to THIS plan. `author` is stamped from your identity server-side — there is no field for it. TAKES AN EXPLICIT `plan_id` (like `plan_remove_card`/`plan_rename`), so a dispatched worker with no plan connection can still write. Unknown plan → 404.
|
|
840
|
+
server.tool("plan_add_note", "Write a milestone note to a plan's timeline, via POST /api/plans/:plan_id/notes (DX-2915). A note is a MILESTONE, not a log — write one for a card (or related group of cards) finishing, an important decision, or a meaningful goal/rule/caveat/architecture-section change; routine step progress stays a card comment, never a note. Terse tone: `title` at most 60 characters, `body` (the wrap-up) at most 250 (both 400 if too long, naming the limit and actual length). Links resolve on read into what they point at (a card's title, a record's ref+body, a section's title): an unknown card, an unparseable or foreign record ref, or an unknown or foreign section id is refused 400 naming exactly which one. A card link does NOT require the card to be a member of this plan; a record/section link MUST belong to THIS plan. `author` is stamped from your identity server-side — there is no field for it. TAKES AN EXPLICIT `plan_id` (like `plan_remove_card`/`plan_rename`), so a dispatched worker with no plan connection can still write. Unknown plan → 404. DX-3072 — returns the new note plus `notes_count` (the plan's total live note count, not the latest page); read the timeline itself with `plan_get({fields:[\"notes\"]})` or the dedicated GET.", {
|
|
841
841
|
plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
|
|
842
842
|
title: z.string().min(1).describe("At most 60 characters."),
|
|
843
843
|
body: z.string().min(1).describe("The wrap-up, at most 250 characters."),
|
|
@@ -848,7 +848,7 @@ server.tool("plan_add_note", "Write a milestone note to a plan's timeline, via P
|
|
|
848
848
|
.describe("Goal/rule/caveat references this note announces, e.g. `[\"G-1\", \"R-3\", \"CAV-2\"]`."),
|
|
849
849
|
section_ids: z.array(z.number().int().positive()).optional().describe("Architecture section ids this note announces."),
|
|
850
850
|
}, async (args) => jsonResult(await planAddNote(client, args)));
|
|
851
|
-
server.tool("plan_update_note", 'Edit a plan note, via PATCH /api/plans/:plan_id/notes/:note_id (DX-2915). `content_hash` MUST be the note\'s `contentHash` from your last read; a mismatch writes NOTHING and returns `{error: "stale_plan_note", currentHash, currentTitle, currentBody, currentLinks}` — merge into those and retry with `content_hash: currentHash`, never blindly. `title`/`body` are each optional and keep their stored value when omitted. The link fields (`card_ids`/`record_refs`/`section_ids`) are all-or-nothing AS A GROUP: omit all three to leave the stored link set untouched; send ANY one of them to REPLACE THE WHOLE SET — there is no per-link add/remove. The hash covers title, body AND the link set. TAKES AN EXPLICIT `plan_id`, same reason as `plan_add_note`. Unknown plan or note id → 404.
|
|
851
|
+
server.tool("plan_update_note", 'Edit a plan note, via PATCH /api/plans/:plan_id/notes/:note_id (DX-2915). `content_hash` MUST be the note\'s `contentHash` from your last read; a mismatch writes NOTHING and returns `{error: "stale_plan_note", currentHash, currentTitle, currentBody, currentLinks}` — merge into those and retry with `content_hash: currentHash`, never blindly. `title`/`body` are each optional and keep their stored value when omitted. The link fields (`card_ids`/`record_refs`/`section_ids`) are all-or-nothing AS A GROUP: omit all three to leave the stored link set untouched; send ANY one of them to REPLACE THE WHOLE SET — there is no per-link add/remove. The hash covers title, body AND the link set. TAKES AN EXPLICIT `plan_id`, same reason as `plan_add_note`. Unknown plan or note id → 404. DX-3072 — returns the edited note plus `notes_count` (see `plan_add_note`), not the latest page.', {
|
|
852
852
|
plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
|
|
853
853
|
note_id: z.number().int().positive().describe("The note id to edit."),
|
|
854
854
|
content_hash: z.string().describe("The note's `contentHash` from your last read. Required."),
|
|
@@ -858,15 +858,15 @@ server.tool("plan_update_note", 'Edit a plan note, via PATCH /api/plans/:plan_id
|
|
|
858
858
|
record_refs: z.array(z.string().min(1)).optional().describe("REPLACES the whole link set when sent (with card_ids/section_ids)."),
|
|
859
859
|
section_ids: z.array(z.number().int().positive()).optional().describe("REPLACES the whole link set when sent (with card_ids/record_refs)."),
|
|
860
860
|
}, async (args) => jsonResult(await planUpdateNote(client, args)));
|
|
861
|
-
server.tool("plan_delete_note", 'Soft-delete a plan note, via DELETE /api/plans/:plan_id/notes/:note_id (DX-2915). `content_hash` must be the note\'s `contentHash` from your last read; a stale hash deletes nothing and returns `{error: "stale_plan_note", currentHash, currentTitle, currentBody, currentLinks}` — the same shape `plan_update_note` uses. TAKES AN EXPLICIT `plan_id`, same reason as `plan_add_note`. Unknown plan, unknown note, or an already-deleted note → 404.
|
|
861
|
+
server.tool("plan_delete_note", 'Soft-delete a plan note, via DELETE /api/plans/:plan_id/notes/:note_id (DX-2915). `content_hash` must be the note\'s `contentHash` from your last read; a stale hash deletes nothing and returns `{error: "stale_plan_note", currentHash, currentTitle, currentBody, currentLinks}` — the same shape `plan_update_note` uses. TAKES AN EXPLICIT `plan_id`, same reason as `plan_add_note`. Unknown plan, unknown note, or an already-deleted note → 404. DX-3072 — returns `{notes_count}`, the plan\'s remaining live note count, not the latest page.', {
|
|
862
862
|
plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
|
|
863
863
|
note_id: z.number().int().positive().describe("The note id to delete."),
|
|
864
864
|
content_hash: z.string().describe("The note's `contentHash` from your last read. Required."),
|
|
865
865
|
}, async (args) => jsonResult(await planDeleteNote(client, args)));
|
|
866
|
-
server.tool("plan_add_card", "Add an existing card to the plan this session is connected to, via POST /api/plans/mine/cards (DX-2683). The card may live on ANY board — that is what a plan is for. Idempotent: re-adding a card already on the plan is a no-op, not an error, and a card may sit in several plans at once. This adds MEMBERSHIP only; it never edits the card. TAKES NO PLAN ID: the plan is resolved from your connected session. Not connected → `{error: \"session_not_connected\"}`. Unknown card → 404.
|
|
866
|
+
server.tool("plan_add_card", "Add an existing card to the plan this session is connected to, via POST /api/plans/mine/cards (DX-2683). The card may live on ANY board — that is what a plan is for. Idempotent: re-adding a card already on the plan is a no-op, not an error, and a card may sit in several plans at once. This adds MEMBERSHIP only; it never edits the card. TAKES NO PLAN ID: the plan is resolved from your connected session. Not connected → `{error: \"session_not_connected\"}`. Unknown card → 404. DX-3072 — returns `{card_id, member: true, cards_count}` (the attachment's own confirmation plus the plan's total member-card count), not the full member list — a plan can hold hundreds of cards, and the whole list was a ~500KB reply that a caller could not read. Read the list itself with `plan_get({fields:[\"cards\"]})` (paged) when you actually need it.", {
|
|
867
867
|
card_id: z.string().min(1).describe("An existing card id, e.g. `DX-2683`."),
|
|
868
868
|
}, async (args) => jsonResult(await planAddCard(client, args)));
|
|
869
|
-
server.tool("plan_remove_card", "Remove a card from a plan via DELETE /api/plans/:plan_id/cards/:card_id (DX-2740) — the sibling of `plan_add_card`. The card may live on ANY board. Idempotent: removing a card that was never a member is a no-op, not an error — the same idempotent-toggle contract `issue_dependency` add/remove uses. This removes MEMBERSHIP only; it never edits or deletes the card itself, and its membership in every OTHER plan is untouched. Unlike `plan_add_card`, this takes an EXPLICIT `plan_id` rather than acting on your connected session's plan — you may remove a card from any plan you can name. Unknown plan → 404.
|
|
869
|
+
server.tool("plan_remove_card", "Remove a card from a plan via DELETE /api/plans/:plan_id/cards/:card_id (DX-2740) — the sibling of `plan_add_card`. The card may live on ANY board. Idempotent: removing a card that was never a member is a no-op, not an error — the same idempotent-toggle contract `issue_dependency` add/remove uses. This removes MEMBERSHIP only; it never edits or deletes the card itself, and its membership in every OTHER plan is untouched. Unlike `plan_add_card`, this takes an EXPLICIT `plan_id` rather than acting on your connected session's plan — you may remove a card from any plan you can name. Unknown plan → 404. DX-3072 — returns `{card_id, member: false, cards_count}`, the plan's remaining member-card count, not the full list (see `plan_add_card`).", {
|
|
870
870
|
plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
|
|
871
871
|
card_id: z.string().min(1).describe("An existing card id, e.g. `DX-2683`."),
|
|
872
872
|
}, async (args) => jsonResult(await planRemoveCard(client, args)));
|
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.99",
|
|
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",
|