@thehammer/danx-dashboard-mcp 0.1.123 → 0.1.125
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 +55 -0
- package/package.json +1 -1
package/dist/handlers.js
CHANGED
|
@@ -413,6 +413,33 @@ function argCheckers(tool, mode) {
|
|
|
413
413
|
},
|
|
414
414
|
};
|
|
415
415
|
}
|
|
416
|
+
/**
|
|
417
|
+
* Every action-dispatched tool's args carry these three regardless of
|
|
418
|
+
* `action` — never action-specific, so every per-action allow-list passed to
|
|
419
|
+
* `refuseInapplicableFields` is unioned with this rather than repeating it.
|
|
420
|
+
*/
|
|
421
|
+
const STRUCTURAL_ACTION_FIELDS = ["id", "action", "board"];
|
|
422
|
+
/**
|
|
423
|
+
* DX-3310 (code-review nit) — a field the caller sent that means nothing for
|
|
424
|
+
* the chosen `action` used to be silently ignored: `issue_solution`'s `add`
|
|
425
|
+
* built its `content` object from `title`/`body`/`pro`/`con`/`recommended`
|
|
426
|
+
* regardless of action, then simply never read it on `remove`/`add_step`/etc,
|
|
427
|
+
* and `issue_problem`'s `remove` never looked at `statement`/`context`/
|
|
428
|
+
* `solutions` at all. Either way the caller got a 200 with the field quietly
|
|
429
|
+
* discarded — no different from a typo. Refused loud instead, mirroring
|
|
430
|
+
* `argCheckers`' own wording: every ACTUALLY-SUPPLIED (`!== undefined`) key
|
|
431
|
+
* outside the action's own allow-list (plus `STRUCTURAL_ACTION_FIELDS`,
|
|
432
|
+
* always allowed) is named in one error, so a caller fixes every offending
|
|
433
|
+
* field in one round trip rather than one at a time.
|
|
434
|
+
*/
|
|
435
|
+
function refuseInapplicableFields(tool, mode, args, allowed) {
|
|
436
|
+
const offending = Object.keys(args)
|
|
437
|
+
.filter((key) => args[key] !== undefined && !STRUCTURAL_ACTION_FIELDS.includes(key) && !allowed.has(key))
|
|
438
|
+
.sort();
|
|
439
|
+
if (offending.length > 0) {
|
|
440
|
+
throw new Error(`${tool} ${mode} refuses fields that do not apply to this action: ${offending.join(", ")}`);
|
|
441
|
+
}
|
|
442
|
+
}
|
|
416
443
|
export async function issueComment(client, args) {
|
|
417
444
|
const idEnc = encodeURIComponent(args.id);
|
|
418
445
|
const board = args.board;
|
|
@@ -614,10 +641,25 @@ export async function issueChecklist(client, args) {
|
|
|
614
641
|
*
|
|
615
642
|
* No answer action, for the reason `issueSolution` gives.
|
|
616
643
|
*/
|
|
644
|
+
/**
|
|
645
|
+
* DX-3310 (nit) — per-action allow-list for `refuseInapplicableFields`.
|
|
646
|
+
* `id`/`action`/`board` are structural and never listed (every action takes
|
|
647
|
+
* them). `type` is listed under `edit` even though it can never actually be
|
|
648
|
+
* SENT there (see the dedicated throw below) — that keeps the generic
|
|
649
|
+
* refusal from firing first with a less specific message than the one that
|
|
650
|
+
* already names the real reason.
|
|
651
|
+
*/
|
|
652
|
+
const PROBLEM_ACTION_FIELDS = {
|
|
653
|
+
list: [],
|
|
654
|
+
add: ["statement", "context", "type", "summary", "solutions"],
|
|
655
|
+
edit: ["problem_id", "base_hash", "statement", "context", "summary", "type"],
|
|
656
|
+
remove: ["problem_id", "base_hash"],
|
|
657
|
+
};
|
|
617
658
|
export async function issueProblem(client, args) {
|
|
618
659
|
const idEnc = encodeURIComponent(args.id);
|
|
619
660
|
const board = args.board;
|
|
620
661
|
const need = argCheckers("issue_problem", `action=${args.action}`);
|
|
662
|
+
refuseInapplicableFields("issue_problem", `action=${args.action}`, args, new Set(PROBLEM_ACTION_FIELDS[args.action]));
|
|
621
663
|
switch (args.action) {
|
|
622
664
|
case "list":
|
|
623
665
|
return client.request({ method: "GET", path: `/${idEnc}/problems`, board });
|
|
@@ -693,11 +735,24 @@ export async function issueProblem(client, args) {
|
|
|
693
735
|
* question could release the very stop it set to wait for a human. The operator
|
|
694
736
|
* answers in the dashboard.
|
|
695
737
|
*/
|
|
738
|
+
const SOLUTION_ACTION_FIELDS = {
|
|
739
|
+
add: ["title", "body", "pro", "con", "recommended", "steps"],
|
|
740
|
+
edit: ["solution_id", "base_hash", "title", "body", "pro", "con", "recommended", "steps", "steps_base_hash"],
|
|
741
|
+
remove: ["solution_id", "base_hash"],
|
|
742
|
+
add_step: ["solution_id", "title", "description", "parent_step_id", "position"],
|
|
743
|
+
edit_step: ["solution_id", "step_id", "base_hash", "title", "description"],
|
|
744
|
+
remove_step: ["solution_id", "step_id", "base_hash"],
|
|
745
|
+
};
|
|
696
746
|
export async function issueSolution(client, args) {
|
|
697
747
|
const need = argCheckers("issue_solution", `action=${args.action}`);
|
|
698
748
|
// DX-2735: checked at runtime too, not only by the schema — a caller that skips
|
|
699
749
|
// the MCP boundary must never build `/problems/undefined/solutions`.
|
|
700
750
|
const problemId = need.id(args.problem_id, "problem_id");
|
|
751
|
+
// DX-3310 (nit) — `problem_id` is unioned in here rather than listed in
|
|
752
|
+
// every `SOLUTION_ACTION_FIELDS` entry: unlike `issue_problem` (where it
|
|
753
|
+
// only applies to edit/remove), `issue_solution` requires it for every
|
|
754
|
+
// action (checked above), so it is structural FOR THIS TOOL specifically.
|
|
755
|
+
refuseInapplicableFields("issue_solution", `action=${args.action}`, args, new Set(["problem_id", ...SOLUTION_ACTION_FIELDS[args.action]]));
|
|
701
756
|
const base = `/${encodeURIComponent(args.id)}/problems/${problemId}/solutions`;
|
|
702
757
|
const board = args.board;
|
|
703
758
|
const content = {};
|
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.125",
|
|
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",
|