@thehammer/danx-dashboard-mcp 0.1.281 → 0.1.283

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 CHANGED
@@ -61,7 +61,7 @@ Card and plan reads/writes go through `danxbot_api` against the issue/plan route
61
61
 
62
62
  ## `plan_events_wait` — a working session's plan events (DX-4233)
63
63
 
64
- A program calls this, not a person: the danxbot Claude Code plugin's relay loop, inside the session, calls it over and over on the session's own danx-dashboard MCP server and hands every event it gets to the session. The `bridge` subcommand is gone: no process is spawned, no `npx` cold start, and nothing outlives the session, because the stream lives in the MCP server, which ends with its session.
64
+ A program calls this, not a person: the danxbot Claude Code plugin's relay loop, inside the session, calls it over and over on the session's own danx-dashboard MCP server and hands every event it gets to the session. A plan-orchestrator dispatch (a headless session that works one plan) has no plugin relay, so it keeps ONE background waiter sub-agent that loops on this tool until events exist and returns them with the last cursor, and the orchestrator passes that cursor to the next waiter it arms (DX-4667); the `plan-orchestrator` profile alone is granted the tool on a dispatch (it is an opt-in tool). The `bridge` subcommand is gone: no process is spawned, no `npx` cold start, and nothing outlives the session, because the stream lives in the MCP server, which ends with its session.
65
65
 
66
66
  Input `{plan_id: number, cursor: string | null, timeout_ms: 1000..30000, transcript_path?: string}`. Answer, one of:
67
67
 
package/dist/index.js CHANGED
@@ -172,7 +172,7 @@ let permissionRequests;
172
172
  // DX-4523 - late-bound with `access` and `client`; see `tool-surface.ts` and `permission-watch.ts`.
173
173
  let toolAccessSync;
174
174
  let permissionWatch;
175
- // DX-4233 - late-bound with `access`; see `plan-events-relay.ts`. The plugin's relay loop calls `plan_events_wait` on it.
175
+ // DX-4233 - late-bound with `access`; see `plan-events-relay.ts`. The plugin's relay loop (a person's session) and a plan orchestrator's waiter sub-agent (DX-4667) call `plan_events_wait` on it.
176
176
  let planEventRelay;
177
177
  /**
178
178
  * Compose the dispatch's qualified board id (`<repo>:<slug>`) from the two env
@@ -788,15 +788,16 @@ async (args) => {
788
788
  });
789
789
  // ---------------- plan_events_wait (DX-4233) ----------------
790
790
  strictTool("plan_events_wait",
791
- // DX-4233 - for the danxbot plugin's relay loop, not for a model to call. It answers `events` (each `{cursor, text}`; empty after
792
- // `timeout_ms`), the cursor to send being the last delivered event's, or `stopped` with the remedy to tell the session. General-audience text
793
- // (DX-4550): nothing here names our own setup.
794
- "Waits for events on the plan this session is connected to. For the danxbot plugin's relay, which calls it in a loop " +
795
- "and hands every event to the session: a model never calls it. `plan_id` is the plan the session is on (a different " +
796
- "plan than the last call's starts clean). `cursor` is the `cursor` of the last event delivered, as answered (null " +
791
+ // DX-4233 - for the danxbot plugin's relay loop. DX-4667 - and for a background waiter sub-agent of a plan orchestrator, which
792
+ // loops on it until events exist and returns them (and the cursor) to the orchestrator it wakes. It answers `events` (each
793
+ // `{cursor, text}`; empty after `timeout_ms`), the cursor to send being the last delivered event's, or `stopped` with the remedy
794
+ // to tell the session. General-audience text (DX-4550): nothing here names our own setup.
795
+ "Waits for events on the plan this session is connected to. For a relay or a background waiter that calls it in a loop " +
796
+ "and hands every event to the session; do not call it for any other reason. `plan_id` is the plan the session is on (a " +
797
+ "different plan than the last call's starts clean). `cursor` is the `cursor` of the last event delivered, as answered (null " +
797
798
  "when none). Answers `{events: [{cursor, text}]}`, with `events` empty once `timeout_ms` passes, or `{stopped: " +
798
799
  "{reason, detail, fix}}` when the stream ended: tell the session the `fix`. An event stays until a later call's " +
799
- "`cursor` passes it. " +
800
+ "`cursor` passes it. Only one wait is pending per session: a newer call ends the older. " +
800
801
  "`transcript_path` is the session's transcript file, for its usage and title.", {
801
802
  plan_id: z.number().int().positive().describe("Id of the plan this session is connected to."),
802
803
  cursor: z
@@ -1,8 +1,10 @@
1
1
  /**
2
2
  * DX-4233 - the IN-PROCESS relay of a session's plan events: what the `plan_events_wait` tool serves.
3
3
  *
4
- * WHO CALLS IT. The danxbot plugin's relay loop, inside the Claude Code session, calls `plan_events_wait` over and over on
5
- * this session's own danx-dashboard MCP server and hands every record it gets to the session. This class is everything on the
4
+ * WHO CALLS IT. In a person's session, the danxbot plugin's relay loop calls `plan_events_wait` over and over on this
5
+ * session's own danx-dashboard MCP server and hands every record it gets to the session. In a plan orchestrator's dispatch
6
+ * (DX-4667), which loads no relay, a background waiter sub-agent calls it the same way and returns the records and the last
7
+ * cursor to the orchestrator. This class is everything on the
6
8
  * server side of that call: it opens the session's dashboard event stream (the mint / stream / heartbeat contract of
7
9
  * `session-stream.ts`), buffers records for the caller and reports the session's listener health. There is no process to start,
8
10
  * watch or kill: the stream lives exactly as long as the MCP server, which lives exactly as long as its session.
@@ -137,7 +139,7 @@ export class PlanEventRelay {
137
139
  this.pendingStop = null;
138
140
  return { stopped };
139
141
  }
140
- // DX-4233: a server holding no key answers a stop without calling the dashboard (a dispatch never reaches here).
142
+ // DX-4233: a server holding no key answers a stop without calling the dashboard (a dispatch always holds its env key, so it never takes this branch).
141
143
  if (this.deps.auth() === null) {
142
144
  return { stopped: this.stopRecord("credential_unavailable", "this session holds no dashboard key") };
143
145
  }
@@ -32,6 +32,7 @@ export const GET_ROUTE_BOARD_SCOPE = [
32
32
  ["/api/plans/:planId/records", "refuse"],
33
33
  ["/api/plans/:planId/usage-series", "refuse"],
34
34
  ["/api/plans/mine/architecture/sections", "refuse"],
35
+ ["/api/plans/mine/records", "refuse"],
35
36
  ["/api/platform/signup-codes", "refuse"],
36
37
  ["/api/platform/teams", "refuse"],
37
38
  ["/api/reminders", "refuse"],
@@ -29,7 +29,6 @@ const BOOTSTRAP = { permission: null, callback: null };
29
29
  const needs = (permission) => ({ permission, callback: null });
30
30
  /** DX-4632: usable with no key (a session that has not signed in yet) and only on a person's session. */
31
31
  const OPERATOR_SESSION_BOOTSTRAP = { permission: null, callback: null, operatorSessionOnly: true };
32
- const operatorSessionNeeds = (permission) => ({ permission, callback: null, operatorSessionOnly: true });
33
32
  const needsAndPostsTo = (permission, callback) => ({ permission, callback });
34
33
  /**
35
34
  * Every tool this server registers, with what makes it usable. `index.ts` refuses to register a tool that is not
@@ -49,10 +48,14 @@ export const TOOL_ACCESS = {
49
48
  design_publish: needs("cards.edit"),
50
49
  // DX-4233 - the plugin's relay waits on the session's plan events here: it opens the listener ticket
51
50
  // (`POST /api/plan-sessions/me/stream-ticket`) and heartbeats (`.../me/heartbeat`), both `plans.view`.
52
- plan_events_wait: operatorSessionNeeds("plans.view"),
51
+ // DX-4667 - not operator-session-only: a plan orchestrator's dispatch calls it too, through a background waiter sub-agent that
52
+ // wakes the orchestrator when a person answers. What keeps it off every other dispatch is the opt-in (`MCP_SERVER_OPT_IN_TOOLS`,
53
+ // `src/tokens/tool-defs.ts`) granted to the `plan-orchestrator` profile alone, not this table.
54
+ plan_events_wait: needs("plans.view"),
53
55
  // DX-4632 - the plugin's session-start hook asks which plan the session this one replaced was working. A restarted session boots
54
- // signed out, so it holds no permission: like `plan_connect` it is a bootstrap tool, and (like `plan_events_wait`) a person's
55
- // session's only. The lookup asks the dashboard as the predecessor, with the credential the predecessor's record names.
56
+ // signed out, so it holds no permission: like `plan_connect` it is a bootstrap tool, and a person's session's only (a dispatch
57
+ // never loads the plugin that calls it). The lookup asks the dashboard as the predecessor, with the credential the predecessor's
58
+ // record names.
56
59
  restart_notice: OPERATOR_SESSION_BOOTSTRAP,
57
60
  // The one dispatch callback that stays a tool (DX-4525): it reads local files. Its dashboard route's own policy names the
58
61
  // permission (`route-policies/index.ts`); every other callback is a `danxbot_api` call to a route under `callback-routes.ts`.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.281",
3
+ "version": "0.1.283",
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",