@thehammer/danx-dashboard-mcp 0.1.200 → 0.1.202
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/danxbot-api-examples.js +8 -3
- package/dist/index.js +15 -1
- package/dist/terms-halt.js +33 -0
- package/package.json +1 -1
|
@@ -16,6 +16,11 @@
|
|
|
16
16
|
* retro before the `complete` transition (the list went AC tick -> retro,
|
|
17
17
|
* with no transition between) and 14 of 24 writing the transition with no
|
|
18
18
|
* spec call: the transition is now a shown write, placed before the retro.
|
|
19
|
+
* DX-3923 (phase E) — four triage card reads asked `fields` for
|
|
20
|
+
* `status_derived`, a LIST filter key the card-list example showed, so the
|
|
21
|
+
* list example filters by `q`/`include_closed` instead. Two haiku AC ticks
|
|
22
|
+
* read `ac` (no checklist id) and sent `ac` / `0` as the checklist id: the
|
|
23
|
+
* card read shows only `checklists`, and both tick ids name it.
|
|
19
24
|
*
|
|
20
25
|
* Kept as data, not prose, so danxbot's own tests can check each example
|
|
21
26
|
* against the live server contract (route table, resource fields, body
|
|
@@ -26,12 +31,12 @@ export const DANXBOT_API_EXAMPLES = {
|
|
|
26
31
|
cardRead: {
|
|
27
32
|
method: "GET",
|
|
28
33
|
path: "/api/issues/<id>",
|
|
29
|
-
query: { fields: { description: true,
|
|
34
|
+
query: { fields: { description: true, comments: true, checklists: true, parent_title: true } },
|
|
30
35
|
},
|
|
31
36
|
cardList: {
|
|
32
37
|
method: "GET",
|
|
33
38
|
path: "/api/issues",
|
|
34
|
-
query: { filter: {
|
|
39
|
+
query: { filter: { q: "<text>", include_closed: true } },
|
|
35
40
|
},
|
|
36
41
|
cardCreate: {
|
|
37
42
|
method: "POST",
|
|
@@ -50,7 +55,7 @@ export const DANXBOT_API_EXAMPLES = {
|
|
|
50
55
|
},
|
|
51
56
|
acTick: {
|
|
52
57
|
method: "PATCH",
|
|
53
|
-
path: "/api/issues/<id>/checklists/<checklists[].id>/items/<items[].id>",
|
|
58
|
+
path: "/api/issues/<id>/checklists/<checklists[].id>/items/<checklists[].items[].id>",
|
|
54
59
|
body: { status: "passing" },
|
|
55
60
|
},
|
|
56
61
|
transition: {
|
package/dist/index.js
CHANGED
|
@@ -86,6 +86,7 @@ import { z } from "zod";
|
|
|
86
86
|
import { DashboardHttpClient } from "./http-client.js";
|
|
87
87
|
import { planConnect, danxbotApi, danxbotApiList, danxbotApiSpec, attachFile, DANXBOT_API_METHODS, } from "./handlers.js";
|
|
88
88
|
import { bodyValueSchema, queryValueSchema } from "./types.js";
|
|
89
|
+
import { termsAcceptanceCallHalt, termsHaltFor } from "./terms-halt.js";
|
|
89
90
|
import { danxbotApiExamplesText } from "./danxbot-api-examples.js";
|
|
90
91
|
import { registerLifecycleTools, applyLifecycleVisibility, readLifecycleUrlsFromEnv, } from "./lifecycle.js";
|
|
91
92
|
function readEnvOrDie(name) {
|
|
@@ -278,6 +279,9 @@ export const server = new McpServer({
|
|
|
278
279
|
name: "danx-dashboard-mcp",
|
|
279
280
|
version: "0.1.0",
|
|
280
281
|
}, { instructions: SERVER_INSTRUCTIONS });
|
|
282
|
+
function haltResult(text) {
|
|
283
|
+
return { content: [{ type: "text", text }], isError: true };
|
|
284
|
+
}
|
|
281
285
|
function jsonResult(value) {
|
|
282
286
|
return {
|
|
283
287
|
content: [{ type: "text", text: JSON.stringify(value, null, 2) }],
|
|
@@ -385,7 +389,17 @@ strictTool("danxbot_api", "Call any dashboard API route your credential may reac
|
|
|
385
389
|
},
|
|
386
390
|
// DX-3997 — the result rides back verbatim, plus the dashboard's own `reminders[]` entries
|
|
387
391
|
// for this plan-connected session (the server decides; `listener-reminder.ts` only relays).
|
|
388
|
-
async (args) =>
|
|
392
|
+
async (args) => {
|
|
393
|
+
// DX-4148 — an agent never accepts the Terms, and a terms refusal halts it (see terms-halt.ts).
|
|
394
|
+
const refused = termsAcceptanceCallHalt(args, config.baseUrl);
|
|
395
|
+
if (refused)
|
|
396
|
+
return haltResult(refused);
|
|
397
|
+
const result = await danxbotApi(client, args);
|
|
398
|
+
const halt = termsHaltFor(result);
|
|
399
|
+
if (halt)
|
|
400
|
+
return haltResult(halt);
|
|
401
|
+
return jsonResult(await listenerReminder.apply(result));
|
|
402
|
+
});
|
|
389
403
|
// ---------------- danxbot_api_list (DX-3465 A5, PLN-11 R-25) ----------------
|
|
390
404
|
strictTool("danxbot_api_list", "Lists the dashboard API routes your credential may call right now — `[{method, path, summary, permission}]`, " +
|
|
391
405
|
"one line each, in the route TEMPLATE form `danxbot_api_spec` also expects (e.g. " +
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
const TERMS_ACCEPTANCE_PATH = "/api/account/terms-acceptance";
|
|
2
|
+
// Mirrors `termsAcceptUrl("/")` in src/lib/terms-accept-path.ts (a published package cannot import it);
|
|
3
|
+
// terms-gate.integration.test.ts pins the server's link to the same literal. Only meaningful for an
|
|
4
|
+
// operator session: a dispatch's DANXBOT_DASHBOARD_URL is internal, and agent credentials are never terms-gated.
|
|
5
|
+
const ACCEPT_SCREEN_PATH = "/terms/accept?return=%2F";
|
|
6
|
+
/** Keep in step with `termsRefusalInstruction` in src/dashboard/route-policy.ts. */
|
|
7
|
+
export function haltText(acceptUrl) {
|
|
8
|
+
return ("STOP ALL WORK NOW. The dashboard refuses your access until the person you act for accepts the " +
|
|
9
|
+
"current Terms of Use.\n\n" +
|
|
10
|
+
`Give the person this link to accept them: ${acceptUrl}\n\n` +
|
|
11
|
+
"Do not retry, do not look for another route, and never accept the Terms yourself. " +
|
|
12
|
+
"A dispatched agent ends with danxbot_complete({status: \"failed\", summary}) naming this link, " +
|
|
13
|
+
"and never blocks the card.");
|
|
14
|
+
}
|
|
15
|
+
/** The halt text for a `403 terms_not_accepted` result, or null for any other result. */
|
|
16
|
+
export function termsHaltFor(result) {
|
|
17
|
+
if (result.ok || result.status !== 403)
|
|
18
|
+
return null;
|
|
19
|
+
const body = result.body;
|
|
20
|
+
if (body?.error !== "terms_not_accepted")
|
|
21
|
+
return null;
|
|
22
|
+
if (typeof body.acceptUrl !== "string") {
|
|
23
|
+
throw new Error("[danx-dashboard-mcp] a terms_not_accepted refusal carried no acceptUrl");
|
|
24
|
+
}
|
|
25
|
+
return haltText(body.acceptUrl);
|
|
26
|
+
}
|
|
27
|
+
/** The refusal for a call that would accept the Terms (never an agent's act), or null for any other call. */
|
|
28
|
+
export function termsAcceptanceCallHalt(args, dashboardUrl) {
|
|
29
|
+
const path = args.path.split("?")[0].replace(/\/+$/, "");
|
|
30
|
+
if (args.method !== "POST" || path !== TERMS_ACCEPTANCE_PATH)
|
|
31
|
+
return null;
|
|
32
|
+
return haltText(`${dashboardUrl.replace(/\/+$/, "")}${ACCEPT_SCREEN_PATH}`);
|
|
33
|
+
}
|
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.202",
|
|
4
4
|
"description": "Stdio MCP server fronting danxbot's dashboard API for agents (operator sessions and dispatched workers alike): the generic danxbot_api/danxbot_api_list/danxbot_api_spec tools reach any route the caller's credential may call, plus dedicated agent-specific tools (plan_connect, danxbot_complete, and other per-dispatch lifecycle callbacks) (DX-3465, PLN-11 R-25).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|