@thehammer/danx-dashboard-mcp 0.1.142 → 0.1.143
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 +61 -0
- package/dist/index.js +50 -52
- package/package.json +42 -42
package/dist/handlers.js
CHANGED
|
@@ -336,6 +336,39 @@ function argCheckers(tool, mode) {
|
|
|
336
336
|
},
|
|
337
337
|
};
|
|
338
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* Every action-dispatched tool's args carry these three regardless of
|
|
341
|
+
* `action` — never action-specific, so every per-action allow-list passed to
|
|
342
|
+
* `refuseInapplicableFields` is unioned with this rather than repeating it.
|
|
343
|
+
*/
|
|
344
|
+
const STRUCTURAL_ACTION_FIELDS = ["id", "action", "board"];
|
|
345
|
+
/**
|
|
346
|
+
* DX-3310 (code-review nit) — a field the caller sent that means nothing for
|
|
347
|
+
* the chosen `action` used to be silently ignored: `issue_solution`'s `add`
|
|
348
|
+
* built its `content` object from `title`/`body`/`pro`/`con`/`recommended`
|
|
349
|
+
* regardless of action, then simply never read it on `remove`/`add_step`/etc,
|
|
350
|
+
* and `issue_problem`'s `remove` never looked at `statement`/`context`/
|
|
351
|
+
* `solutions` at all. Either way the caller got a 200 with the field quietly
|
|
352
|
+
* discarded — no different from a typo. Refused loud instead, mirroring
|
|
353
|
+
* `argCheckers`' own wording: every ACTUALLY-SUPPLIED (`!== undefined`) key
|
|
354
|
+
* outside the action's own allow-list (plus `STRUCTURAL_ACTION_FIELDS`,
|
|
355
|
+
* always allowed) is named in one error, so a caller fixes every offending
|
|
356
|
+
* field in one round trip rather than one at a time.
|
|
357
|
+
*/
|
|
358
|
+
// A9 (code-review fix) — takes `object` rather than `Record<string, unknown>`
|
|
359
|
+
// so every caller (each with its own specific `*Args` interface, no index
|
|
360
|
+
// signature) passes `args` directly, with no `as unknown as Record<...>`
|
|
361
|
+
// bridge cast at the call site. `Object.entries` reads the key/value pairs
|
|
362
|
+
// without needing an index signature on the input type either.
|
|
363
|
+
function refuseInapplicableFields(tool, mode, args, allowed) {
|
|
364
|
+
const offending = Object.entries(args)
|
|
365
|
+
.filter(([key, value]) => value !== undefined && !STRUCTURAL_ACTION_FIELDS.includes(key) && !allowed.has(key))
|
|
366
|
+
.map(([key]) => key)
|
|
367
|
+
.sort();
|
|
368
|
+
if (offending.length > 0) {
|
|
369
|
+
throw new Error(`${tool} ${mode} refuses fields that do not apply to this action: ${offending.join(", ")}`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
339
372
|
export async function issueComment(client, args) {
|
|
340
373
|
const idEnc = encodeURIComponent(args.id);
|
|
341
374
|
const board = args.board;
|
|
@@ -537,10 +570,25 @@ export async function issueChecklist(client, args) {
|
|
|
537
570
|
*
|
|
538
571
|
* No answer action, for the reason `issueSolution` gives.
|
|
539
572
|
*/
|
|
573
|
+
/**
|
|
574
|
+
* DX-3310 (nit) — per-action allow-list for `refuseInapplicableFields`.
|
|
575
|
+
* `id`/`action`/`board` are structural and never listed (every action takes
|
|
576
|
+
* them). `type` is listed under `edit` even though it can never actually be
|
|
577
|
+
* SENT there (see the dedicated throw below) — that keeps the generic
|
|
578
|
+
* refusal from firing first with a less specific message than the one that
|
|
579
|
+
* already names the real reason.
|
|
580
|
+
*/
|
|
581
|
+
const PROBLEM_ACTION_FIELDS = {
|
|
582
|
+
list: [],
|
|
583
|
+
add: ["statement", "context", "type", "summary", "solutions"],
|
|
584
|
+
edit: ["problem_id", "base_hash", "statement", "context", "summary", "type"],
|
|
585
|
+
remove: ["problem_id", "base_hash"],
|
|
586
|
+
};
|
|
540
587
|
export async function issueProblem(client, args) {
|
|
541
588
|
const idEnc = encodeURIComponent(args.id);
|
|
542
589
|
const board = args.board;
|
|
543
590
|
const need = argCheckers("issue_problem", `action=${args.action}`);
|
|
591
|
+
refuseInapplicableFields("issue_problem", `action=${args.action}`, args, new Set(PROBLEM_ACTION_FIELDS[args.action]));
|
|
544
592
|
switch (args.action) {
|
|
545
593
|
case "list":
|
|
546
594
|
return client.request({ method: "GET", path: `/${idEnc}/problems`, board });
|
|
@@ -621,11 +669,24 @@ export async function issueProblem(client, args) {
|
|
|
621
669
|
* question could release the very stop it set to wait for a human. The operator
|
|
622
670
|
* answers in the dashboard.
|
|
623
671
|
*/
|
|
672
|
+
const SOLUTION_ACTION_FIELDS = {
|
|
673
|
+
add: ["title", "body", "pro", "con", "recommended", "steps"],
|
|
674
|
+
edit: ["solution_id", "base_hash", "title", "body", "pro", "con", "recommended", "steps", "steps_base_hash"],
|
|
675
|
+
remove: ["solution_id", "base_hash"],
|
|
676
|
+
add_step: ["solution_id", "title", "description", "parent_step_id", "position"],
|
|
677
|
+
edit_step: ["solution_id", "step_id", "base_hash", "title", "description"],
|
|
678
|
+
remove_step: ["solution_id", "step_id", "base_hash"],
|
|
679
|
+
};
|
|
624
680
|
export async function issueSolution(client, args) {
|
|
625
681
|
const need = argCheckers("issue_solution", `action=${args.action}`);
|
|
626
682
|
// DX-2735: checked at runtime too, not only by the schema — a caller that skips
|
|
627
683
|
// the MCP boundary must never build `/problems/undefined/solutions`.
|
|
628
684
|
const problemId = need.id(args.problem_id, "problem_id");
|
|
685
|
+
// DX-3310 (nit) — `problem_id` is unioned in here rather than listed in
|
|
686
|
+
// every `SOLUTION_ACTION_FIELDS` entry: unlike `issue_problem` (where it
|
|
687
|
+
// only applies to edit/remove), `issue_solution` requires it for every
|
|
688
|
+
// action (checked above), so it is structural FOR THIS TOOL specifically.
|
|
689
|
+
refuseInapplicableFields("issue_solution", `action=${args.action}`, args, new Set(["problem_id", ...SOLUTION_ACTION_FIELDS[args.action]]));
|
|
629
690
|
const base = `/${encodeURIComponent(args.id)}/problems/${problemId}/solutions`;
|
|
630
691
|
const board = args.board;
|
|
631
692
|
const content = {};
|
package/dist/index.js
CHANGED
|
@@ -342,14 +342,14 @@ const boardField = {
|
|
|
342
342
|
.string()
|
|
343
343
|
.min(1)
|
|
344
344
|
.optional()
|
|
345
|
-
.describe("
|
|
345
|
+
.describe("Board id `<repo>:<slug>`; omit for this dispatch's board. Unknown → 404."),
|
|
346
346
|
};
|
|
347
347
|
// The three prose fields of a card, each with ONE job. Shared by issue_create
|
|
348
348
|
// (root + phase children) and issue_edit so the guidance an agent reads is
|
|
349
349
|
// identical wherever it writes the field.
|
|
350
|
-
const TITLE_DESCRIBE = 'Short, specific label naming the domain
|
|
351
|
-
const SUMMARY_DESCRIBE = "1–3 plain-language sentences, no markdown/jargon
|
|
352
|
-
const DESCRIPTION_DESCRIBE = 'The full body ("Context"): evidence, examples, technical detail; markdown, collapsed by default.
|
|
350
|
+
const TITLE_DESCRIBE = 'Short, specific label naming the domain (e.g. "Guest checkout rejects gift-card carts"). Never generic ("Fix bug", "Follow-up").';
|
|
351
|
+
const SUMMARY_DESCRIBE = "1–3 plain-language sentences, no markdown/jargon: what the card is and why it matters. Always shown, never collapsed — not a second title, not a teaser.";
|
|
352
|
+
const DESCRIPTION_DESCRIBE = 'The full body ("Context"): evidence, examples, technical detail; markdown, collapsed by default. Operator questions go in issue_problem. Style: see issue_comment\'s `text` param.';
|
|
353
353
|
// DX-3335 — condensed from the now-deleted danxbot:comment-style skill (PLN-11
|
|
354
354
|
// R-16: fold a skill's rule into the tool description it governs rather than
|
|
355
355
|
// require a separate load). Shared by every prose-writing field below so an
|
|
@@ -412,7 +412,7 @@ strictTool("issue_get",
|
|
|
412
412
|
.optional()
|
|
413
413
|
.describe("Single-id form only. How many comments the page holds (server default 20), the window anchored at the " +
|
|
414
414
|
"NEWEST comment. An out-of-range value (server max 200) is refused by the server with its own 400, " +
|
|
415
|
-
"never silently clamped here. Requires `fields
|
|
415
|
+
"never silently clamped here. Requires `fields:[\"comments\"]`."),
|
|
416
416
|
comments_offset: z
|
|
417
417
|
.number()
|
|
418
418
|
.int()
|
|
@@ -488,7 +488,7 @@ strictTool("issue_create", '`plan` is REQUIRED on every create — no default, n
|
|
|
488
488
|
priority: z
|
|
489
489
|
.union([z.enum(PRIORITY_TIER_WORDS), z.number()])
|
|
490
490
|
.optional()
|
|
491
|
-
.describe('
|
|
491
|
+
.describe('Tier word ("lowest"…"critical", prefer this) or number in [0,6); higher = more urgent. Omit for the route\'s own default.'),
|
|
492
492
|
// DX-3238 — `assigned_agent` is DELIBERATELY left undeclared here, unlike
|
|
493
493
|
// `priority` above. The route accepts it (`ALLOWED_CREATE_KEYS`,
|
|
494
494
|
// create.ts:39-56) but it only matters for the create→`list_id`-lands-
|
|
@@ -525,7 +525,7 @@ strictTool("issue_edit", 'Patch a card. ALLOWED keys: title, summary, descriptio
|
|
|
525
525
|
status: z
|
|
526
526
|
.enum(CHECKLIST_ITEM_STATUSES)
|
|
527
527
|
.optional()
|
|
528
|
-
.describe("Optional full status; omitted → from `checked`.
|
|
528
|
+
.describe("Optional full status; omitted → from `checked`. See tool description for the deferred/📡 rules."),
|
|
529
529
|
detail: z
|
|
530
530
|
.string()
|
|
531
531
|
.optional()
|
|
@@ -552,7 +552,7 @@ strictTool("issue_edit", 'Patch a card. ALLOWED keys: title, summary, descriptio
|
|
|
552
552
|
priority: z
|
|
553
553
|
.union([z.enum(PRIORITY_TIER_WORDS), z.number()])
|
|
554
554
|
.optional()
|
|
555
|
-
.describe('
|
|
555
|
+
.describe('Tier word ("lowest"…"critical", prefer this) or number in [0,6); higher = more urgent — see tool description for why this is the only way to set it.'),
|
|
556
556
|
list_id: z.string().min(1).nullable().optional(),
|
|
557
557
|
triage_enabled: z
|
|
558
558
|
.boolean()
|
|
@@ -647,15 +647,15 @@ const STEP_INPUT = z.lazy(() => z
|
|
|
647
647
|
.int()
|
|
648
648
|
.positive()
|
|
649
649
|
.optional()
|
|
650
|
-
.describe("present ->
|
|
651
|
-
"
|
|
652
|
-
"
|
|
653
|
-
"
|
|
650
|
+
.describe("present -> matches an existing step by this id (400 if not a live child of this exact parent scope, " +
|
|
651
|
+
"never claimed by the fallback below); absent -> matches the first unclaimed same-titled sibling " +
|
|
652
|
+
"under the same parent, else creates new (title-based, never positional — keeps later id-less " +
|
|
653
|
+
"siblings' ids intact on a middle insert). Send ids anyway when identity matters (checked progress, " +
|
|
654
|
+
"a later edit) or siblings share a title."),
|
|
654
655
|
title: z
|
|
655
656
|
.string()
|
|
656
657
|
.min(1)
|
|
657
|
-
.describe("one imperative line
|
|
658
|
-
"in the title itself."),
|
|
658
|
+
.describe("one imperative line — labels (\"1.\", \"2a\") are DERIVED on read, never put one in the title."),
|
|
659
659
|
description: z.string().nullable().optional().describe("markdown detail, or null/omitted for none"),
|
|
660
660
|
steps: z.array(STEP_INPUT).optional().describe("this step's own children, nested — refused past 3 levels total"),
|
|
661
661
|
})
|
|
@@ -669,9 +669,7 @@ strictTool("issue_problem", "A card's PROBLEMS: one statement the operator must
|
|
|
669
669
|
.string()
|
|
670
670
|
.min(1)
|
|
671
671
|
.optional()
|
|
672
|
-
.describe("add/edit; ONE plain sentence, at most 200 characters
|
|
673
|
-
"WHAT IS TO BE DONE — the deed itself, never phrased as a question (\"Rotate the staging DB credential\", " +
|
|
674
|
-
"not \"Should we rotate it?\")."),
|
|
672
|
+
.describe("add/edit; ONE plain sentence, at most 200 characters — see `type` below for question vs action phrasing."),
|
|
675
673
|
context: z
|
|
676
674
|
.string()
|
|
677
675
|
.nullable()
|
|
@@ -697,16 +695,14 @@ strictTool("issue_problem", "A card's PROBLEMS: one statement the operator must
|
|
|
697
695
|
.string()
|
|
698
696
|
.nullable()
|
|
699
697
|
.optional()
|
|
700
|
-
.describe("add/edit. Plain text, 1-3 sentences — never markdown
|
|
701
|
-
"
|
|
702
|
-
"
|
|
703
|
-
"
|
|
704
|
-
"when given (omit/null on a question means none). edit: omit to keep the stored summary, null to clear it " +
|
|
705
|
-
"(refused if the problem is a live action)."),
|
|
698
|
+
.describe("add/edit. Plain text, 1-3 sentences — never markdown (that's `context`'s job). Required+non-empty for an " +
|
|
699
|
+
"action problem (why needed + why you can't do it yourself — see `type`; refused 400 if missing on add or " +
|
|
700
|
+
"cleared on a live action edit); optional for a question. add: sent only when given (omit/null on a " +
|
|
701
|
+
"question means none). edit: omit keeps the stored summary, null clears it."),
|
|
706
702
|
solutions: z
|
|
707
703
|
.array(z.object({ title: z.string().min(1), ...SOLUTION_FIELDS, steps: z.array(STEP_INPUT).optional() }).strict())
|
|
708
704
|
.optional()
|
|
709
|
-
.describe("add only; fields as issue_solution add, INCLUDING steps
|
|
705
|
+
.describe("add only; fields as issue_solution add, INCLUDING steps — an ACTION problem's inline " +
|
|
710
706
|
"solutions must each carry at least one step (refused 400 otherwise); a QUESTION's may carry any number, " +
|
|
711
707
|
"including none."),
|
|
712
708
|
...boardField,
|
|
@@ -715,27 +711,29 @@ strictTool("issue_problem", "A card's PROBLEMS: one statement the operator must
|
|
|
715
711
|
strictTool("issue_solution", "One problem's options, AND one solution's individual " +
|
|
716
712
|
"procedure steps; `problem_id` is REQUIRED (from issue_problem list/add; " +
|
|
717
713
|
"another problem's solution id → 404). add {title, body?, pro?, con?, recommended?, steps?}: title names the " +
|
|
718
|
-
"option
|
|
719
|
-
"
|
|
720
|
-
"
|
|
721
|
-
"
|
|
722
|
-
"
|
|
723
|
-
"
|
|
724
|
-
"
|
|
725
|
-
"
|
|
726
|
-
"
|
|
727
|
-
"
|
|
728
|
-
"
|
|
729
|
-
"
|
|
730
|
-
"
|
|
731
|
-
"
|
|
732
|
-
"
|
|
733
|
-
"
|
|
734
|
-
"
|
|
735
|
-
"
|
|
736
|
-
"
|
|
737
|
-
"
|
|
738
|
-
"
|
|
714
|
+
"option, body/pro/con its markdown detail and case for/against; `steps` authors the WHOLE procedure in one " +
|
|
715
|
+
"call — {title, description?, steps?}[], nestable to 3 levels (4th refused 400, naming the step + limit). " +
|
|
716
|
+
"ACTION problem: `steps` must be non-empty (400 otherwise); QUESTION: may be omitted/empty. Labels " +
|
|
717
|
+
"(\"1\", \"2a\", \"2a.i\") are DERIVED on read from position — never put one in a title. edit :sid {base_hash, " +
|
|
718
|
+
"...changed fields, steps?, steps_base_hash?}: `steps` omitted leaves the procedure untouched; an explicit " +
|
|
719
|
+
"array (incl. [], refused if it leaves an action's solution with zero steps) DIFFS against it — send an " +
|
|
720
|
+
"existing step's `id` to keep it (even while retitling/reordering), omit `id` for a new step; any existing " +
|
|
721
|
+
"step left out is removed. An id-less node matches the first unclaimed same-titled sibling under the same " +
|
|
722
|
+
"parent (never positional), so an id-less MIDDLE insert keeps later id-less siblings' ids — send ids anyway " +
|
|
723
|
+
"when identity matters. `steps_base_hash` REQUIRED whenever `steps` is sent — the solution's current " +
|
|
724
|
+
"`steps_hash`; stale → 409 `stale_steps` with the current tree. Never resend the whole tree to fix one word " +
|
|
725
|
+
"— use add_step/edit_step/remove_step below. remove :sid {base_hash}: stale → 409 `stale_solution` with " +
|
|
726
|
+
"currentHash + currentSolution, merge and retry. At most ONE live recommended per problem — a second → 409 " +
|
|
727
|
+
"naming `recommended_solution_id`. A CHOSEN option's words and WHOLE PROCEDURE are frozen (409 — add a new " +
|
|
728
|
+
"one instead); every steps route below refuses once a decision has chosen this solution.\n\n" +
|
|
729
|
+
"Granular single-step actions, no tree resend: add_step {solution_id, title, description?, parent_step_id?, " +
|
|
730
|
+
"position?} — title is one imperative line (labels derived, never typed); parent_step_id omitted/null = " +
|
|
731
|
+
"top-level; position 1-indexed among current live children, omitted = append, beyond sibling count → 400 " +
|
|
732
|
+
"(never clamped); nesting past depth 3 → 400. edit_step {solution_id, step_id, base_hash, title?, " +
|
|
733
|
+
"description?} — never moves a step. remove_step {solution_id, step_id, base_hash} — soft-deletes it and " +
|
|
734
|
+
"its live children; refused 409 if this solution belongs to an ACTION problem and removing it leaves zero " +
|
|
735
|
+
"live steps. Stale base_hash on a step → 409 `stale_step` with currentHash + currentStep (real derived " +
|
|
736
|
+
"`label`).", {
|
|
739
737
|
id: z.string().min(1),
|
|
740
738
|
action: z.enum(["add", "edit", "remove", "add_step", "edit_step", "remove_step"]),
|
|
741
739
|
problem_id: z.number().int().positive(),
|
|
@@ -833,8 +831,8 @@ strictTool("quality_gate_instruction", "Fetch a named quality gate's current ins
|
|
|
833
831
|
// ---------------- issue_retro ----------------
|
|
834
832
|
strictTool("issue_retro", "Replace the retro block: {good, bad, correctable_danxbot_problem, correctable_danxbot_problem_description, action_item_ids[], commits[], tests[]}. Refuses 409 unless the card is terminal (completed_at or cancelled_at). good/bad upsert; the three list fields REPLACE (soft-delete prior live rows, fresh ordinals). action_item_ids[] entries match <PREFIX>-N. commits[] entries: {sha, subject?}. tests[] is required (empty array = ran none): one row per test GROUP that ran (name the group, not individual unit tests) or per e2e test (kind:'e2e', listed explicitly since few/expensive); each row {name, kind:'group'|'e2e', num_tests, num_passing_tests, duration_ms} required, num_assertions/num_passing_assertions nullable (omit when the runner has no assertion totals).", {
|
|
835
833
|
id: z.string().min(1),
|
|
836
|
-
good: z.string().describe("Free-form markdown (prose or
|
|
837
|
-
bad: z.string().describe("Free-form markdown (prose or
|
|
834
|
+
good: z.string().describe("Free-form markdown (prose or list, they render identically). Style: see issue_comment's `text` param."),
|
|
835
|
+
bad: z.string().describe("Free-form markdown (prose or list, they render identically). Style: see issue_comment's `text` param."),
|
|
838
836
|
correctable_danxbot_problem: z
|
|
839
837
|
.boolean()
|
|
840
838
|
.describe("Required. Did this dispatch hit a problem in danxbot's OWN code/configuration (not \"this card was hard\") that danxbot could change so it stops happening? A true+described answer starts exactly one automated repair (fix or a ready card) — the only reliable channel for a danxbot defect found mid-dispatch. Answer explicitly every time, never a default."),
|
|
@@ -947,14 +945,14 @@ strictTool("plan_get", "Read a plan. Pass `plan_id` to read ANY plan; OMIT to re
|
|
|
947
945
|
.int()
|
|
948
946
|
.nonnegative()
|
|
949
947
|
.optional()
|
|
950
|
-
.describe("Where the `cards` page starts (default 0). Requires `fields
|
|
948
|
+
.describe("Where the `cards` page starts (default 0). Requires `fields:[\"cards\"]`. Page with cards_offset while cards_offset + cards.length < cards_total."),
|
|
951
949
|
cards_limit: z
|
|
952
950
|
.number()
|
|
953
951
|
.int()
|
|
954
952
|
.positive()
|
|
955
953
|
.max(LIST_PAGE_MAX_LIMIT)
|
|
956
954
|
.optional()
|
|
957
|
-
.describe("How many cards one page holds, 1.." + LIST_PAGE_MAX_LIMIT + " (default " + PLAN_GET_CARDS_DEFAULT_LIMIT + "). Requires `fields
|
|
955
|
+
.describe("How many cards one page holds, 1.." + LIST_PAGE_MAX_LIMIT + " (default " + PLAN_GET_CARDS_DEFAULT_LIMIT + "). Requires `fields:[\"cards\"]`."),
|
|
958
956
|
events_limit: z
|
|
959
957
|
.number()
|
|
960
958
|
.int()
|
|
@@ -1049,9 +1047,9 @@ strictTool("plan_update_note", 'Edit a plan note. `content_hash` must be the not
|
|
|
1049
1047
|
content_hash: z.string().describe("The note's `contentHash` from your last read. Required."),
|
|
1050
1048
|
title: z.string().min(1).optional().describe("New title, at most 60 characters. Omit to keep the stored title."),
|
|
1051
1049
|
body: z.string().min(1).optional().describe("New wrap-up, at most 250 characters. Omit to keep the stored body."),
|
|
1052
|
-
card_ids: z.array(z.string().min(1)).optional().describe("
|
|
1053
|
-
record_refs: z.array(z.string().min(1)).optional().describe("
|
|
1054
|
-
section_ids: z.array(z.number().int().positive()).optional().describe("
|
|
1050
|
+
card_ids: z.array(z.string().min(1)).optional().describe("Part of the link-set group — see tool description."),
|
|
1051
|
+
record_refs: z.array(z.string().min(1)).optional().describe("Part of the link-set group — see tool description."),
|
|
1052
|
+
section_ids: z.array(z.number().int().positive()).optional().describe("Part of the link-set group — see tool description."),
|
|
1055
1053
|
}, async (args) => jsonResult(await planUpdateNote(client, args)));
|
|
1056
1054
|
strictTool("plan_delete_note", 'Soft-delete a plan note. `content_hash` must be the note\'s last-read `contentHash` — stale deletes nothing, returns `{error: "stale_plan_note", currentHash, currentTitle, currentBody, currentLinks}` (same shape as `plan_update_note`). Takes an explicit `plan_id` (see `plan_add_note`). Unknown plan, note, or already-deleted note → 404. Returns `{notes_count}`.', {
|
|
1057
1055
|
plan_id: z.number().int().positive().describe("The plan id, from `plan_list`."),
|
package/package.json
CHANGED
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Stdio MCP server wrapping danxbot's dashboard /api/issues/* normalized DB-backed HTTP routes for dispatched agents (DX-704 Phase 2).",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"type": "module",
|
|
7
|
-
"main": "dist/index.js",
|
|
8
|
-
"bin": {
|
|
9
|
-
"danx-dashboard-mcp": "dist/index.js"
|
|
10
|
-
},
|
|
11
|
-
"files": [
|
|
12
|
-
"dist",
|
|
13
|
-
"README.md"
|
|
14
|
-
],
|
|
15
|
-
"repository": {
|
|
16
|
-
"type": "git",
|
|
17
|
-
"url": "https://github.com/newms87/danxbot.git",
|
|
18
|
-
"directory": "packages/danx-dashboard-mcp"
|
|
19
|
-
},
|
|
20
|
-
"engines": {
|
|
21
|
-
"node": ">=20"
|
|
22
|
-
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
25
|
-
"verify:boot": "bash scripts/verify-boot.sh",
|
|
26
|
-
"start": "node dist/index.js",
|
|
27
|
-
"dev": "tsx src/index.ts",
|
|
28
|
-
"gen-tool-defs": "tsx scripts/gen-tool-defs.ts",
|
|
29
|
-
"test": "vitest run",
|
|
30
|
-
"test:watch": "vitest"
|
|
31
|
-
},
|
|
32
|
-
"dependencies": {
|
|
33
|
-
"@modelcontextprotocol/sdk": "1.29.0",
|
|
34
|
-
"zod": "^3.25.76"
|
|
35
|
-
},
|
|
36
|
-
"devDependencies": {
|
|
37
|
-
"@types/node": "^22.0.0",
|
|
38
|
-
"tsx": "^4.0.0",
|
|
39
|
-
"typescript": "^5.7.0",
|
|
40
|
-
"vitest": "^4.1.5"
|
|
41
|
-
}
|
|
42
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@thehammer/danx-dashboard-mcp",
|
|
3
|
+
"version": "0.1.143",
|
|
4
|
+
"description": "Stdio MCP server wrapping danxbot's dashboard /api/issues/* normalized DB-backed HTTP routes for dispatched agents (DX-704 Phase 2).",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"bin": {
|
|
9
|
+
"danx-dashboard-mcp": "dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"files": [
|
|
12
|
+
"dist",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "https://github.com/newms87/danxbot.git",
|
|
18
|
+
"directory": "packages/danx-dashboard-mcp"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=20"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.json && node -e \"require('fs').chmodSync('dist/index.js', 0o755)\"",
|
|
25
|
+
"verify:boot": "bash scripts/verify-boot.sh",
|
|
26
|
+
"start": "node dist/index.js",
|
|
27
|
+
"dev": "tsx src/index.ts",
|
|
28
|
+
"gen-tool-defs": "tsx scripts/gen-tool-defs.ts",
|
|
29
|
+
"test": "vitest run",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@modelcontextprotocol/sdk": "1.29.0",
|
|
34
|
+
"zod": "^3.25.76"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@types/node": "^22.0.0",
|
|
38
|
+
"tsx": "^4.0.0",
|
|
39
|
+
"typescript": "^5.7.0",
|
|
40
|
+
"vitest": "^4.1.5"
|
|
41
|
+
}
|
|
42
|
+
}
|