@thehammer/danx-dashboard-mcp 0.1.91 → 0.1.93
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/bridge.js +69 -4
- package/dist/handlers.js +67 -0
- package/dist/index.js +29 -4
- package/package.json +1 -2
package/dist/bridge.js
CHANGED
|
@@ -42,9 +42,15 @@
|
|
|
42
42
|
* 5. Streams through `runListener` (`listen.ts`), which reconnects within the
|
|
43
43
|
* ticket, and re-mints when that ticket's lease runs out or a fresh ticket
|
|
44
44
|
* is refused once.
|
|
45
|
-
* 6. Writes JSON Lines to stdout: ONE `{type:"ready"
|
|
46
|
-
* minted and streaming is about to start
|
|
47
|
-
*
|
|
45
|
+
* 6. Writes JSON Lines to stdout: ONE `{type:"ready", boards, cardCount, degraded}`
|
|
46
|
+
* once a ticket has been minted and streaming is about to start —
|
|
47
|
+
* DX-2970's `fetchPlanInventory`, a single BEST-EFFORT (never a gate)
|
|
48
|
+
* read of the connected plan's real board/card counts, purely so a
|
|
49
|
+
* consumer can tell "healthy, connected plan has cards" apart from
|
|
50
|
+
* "degraded — the connected plan has no cards, or its inventory
|
|
51
|
+
* couldn't be read" instead of both reading as an absent/empty field —
|
|
52
|
+
* then `{type:"event", id, text}` per event, and ONE final
|
|
53
|
+
* `{type:"stopped", reason, detail, fix}` before exiting.
|
|
48
54
|
*
|
|
49
55
|
* TERMINAL OUTCOMES — each ends the process with its stop record, never a retry:
|
|
50
56
|
* - `no_connection_record` this session has no connection record to read;
|
|
@@ -156,6 +162,12 @@ function formatDigest(entries) {
|
|
|
156
162
|
}
|
|
157
163
|
export const BRIDGE_SUBCOMMAND = "bridge";
|
|
158
164
|
export const STREAM_TICKET_PATH = "/api/plan-sessions/me/stream-ticket";
|
|
165
|
+
// DX-2970 — the bare (no `fields=`) cheap-scalars read, used ONLY to report the
|
|
166
|
+
// connected plan's real `boards`/`cardCount` on the `ready` record (see
|
|
167
|
+
// `fetchPlanInventory`). This is NOT a revival of DX-2920's deleted
|
|
168
|
+
// `verifyPlanBoardsReadable` gate — it never refuses a mint, never blocks
|
|
169
|
+
// streaming, and is best-effort (one attempt, no retry/backoff).
|
|
170
|
+
export const PLAN_MINE_PATH = "/api/plans/mine";
|
|
159
171
|
export const SESSION_ID_HEADER = "x-danx-session-id";
|
|
160
172
|
/** How long one dashboard request may take before it counts as a transient failure. */
|
|
161
173
|
export const REQUEST_TIMEOUT_MS = 10_000;
|
|
@@ -427,6 +439,44 @@ export async function mintTicket(options, deps) {
|
|
|
427
439
|
leaseMs: ticket.leaseMs,
|
|
428
440
|
};
|
|
429
441
|
}
|
|
442
|
+
/** What is wrong with a 2xx bare `/api/plans/mine` body, or `null` when it carries a usable inventory. */
|
|
443
|
+
function badInventoryReason(body) {
|
|
444
|
+
if (typeof body !== "object" || body === null)
|
|
445
|
+
return "the body is not a JSON object";
|
|
446
|
+
const b = body;
|
|
447
|
+
if (!Array.isArray(b.boards) || b.boards.some((x) => typeof x !== "string"))
|
|
448
|
+
return "boards is not a string array";
|
|
449
|
+
if (typeof b.cardCount !== "number" || !Number.isSafeInteger(b.cardCount) || b.cardCount < 0) {
|
|
450
|
+
return "cardCount is not a non-negative integer";
|
|
451
|
+
}
|
|
452
|
+
return null;
|
|
453
|
+
}
|
|
454
|
+
/**
|
|
455
|
+
* DX-2970 — a single BEST-EFFORT read of the connected plan's real
|
|
456
|
+
* `boards`/`cardCount`, purely for accurate `ready`-record reporting. NEVER a
|
|
457
|
+
* gate: unlike DX-2920's deleted `verifyPlanBoardsReadable`, a failure here
|
|
458
|
+
* (network, timeout, non-2xx, unusable body) is NOT retried and NEVER blocks
|
|
459
|
+
* or delays streaming — `runBridge` reports `degraded: true` with `boards`/
|
|
460
|
+
* `cardCount` both `null` and proceeds exactly as it would have before this
|
|
461
|
+
* read existed. The bare (no `fields=`) call is deliberately the cheapest
|
|
462
|
+
* read the route offers — the plan's own "cheap scalars," always present on
|
|
463
|
+
* every `GET /api/plans/mine` response regardless of `fields`.
|
|
464
|
+
*/
|
|
465
|
+
export async function fetchPlanInventory(options, deps) {
|
|
466
|
+
const outcome = await request(options, deps, "plan inventory read", `${options.dashboardUrl}${PLAN_MINE_PATH}`);
|
|
467
|
+
if (outcome.kind === "transient")
|
|
468
|
+
return { kind: "unavailable", detail: outcome.detail };
|
|
469
|
+
const { status, text, body } = outcome;
|
|
470
|
+
if (status < 200 || status >= 300) {
|
|
471
|
+
return { kind: "unavailable", detail: `plan inventory read HTTP ${status} ${oneLine(text, 300)}` };
|
|
472
|
+
}
|
|
473
|
+
const bad = badInventoryReason(body);
|
|
474
|
+
if (bad !== null) {
|
|
475
|
+
return { kind: "unavailable", detail: `plan inventory read HTTP ${status}: ${bad}` };
|
|
476
|
+
}
|
|
477
|
+
const b = body;
|
|
478
|
+
return { kind: "ok", boards: b.boards, cardCount: b.cardCount };
|
|
479
|
+
}
|
|
430
480
|
/**
|
|
431
481
|
* DX-2920 — a stream admission refusal (`listen.ts`'s STRUCTURED `refused`
|
|
432
482
|
* outcome — `{status, errorCode, body}`, parsed once in `connectOnce`) whose
|
|
@@ -579,9 +629,24 @@ export async function runBridge(options, deps) {
|
|
|
579
629
|
// board reach is proven by the dashboard's own mint refusal rather than a
|
|
580
630
|
// client-side check run here (see the module docblock + the deleted
|
|
581
631
|
// `verifyPlanBoardsReadable`). A re-mint (a lapsed lease) never repeats it.
|
|
632
|
+
// DX-2970 — the inventory read (`fetchPlanInventory`) runs on this SAME
|
|
633
|
+
// first-mint-only path, for the same reason: it is a property of the
|
|
634
|
+
// credential and the plan, and a re-mint changes neither. Best-effort —
|
|
635
|
+
// never gates the `for(;;)` loop, never retried, never delays streaming.
|
|
582
636
|
if (!readyEmitted) {
|
|
583
637
|
readyEmitted = true;
|
|
584
|
-
|
|
638
|
+
const inventory = await fetchPlanInventory(options, deps);
|
|
639
|
+
if (inventory.kind === "ok") {
|
|
640
|
+
deps.write({
|
|
641
|
+
type: "ready",
|
|
642
|
+
boards: inventory.boards,
|
|
643
|
+
cardCount: inventory.cardCount,
|
|
644
|
+
degraded: inventory.boards.length === 0 || inventory.cardCount === 0,
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
else {
|
|
648
|
+
deps.write({ type: "ready", boards: null, cardCount: null, degraded: true });
|
|
649
|
+
}
|
|
585
650
|
}
|
|
586
651
|
backoff = MINT_INITIAL_BACKOFF_MS;
|
|
587
652
|
const run = { stopped: null, emitted: false };
|
package/dist/handlers.js
CHANGED
|
@@ -70,6 +70,15 @@ export const LIST_PAGE_MAX_LIMIT = 1000;
|
|
|
70
70
|
* `__tests__/handlers.test.ts` asserts the two agree.
|
|
71
71
|
*/
|
|
72
72
|
export const PLAN_GET_CARDS_DEFAULT_LIMIT = 200;
|
|
73
|
+
/**
|
|
74
|
+
* DX-3027 — `plan_get`'s `events_limit` default/max — mirrors the server's
|
|
75
|
+
* `src/issues/plans-routes.ts#PLAN_GET_EVENTS_DEFAULT_LIMIT` /
|
|
76
|
+
* `PLAN_GET_EVENTS_MAX_LIMIT`. Deliberately smaller than `LIST_PAGE_MAX_LIMIT`
|
|
77
|
+
* above — an event page is read far more often, and a wide page defeats the
|
|
78
|
+
* point of a cursor. `__tests__/handlers.test.ts` asserts the two agree.
|
|
79
|
+
*/
|
|
80
|
+
export const PLAN_GET_EVENTS_DEFAULT_LIMIT = 50;
|
|
81
|
+
export const PLAN_GET_EVENTS_MAX_LIMIT = 200;
|
|
73
82
|
/**
|
|
74
83
|
* Fetch one card via `GET /api/issues/:id`, or many via
|
|
75
84
|
* `GET /api/issues/batch?ids=...` (DX-2727) — a real batch read that keeps
|
|
@@ -857,7 +866,54 @@ export const PLAN_FIELD_GROUPS = [
|
|
|
857
866
|
"architecture",
|
|
858
867
|
"sessions",
|
|
859
868
|
"notes",
|
|
869
|
+
"events",
|
|
870
|
+
];
|
|
871
|
+
/**
|
|
872
|
+
* DX-3027 — every kind `plan_get`'s `events_kinds` filter accepts. The ONE
|
|
873
|
+
* copy in this package (the `plan_get` zod enum in `index.ts` reads this
|
|
874
|
+
* const), duplicated from the server's `src/issues/db/plan-events.ts#PLAN_EVENT_KINDS`
|
|
875
|
+
* because the published package cannot import server source at runtime.
|
|
876
|
+
* `__tests__/handlers.test.ts` asserts the two are equal, so drift fails a
|
|
877
|
+
* test rather than a live call.
|
|
878
|
+
*/
|
|
879
|
+
export const PLAN_EVENT_KINDS = [
|
|
880
|
+
"comment_added",
|
|
881
|
+
"solution_answered",
|
|
882
|
+
"problem_added",
|
|
883
|
+
"problem_unanswered",
|
|
884
|
+
"problem_answer_changed",
|
|
885
|
+
"blocked",
|
|
886
|
+
"unblocked",
|
|
887
|
+
"transitioned",
|
|
888
|
+
"edited",
|
|
889
|
+
"dependency_added",
|
|
890
|
+
"dependency_removed",
|
|
891
|
+
"card_created",
|
|
892
|
+
"plan_created",
|
|
893
|
+
"plan_renamed",
|
|
894
|
+
"card_added_to_plan",
|
|
895
|
+
"card_removed_from_plan",
|
|
896
|
+
"record_added",
|
|
897
|
+
"record_updated",
|
|
898
|
+
"record_removed",
|
|
899
|
+
"note_added",
|
|
900
|
+
"note_updated",
|
|
901
|
+
"note_removed",
|
|
902
|
+
"architecture_section_added",
|
|
903
|
+
"architecture_section_updated",
|
|
904
|
+
"architecture_section_removed",
|
|
905
|
+
"architecture_sections_reordered",
|
|
906
|
+
"session_connected",
|
|
907
|
+
"session_switched_away",
|
|
908
|
+
"idle_nudge_sent",
|
|
860
909
|
];
|
|
910
|
+
/**
|
|
911
|
+
* DX-3027 — every origin `plan_get`'s `events_origin` filter accepts.
|
|
912
|
+
* Mirrors the server's `src/issues/db/plan-events.ts#PlanEventOrigin`
|
|
913
|
+
* (itself `ActivityOrigin`) — same drift protection as `PLAN_EVENT_KINDS`
|
|
914
|
+
* above.
|
|
915
|
+
*/
|
|
916
|
+
export const PLAN_EVENT_ORIGINS = ["operator", "agent", "machine"];
|
|
861
917
|
/**
|
|
862
918
|
* DX-2834 — the plan-status taxonomy, mirroring `PLAN_FIELD_GROUPS` just
|
|
863
919
|
* above: the ONE copy in this package (the `plan_list` zod enum in
|
|
@@ -892,6 +948,17 @@ export async function planGet(client, args = {}) {
|
|
|
892
948
|
query.cards_offset = args.cards_offset;
|
|
893
949
|
if (args.cards_limit !== undefined)
|
|
894
950
|
query.cards_limit = args.cards_limit;
|
|
951
|
+
if (args.events_limit !== undefined)
|
|
952
|
+
query.events_limit = args.events_limit;
|
|
953
|
+
if (args.events_before !== undefined)
|
|
954
|
+
query.events_before = args.events_before;
|
|
955
|
+
if (args.events_kinds !== undefined && args.events_kinds.length > 0) {
|
|
956
|
+
query.events_kinds = args.events_kinds.join(",");
|
|
957
|
+
}
|
|
958
|
+
if (args.events_origin !== undefined)
|
|
959
|
+
query.events_origin = args.events_origin;
|
|
960
|
+
if (args.events_writer !== undefined)
|
|
961
|
+
query.events_writer = args.events_writer;
|
|
895
962
|
return client.request({
|
|
896
963
|
method: "GET",
|
|
897
964
|
path: args.plan_id === undefined ? "/mine" : `/${args.plan_id}/full`,
|
package/dist/index.js
CHANGED
|
@@ -101,7 +101,7 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
101
101
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
102
102
|
import { z } from "zod";
|
|
103
103
|
import { DashboardHttpClient } from "./http-client.js";
|
|
104
|
-
import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueProblem, issueQualityGate, issueQualityGateVerdict, issueRetireBranch, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, failureCategoryCreate, failureCategoryList, failureCategoryUpdate, planAddArchitectureSection, planAddCard, planAddNote, planAddRecord, planConnect, planCreate, planDeleteArchitectureSection, planDeleteNote, planDeleteRecord, planGet, PLAN_FIELD_GROUPS, PLAN_STATUSES, ISSUE_BATCH_GET_MAX, LIST_PAGE_MAX_LIMIT, PLAN_GET_CARDS_DEFAULT_LIMIT, planGetArchitectureSection, planGetRecord, planList, planRemoveCard, planRename, planReorderArchitectureSection, planUpdateArchitectureSection, planUpdateNote, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, } from "./handlers.js";
|
|
104
|
+
import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueProblem, issueQualityGate, issueQualityGateVerdict, issueRetireBranch, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, failureCategoryCreate, failureCategoryList, failureCategoryUpdate, planAddArchitectureSection, planAddCard, planAddNote, planAddRecord, planConnect, planCreate, planDeleteArchitectureSection, planDeleteNote, planDeleteRecord, planGet, PLAN_FIELD_GROUPS, PLAN_EVENT_KINDS, PLAN_EVENT_ORIGINS, PLAN_STATUSES, ISSUE_BATCH_GET_MAX, LIST_PAGE_MAX_LIMIT, PLAN_GET_CARDS_DEFAULT_LIMIT, PLAN_GET_EVENTS_DEFAULT_LIMIT, PLAN_GET_EVENTS_MAX_LIMIT, planGetArchitectureSection, planGetRecord, planList, planRemoveCard, planRename, planReorderArchitectureSection, planUpdateArchitectureSection, planUpdateNote, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, } from "./handlers.js";
|
|
105
105
|
import { PRIORITY_TIER_WORDS } from "./priority.js";
|
|
106
106
|
function readEnvOrDie(name) {
|
|
107
107
|
const v = process.env[name];
|
|
@@ -553,7 +553,7 @@ server.tool("issue_problem", "A card's PROBLEMS via /api/issues/:id/problems[/:p
|
|
|
553
553
|
action: z.enum(["list", "add", "edit", "remove"]),
|
|
554
554
|
problem_id: z.number().int().positive().optional().describe("edit/remove"),
|
|
555
555
|
base_hash: z.string().min(1).optional().describe("content_hash last read; edit/remove"),
|
|
556
|
-
statement: z.string().min(1).optional().describe("add/edit; ONE plain question, at most
|
|
556
|
+
statement: z.string().min(1).optional().describe("add/edit; ONE plain question, at most 200 characters"),
|
|
557
557
|
context: z
|
|
558
558
|
.string()
|
|
559
559
|
.nullable()
|
|
@@ -723,7 +723,7 @@ server.tool("plan_list", "List every plan via GET /api/plans (DX-2683), and lear
|
|
|
723
723
|
.optional()
|
|
724
724
|
.describe("Filter to one computed status: awaiting-session, planning, building, complete. Omit for every plan."),
|
|
725
725
|
}, async (args) => jsonResult(await planList(client, args)));
|
|
726
|
-
server.tool("plan_get", "Read a plan via GET /api/plans (DX-2683). Pass `plan_id` to read ANY plan; OMIT to read the plan this session is connected to — omitting while connected to none fails loud `{error: \"session_not_connected\"}`, connect first. A BARE call (no `fields`) returns ONLY the plan's cheap scalars: `{plan, boards, cardCount, bucketCounts, status, session, sessionListenerAttached, available_field_groups}` — no cards, records, or architecture body. `plan` carries `{id, ref, name, createdAt}`; `ref` is the plan's short reference (`PLN-<id>`) — cite that, not the bare id. `status` (DX-2834) is computed fresh on every read, never stored — see `plan_list` for the four values and what each means. Pass `fields` to opt into: `cards` (member cards, PAGED: `cards_offset` (default 0) and `cards_limit` (1.." + LIST_PAGE_MAX_LIMIT + ", default " + PLAN_GET_CARDS_DEFAULT_LIMIT + ") pick the page, in stable card-reference order (board prefix, then card number — pages never repeat/skip unless membership changes between reads); response carries `cards_total`/`cards_offset` — page while cards_offset + cards.length < cards_total; either paging arg without `fields: [\"cards\"]` is a 400), `records` (every goal+rule+caveat, keyed by kind) or `records:goal` / `records:rule` / `records:caveat` (one kind, cheaper), `architecture` (`{sections: [{id, planId, contentHash, title, content, sortOrder, createdAt, updatedAt}]}`), `sessions` (every session connected to the plan). `session`/`sessionListenerAttached` and `available_field_groups` ride every response regardless. `sessionListenerAttached` is `false` for a few seconds right after `plan_connect` while the plugin's event bridge starts; still `false` after that while connected means the plan's card events are not reaching you — tell the operator. ALWAYS `plan_get`/`plan_get_architecture_section` immediately before `plan_update_architecture_section`/`plan_delete_architecture_section` and pass the returned `contentHash` back as `base_hash`.", {
|
|
726
|
+
server.tool("plan_get", "Read a plan via GET /api/plans (DX-2683). Pass `plan_id` to read ANY plan; OMIT to read the plan this session is connected to — omitting while connected to none fails loud `{error: \"session_not_connected\"}`, connect first. A BARE call (no `fields`) returns ONLY the plan's cheap scalars: `{plan, boards, cardCount, bucketCounts, status, session, sessionListenerAttached, available_field_groups}` — no cards, records, or architecture body. `plan` carries `{id, ref, name, createdAt}`; `ref` is the plan's short reference (`PLN-<id>`) — cite that, not the bare id. `status` (DX-2834) is computed fresh on every read, never stored — see `plan_list` for the four values and what each means. Pass `fields` to opt into: `cards` (member cards, PAGED: `cards_offset` (default 0) and `cards_limit` (1.." + LIST_PAGE_MAX_LIMIT + ", default " + PLAN_GET_CARDS_DEFAULT_LIMIT + ") pick the page, in stable card-reference order (board prefix, then card number — pages never repeat/skip unless membership changes between reads); response carries `cards_total`/`cards_offset` — page while cards_offset + cards.length < cards_total; either paging arg without `fields: [\"cards\"]` is a 400), `records` (every goal+rule+caveat, keyed by kind) or `records:goal` / `records:rule` / `records:caveat` (one kind, cheaper), `architecture` (`{sections: [{id, planId, contentHash, title, content, sortOrder, createdAt, updatedAt}]}`), `sessions` (every session connected to the plan), `events` (DX-2987/DX-3027 — the plan's durable event ledger: every human action and every bridge message recorded on it, cursor-paged newest first via `events_limit` (1.." + PLAN_GET_EVENTS_MAX_LIMIT + ", default " + PLAN_GET_EVENTS_DEFAULT_LIMIT + ") and `events_before` (an opaque cursor — pass a previous page's `next_cursor` to read older; omit for the newest page); filter with `events_kinds` (one or more of " + PLAN_EVENT_KINDS.join(", ") + "), `events_origin` (one of " + PLAN_EVENT_ORIGINS.join(", ") + "), `events_writer` (exact writer name); every `events_*` param without `fields: [\"events\"]` is a 400, same as the `cards_*` params above; response carries `events: {items: [{id, at, kind, writer, origin, originSessionId, targetSessionId, cardId, cardTitle, boardId, detail}], next_cursor}` — `next_cursor` is `null` on the last page; an event on a card whose board you cannot read is left out, plan-level events are always visible). `session`/`sessionListenerAttached` and `available_field_groups` ride every response regardless. `sessionListenerAttached` is `false` for a few seconds right after `plan_connect` while the plugin's event bridge starts; still `false` after that while connected means the plan's card events are not reaching you — tell the operator. ALWAYS `plan_get`/`plan_get_architecture_section` immediately before `plan_update_architecture_section`/`plan_delete_architecture_section` and pass the returned `contentHash` back as `base_hash`.", {
|
|
727
727
|
plan_id: z
|
|
728
728
|
.number()
|
|
729
729
|
.int()
|
|
@@ -733,7 +733,7 @@ server.tool("plan_get", "Read a plan via GET /api/plans (DX-2683). Pass `plan_id
|
|
|
733
733
|
fields: z
|
|
734
734
|
.array(z.enum(PLAN_FIELD_GROUPS))
|
|
735
735
|
.optional()
|
|
736
|
-
.describe("Opt-in field-GROUPS: cards, records (every kind) or records:goal/records:rule/records:caveat (one kind), architecture, sessions. Absent/empty = cheap scalars only (plan, boards, cardCount, bucketCounts) plus session state."),
|
|
736
|
+
.describe("Opt-in field-GROUPS: cards, records (every kind) or records:goal/records:rule/records:caveat (one kind), architecture, sessions, notes, events. Absent/empty = cheap scalars only (plan, boards, cardCount, bucketCounts) plus session state."),
|
|
737
737
|
cards_offset: z
|
|
738
738
|
.number()
|
|
739
739
|
.int()
|
|
@@ -747,6 +747,31 @@ server.tool("plan_get", "Read a plan via GET /api/plans (DX-2683). Pass `plan_id
|
|
|
747
747
|
.max(LIST_PAGE_MAX_LIMIT)
|
|
748
748
|
.optional()
|
|
749
749
|
.describe("How many cards one page holds, 1.." + LIST_PAGE_MAX_LIMIT + " (default " + PLAN_GET_CARDS_DEFAULT_LIMIT + "). Requires `fields` to include `cards`."),
|
|
750
|
+
events_limit: z
|
|
751
|
+
.number()
|
|
752
|
+
.int()
|
|
753
|
+
.positive()
|
|
754
|
+
.max(PLAN_GET_EVENTS_MAX_LIMIT)
|
|
755
|
+
.optional()
|
|
756
|
+
.describe("DX-3027 — how many ledger events one page holds, 1.." + PLAN_GET_EVENTS_MAX_LIMIT + " (default " + PLAN_GET_EVENTS_DEFAULT_LIMIT + "). Requires `fields` to include `events`."),
|
|
757
|
+
events_before: z
|
|
758
|
+
.string()
|
|
759
|
+
.min(1)
|
|
760
|
+
.optional()
|
|
761
|
+
.describe("DX-3027 — an opaque cursor from a previous page's `next_cursor`. Omit for the newest page. Requires `fields` to include `events`."),
|
|
762
|
+
events_kinds: z
|
|
763
|
+
.array(z.enum(PLAN_EVENT_KINDS))
|
|
764
|
+
.optional()
|
|
765
|
+
.describe("DX-3027 — only these event kinds. Omit for every kind. Requires `fields` to include `events`."),
|
|
766
|
+
events_origin: z
|
|
767
|
+
.enum(PLAN_EVENT_ORIGINS)
|
|
768
|
+
.optional()
|
|
769
|
+
.describe("DX-3027 — only events with this origin. Omit for every origin. Requires `fields` to include `events`."),
|
|
770
|
+
events_writer: z
|
|
771
|
+
.string()
|
|
772
|
+
.min(1)
|
|
773
|
+
.optional()
|
|
774
|
+
.describe("DX-3027 — only events with this exact writer name. Omit for every writer. Requires `fields` to include `events`."),
|
|
750
775
|
}, async (args) => jsonResult(await planGet(client, args)));
|
|
751
776
|
server.tool("plan_create", "Create a new, empty plan via POST /api/plans (DX-2531). GLOBAL — a plan is not board-scoped, and this call adds no cards, no records, and no architecture sections; it does NOT connect any session to the new plan (call `plan_connect` separately, exactly as adding a card to a plan is its own separate step). Returns `{ok, status, body: {plan: {id, ref, name, createdAt}}}` — `ref` is the plan's short reference (`PLN-<id>`). Use the returned `plan.id` with `plan_connect` to start working on it, or with `plan_get({plan_id})` to browse it.", {
|
|
752
777
|
name: z.string().min(1).describe("The plan's name — shown in the Plans list."),
|
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.93",
|
|
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",
|
|
@@ -31,7 +31,6 @@
|
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@modelcontextprotocol/sdk": "1.29.0",
|
|
34
|
-
"danxbot": "file:../..",
|
|
35
34
|
"zod": "^3.25.76"
|
|
36
35
|
},
|
|
37
36
|
"devDependencies": {
|