@thehammer/danx-dashboard-mcp 0.1.152 → 0.1.154

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
@@ -21,7 +21,7 @@ All exposed as `mcp__danx-dashboard__<name>` once wired through the workspace `m
21
21
 
22
22
  | Tool | HTTP | Notes |
23
23
  |---|---|---|
24
- | `issue_list` | `GET /api/issues` | filters: `type`, `parent_id` (null → root-only), `dispatchable_derived`, `assigned_agent`, `include_closed`, `limit`, `offset` |
24
+ | `issue_list` | `GET /api/issues` | filters: `type`, `parent_id` (null → root-only), `dispatchable_derived`, `assigned_agent`, `include_closed`, `limit`, `offset`. `fields` (DX-3427) is the SAME recursive field tree `issue_get` takes, against the same issue resource — list-row fields (`ac_total`, `comments_count`, `waiting_on`, `quality_gates`, `next_pre_gate`, …) sit alongside every detail field. See `resource_fields` |
25
25
  | `issue_get` | `GET /api/issues/:id` or `GET /api/issues/batch` | Pass `id` for one card, or `ids[]` (DX-2727, at most 100) to resolve many across boards in ONE call — global, so `ids` with `board` throws; per-id `not_found` rather than a whole-call 404. Minimal scalars by default; `fields` (DX-3426) opts in via a recursive field TREE — `{"description": true, "problems": {"solutions": true}, "comments": {"limit": 10}}` — not a flat group list; a cursor-paged relation (today only `comments`) takes its page args (`limit`/`before`) inside its own nested object, so paging now works on the batch form too. See `resource_fields` |
26
26
  | `resource_fields` | `GET /api/resources/:resource/fields` | DX-3426 — what an `issue_get`-style field tree may name for one resource: `{resource, description, always, hashes, fields, relations}`. Install-global, no `board`. `resource: "issue"` is the root; a relation's own `resource` in the response is what to call this again with, one level deeper |
27
27
  | `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` |
package/dist/handlers.js CHANGED
@@ -25,19 +25,21 @@ import { readFile } from "node:fs/promises";
25
25
  import { basename, extname, isAbsolute } from "node:path";
26
26
  import { resolvePriority } from "./priority.js";
27
27
  /**
28
- * `filter`/`fields`/`sort` are JSON/CSV-encoded onto the query string (the
29
- * route parses `?filter=<JSON>`, `?fields=a,b`, `?sort=<JSON>` — see
30
- * `src/issues/read/reader.ts#parseEnvelope`); each is omitted entirely when
31
- * empty so the wire carries no `{}`/`[]` noise. `board` stays a flat
32
- * top-level param, resolved by the HTTP client exactly as before.
28
+ * `filter`/`fields`/`sort` are JSON-encoded onto the query string (the route
29
+ * parses `?filter=<JSON>`, `?fields=<JSON>`, `?sort=<JSON>` — see
30
+ * `src/issues/read/reader.ts#parseEnvelope` + `handleGetIssues`); each is
31
+ * omitted entirely when empty so the wire carries no `{}`/`[]` noise. `board`
32
+ * stays a flat top-level param, resolved by the HTTP client exactly as
33
+ * before.
33
34
  */
34
35
  export async function issueList(client, args) {
35
36
  const query = {};
36
37
  if (args.filter !== undefined && Object.keys(args.filter).length > 0) {
37
38
  query.filter = JSON.stringify(args.filter);
38
39
  }
39
- if (args.fields !== undefined && args.fields.length > 0) {
40
- query.fields = args.fields.join(",");
40
+ // DX-3427 — a JSON-encoded field tree, mirroring `issue_get` below.
41
+ if (args.fields !== undefined && Object.keys(args.fields).length > 0) {
42
+ query.fields = JSON.stringify(args.fields);
41
43
  }
42
44
  if (args.sort !== undefined && args.sort.length > 0) {
43
45
  query.sort = JSON.stringify(args.sort);
package/dist/index.js CHANGED
@@ -277,25 +277,6 @@ const EFFORT_VALUES = [
277
277
  // still be CREATED through this MCP.
278
278
  const ISSUE_TYPES = ["Epic", "Bug", "Feature", "Story", "Chore", "Task"];
279
279
  const NON_EPIC_TYPES = ["Bug", "Feature", "Story", "Chore", "Task"];
280
- // DX-935 / DX-937 — field-group taxonomy for the LIST read envelope,
281
- // hand-copied from `src/issues/read/field-groups.ts` (`LIST_GROUPS` — this
282
- // package cannot import server source). Drift surfaces at runtime as a server
283
- // 400, not silently. DX-3426 moved `issue_get` to the field tree
284
- // (`field-tree.ts`); DX-3427 moves the list the same way and deletes this.
285
- const LIST_FIELD_GROUPS = [
286
- "description",
287
- // DX-2735: replaced the flat "solutions" group (hard cut, no alias).
288
- "problems",
289
- "ac",
290
- "comments",
291
- "retro",
292
- "dependencies",
293
- "triage",
294
- "assignment",
295
- "quality_gates",
296
- "children",
297
- "effort",
298
- ];
299
280
  const SORT_ORDERS = ["asc", "desc"];
300
281
  const sortField = z
301
282
  .array(z.object({
@@ -360,7 +341,7 @@ const MARKDOWN_STYLE_DESCRIBE = "Renders as markdown here. Use `##`/`###` header
360
341
  strictTool("issue_list",
361
342
  // DX-2735: trimmed to pay for the problem tools inside the work-profile
362
343
  // injected-surface budget — same facts, no repeated prose.
363
- "List cards. Board-scoped; see `board`. `filter`: type, parent_id, dispatchable_derived, status_derived[], self_dispatchable_derived, assigned_agent, include_closed, include_deleted, q (free text over id+title+description). THE DEFAULT ROW IS MINIMAL — scalars only (id, type, title, status, parent_id, priority, created_at, updated_at, assigned_agent, content_hash), no joins; opt into heavy data with `fields` groups: description (+ summary), problems (open_problem_count — a card needs a human exactly when this is > 0), ac, comments, retro, dependencies, triage, assignment, quality_gates, children, effort. `sort`: [{column, order}] over id|priority|repo_name|title|type|status_derived|triage_ice_total|created_at|updated_at (default order: see `sort`'s own field description). `limit`/`offset` page (uncapped by default). `include_closed` defaults to FALSE: a bare call silently excludes every Done/Cancelled card (leaf AND container alike). The response always carries `total` (the full count matching every filter except limit/offset — compare against `issues.length` to tell an exhausted list from a truncated one) and, whenever `include_closed` was not explicitly `true`, `closed_excluded` (how many additional terminal cards the default withheld — re-call with `include_closed: true` to see them). issue_get reads one card in full.", {
344
+ "List cards. Board-scoped; see `board`. `filter`: type, parent_id, dispatchable_derived, status_derived[], self_dispatchable_derived, assigned_agent, include_closed, include_deleted, q (free text over id+title+description). THE DEFAULT ROW IS MINIMAL — scalars only (id, type, title, status, parent_id, priority, created_at, updated_at, assigned_agent, content_hash), no joins; `fields` opts into more — the SAME recursive field-tree JSON `issue_get` takes, against the SAME issue resource (list-row fields like `ac_total`/`comments_count`/`waiting_on`/`quality_gates`/`next_pre_gate` sit alongside every detail field `issue_get` can name). Call `resource_fields({resource:\"issue\"})` for the full, current list of what `fields` may name — never guess a name; unknown → 400 `unknown_field`. `sort`: [{column, order}] over id|priority|repo_name|title|type|status_derived|triage_ice_total|created_at|updated_at (default order: see `sort`'s own field description). `limit`/`offset` page (uncapped by default). `include_closed` defaults to FALSE: a bare call silently excludes every Done/Cancelled card (leaf AND container alike). The response always carries `total` (the full count matching every filter except limit/offset — compare against `issues.length` to tell an exhausted list from a truncated one) and, whenever `include_closed` was not explicitly `true`, `closed_excluded` (how many additional terminal cards the default withheld — re-call with `include_closed: true` to see them). issue_get reads one card in full.", {
364
345
  filter: z
365
346
  .object({
366
347
  q: z.string().optional(),
@@ -381,10 +362,12 @@ strictTool("issue_list",
381
362
  // its own `.strict()`.
382
363
  .strict()
383
364
  .optional(),
384
- fields: z
385
- .array(z.enum(LIST_FIELD_GROUPS))
365
+ // DX-3427 — a field TREE, exactly like `issue_get`'s `fields` below
366
+ // (against the same shared issue resource) — replaces the retired
367
+ // CSV field-GROUP list.
368
+ fields: fieldTreeSchema
386
369
  .optional()
387
- .describe("Field groups to add; absent = minimal scalars."),
370
+ .describe("A field tree (see tool description); absent/empty = minimal scalars. `resource_fields({resource:\"issue\"})` names every valid key."),
388
371
  sort: sortField,
389
372
  limit: z.number().int().positive().max(LIST_PAGE_MAX_LIMIT).optional(),
390
373
  offset: z.number().int().nonnegative().optional(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.152",
3
+ "version": "0.1.154",
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",