@thehammer/danx-dashboard-mcp 0.1.54 → 0.1.56

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/handlers.js CHANGED
@@ -830,6 +830,36 @@ export async function planAddCard(client, args) {
830
830
  body: { card_id: args.card_id },
831
831
  });
832
832
  }
833
+ /**
834
+ * Remove a card from a plan, via `DELETE /api/plans/:plan_id/cards/:card_id`
835
+ * — the existing route `plan_add_card`'s sibling. TAKES AN EXPLICIT
836
+ * `plan_id`, unlike `plan_add_card`: this is a membership toggle on a named
837
+ * plan (like `issue_dependency` add/remove), not a write scoped to the
838
+ * caller's connected session, so there is no `/mine` form of it. Idempotent
839
+ * — removing a card that was never a member is a no-op, not an error.
840
+ * Returns the plan's remaining member list.
841
+ */
842
+ export async function planRemoveCard(client, args) {
843
+ return client.request({
844
+ method: "DELETE",
845
+ path: `/${args.plan_id}/cards/${encodeURIComponent(args.card_id)}`,
846
+ basePath: PLANS_BASE_PATH,
847
+ });
848
+ }
849
+ /**
850
+ * Rename a plan, via `PATCH /api/plans/:plan_id` `{name}`. TAKES AN
851
+ * EXPLICIT `plan_id` for the same reason `plan_remove_card` does — renaming
852
+ * a plan is not a write scoped to the caller's connected session, it names
853
+ * the plan directly. The new name shows up immediately in `plan_list`.
854
+ */
855
+ export async function planRename(client, args) {
856
+ return client.request({
857
+ method: "PATCH",
858
+ path: `/${args.plan_id}`,
859
+ basePath: PLANS_BASE_PATH,
860
+ body: { name: args.name },
861
+ });
862
+ }
833
863
  /**
834
864
  * Write the connected plan's architecture document, under the same
835
865
  * optimistic-concurrency guard the dashboard editor uses: `base_hash` must be
package/dist/index.js CHANGED
@@ -41,6 +41,8 @@
41
41
  * - plan_update_record PATCH /api/plans/mine/records/:rid (DX-2681 follow-up)
42
42
  * - plan_delete_record DELETE /api/plans/mine/records/:rid (DX-2681 follow-up)
43
43
  * - plan_add_card POST /api/plans/mine/cards
44
+ * - plan_remove_card DELETE /api/plans/:plan_id/cards/:card_id (DX-2740)
45
+ * - plan_rename PATCH /api/plans/:plan_id (DX-2740)
44
46
  * - plan_set_architecture PUT /api/plans/mine/architecture
45
47
  *
46
48
  * DX-2683 — THE PLAN TOOLS ARE SESSION-BOUND, and asymmetrically so. Reads
@@ -82,7 +84,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
82
84
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
83
85
  import { z } from "zod";
84
86
  import { DashboardHttpClient } from "./http-client.js";
85
- import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueQualityGate, issueQualityGateVerdict, issueRequiresHuman, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, planAddCard, planAddRecord, planConnect, planCreate, planDeleteRecord, planGet, planGetRecord, planList, planSetArchitecture, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, } from "./handlers.js";
87
+ import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueQualityGate, issueQualityGateVerdict, issueRequiresHuman, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, planAddCard, planAddRecord, planConnect, planCreate, planDeleteRecord, planGet, planGetRecord, planList, planRemoveCard, planRename, planSetArchitecture, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, } from "./handlers.js";
86
88
  import { PRIORITY_TIER_WORDS } from "./priority.js";
87
89
  function readEnvOrDie(name) {
88
90
  const v = process.env[name];
@@ -671,6 +673,14 @@ server.tool("plan_delete_record", 'Soft-delete a goal/rule/caveat of your connec
671
673
  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. Returns the plan's full member list.", {
672
674
  card_id: z.string().min(1).describe("An existing card id, e.g. `DX-2683`."),
673
675
  }, async (args) => jsonResult(await planAddCard(client, args)));
676
+ 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. Returns the plan's remaining member list.", {
677
+ plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
678
+ card_id: z.string().min(1).describe("An existing card id, e.g. `DX-2683`."),
679
+ }, async (args) => jsonResult(await planRemoveCard(client, args)));
680
+ server.tool("plan_rename", "Rename a plan via PATCH /api/plans/:plan_id (DX-2740) — the ONLY way to change a plan's `name`; nothing else in this tool surface can fix a stale name. Takes an EXPLICIT `plan_id`, not your connected session's plan, so you may rename any plan you can name. `name` must be a non-empty string (400 otherwise). The new name is visible immediately in a follow-up `plan_list` or `plan_get`. Unknown plan → 404. Returns the renamed plan `{id, name, createdAt}`.", {
681
+ plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
682
+ name: z.string().min(1).describe("The plan's new name."),
683
+ }, async (args) => jsonResult(await planRename(client, args)));
674
684
  server.tool("plan_set_architecture", 'Write the ARCHITECTURE DOCUMENT of the plan this session is connected to, via PUT /api/plans/mine/architecture (DX-2683). One markdown document per plan — how the work is shaped, not a task list. `base_hash` MUST be the `architecture.contentHash` from the immediately-prior `plan_get`; the server compares it against the current hash and, on a mismatch, fails loud with `{ok: false, body: {error: "stale_plan_architecture", currentHash}}` rather than overwriting whoever wrote in between. On that refusal: re-`plan_get`, re-merge your changes into the fresh content, and retry with the new hash — never retry blindly. A never-written document reads as `contentHash: ""`, so a true first write passes `base_hash: ""`. Writing REPLACES the whole document, so send the full merged markdown, not a fragment. TAKES NO PLAN ID: the plan is resolved from your connected session.', {
675
685
  content: z.string().describe("The complete markdown document, replacing what is stored."),
676
686
  base_hash: z
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.54",
3
+ "version": "0.1.56",
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",