@thehammer/danx-dashboard-mcp 0.1.134 → 0.1.138
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/README.md +1 -1
- package/dist/background-work.js +156 -0
- package/dist/index.js +21 -1
- package/dist/mantra.js +128 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ All exposed as `mcp__danx-dashboard__<name>` once wired through the workspace `m
|
|
|
26
26
|
| `issue_create` | `POST /api/issues` | Epic REQUIRES non-empty `phase_children[]` (atomic insert). `title` = short domain-naming label; `summary` = 1–3 plain-language sentences, always shown; `description` = the collapsed "Context" body. Root and every phase child take their own `summary` |
|
|
27
27
|
| `issue_edit` | `PATCH /api/issues/:id/edit` | Prose + structured keys (`title`, `summary` (null clears), `description`, `ac`, `checklists`, `effort_level`, `parent_id`, `priority`, `list_id`); semantic keys refused with 400 + pointer to dedicated handler. `priority` (DX-1532) takes a tier word (`low`/`high`/…) or a number in `[0,6)` — the ONLY way to set the numeric column the Trello label + dashboard badge read; never set priority via description prose |
|
|
28
28
|
| `issue_transition` | `POST /api/issues/:id/transition` | Actions: ready, pickup, rollback_pickup, complete, cancel, block, unblock, archive, reopen. `block` is a dispatch hold only — it never marks the card as needing a human; use `issue_problem` add for that |
|
|
29
|
-
| `issue_problem` | `GET/POST/PATCH/DELETE /api/issues/:id/problems[/:pid]` | Actions list / add / edit / remove. A problem is one statement the operator must resolve (a question or a plan flaw) with its own solutions and answers; `add` takes `statement` + optional `solutions[]` in one transaction and IS what puts the card in front of a human (`open_problem_count > 0`) — a successful add also returns `
|
|
29
|
+
| `issue_problem` | `GET/POST/PATCH/DELETE /api/issues/:id/problems[/:pid]` | Actions list / add / edit / remove. A problem is one statement the operator must resolve (a question or a plan flaw) with its own solutions and answers; `add` takes `statement` + optional `solutions[]` in one transaction and IS what puts the card in front of a human (`open_problem_count > 0`) — a successful add also returns a `reminders: [{key, text}]` array (DX-3365 — the reminder registry's middleware, DB-driven and operator-overridable from the dashboard). Edit + remove are hash-guarded (`base_hash`, 409 `stale_problem`); removing the card's last open problem is always allowed — it just means the card no longer needs a human |
|
|
30
30
|
| `issue_solution` | `POST/PATCH/DELETE /api/issues/:id/problems/:pid/solutions[/:sid]` | Actions add / edit / remove, `problem_id` required (list via `issue_problem list`). Edit + remove are hash-guarded (`base_hash`, 409 `stale_solution` with the current row); at most one live `recommended` per problem; a chosen option cannot be edited. No answer action on any tool — the operator answers in the dashboard |
|
|
31
31
|
| `issue_triage` | `POST /api/issues/:id/triage` | Send `{confidence, reason}` — an integer 0-5 score; the server computes the verdict (approve/cancel/keep/defer) against the board's configured thresholds (DX-2086). `keep`/`defer` now block the card. None of these are a cross-card ordering gate; use `issue_dependency` to sequence cards |
|
|
32
32
|
| `issue_comment` | `POST/PATCH/DELETE /api/issues/:id/comments[/:cid]` | Author server-stamped, soft-delete preserved |
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `danx-dashboard-mcp background-work <count|clear>` — the ONE-SHOT client of
|
|
3
|
+
* `PUT /api/plan-sessions/me/background-work` (DX-3367).
|
|
4
|
+
*
|
|
5
|
+
* WHO RUNS IT. The danxbot Claude Code plugin's `Stop` / `SubagentStop` /
|
|
6
|
+
* `SessionStart` / `StopFailure` hooks (`danxbot/hooks/hooks.json` in
|
|
7
|
+
* claude-plugins), via the SAME `npx -y @thehammer/danx-dashboard-mcp` pin
|
|
8
|
+
* the plugin already uses for `bridge` and `plan-state` — no separate
|
|
9
|
+
* install, no deep import of this package's `dist/` internals. The plugin
|
|
10
|
+
* decides WHAT to report (parsing `Stop`/`SubagentStop`'s own
|
|
11
|
+
* `background_tasks` snapshot, counting only `shell` / `subagent` /
|
|
12
|
+
* `workflow` entries, sending `clear` on `SessionStart`/`StopFailure`); this
|
|
13
|
+
* module only delivers the report.
|
|
14
|
+
*
|
|
15
|
+
* THE CONTRACT, mirroring `plan-state.ts`'s (binding the same way, so a
|
|
16
|
+
* future plugin build against this module can trust it without re-reading
|
|
17
|
+
* this file):
|
|
18
|
+
* - print EXACTLY ONE JSON line on stdout, and ALWAYS exit 0 — success or
|
|
19
|
+
* failure alike, because a hook must stay silent rather than spam the
|
|
20
|
+
* session's own turn with a stack trace;
|
|
21
|
+
* - a session with no connection record (never `plan_connect`-ed, e.g. an
|
|
22
|
+
* operator session that never joined a plan) is a silent, expected
|
|
23
|
+
* no-op — `{"ok":false,"reason":"no_connection_record"}` — the plugin
|
|
24
|
+
* hook simply does not forward anything further in this case, exactly
|
|
25
|
+
* what "no-op when the session isn't plan-connected" means in practice;
|
|
26
|
+
* - on success, `{"ok":true,"count":<number|null>}` echoes what was
|
|
27
|
+
* stored;
|
|
28
|
+
* - human-readable diagnostics go to stderr only, never stdout.
|
|
29
|
+
*
|
|
30
|
+
* THE CREDENTIAL — the SAME resolver `bridge` and `plan-state` use, via
|
|
31
|
+
* `resolveBridgeOptions` (`bridge.ts`): the dashboard URL, credential
|
|
32
|
+
* source, and credential come from the connection record this session's OWN
|
|
33
|
+
* danx-dashboard MCP server wrote on its last successful `plan_connect`
|
|
34
|
+
* (`session-connection.ts`), never from this process's own ambient env —
|
|
35
|
+
* the identical DX-2862 reasoning `plan-state.ts` already gives for itself.
|
|
36
|
+
*
|
|
37
|
+
* A HARD TIMEOUT — `BACKGROUND_WORK_REQUEST_TIMEOUT_MS`. A `Stop` hook
|
|
38
|
+
* blocks the session's turn boundary, so this call must never hang: a slow
|
|
39
|
+
* or wedged dashboard is `{ok:false, reason:"timeout"}`, not a stuck hook.
|
|
40
|
+
*/
|
|
41
|
+
import { SESSION_ID_HEADER, resolveBridgeOptions } from "./bridge.js";
|
|
42
|
+
export const BACKGROUND_WORK_SUBCOMMAND = "background-work";
|
|
43
|
+
export const BACKGROUND_WORK_PATH = "/api/plan-sessions/me/background-work";
|
|
44
|
+
/** How long this one-shot call may take before it counts as a timeout. */
|
|
45
|
+
export const BACKGROUND_WORK_REQUEST_TIMEOUT_MS = 5_000;
|
|
46
|
+
const USAGE = `usage: CLAUDE_CODE_SESSION_ID=<session-id> danx-dashboard-mcp ${BACKGROUND_WORK_SUBCOMMAND} <non-negative-integer|clear>`;
|
|
47
|
+
/**
|
|
48
|
+
* Parse the ONE positional argument: `clear`, or a non-negative integer
|
|
49
|
+
* string. Anything else is a usage error — never guessed or floored.
|
|
50
|
+
*/
|
|
51
|
+
export function parseCountArg(raw) {
|
|
52
|
+
if (raw === "clear")
|
|
53
|
+
return null;
|
|
54
|
+
if (!/^\d+$/.test(raw))
|
|
55
|
+
return { error: `count must be "clear" or a non-negative integer, got "${raw}"` };
|
|
56
|
+
return Number.parseInt(raw, 10);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* One authenticated PUT, with a hard timeout, classified into the output
|
|
60
|
+
* shape this subcommand always prints — never a throw, never a non-2xx
|
|
61
|
+
* bubbling past this function.
|
|
62
|
+
*/
|
|
63
|
+
export async function putBackgroundWork(options, count, deps) {
|
|
64
|
+
const controller = new AbortController();
|
|
65
|
+
const timer = setTimeout(() => controller.abort(), deps.requestTimeoutMs);
|
|
66
|
+
let status;
|
|
67
|
+
let text;
|
|
68
|
+
try {
|
|
69
|
+
try {
|
|
70
|
+
const response = await deps.fetch(`${options.dashboardUrl}${BACKGROUND_WORK_PATH}`, {
|
|
71
|
+
method: "PUT",
|
|
72
|
+
headers: {
|
|
73
|
+
Authorization: `Bearer ${options.token}`,
|
|
74
|
+
Accept: "application/json",
|
|
75
|
+
"Content-Type": "application/json",
|
|
76
|
+
[SESSION_ID_HEADER]: options.sessionId,
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({ count }),
|
|
79
|
+
signal: controller.signal,
|
|
80
|
+
});
|
|
81
|
+
status = response.status;
|
|
82
|
+
text = await response.text();
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
return { ok: false, reason: controller.signal.aborted ? "timeout" : "request_failed" };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
finally {
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
}
|
|
91
|
+
let body = null;
|
|
92
|
+
try {
|
|
93
|
+
body = text === "" ? null : JSON.parse(text);
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return { ok: false, reason: "bad_response" };
|
|
97
|
+
}
|
|
98
|
+
if (status === 401 || status === 403) {
|
|
99
|
+
return { ok: false, reason: "unauthorized" };
|
|
100
|
+
}
|
|
101
|
+
if (status < 200 || status >= 300) {
|
|
102
|
+
return { ok: false, reason: "http_error" };
|
|
103
|
+
}
|
|
104
|
+
if (typeof body !== "object" ||
|
|
105
|
+
body === null ||
|
|
106
|
+
!("count" in body) ||
|
|
107
|
+
(body.count !== null && typeof body.count !== "number")) {
|
|
108
|
+
return { ok: false, reason: "bad_response" };
|
|
109
|
+
}
|
|
110
|
+
return { ok: true, count: body.count };
|
|
111
|
+
}
|
|
112
|
+
/** The bin's `background-work` subcommand, wired to the real process. Always resolves to exit code 0. */
|
|
113
|
+
export async function runBackgroundWorkCommand(argv, env = process.env,
|
|
114
|
+
/** Test seam: the home the session's connection record is read from. */
|
|
115
|
+
resolveFrom = {}) {
|
|
116
|
+
const write = (output) => {
|
|
117
|
+
process.stdout.write(`${JSON.stringify(output)}\n`);
|
|
118
|
+
};
|
|
119
|
+
if (argv.length !== 1) {
|
|
120
|
+
write({ ok: false, reason: "usage" });
|
|
121
|
+
process.stderr.write(`${USAGE}\n`);
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
|
124
|
+
const parsed = parseCountArg(argv[0]);
|
|
125
|
+
if (typeof parsed === "object" && parsed !== null && "error" in parsed) {
|
|
126
|
+
write({ ok: false, reason: "usage" });
|
|
127
|
+
process.stderr.write(`${parsed.error}\n${USAGE}\n`);
|
|
128
|
+
return 0;
|
|
129
|
+
}
|
|
130
|
+
const count = parsed;
|
|
131
|
+
const sessionId = env.CLAUDE_CODE_SESSION_ID;
|
|
132
|
+
if (!sessionId) {
|
|
133
|
+
write({ ok: false, reason: "no_session_id" });
|
|
134
|
+
process.stderr.write(`${USAGE}\n`);
|
|
135
|
+
return 0;
|
|
136
|
+
}
|
|
137
|
+
let options;
|
|
138
|
+
try {
|
|
139
|
+
options = resolveBridgeOptions({ sessionId, resumeIds: [] }, env, resolveFrom);
|
|
140
|
+
}
|
|
141
|
+
catch (err) {
|
|
142
|
+
const start = err;
|
|
143
|
+
// A session that never `plan_connect`-ed has no connection record — this
|
|
144
|
+
// is the ORDINARY "not plan-connected" case, not an error the plugin
|
|
145
|
+
// should surface: it simply forwards nothing further this turn.
|
|
146
|
+
write({ ok: false, reason: start.reason });
|
|
147
|
+
process.stderr.write(`${start.reason}: ${start.message}. Fix: ${start.fix}.\n`);
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
const output = await putBackgroundWork(options, count, {
|
|
151
|
+
fetch: (input, init) => fetch(input, init),
|
|
152
|
+
requestTimeoutMs: BACKGROUND_WORK_REQUEST_TIMEOUT_MS,
|
|
153
|
+
});
|
|
154
|
+
write(output);
|
|
155
|
+
return 0;
|
|
156
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -96,6 +96,8 @@
|
|
|
96
96
|
import { isEntrypointModule } from "./entrypoint.js";
|
|
97
97
|
import { BRIDGE_SUBCOMMAND, runBridgeCommand } from "./bridge.js";
|
|
98
98
|
import { PLAN_STATE_SUBCOMMAND, runPlanStateCommand } from "./plan-state.js";
|
|
99
|
+
import { BACKGROUND_WORK_SUBCOMMAND, runBackgroundWorkCommand } from "./background-work.js";
|
|
100
|
+
import { MANTRA_SUBCOMMAND, runMantraCommand } from "./mantra.js";
|
|
99
101
|
import { resolveDeclaredCredential } from "./credential.js";
|
|
100
102
|
import { recordSessionConnectionAfterConnect } from "./session-connection.js";
|
|
101
103
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -1180,9 +1182,27 @@ if (isEntrypointModule(import.meta.url, process.argv[1])) {
|
|
|
1180
1182
|
process.exit(0);
|
|
1181
1183
|
});
|
|
1182
1184
|
}
|
|
1185
|
+
else if (subcommand === BACKGROUND_WORK_SUBCOMMAND) {
|
|
1186
|
+
runBackgroundWorkCommand(rest).then((code) => process.exit(code), (err) => {
|
|
1187
|
+
console.error(`[danx-dashboard-mcp] background-work fatal: ${err.message}`);
|
|
1188
|
+
process.stdout.write(`${JSON.stringify({ ok: false, reason: "fatal" })}\n`);
|
|
1189
|
+
process.exit(0);
|
|
1190
|
+
});
|
|
1191
|
+
}
|
|
1192
|
+
else if (subcommand === MANTRA_SUBCOMMAND) {
|
|
1193
|
+
// DX-3366 — unlike plan-state/background-work, a rejection here is a
|
|
1194
|
+
// genuine fatal (never expected: runMantraCommand itself already
|
|
1195
|
+
// classifies every network/shape failure into its own {ok:false} exit
|
|
1196
|
+
// 1), so it exits 1 too rather than the "always 0, stay silent" contract
|
|
1197
|
+
// those two hook-only subcommands use.
|
|
1198
|
+
runMantraCommand(rest).then((code) => process.exit(code), (err) => {
|
|
1199
|
+
console.error(`[danx-dashboard-mcp] mantra fatal: ${err.message}`);
|
|
1200
|
+
process.exit(1);
|
|
1201
|
+
});
|
|
1202
|
+
}
|
|
1183
1203
|
else if (subcommand !== undefined) {
|
|
1184
1204
|
console.error(`[danx-dashboard-mcp] unknown subcommand "${subcommand}" (the only ones are ` +
|
|
1185
|
-
`"${BRIDGE_SUBCOMMAND}" and "${
|
|
1205
|
+
`"${BRIDGE_SUBCOMMAND}", "${PLAN_STATE_SUBCOMMAND}", "${BACKGROUND_WORK_SUBCOMMAND}", and "${MANTRA_SUBCOMMAND}")`);
|
|
1186
1206
|
process.exit(2);
|
|
1187
1207
|
}
|
|
1188
1208
|
else {
|
package/dist/mantra.js
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `danx-dashboard-mcp mantra` — the ONE-SHOT client of
|
|
3
|
+
* `GET /api/reminders/mantra.session_start` (DX-3366).
|
|
4
|
+
*
|
|
5
|
+
* WHO RUNS IT. The claude-plugins `danxbot` plugin's `mantra.sh` SessionStart
|
|
6
|
+
* hook, via the SAME `npx -y @thehammer/danx-dashboard-mcp@<pin>` path the
|
|
7
|
+
* plugin's other dashboard calls already use (`plan-state`, `bridge`) — never
|
|
8
|
+
* a separate install, never a deep import of this package's `dist/`
|
|
9
|
+
* internals.
|
|
10
|
+
*
|
|
11
|
+
* THE CREDENTIAL — the SAME resolver `bridge` / `plan-state` use
|
|
12
|
+
* (`resolveBridgeOptions`, `bridge.ts`): the dashboard URL, credential source
|
|
13
|
+
* and credential all come from the connection record this session's OWN
|
|
14
|
+
* danx-dashboard MCP server wrote on its last successful `plan_connect`
|
|
15
|
+
* (`session-connection.ts`), never from this process's own ambient env. The
|
|
16
|
+
* mantra is only ever fetched once a session is already connected (`mantra.sh`
|
|
17
|
+
* checks that first via `plan-connection.mjs` and stays with the short nudge
|
|
18
|
+
* otherwise), so a connection record always exists by the time this runs.
|
|
19
|
+
*
|
|
20
|
+
* THE CONTRACT — unlike `plan-state` (which must stay silent for a hook, so it
|
|
21
|
+
* always exits 0), this subcommand's caller (`mantra.sh`) needs to tell
|
|
22
|
+
* success from failure so it can fall back to the git-committed `mantra.md` +
|
|
23
|
+
* a one-line notice (DX-3366 AC: "Dashboard unreachable → the hook prints the
|
|
24
|
+
* committed file and says so in one line; never silently"). So:
|
|
25
|
+
*
|
|
26
|
+
* - success: the reminder's EFFECTIVE text (`override_text ?? default_text`)
|
|
27
|
+
* on stdout, nothing else, exit 0.
|
|
28
|
+
* - failure (missing env, no connection record, network error, timeout,
|
|
29
|
+
* non-2xx, 404, bad shape): NOTHING on stdout, one short reason on
|
|
30
|
+
* stderr, exit 1. `mantra.sh` is the one that decides what to print for
|
|
31
|
+
* a human — this module only reports whether the fetch worked.
|
|
32
|
+
*
|
|
33
|
+
* A HARD TIMEOUT (mirrors `plan-state.ts`) — a SessionStart hook must never
|
|
34
|
+
* hang on a wedged dashboard.
|
|
35
|
+
*/
|
|
36
|
+
import { SESSION_ID_HEADER, resolveBridgeOptions } from "./bridge.js";
|
|
37
|
+
export const MANTRA_SUBCOMMAND = "mantra";
|
|
38
|
+
export const MANTRA_REMINDER_KEY = "mantra.session_start";
|
|
39
|
+
export const MANTRA_REQUEST_TIMEOUT_MS = 5_000;
|
|
40
|
+
const USAGE = `usage: CLAUDE_CODE_SESSION_ID=<session-id> danx-dashboard-mcp ${MANTRA_SUBCOMMAND}`;
|
|
41
|
+
function reminderPath(key) {
|
|
42
|
+
return `/api/reminders/${encodeURIComponent(key)}`;
|
|
43
|
+
}
|
|
44
|
+
/** One authenticated GET, with a hard timeout — never throws, never bubbles a non-2xx. */
|
|
45
|
+
export async function fetchMantraText(options, deps) {
|
|
46
|
+
const controller = new AbortController();
|
|
47
|
+
const timer = setTimeout(() => controller.abort(), deps.requestTimeoutMs);
|
|
48
|
+
let status;
|
|
49
|
+
let text;
|
|
50
|
+
try {
|
|
51
|
+
try {
|
|
52
|
+
const response = await deps.fetch(`${options.dashboardUrl}${reminderPath(MANTRA_REMINDER_KEY)}`, {
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers: {
|
|
55
|
+
Authorization: `Bearer ${options.token}`,
|
|
56
|
+
Accept: "application/json",
|
|
57
|
+
[SESSION_ID_HEADER]: options.sessionId,
|
|
58
|
+
},
|
|
59
|
+
signal: controller.signal,
|
|
60
|
+
});
|
|
61
|
+
status = response.status;
|
|
62
|
+
text = await response.text();
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
return { ok: false, reason: controller.signal.aborted ? "timeout" : "request_failed" };
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
finally {
|
|
69
|
+
clearTimeout(timer);
|
|
70
|
+
}
|
|
71
|
+
if (status === 401 || status === 403)
|
|
72
|
+
return { ok: false, reason: "unauthorized" };
|
|
73
|
+
if (status === 404)
|
|
74
|
+
return { ok: false, reason: "not_found" };
|
|
75
|
+
if (status < 200 || status >= 300)
|
|
76
|
+
return { ok: false, reason: "http_error" };
|
|
77
|
+
let body;
|
|
78
|
+
try {
|
|
79
|
+
body = text === "" ? null : JSON.parse(text);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return { ok: false, reason: "bad_response" };
|
|
83
|
+
}
|
|
84
|
+
const effectiveText = typeof body === "object" && body !== null && typeof body.effective_text === "string"
|
|
85
|
+
? body.effective_text
|
|
86
|
+
: null;
|
|
87
|
+
if (effectiveText === null)
|
|
88
|
+
return { ok: false, reason: "bad_response" };
|
|
89
|
+
return { ok: true, text: effectiveText };
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* The bin's `mantra` subcommand, wired to the real process. Success prints
|
|
93
|
+
* the effective text on stdout and returns 0; every failure prints nothing on
|
|
94
|
+
* stdout, one reason on stderr, and returns 1 — `mantra.sh` reads the exit
|
|
95
|
+
* code, never parses stdout to guess.
|
|
96
|
+
*/
|
|
97
|
+
export async function runMantraCommand(argv, env = process.env,
|
|
98
|
+
/** Test seam: the home the session's connection record is read from. */
|
|
99
|
+
resolveFrom = {}) {
|
|
100
|
+
if (argv.length !== 0) {
|
|
101
|
+
process.stderr.write(`unrecognized arguments\n${USAGE}\n`);
|
|
102
|
+
return 1;
|
|
103
|
+
}
|
|
104
|
+
const sessionId = env.CLAUDE_CODE_SESSION_ID;
|
|
105
|
+
if (!sessionId) {
|
|
106
|
+
process.stderr.write(`no_session_id: CLAUDE_CODE_SESSION_ID is not set\n${USAGE}\n`);
|
|
107
|
+
return 1;
|
|
108
|
+
}
|
|
109
|
+
let options;
|
|
110
|
+
try {
|
|
111
|
+
options = resolveBridgeOptions({ sessionId, resumeIds: [] }, env, resolveFrom);
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
const start = err;
|
|
115
|
+
process.stderr.write(`${start.reason}: ${start.message}. Fix: ${start.fix}.\n`);
|
|
116
|
+
return 1;
|
|
117
|
+
}
|
|
118
|
+
const output = await fetchMantraText(options, {
|
|
119
|
+
fetch: (input, init) => fetch(input, init),
|
|
120
|
+
requestTimeoutMs: MANTRA_REQUEST_TIMEOUT_MS,
|
|
121
|
+
});
|
|
122
|
+
if (!output.ok) {
|
|
123
|
+
process.stderr.write(`${output.reason}: could not fetch the effective mantra text from the dashboard\n`);
|
|
124
|
+
return 1;
|
|
125
|
+
}
|
|
126
|
+
process.stdout.write(output.text);
|
|
127
|
+
return 0;
|
|
128
|
+
}
|
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.138",
|
|
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",
|