@thehammer/danx-dashboard-mcp 0.1.302 → 0.1.304
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/http-client.js +10 -1
- package/dist/index.js +11 -6
- package/dist/permission-request.js +1 -1
- package/dist/session-options.js +2 -0
- package/dist/session-stream.js +3 -1
- package/package.json +1 -1
package/dist/http-client.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { boardStampOf } from "./route-board-scope.js";
|
|
2
2
|
import { assertVersionFloorSatisfied, MCP_MIN_VERSION_HEADER } from "./version-floor.js";
|
|
3
|
+
import { PLUGIN_BACKGROUND_HEADER } from "./session-options.js";
|
|
3
4
|
export class DashboardHttpClient {
|
|
4
5
|
config;
|
|
5
6
|
fetchImpl;
|
|
@@ -17,6 +18,14 @@ export class DashboardHttpClient {
|
|
|
17
18
|
setToken(token) {
|
|
18
19
|
this.config.token = token;
|
|
19
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* DX-5076 - the ONE entry for a request this process makes on its own (the listener's health read, the key-permission read, the permission claim
|
|
23
|
+
* poll): it is never the agent acting, so it carries the background mark and ends no agent pause. A call the agent makes (`danxbot_api`, a
|
|
24
|
+
* permission-request create) goes through `request` instead. No call site sets `background` by hand.
|
|
25
|
+
*/
|
|
26
|
+
requestOwn(args) {
|
|
27
|
+
return this.request({ ...args, background: true });
|
|
28
|
+
}
|
|
20
29
|
async request(args) {
|
|
21
30
|
if (this.config.token === "") {
|
|
22
31
|
// DX-4390 — a caller reaches here only past the sign-in halt; a bearer-less call is a bug.
|
|
@@ -51,7 +60,7 @@ export class DashboardHttpClient {
|
|
|
51
60
|
headers["x-danx-session-id"] = this.config.session.id;
|
|
52
61
|
}
|
|
53
62
|
if (args.background === true) {
|
|
54
|
-
headers[
|
|
63
|
+
headers[PLUGIN_BACKGROUND_HEADER] = "1";
|
|
55
64
|
}
|
|
56
65
|
let bodyString;
|
|
57
66
|
if (args.body !== undefined) {
|
package/dist/index.js
CHANGED
|
@@ -217,11 +217,12 @@ function boot() {
|
|
|
217
217
|
permissionRequests = new PermissionRequests({
|
|
218
218
|
access,
|
|
219
219
|
request: (args) => dashboardClient.request(args),
|
|
220
|
+
requestOwn: (args) => dashboardClient.requestOwn(args),
|
|
220
221
|
log: (line) => console.error(line),
|
|
221
222
|
});
|
|
222
223
|
listenerReminder = createListenerReminder({
|
|
223
224
|
sessionId: config.session?.id,
|
|
224
|
-
fetchHealth: () => dashboardClient.
|
|
225
|
+
fetchHealth: () => dashboardClient.requestOwn({ method: "GET", basePath: "", path: "/api/plans", query: { limit: 1 } }),
|
|
225
226
|
isPlanConnected: isPlanConnectedOnThisMachine,
|
|
226
227
|
now: Date.now,
|
|
227
228
|
logLine: (line) => console.error(line),
|
|
@@ -270,7 +271,8 @@ function boot() {
|
|
|
270
271
|
async function readKeyPermissions() {
|
|
271
272
|
if (!access.isSignedIn())
|
|
272
273
|
return { outcome: "no-key" };
|
|
273
|
-
|
|
274
|
+
// DX-5076: this process reading its own key's permissions is not the agent acting.
|
|
275
|
+
const guarded = await access.guard(() => client.requestOwn({ method: "GET", basePath: "", path: "/api/auth/me" }));
|
|
274
276
|
if ("halt" in guarded)
|
|
275
277
|
return { outcome: "no-key" };
|
|
276
278
|
const { result } = guarded;
|
|
@@ -396,7 +398,7 @@ function readSessionConfig() {
|
|
|
396
398
|
*/
|
|
397
399
|
const SERVER_INSTRUCTIONS = `\
|
|
398
400
|
\`danxbot_api\` is your primary channel to the dashboard: every card, plan, Slack and dispatch-callback job (ending
|
|
399
|
-
|
|
401
|
+
yours too) is a call to it. Find a route with \`danxbot_api_list\`. Read its exact shape with
|
|
400
402
|
\`danxbot_api_spec\` before the first call to any route you have not called before; never guess a body.
|
|
401
403
|
|
|
402
404
|
RESOURCES. Most collections take GET (list) and POST (create); a single resource (\`<collection>/<id>\`) takes
|
|
@@ -405,7 +407,7 @@ GET, PATCH or PUT (send only the fields you change) and DELETE. A card is edited
|
|
|
405
407
|
\`total\` (page with \`limit\` and \`offset\`). Name each other field or relation you need in the \`fields\` query
|
|
406
408
|
tree. \`GET /api/resources/<resource>/fields\` lists the names.
|
|
407
409
|
|
|
408
|
-
STALE HASH. A write to contested text (
|
|
410
|
+
STALE HASH. A write to contested text (card text, a plan record, a problem) sends
|
|
409
411
|
back the \`content_hash\` or \`base_hash\` you read from that resource. A stale hash is refused 409 with the current
|
|
410
412
|
hash and content: merge your change into that and retry with the fresh hash.
|
|
411
413
|
|
|
@@ -415,8 +417,11 @@ REFUSALS. A 4xx body is \`{error, ...}\`, and its other fields name the fix: \`o
|
|
|
415
417
|
WRITING STANDARD, for every title, summary, description, comment, problem, solution and plan record you write. The
|
|
416
418
|
reader is new to the subject and will not read the code or docs: what you write is their documentation. Open with
|
|
417
419
|
plain background (what the thing is, why it exists, what is wrong or what the choice changes) before evidence.
|
|
418
|
-
Define each term a newcomer would not know. Cite exact ids, SHAs and paths. Be brief
|
|
419
|
-
|
|
420
|
+
Define each term a newcomer would not know. Cite exact ids, SHAs and paths. Be brief; never transcribe your
|
|
421
|
+
reasoning. A title or summary alone must let the reader decide.
|
|
422
|
+
|
|
423
|
+
DESIGN. UI designs and style options go on a Design card (\`design_publish\`), never an Artifact or HTML page.
|
|
424
|
+
Decide choices yourself; show alternatives as design states, never ask.
|
|
420
425
|
`;
|
|
421
426
|
assertWithinTextLimit("the server instructions", SERVER_INSTRUCTIONS);
|
|
422
427
|
export const server = new McpServer({
|
|
@@ -96,7 +96,7 @@ export class PermissionRequests {
|
|
|
96
96
|
let guarded;
|
|
97
97
|
try {
|
|
98
98
|
// `guard` runs before EVERY claim (DX-4418): a revoked or signed-out session answers its halt without a request.
|
|
99
|
-
guarded = await this.deps.access.guard(() => this.deps.
|
|
99
|
+
guarded = await this.deps.access.guard(() => this.deps.requestOwn({ method: "POST", basePath: PERMISSION_REQUESTS_BASE_PATH, path: `/${request.publicId}/claim` }));
|
|
100
100
|
}
|
|
101
101
|
catch (err) {
|
|
102
102
|
// An outdated MCP will never get a different answer; any other failure is retried until the request ends.
|
package/dist/session-options.js
CHANGED
|
@@ -10,6 +10,8 @@ import { credentialFingerprint, describeCredentialSource, readCredential } from
|
|
|
10
10
|
import { readSessionConnection } from "./session-connection.js";
|
|
11
11
|
import { fixForStop } from "./stop-fixes.js";
|
|
12
12
|
export const SESSION_ID_HEADER = "x-danx-session-id";
|
|
13
|
+
/** DX-5033 / DX-5076 - marks a request as this process's own background work (not the agent acting): the dashboard ends no agent pause on it. */
|
|
14
|
+
export const PLUGIN_BACKGROUND_HEADER = "x-danx-plugin-background";
|
|
13
15
|
/** A start this process cannot make, named the way the session will be told about it. */
|
|
14
16
|
export class SessionStartError extends Error {
|
|
15
17
|
// `ListenStopReason`, not `string`: a start-time failure is always one of `no_connection_record` / `credential_unavailable`
|
package/dist/session-stream.js
CHANGED
|
@@ -10,7 +10,7 @@ import { errorCodeOf } from "./json-body.js";
|
|
|
10
10
|
import { boundRequest } from "./request-bound.js";
|
|
11
11
|
import { keyRevocationOf, keyRevokedHalt } from "./key-revoked-halt.js";
|
|
12
12
|
import { oneLine } from "./one-line.js";
|
|
13
|
-
import { SESSION_ID_HEADER } from "./session-options.js";
|
|
13
|
+
import { PLUGIN_BACKGROUND_HEADER, SESSION_ID_HEADER } from "./session-options.js";
|
|
14
14
|
import { isClaimState } from "@thehammer/danx-session-telemetry";
|
|
15
15
|
/**
|
|
16
16
|
* DX-3912 — the longest heartbeat `reason` the relay sends: one under the dashboard's own
|
|
@@ -74,6 +74,8 @@ async function request(options, deps, what, url, init = { method: "GET" }) {
|
|
|
74
74
|
Authorization: `Bearer ${options.token}`,
|
|
75
75
|
Accept: "application/json",
|
|
76
76
|
[SESSION_ID_HEADER]: options.sessionId,
|
|
77
|
+
// DX-5076: every request this module makes is the relay's own work (ticket mint, inventory read, heartbeat), never the agent acting, so it must not end an agent pause. One marking point, so a new request here cannot forget it.
|
|
78
|
+
[PLUGIN_BACKGROUND_HEADER]: "1",
|
|
77
79
|
};
|
|
78
80
|
if (init.body !== undefined)
|
|
79
81
|
headers["Content-Type"] = "application/json";
|
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.304",
|
|
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, request_permission, attach_file, and danxbot_slack_reply) (DX-3465, PLN-11 R-25).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|