@thehammer/danx-dashboard-mcp 0.1.149 → 0.1.150
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 +2 -1
- package/dist/entrypoint.test.js +43 -0
- package/dist/field-tree.js +35 -0
- package/dist/handlers.js +25 -13
- package/dist/index.js +28 -35
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,7 +22,8 @@ All exposed as `mcp__danx-dashboard__<name>` once wired through the workspace `m
|
|
|
22
22
|
| Tool | HTTP | Notes |
|
|
23
23
|
|---|---|---|
|
|
24
24
|
| `issue_list` | `GET /api/issues` | filters: `type`, `parent_id` (null → root-only), `dispatchable_derived`, `assigned_agent`, `include_closed`, `limit`, `offset` |
|
|
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` opts in |
|
|
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
|
+
| `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 |
|
|
26
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` |
|
|
27
28
|
| `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
29
|
| `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 |
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { describe, it, expect, beforeAll, afterAll } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, symlinkSync, rmSync, realpathSync, } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { pathToFileURL } from "node:url";
|
|
6
|
+
import { isEntrypointModule } from "./entrypoint.js";
|
|
7
|
+
describe("isEntrypointModule (DX-1647)", () => {
|
|
8
|
+
let dir;
|
|
9
|
+
let real;
|
|
10
|
+
let link;
|
|
11
|
+
beforeAll(() => {
|
|
12
|
+
dir = mkdtempSync(join(tmpdir(), "entrypoint-test-"));
|
|
13
|
+
real = join(dir, "index.js");
|
|
14
|
+
writeFileSync(real, "// stub entry\n");
|
|
15
|
+
link = join(dir, "danx-dashboard-mcp"); // mimics node_modules/.bin symlink
|
|
16
|
+
symlinkSync(real, link);
|
|
17
|
+
});
|
|
18
|
+
afterAll(() => {
|
|
19
|
+
rmSync(dir, { recursive: true, force: true });
|
|
20
|
+
});
|
|
21
|
+
// import.meta.url always reports the module REALPATH — model that here.
|
|
22
|
+
const moduleUrl = () => pathToFileURL(realpathSync(real)).href;
|
|
23
|
+
it("true when argv[1] is the real file (direct `node index.js`)", () => {
|
|
24
|
+
expect(isEntrypointModule(moduleUrl(), real)).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
it("true when argv[1] is a SYMLINK to the file (npx / global bin) — the fleet regression", () => {
|
|
27
|
+
// The symlink path !== the realpath, but the entrypoint MUST still be
|
|
28
|
+
// detected, or npx imports the module and exits without booting the server.
|
|
29
|
+
expect(link).not.toBe(realpathSync(link));
|
|
30
|
+
expect(isEntrypointModule(moduleUrl(), link)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
it("false when argv[1] is undefined (module imported, not run as bin)", () => {
|
|
33
|
+
expect(isEntrypointModule(moduleUrl(), undefined)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
it("false when argv[1] is an empty string", () => {
|
|
36
|
+
expect(isEntrypointModule(moduleUrl(), "")).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
it("false when argv[1] points at a different real file", () => {
|
|
39
|
+
const other = join(dir, "other.js");
|
|
40
|
+
writeFileSync(other, "// other\n");
|
|
41
|
+
expect(isEntrypointModule(moduleUrl(), other)).toBe(false);
|
|
42
|
+
});
|
|
43
|
+
});
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DX-3426 — the shared recursive FIELD-TREE shape.
|
|
3
|
+
*
|
|
4
|
+
* The dashboard's single-card and batch card reads (`issue_get` today;
|
|
5
|
+
* `issue_list` / `plan_get` in later cards, per DX-3426's own follow-up scope)
|
|
6
|
+
* take a nested JSON tree instead of a flat field-GROUP list: each key names a
|
|
7
|
+
* field or relation of the resource at that level, and each value is either
|
|
8
|
+
* `true` (the field, or a relation's child with its default fields), a
|
|
9
|
+
* non-negative integer (a cursor-paged relation's own page ARGUMENT — e.g.
|
|
10
|
+
* `{"comments": {"limit": 10, "before": 812}}`, where `limit`/`before` are
|
|
11
|
+
* themselves plain integer values inside the nested object), or another
|
|
12
|
+
* field tree (a relation's child with a NAMED field selection). Example:
|
|
13
|
+
*
|
|
14
|
+
* {"description": true, "ac": true, "problems": {"solutions": {"steps": true}, "decisions": true}, "comments": {"limit": 10}}
|
|
15
|
+
*
|
|
16
|
+
* One schema, one module — every tool that accepts a field tree imports THIS
|
|
17
|
+
* export rather than redeclaring the shape, so the recursion is defined once
|
|
18
|
+
* and every caller's JSON Schema serializes the same way. `z.lazy` is
|
|
19
|
+
* required because the schema refers to itself; `zod-to-json-schema` (the
|
|
20
|
+
* MCP SDK's own zod v3 → JSON Schema converter, see
|
|
21
|
+
* `@modelcontextprotocol/sdk`'s `zod-json-schema-compat.ts`) resolves that
|
|
22
|
+
* self-reference to a local `$ref` rather than unrolling it infinitely —
|
|
23
|
+
* verified directly against the listed tool schema in
|
|
24
|
+
* `__tests__/tool-defs.test.ts`, not assumed from reading the library's
|
|
25
|
+
* source. `issue_solution`'s pre-existing `steps` field (DX-3310) is the
|
|
26
|
+
* same `z.lazy` self-reference pattern already shipping on this server.
|
|
27
|
+
*/
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
/**
|
|
30
|
+
* The shared schema. A record whose values are `true`, a non-negative
|
|
31
|
+
* integer, or the same schema recursively — generic on purpose (no
|
|
32
|
+
* issue-specific field names baked in here) so `issue_list` / `plan_get` can
|
|
33
|
+
* reuse it unchanged in their own later cards.
|
|
34
|
+
*/
|
|
35
|
+
export const fieldTreeSchema = z.lazy(() => z.record(z.union([z.literal(true), z.number().int().nonnegative(), fieldTreeSchema])));
|
package/dist/handlers.js
CHANGED
|
@@ -95,16 +95,15 @@ export async function issueGet(client, args) {
|
|
|
95
95
|
if (args.ids !== undefined && args.board !== undefined) {
|
|
96
96
|
throw new Error("issue_get: board does not apply to the ids batch form — it resolves every id globally; drop board");
|
|
97
97
|
}
|
|
98
|
-
// DX-3321 — comments paging is a SINGLE-id-route feature
|
|
99
|
-
// (`GET /api/issues/:id?comments_limit=&comments_offset=`); the batch
|
|
100
|
-
// route has no per-card paging at all. Refuse rather than silently drop,
|
|
101
|
-
// same as the `ids` + `board` refusal just above.
|
|
102
|
-
if (args.ids !== undefined && (args.comments_limit !== undefined || args.comments_offset !== undefined)) {
|
|
103
|
-
throw new Error("issue_get: comments_limit/comments_offset do not apply to the ids batch form — it has no per-card comment paging; drop them or use the single-id form");
|
|
104
|
-
}
|
|
105
98
|
const query = {};
|
|
106
|
-
|
|
107
|
-
|
|
99
|
+
// DX-3426 — the tree travels as ONE JSON-encoded query param (mirrors how
|
|
100
|
+
// `issue_list`'s `filter`/`sort` envelopes already ride the wire above),
|
|
101
|
+
// rather than the old CSV-of-group-names. Sent on both the single and
|
|
102
|
+
// batch forms — comment paging (or any other relation argument) now lives
|
|
103
|
+
// INSIDE the tree, so there is nothing left for the old ids+comments_*
|
|
104
|
+
// refusal to guard against.
|
|
105
|
+
if (args.fields !== undefined && Object.keys(args.fields).length > 0) {
|
|
106
|
+
query.fields = JSON.stringify(args.fields);
|
|
108
107
|
}
|
|
109
108
|
if (args.ids !== undefined) {
|
|
110
109
|
if (args.ids.length === 0) {
|
|
@@ -118,10 +117,6 @@ export async function issueGet(client, args) {
|
|
|
118
117
|
if (args.id === undefined) {
|
|
119
118
|
throw new Error("issue_get: pass exactly one of id or ids");
|
|
120
119
|
}
|
|
121
|
-
if (args.comments_limit !== undefined)
|
|
122
|
-
query.comments_limit = args.comments_limit;
|
|
123
|
-
if (args.comments_offset !== undefined)
|
|
124
|
-
query.comments_offset = args.comments_offset;
|
|
125
120
|
return client.request({
|
|
126
121
|
method: "GET",
|
|
127
122
|
path: `/${encodeURIComponent(args.id)}`,
|
|
@@ -1723,3 +1718,20 @@ export async function dispatchTranscriptSearch(client, args) {
|
|
|
1723
1718
|
},
|
|
1724
1719
|
};
|
|
1725
1720
|
}
|
|
1721
|
+
// ---------------- resource_fields (DX-3426) ----------------
|
|
1722
|
+
const RESOURCES_BASE_PATH = "/api/resources";
|
|
1723
|
+
/**
|
|
1724
|
+
* `GET /api/resources/:resource/fields` — what a field TREE (`issue_get`'s
|
|
1725
|
+
* `fields`, and any later tool built on the same shape) may name for ONE
|
|
1726
|
+
* resource: `{resource, description, always, hashes, fields, relations}`.
|
|
1727
|
+
* Install-global (no `board` — mirrors `failure_category_list`'s own
|
|
1728
|
+
* board-less rationale just above: nothing server-side would read one).
|
|
1729
|
+
* Unknown resource → 404 `unknown_resource`.
|
|
1730
|
+
*/
|
|
1731
|
+
export async function resourceFields(client, args) {
|
|
1732
|
+
return client.request({
|
|
1733
|
+
method: "GET",
|
|
1734
|
+
path: `/${encodeURIComponent(args.resource)}/fields`,
|
|
1735
|
+
basePath: RESOURCES_BASE_PATH,
|
|
1736
|
+
});
|
|
1737
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
* through the workspace `mcp.template.json`):
|
|
13
13
|
*
|
|
14
14
|
* - issue_list GET /api/issues
|
|
15
|
-
* - issue_get GET /api/issues/:id | /api/issues/batch (DX-2727 ids[] batch
|
|
15
|
+
* - issue_get GET /api/issues/:id | /api/issues/batch (DX-2727 ids[] batch;
|
|
16
|
+
* DX-3426 `fields` is a recursive field TREE, not a flat group list —
|
|
17
|
+
* see `resource_fields` below and `./field-tree.ts`)
|
|
18
|
+
* - resource_fields GET /api/resources/:resource/fields (DX-3426 — what `fields` may
|
|
19
|
+
* name for a resource; install-global, no `board`)
|
|
16
20
|
* - issue_create POST /api/issues
|
|
17
21
|
* - issue_edit PATCH /api/issues/:id/edit
|
|
18
22
|
* - issue_transition POST /api/issues/:id/transition
|
|
@@ -106,8 +110,9 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
106
110
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
107
111
|
import { z } from "zod";
|
|
108
112
|
import { DashboardHttpClient } from "./http-client.js";
|
|
109
|
-
import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueProblem, issueQualityGate, issueQualityGateVerdict, issueRetireBranch, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, failureCategoryCreate, failureCategoryList, failureCategoryUpdate, dispatchTranscriptSearch, planAddArchitectureSection, planAddCard, planAddNote, planAddRecord, planConnect, planCreate, planDeleteArchitectureSection, planDeleteNote, planDeleteRecord, planGet, PLAN_FIELD_GROUPS, PLAN_EVENT_KINDS, PLAN_EVENT_ORIGINS, PLAN_STATUSES, ISSUE_BATCH_GET_MAX, LIST_PAGE_MAX_LIMIT, PLAN_GET_CARDS_DEFAULT_LIMIT, PLAN_GET_EVENTS_DEFAULT_LIMIT, PLAN_GET_EVENTS_MAX_LIMIT, planGetArchitectureSection, planGetRecord, planList, planRemoveCard, planRename, planReorderArchitectureSection, planUpdateArchitectureSection, planUpdateNote, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, qualityGateInstruction, } from "./handlers.js";
|
|
113
|
+
import { issueAttach, issueChecklist, issueComment, issueCreate, issueDependency, issueEdit, issueGet, issueList, issueProblem, issueQualityGate, issueQualityGateVerdict, issueRetireBranch, issueRetro, issueSolution, issueTransition, issueTriage, briefGetPage, briefList, briefSetPage, failureCategoryCreate, failureCategoryList, failureCategoryUpdate, dispatchTranscriptSearch, planAddArchitectureSection, planAddCard, planAddNote, planAddRecord, planConnect, planCreate, planDeleteArchitectureSection, planDeleteNote, planDeleteRecord, planGet, PLAN_FIELD_GROUPS, PLAN_EVENT_KINDS, PLAN_EVENT_ORIGINS, PLAN_STATUSES, ISSUE_BATCH_GET_MAX, LIST_PAGE_MAX_LIMIT, PLAN_GET_CARDS_DEFAULT_LIMIT, PLAN_GET_EVENTS_DEFAULT_LIMIT, PLAN_GET_EVENTS_MAX_LIMIT, planGetArchitectureSection, planGetRecord, planList, planRemoveCard, planRename, planReorderArchitectureSection, planUpdateArchitectureSection, planUpdateNote, planUpdateRecord, repoKnowledgeGet, repoKnowledgeSet, qualityGateInstruction, resourceFields, } from "./handlers.js";
|
|
110
114
|
import { PRIORITY_TIER_WORDS } from "./priority.js";
|
|
115
|
+
import { fieldTreeSchema } from "./field-tree.js";
|
|
111
116
|
function readEnvOrDie(name) {
|
|
112
117
|
const v = process.env[name];
|
|
113
118
|
if (typeof v !== "string" || v === "") {
|
|
@@ -272,10 +277,11 @@ const EFFORT_VALUES = [
|
|
|
272
277
|
// still be CREATED through this MCP.
|
|
273
278
|
const ISSUE_TYPES = ["Epic", "Bug", "Feature", "Story", "Chore", "Task"];
|
|
274
279
|
const NON_EPIC_TYPES = ["Bug", "Feature", "Story", "Chore", "Task"];
|
|
275
|
-
// DX-935 / DX-937 — field-group taxonomy for the
|
|
276
|
-
// hand-copied from `src/issues/read/field-groups.ts` (LIST_GROUPS
|
|
277
|
-
//
|
|
278
|
-
//
|
|
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.
|
|
279
285
|
const LIST_FIELD_GROUPS = [
|
|
280
286
|
"description",
|
|
281
287
|
// DX-2735: replaced the flat "solutions" group (hard cut, no alias).
|
|
@@ -290,13 +296,6 @@ const LIST_FIELD_GROUPS = [
|
|
|
290
296
|
"children",
|
|
291
297
|
"effort",
|
|
292
298
|
];
|
|
293
|
-
const GET_FIELD_GROUPS = [
|
|
294
|
-
...LIST_FIELD_GROUPS,
|
|
295
|
-
"mirrors",
|
|
296
|
-
"code_review_items",
|
|
297
|
-
// DX-2835 — every plan this card is on ({id, ref, name}[]), detail only.
|
|
298
|
-
"plans",
|
|
299
|
-
];
|
|
300
299
|
const SORT_ORDERS = ["asc", "desc"];
|
|
301
300
|
const sortField = z
|
|
302
301
|
.array(z.object({
|
|
@@ -393,33 +392,27 @@ strictTool("issue_list",
|
|
|
393
392
|
}, async (args) => jsonResult(await issueList(client, args)));
|
|
394
393
|
// ---------------- issue_get ----------------
|
|
395
394
|
strictTool("issue_get",
|
|
396
|
-
// DX-
|
|
397
|
-
//
|
|
398
|
-
|
|
395
|
+
// DX-3426: field GROUPS replaced by a recursive field TREE (see
|
|
396
|
+
// `./field-tree.ts`) — comment paging now lives INSIDE the tree (a
|
|
397
|
+
// cursor-paged relation, today only `comments`, takes `limit`/`before` in
|
|
398
|
+
// its own nested object) instead of top-level `comments_limit`/
|
|
399
|
+
// `comments_offset`, which is also why the old "ids + comments_* refused"
|
|
400
|
+
// rule is gone: paging is per-relation now, so it works on the batch form
|
|
401
|
+
// too. Kept compact — call `resource_fields({resource:"issue"})` for the
|
|
402
|
+
// full, current list of what `fields` may name, rather than enumerating it
|
|
403
|
+
// here (it would drift the moment a field/relation is added or removed).
|
|
404
|
+
"Fetch one card (`id`) or many (`ids`, at most " + ISSUE_BATCH_GET_MAX + " — split larger sets); pass exactly one. Ids are globally unique, so both resolve from any board; the batch form is global and throws with `board`. THE DEFAULT ROW IS THE ALWAYS-ON FIELDS ONLY — identity, lifecycle stamps, derived status/list/blocked, assigned_agent, and content_hash (the concurrency token issue_edit needs for title/description/checklists); every child hash is likewise always returned. `fields` opts into more, per card in a batch too: a JSON object tree — each key names a field or relation, each value is `true` (that field, or a relation's child with its default fields), a nested object (a relation's child with named fields), or on the cursor-paged `comments` relation the nested object may also carry `limit`/`before` (comment id) to page — e.g. `{\"description\": true, \"problems\": {\"solutions\": {\"steps\": true}}, \"comments\": {\"limit\": 10}}`; a paged response also carries `comments_page: {limit, total, next_cursor}` (`next_cursor` → pass back as `before` for the next older page, null = none). Call `resource_fields({resource:\"issue\"})` for exactly what may be requested (every field/relation name, each relation's own child resource, and its paging shape if any) — never guess a name. Unknown field/relation → 400 `unknown_field`; bad shape → 400 `invalid_fields`; bad `limit`/`before` → 400 `invalid_field_argument`; a field you lack permission for → 403 `forbidden_field`. Single form: unknown id → 404. Batch form: `{issues: [...], not_found: [...ids]}` — an unknown or deleted id never fails the call.", {
|
|
399
405
|
id: z.string().min(1).optional(),
|
|
400
406
|
ids: z.array(z.string().min(1)).min(1).max(ISSUE_BATCH_GET_MAX).optional(),
|
|
401
|
-
fields:
|
|
402
|
-
.array(z.enum(GET_FIELD_GROUPS))
|
|
403
|
-
.optional()
|
|
404
|
-
.describe("Field groups to add; absent = minimal scalars."),
|
|
405
|
-
comments_limit: z
|
|
406
|
-
.number()
|
|
407
|
-
.int()
|
|
408
|
-
.positive()
|
|
409
|
-
.optional()
|
|
410
|
-
.describe("Single-id form only. How many comments the page holds (server default 20), the window anchored at the " +
|
|
411
|
-
"NEWEST comment. An out-of-range value (server max 200) is refused by the server with its own 400, " +
|
|
412
|
-
"never silently clamped here. Requires `fields:[\"comments\"]`."),
|
|
413
|
-
comments_offset: z
|
|
414
|
-
.number()
|
|
415
|
-
.int()
|
|
416
|
-
.nonnegative()
|
|
407
|
+
fields: fieldTreeSchema
|
|
417
408
|
.optional()
|
|
418
|
-
.describe("
|
|
419
|
-
"forward while comments_offset + comments.length < comments_total to reach older comments. Requires " +
|
|
420
|
-
"`fields` to include `comments`."),
|
|
409
|
+
.describe("A field tree (see tool description); absent/empty = minimal scalars. `resource_fields({resource:\"issue\"})` names every valid key."),
|
|
421
410
|
...boardField,
|
|
422
411
|
}, async (args) => jsonResult(await issueGet(client, args)));
|
|
412
|
+
// ---------------- resource_fields ----------------
|
|
413
|
+
strictTool("resource_fields", "DX-3426: what a field TREE (`issue_get`'s `fields`, and any later tool that takes one) may name for one resource — `{resource, description, always: string[], hashes: string[], fields: [{name, description}], relations: [{name, description, resource, paging?: {default_limit, max_limit, page_field}}]}`. `always`/`hashes` ride every response regardless of `fields` (never request them). A relation's `resource` names what to call this same tool with next, to go one level deeper. Install-global, not board-scoped. `resource: \"issue\"` is the root; unknown resource → 404 `unknown_resource`.", {
|
|
414
|
+
resource: z.string().min(1).describe('Resource name, e.g. "issue", or a relation\'s own `resource` from a prior call.'),
|
|
415
|
+
}, async (args) => jsonResult(await resourceFields(client, args)));
|
|
423
416
|
// ---------------- issue_create ----------------
|
|
424
417
|
strictTool("issue_create", '`plan` is REQUIRED on every create — no default, no inference; see its own field description for the two values and the refusal. This replaces the plan_add_card follow-up at creation time; plan_add_card remains for putting an EXISTING card on a plan. ' +
|
|
425
418
|
'Create a card. Board-scoped; see `board`. type=Epic REQUIRES non-empty phase_children[] (epic and phases inserted in one transaction; children get the epic as parent); other types refuse phase_children[] (400). Status starts at Review. `list_id` places the card straight into a column — a board_lists id or the list\'s display NAME (case-insensitive, emoji-tolerant): a `ready`-type queue lands it in ToDo, a `completed` list in Done, with no follow-up transition. Not valid on Epic; unknown name/id → 400. `quality_gates` adds gates beyond the board\'s type defaults (see its own field description); add one later with `issue_quality_gate`. ALWAYS pass `triage_enabled` explicitly on the root card and every phase child (see its own field description).', {
|
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.150",
|
|
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",
|