@thehammer/danx-dashboard-mcp 0.1.168 → 0.1.170

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
@@ -1,8 +1,8 @@
1
1
  # @thehammer/danx-dashboard-mcp
2
2
 
3
- Stdio MCP server wrapping danxbot's dashboard `/api/issues/*` normalized DB-backed HTTP routes. Replaces the legacy "agents Edit/Write `.yml` files directly" pattern from before DX-704 / DX-811.
3
+ Stdio MCP server fronting danxbot's dashboard HTTP API. Since DX-3465 it exposes three generic tools (`danxbot_api`, `danxbot_api_list`, `danxbot_api_spec` — see "Tool surface" below) that reach any route the caller's credential may reach, plus a handful of dedicated tools for agent-specific operations (`plan_connect`, `danxbot_complete`, Slack/template-app callbacks, …) — there is no per-operation tool any more.
4
4
 
5
- Each tool is a thin envelope over one HTTP route — Zod-validated at the MCP boundary, fetch under the hood, server response passed back to the agent verbatim. Refusal envelopes (`{error, ...extra}` with `failed_gate`, `non_terminal_phases`, `offending_keys`, etc.) come through as `{ok: false, status, body}` so the agent can pick the right next action without guessing. 5xx and network failures throw.
5
+ `danxbot_api` is Zod-validated at the MCP boundary, fetches under the hood, and passes the server response back to the agent verbatim. Refusal envelopes (`{error, ...extra}` with `failed_gate`, `non_terminal_phases`, `offending_keys`, etc.) come through as `{ok: false, status, body}` so the agent can pick the right next action without guessing. 5xx and network failures throw.
6
6
 
7
7
  ## Env vars (required, fail-loud at boot)
8
8
 
@@ -102,19 +102,14 @@ export class DashboardHttpClient {
102
102
  for (const [k, v] of Object.entries(extraQuery)) {
103
103
  if (v === undefined || v === null)
104
104
  continue;
105
- // DX-3465 round 2 (code-quality gate finding, at 27c9e4005) — a
106
- // field TREE (`fields`, a plain object) is the only query param the
107
- // server parses as JSON (`src/resources/field-tree.ts`); every
108
- // LIST-valued param it has (`ids` on `GET /api/issues/batch`,
109
- // `status` on the boards list, `endpoints` on `GET /api/openapi`)
110
- // splits on commas instead. Round 1 JSON-encoded both objects and
111
- // arrays alike, which silently broke every array-valued param: the
112
- // server saw a JSON literal where it expected a comma list and
113
- // returned a wrong-but-200 answer (e.g. `ids=["DX-1","DX-2"]`
114
- // resolves zero cards) instead of the old loud zod refusal. Arrays
115
- // are `typeof "object"` too, so they must be checked BEFORE the
116
- // plain-object branch. A plain scalar still stringifies the same
117
- // way it always did.
105
+ // DX-3465 — a field TREE (`fields`, a plain object) is the only
106
+ // query param the server parses as JSON
107
+ // (`src/resources/field-tree.ts`); every LIST-valued param it has
108
+ // (`ids` on `GET /api/issues/batch`, `status` on the boards list,
109
+ // `endpoints` on `GET /api/openapi`) splits on commas instead, never
110
+ // JSON. Arrays are `typeof "object"` too, so the array check must
111
+ // come BEFORE the plain-object branch. A plain scalar stringifies
112
+ // as before.
118
113
  params.set(k, Array.isArray(v)
119
114
  ? v.map((item) => String(item)).join(",")
120
115
  : typeof v === "object"
package/dist/index.js CHANGED
@@ -84,6 +84,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
84
84
  import { z } from "zod";
85
85
  import { DashboardHttpClient } from "./http-client.js";
86
86
  import { planConnect, danxbotApi, danxbotApiList, danxbotApiSpec, attachFile, DANXBOT_API_METHODS, } from "./handlers.js";
87
+ import { queryValueSchema } from "./types.js";
87
88
  import { registerLifecycleTools, applyLifecycleVisibility, readLifecycleUrlsFromEnv, } from "./lifecycle.js";
88
89
  function readEnvOrDie(name) {
89
90
  const v = process.env[name];
@@ -337,28 +338,16 @@ strictTool("danxbot_api", "Call any dashboard API route your credential may reac
337
338
  .min(1)
338
339
  .startsWith("/api/")
339
340
  .describe('Full path starting with "/api/" — never a route template.'),
340
- // DX-3465 (code-quality gate finding, at 275f82c0d) — a query value may
341
- // be a field TREE (a plain object, e.g.
342
- // `{"description":true,"comments":{"limit":20}}`), not just a scalar —
343
- // SERVER_INSTRUCTIONS below says "ALWAYS pass `fields` … a field tree"
344
- // and the OpenAPI spec agrees (`fields`'s schema is `{type:"object"}`),
345
- // so the schema here must accept what those two tell the caller to
346
- // send. It may also be an array (e.g. `ids` on the batch-get route) —
347
- // DX-3465 round 2 (code-quality gate finding, at 27c9e4005) —
348
- // `DashboardHttpClient#buildUrl` JSON-encodes a plain-object value but
341
+ // DX-3465 — a query value may be a scalar, a field TREE (a plain
342
+ // object, e.g. `{"description":true,"comments":{"limit":20}}` — see
343
+ // SERVER_INSTRUCTIONS below and the OpenAPI spec's `fields` schema), or
344
+ // an array of scalars (e.g. `ids` on the batch-get route).
345
+ // `DashboardHttpClient#buildUrl` JSON-encodes a plain object but
349
346
  // comma-joins an array, matching the two shapes the server actually
350
- // parses (a JSON field tree vs. a comma-separated list param); a scalar
351
- // serializes as before.
352
- query: z
353
- .record(z.union([
354
- z.string(),
355
- z.number(),
356
- z.boolean(),
357
- z.null(),
358
- z.record(z.unknown()),
359
- z.array(z.unknown()),
360
- ]))
361
- .optional(),
347
+ // parses (a JSON field tree vs. a comma-separated list param) — so array
348
+ // elements are restricted to scalars, the only thing a comma-join can
349
+ // represent losslessly.
350
+ query: queryValueSchema.optional(),
362
351
  body: z.unknown().optional(),
363
352
  ...boardField,
364
353
  }, async (args) => jsonResult(await danxbotApi(client, args)));
package/dist/types.js CHANGED
@@ -1 +1,16 @@
1
- export {};
1
+ import { z } from "zod";
2
+ /**
3
+ * The zod schema for `danxbot_api`'s `query` argument, matching the
4
+ * `QueryValue` type above — a scalar, a field-tree object, or an array
5
+ * whose elements are themselves scalars (never objects; a comma-join
6
+ * cannot represent one losslessly). Exported so it can be exercised
7
+ * directly in tests without going through the MCP tool-call machinery.
8
+ */
9
+ export const queryValueSchema = z.record(z.union([
10
+ z.string(),
11
+ z.number(),
12
+ z.boolean(),
13
+ z.null(),
14
+ z.record(z.unknown()),
15
+ z.array(z.union([z.string(), z.number(), z.boolean()])),
16
+ ]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@thehammer/danx-dashboard-mcp",
3
- "version": "0.1.168",
3
+ "version": "0.1.170",
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",