@hegemonart/get-design-done 1.27.6 → 1.27.7

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.
Files changed (53) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +6 -3
  3. package/CHANGELOG.md +49 -0
  4. package/package.json +5 -4
  5. package/reference/registry.json +7 -0
  6. package/reference/schemas/mcp-gdd-tools.schema.json +381 -0
  7. package/scripts/install.cjs +42 -0
  8. package/scripts/lib/gsd-health-mirror/index.cjs +105 -0
  9. package/scripts/lib/gsd-health-mirror/index.d.cts +14 -0
  10. package/scripts/lib/install/mcp-register.cjs +235 -0
  11. package/scripts/lib/install/mcp-register.d.cts +64 -0
  12. package/scripts/lib/intel-store/index.cjs +55 -0
  13. package/scripts/lib/intel-store/index.d.cts +11 -0
  14. package/scripts/lib/mcp-tools-lint/index.cjs +216 -0
  15. package/scripts/lib/mcp-tools-lint/index.d.cts +74 -0
  16. package/scripts/lib/reflections-reader/index.cjs +107 -0
  17. package/scripts/lib/reflections-reader/index.d.cts +18 -0
  18. package/scripts/lib/roadmap-reader/index.cjs +81 -0
  19. package/scripts/lib/roadmap-reader/index.d.cts +13 -0
  20. package/scripts/lib/snapshot-reader/index.cjs +70 -0
  21. package/scripts/lib/snapshot-reader/index.d.cts +28 -0
  22. package/scripts/mcp-servers/gdd-mcp/README.md +66 -0
  23. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_cycle_recap.schema.json +30 -0
  24. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_decisions_list.schema.json +32 -0
  25. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_events_tail.schema.json +22 -0
  26. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_health.schema.json +30 -0
  27. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_intel_get.schema.json +24 -0
  28. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_learnings_digest.schema.json +22 -0
  29. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_phase_current.schema.json +22 -0
  30. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_phases_list.schema.json +31 -0
  31. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_plans_list.schema.json +33 -0
  32. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_reflections_latest.schema.json +21 -0
  33. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_status.schema.json +23 -0
  34. package/scripts/mcp-servers/gdd-mcp/schemas/gdd_telemetry_query.schema.json +23 -0
  35. package/scripts/mcp-servers/gdd-mcp/server.ts +317 -0
  36. package/scripts/mcp-servers/gdd-mcp/tools/gdd_cycle_recap.ts +37 -0
  37. package/scripts/mcp-servers/gdd-mcp/tools/gdd_decisions_list.ts +33 -0
  38. package/scripts/mcp-servers/gdd-mcp/tools/gdd_events_tail.ts +26 -0
  39. package/scripts/mcp-servers/gdd-mcp/tools/gdd_health.ts +19 -0
  40. package/scripts/mcp-servers/gdd-mcp/tools/gdd_intel_get.ts +32 -0
  41. package/scripts/mcp-servers/gdd-mcp/tools/gdd_learnings_digest.ts +23 -0
  42. package/scripts/mcp-servers/gdd-mcp/tools/gdd_phase_current.ts +29 -0
  43. package/scripts/mcp-servers/gdd-mcp/tools/gdd_phases_list.ts +26 -0
  44. package/scripts/mcp-servers/gdd-mcp/tools/gdd_plans_list.ts +39 -0
  45. package/scripts/mcp-servers/gdd-mcp/tools/gdd_reflections_latest.ts +25 -0
  46. package/scripts/mcp-servers/gdd-mcp/tools/gdd_status.ts +31 -0
  47. package/scripts/mcp-servers/gdd-mcp/tools/gdd_telemetry_query.ts +27 -0
  48. package/scripts/mcp-servers/gdd-mcp/tools/index.ts +75 -0
  49. package/scripts/mcp-servers/gdd-mcp/tools/shared.ts +134 -0
  50. package/skills/health/SKILL.md +36 -0
  51. package/skills/next/SKILL.md +28 -3
  52. package/skills/progress/SKILL.md +21 -6
  53. package/skills/resume/SKILL.md +26 -1
@@ -0,0 +1,74 @@
1
+ // scripts/lib/mcp-tools-lint/index.d.cts — ambient types for the .cjs lib.
2
+ //
3
+ // The runtime consumer is tests/gdd-mcp-tools-lint.test.cjs (CommonJS, so
4
+ // types are not strictly required). This .d.cts ships the Phase 27.6
5
+ // convention (any .cjs lib that may be imported from a .ts file gets a
6
+ // sibling .d.cts) so that a future TypeScript consumer (e.g. a /lint:gdd
7
+ // slash command) gets correct types without a follow-up patch.
8
+
9
+ /** One detected lint failure. */
10
+ export interface LintViolation {
11
+ /** Filename (relative to scan dir) or the dir itself for cap violations. */
12
+ file: string;
13
+ rule: 'forbid-fs-path' | 'max-loc' | 'no-write-names' | 'tool-count-cap';
14
+ /** 1-based source line; 0 for whole-file or whole-directory violations. */
15
+ line: number;
16
+ /** Human-readable diagnostic, e.g. `loc=42 > max=30`. */
17
+ message: string;
18
+ }
19
+
20
+ /** Counts for the scan as a whole. */
21
+ export interface LintSummary {
22
+ files_scanned: number;
23
+ violations_count: number;
24
+ }
25
+
26
+ /** Return shape of {@link lintMcpToolsDir}. */
27
+ export interface LintResult {
28
+ violations: LintViolation[];
29
+ summary: LintSummary;
30
+ }
31
+
32
+ /** Inputs to {@link lintMcpToolsDir}. Only `dir` is required. */
33
+ export interface LintOptions {
34
+ /** Directory to scan. Tool files match `gdd_*.ts`. */
35
+ dir: string;
36
+ /** Max non-blank-non-comment LOC per tool file. Defaults to 30. */
37
+ maxLoc?: number;
38
+ /** Max number of `gdd_*.ts` tool files. Defaults to 12. */
39
+ toolCap?: number;
40
+ /**
41
+ * Filenames in `dir` exempt from forbid-fs-path + max-loc rules.
42
+ * Defaults to {'index.ts', 'shared.ts'}.
43
+ */
44
+ exemptions?: Set<string>;
45
+ }
46
+
47
+ /**
48
+ * Scan a directory of MCP tool .ts files and apply the 4 invariant rules.
49
+ * Pure-static — never executes the modules.
50
+ *
51
+ * - Rule A (`forbid-fs-path`): no fs/path imports in tool files (D-06).
52
+ * - Rule B (`max-loc`): each tool ≤ {@link LintOptions.maxLoc} LOC (D-06).
53
+ * - Rule C (`no-write-names`): no tool name with write-verb substring (D-04).
54
+ * - Rule D (`tool-count-cap`): ≤ {@link LintOptions.toolCap} tool files (D-03).
55
+ */
56
+ export function lintMcpToolsDir(opts: LintOptions): LintResult;
57
+
58
+ /** Ordered list of all rule names this module enforces. */
59
+ export const RULES: readonly LintViolation['rule'][];
60
+
61
+ /** Default exempt filenames (index.ts + shared.ts). */
62
+ export const DEFAULT_EXEMPTIONS: Set<string>;
63
+
64
+ /** Default value for the LOC ceiling. */
65
+ export const DEFAULT_MAX_LOC: number;
66
+
67
+ /** Default value for the tool-count cap. */
68
+ export const DEFAULT_TOOL_CAP: number;
69
+
70
+ /** The regexes Rule A scans for, line by line. */
71
+ export const FORBIDDEN_IMPORT_PATTERNS: readonly RegExp[];
72
+
73
+ /** The regex Rule C matches against the extracted `export const name`. */
74
+ export const WRITE_NAME_PATTERN: RegExp;
@@ -0,0 +1,107 @@
1
+ 'use strict';
2
+ // scripts/lib/reflections-reader/index.cjs — Plan 27.7-02
3
+ //
4
+ // Read post-cycle reflections from <rootDir>/.design/reflections/.
5
+ //
6
+ // Surface:
7
+ // class ReflectionsNotFoundError extends Error — code='directory_not_found'
8
+ // async readLatestReflection(rootDir) — { cycle, path, content } | null
9
+ // async readNReflections(rootDir, n) — same shape, sorted desc by mtime
10
+ // digestReflections(reflections) — string, <= 5120 chars
11
+
12
+ const fs = require('node:fs');
13
+ const path = require('node:path');
14
+
15
+ const DIGEST_CAP_BYTES = 5120;
16
+
17
+ class ReflectionsNotFoundError extends Error {
18
+ constructor(dir) {
19
+ super('source directory not found: ' + dir);
20
+ this.name = 'ReflectionsNotFoundError';
21
+ this.code = 'directory_not_found';
22
+ this.dir = dir;
23
+ }
24
+ }
25
+
26
+ async function listReflectionFiles(dir) {
27
+ const entries = await fs.promises.readdir(dir);
28
+ const candidates = [];
29
+ for (const name of entries) {
30
+ if (!name.endsWith('.md')) continue;
31
+ const full = path.join(dir, name);
32
+ try {
33
+ const stat = await fs.promises.stat(full);
34
+ candidates.push({ name, full, mtime: stat.mtimeMs });
35
+ } catch {
36
+ // ignore — race with concurrent write
37
+ }
38
+ }
39
+ candidates.sort((a, b) => b.mtime - a.mtime);
40
+ return candidates;
41
+ }
42
+
43
+ /** Extract a cycle id from a reflection filename. Convention: filenames
44
+ * like `2026-05-17-cycle27.7.md` or `cycle-NN.md`; we pick the first
45
+ * `cycle*` token, falling back to the basename. */
46
+ function extractCycle(name) {
47
+ const m = name.match(/cycle[-_]?[\w.]+/i);
48
+ if (m) return m[0];
49
+ return name.replace(/\.md$/, '');
50
+ }
51
+
52
+ async function readLatestReflection(rootDir) {
53
+ const dir = path.join(rootDir, '.design', 'reflections');
54
+ if (!fs.existsSync(dir)) {
55
+ throw new ReflectionsNotFoundError(dir);
56
+ }
57
+ const files = await listReflectionFiles(dir);
58
+ if (files.length === 0) return null;
59
+ const f = files[0];
60
+ const content = await fs.promises.readFile(f.full, 'utf8');
61
+ return { cycle: extractCycle(f.name), path: f.full, content };
62
+ }
63
+
64
+ async function readNReflections(rootDir, n) {
65
+ const dir = path.join(rootDir, '.design', 'reflections');
66
+ if (!fs.existsSync(dir)) {
67
+ throw new ReflectionsNotFoundError(dir);
68
+ }
69
+ const files = await listReflectionFiles(dir);
70
+ const take = Math.max(0, Math.min(n, files.length));
71
+ const out = [];
72
+ for (let i = 0; i < take; i++) {
73
+ const f = files[i];
74
+ const content = await fs.promises.readFile(f.full, 'utf8');
75
+ out.push({ cycle: extractCycle(f.name), path: f.full, content });
76
+ }
77
+ return out;
78
+ }
79
+
80
+ /** Aggregate reflections into a compact digest <= 5 KB. Strategy: take
81
+ * the first 300 chars of each reflection's body (skipping any leading
82
+ * frontmatter `---` block); join with `\n---\n`; truncate at the cap. */
83
+ function digestReflections(reflections) {
84
+ const parts = [];
85
+ for (const r of reflections) {
86
+ let body = r.content;
87
+ // Strip leading YAML frontmatter if present
88
+ if (body.startsWith('---')) {
89
+ const closing = body.indexOf('\n---', 3);
90
+ if (closing !== -1) body = body.slice(closing + 4).trim();
91
+ }
92
+ const excerpt = body.slice(0, 300).trim();
93
+ parts.push('[' + r.cycle + '] ' + excerpt);
94
+ }
95
+ let joined = parts.join('\n---\n');
96
+ if (joined.length > DIGEST_CAP_BYTES) {
97
+ joined = joined.slice(0, DIGEST_CAP_BYTES);
98
+ }
99
+ return joined;
100
+ }
101
+
102
+ module.exports = {
103
+ readLatestReflection,
104
+ readNReflections,
105
+ digestReflections,
106
+ ReflectionsNotFoundError,
107
+ };
@@ -0,0 +1,18 @@
1
+ // scripts/lib/reflections-reader/index.d.cts — TypeScript ambient declarations
2
+ // for the reflections-reader CJS module. Plan 27.7-02.
3
+
4
+ export class ReflectionsNotFoundError extends Error {
5
+ code: 'directory_not_found';
6
+ dir: string;
7
+ constructor(dir: string);
8
+ }
9
+
10
+ export interface Reflection {
11
+ cycle: string;
12
+ path: string;
13
+ content: string;
14
+ }
15
+
16
+ export function readLatestReflection(rootDir: string): Promise<Reflection | null>;
17
+ export function readNReflections(rootDir: string, n: number): Promise<Reflection[]>;
18
+ export function digestReflections(reflections: Reflection[]): string;
@@ -0,0 +1,81 @@
1
+ 'use strict';
2
+ // scripts/lib/roadmap-reader/index.cjs — Plan 27.7-02
3
+ //
4
+ // Read + parse .planning/ROADMAP.md into a flat list of phase entries
5
+ // consumed by the gdd_phases_list MCP tool. Pure read; no I/O outside
6
+ // readRoadmapMd().
7
+ //
8
+ // Output shape per phase:
9
+ // { number: '27.7', name: 'GDD MCP Server', version: 'v1.27.7',
10
+ // checkbox_status: 'shipped' | 'planned' | 'unknown' }
11
+ //
12
+ // `checkbox_status` is sourced from the overview list at the top of
13
+ // ROADMAP.md (lines shaped `- [x] [Phase 27]...` or `- [ ] [Phase 27.7]...`).
14
+ // When no overview line matches the heading, we emit `'unknown'`.
15
+
16
+ const fs = require('node:fs');
17
+ const path = require('node:path');
18
+
19
+ /** Read .planning/ROADMAP.md from rootDir; returns full markdown string. */
20
+ async function readRoadmapMd(rootDir) {
21
+ const p = path.join(rootDir, '.planning', 'ROADMAP.md');
22
+ return fs.promises.readFile(p, 'utf8');
23
+ }
24
+
25
+ /**
26
+ * Parse the body of ROADMAP.md into phase entries. Two passes:
27
+ * 1. Scan `- [x|space] [Phase <num>](...)` overview lines → status map
28
+ * 2. Scan `### Phase <num>: <name>` headings → main list
29
+ *
30
+ * The version is pulled from the FIRST line after the heading that
31
+ * matches `**Target version**:` or `— v\S+`. We tolerate trailing
32
+ * parenthetical commentary on the heading by trimming everything after
33
+ * a trailing ` (…)` chunk.
34
+ */
35
+ function parsePhases(md) {
36
+ // Pass 1 — overview checkbox map. Pattern matches both `Phase 27` and
37
+ // `Phase 27.7`; ignores the descriptive name + version that may follow.
38
+ const statusMap = new Map();
39
+ const overviewRe = /^-\s+\[([x\s])\]\s+\[Phase\s+(\S+?)\]/gm;
40
+ let mm;
41
+ while ((mm = overviewRe.exec(md)) !== null) {
42
+ const checked = mm[1] === 'x';
43
+ statusMap.set(mm[2], checked ? 'shipped' : 'planned');
44
+ }
45
+
46
+ // Pass 2 — Phase heading scan. We capture the number + name, then
47
+ // look ahead in the heading's section for a version marker. The
48
+ // heading may have an embedded version (` — v1.X.Y`) or rely on
49
+ // a `**Target version**:` block below.
50
+ const headingRe = /^###\s+Phase\s+(\S+?):\s+([^\n]+?)$/gm;
51
+ const phases = [];
52
+ let h;
53
+ while ((h = headingRe.exec(md)) !== null) {
54
+ const number = h[1];
55
+ let name = h[2].trim();
56
+ // Strip trailing parenthetical commentary (e.g. `(INSERTED)`).
57
+ name = name.replace(/\s+\([^)]*\)\s*$/g, '').trim();
58
+ // Strip trailing version chunk from the heading itself.
59
+ const inlineVer = name.match(/\s+—\s+(v\d+\.\d+(?:\.\d+)?)/);
60
+ let version = inlineVer ? inlineVer[1] : '';
61
+ if (inlineVer) {
62
+ name = name.slice(0, inlineVer.index).trim();
63
+ }
64
+ // If no inline version, look forward in the next ~30 lines for a
65
+ // `**Target version**:` block.
66
+ if (version === '') {
67
+ const after = md.slice(h.index, h.index + 2000);
68
+ const v = after.match(/\*\*Target version\*\*:\s*(v\d+\.\d+(?:\.\d+)?)/);
69
+ if (v) version = v[1];
70
+ }
71
+ phases.push({
72
+ number,
73
+ name,
74
+ version,
75
+ checkbox_status: statusMap.get(number) ?? 'unknown',
76
+ });
77
+ }
78
+ return phases;
79
+ }
80
+
81
+ module.exports = { readRoadmapMd, parsePhases };
@@ -0,0 +1,13 @@
1
+ // scripts/lib/roadmap-reader/index.d.cts — TypeScript ambient declarations
2
+ // for the roadmap-reader CJS module. Plan 27.7-02 — TS imports of CJS
3
+ // need .d.cts siblings (Phase 27.6 lesson).
4
+
5
+ export interface ParsedPhase {
6
+ number: string;
7
+ name: string;
8
+ version: string;
9
+ checkbox_status: 'shipped' | 'planned' | 'unknown';
10
+ }
11
+
12
+ export function readRoadmapMd(rootDir: string): Promise<string>;
13
+ export function parsePhases(md: string): ParsedPhase[];
@@ -0,0 +1,70 @@
1
+ 'use strict';
2
+ // scripts/lib/snapshot-reader/index.cjs — Plan 27.7-02
3
+ //
4
+ // Read latest snapshot under .design/snapshots/ (written by
5
+ // hooks/gdd-precompact-snapshot.js; see Phase 27.6 Plan 05).
6
+ //
7
+ // Surface:
8
+ // class SnapshotNotFoundError extends Error — code='directory_not_found'
9
+ // async readLatestSnapshot(rootDir) — { since, snapshot } | null
10
+ //
11
+ // `since` is the snapshot's embedded `timestamp` (ISO 8601), falling
12
+ // back to the file's mtime if absent.
13
+
14
+ const fs = require('node:fs');
15
+ const path = require('node:path');
16
+
17
+ class SnapshotNotFoundError extends Error {
18
+ constructor(dir) {
19
+ super('source directory not found: ' + dir);
20
+ this.name = 'SnapshotNotFoundError';
21
+ this.code = 'directory_not_found';
22
+ this.dir = dir;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * Read the newest *.json file under <rootDir>/.design/snapshots/.
28
+ * Returns `{ since, snapshot }` or `null` when the directory exists
29
+ * but contains no snapshot files. Throws SnapshotNotFoundError when
30
+ * the directory itself is missing.
31
+ */
32
+ async function readLatestSnapshot(rootDir) {
33
+ const dir = path.join(rootDir, '.design', 'snapshots');
34
+ if (!fs.existsSync(dir)) {
35
+ throw new SnapshotNotFoundError(dir);
36
+ }
37
+ const entries = await fs.promises.readdir(dir);
38
+ const candidates = [];
39
+ for (const name of entries) {
40
+ if (!name.endsWith('.json')) continue;
41
+ if (name === 'last-recap.json') continue;
42
+ const full = path.join(dir, name);
43
+ try {
44
+ const stat = await fs.promises.stat(full);
45
+ candidates.push({ full, mtime: stat.mtimeMs });
46
+ } catch {
47
+ // ignore — race with retention prune
48
+ }
49
+ }
50
+ if (candidates.length === 0) return null;
51
+ candidates.sort((a, b) => b.mtime - a.mtime);
52
+ const winner = candidates[0];
53
+ const body = await fs.promises.readFile(winner.full, 'utf8');
54
+ let snapshot;
55
+ try {
56
+ snapshot = JSON.parse(body);
57
+ } catch (err) {
58
+ // Malformed snapshot — surface a parseable error
59
+ throw new Error(
60
+ 'snapshot parse failed: ' + winner.full + ': ' + (err && err.message ? err.message : String(err)),
61
+ );
62
+ }
63
+ const since =
64
+ typeof snapshot.timestamp === 'string' && snapshot.timestamp.length > 0
65
+ ? snapshot.timestamp
66
+ : new Date(winner.mtime).toISOString();
67
+ return { since, snapshot };
68
+ }
69
+
70
+ module.exports = { readLatestSnapshot, SnapshotNotFoundError };
@@ -0,0 +1,28 @@
1
+ // scripts/lib/snapshot-reader/index.d.cts — TypeScript ambient declarations
2
+ // for the snapshot-reader CJS module. Plan 27.7-02.
3
+
4
+ export class SnapshotNotFoundError extends Error {
5
+ code: 'directory_not_found';
6
+ dir: string;
7
+ constructor(dir: string);
8
+ }
9
+
10
+ export interface SnapshotPayload {
11
+ /** Snapshot body — shape is JSON-loose; consumers project keys. */
12
+ schema_version?: string;
13
+ timestamp?: string;
14
+ cycle_id?: string;
15
+ state_md_sections?: unknown;
16
+ last_n_events?: unknown[];
17
+ last_n_decisions?: unknown[];
18
+ decisions_count?: number;
19
+ completed_plans_count?: number;
20
+ [k: string]: unknown;
21
+ }
22
+
23
+ export interface ReadSnapshotResult {
24
+ since: string;
25
+ snapshot: SnapshotPayload;
26
+ }
27
+
28
+ export function readLatestSnapshot(rootDir: string): Promise<ReadSnapshotResult | null>;
@@ -0,0 +1,66 @@
1
+ # gdd-mcp
2
+
3
+ Read-only MCP server exposing GDD project state — STATE.md sections, phases, decisions, plans, telemetry, intel slices, latest reflections — as 12 typed MCP tools. Sessions prime in 3 MCP calls instead of 100+ file reads, on the same project a per-session reduction of −30% tokens and ~34× wall-clock speedup (see benchmark below).
4
+
5
+ Phase 27.7. Mirror of the `scripts/mcp-servers/gdd-state/` Phase 20 pattern. v1 is read-only — mutation belongs to slash-skills and the lockfile-safe `gdd-state-mcp` writers (Phase 20 surface).
6
+
7
+ ## Tools
8
+
9
+ | Name | Input | Output (one-line summary) |
10
+ |------|-------|---------------------------|
11
+ | `gdd_status` | `{}` | Current cycle phase, branch, last-3 decisions, last-3 completed plans, blocker count |
12
+ | `gdd_phase_current` | `{}` | Current phase + stage + task_progress + status from STATE.md `<position>` |
13
+ | `gdd_phases_list` | `{}` | Phases from ROADMAP.md overview parse |
14
+ | `gdd_plans_list` | `{phase?}` | Plans for the current (or specified) phase from STATE.md `<plans>` |
15
+ | `gdd_decisions_list` | `{status?}` | D-XX decisions from STATE.md `<decisions>`, optionally filtered |
16
+ | `gdd_intel_get` | `{slice_id, shape?}` | Slice query against `.design/intel/` with optional key projection |
17
+ | `gdd_telemetry_query` | `{type?, since?, limit?}` | Typed reader over `.design/telemetry/*.jsonl` |
18
+ | `gdd_cycle_recap` | `{since_snapshot?}` | Diff vs the last Phase 27.6 snapshot (STATE sections + decisions + plans) |
19
+ | `gdd_reflections_latest` | `{}` | Latest cycle reflection excerpt from `.design/reflections/` (≤4 KB) |
20
+ | `gdd_learnings_digest` | `{cycles?}` | Compact lessons digest of recent reflections (≤5 KB) |
21
+ | `gdd_events_tail` | `{type?, limit?}` | Last N events from event-chain with optional type filter |
22
+ | `gdd_health` | `{}` | Read-only mirror of `skills/health/SKILL.md` checks payload |
23
+
24
+ All tools are thin wrappers (≤30 LOC) over `scripts/lib/*` helpers. The lint rule at `scripts/lib/mcp-tools-lint/` enforces no direct `fs.*`/`path.*` imports and zero write-tool names (`_create`, `_update`, `_delete`, `_append`, `_clear`, `_write`, `_set`). The 12-tool cap is hard (D-03) — adding a 13th requires a new plan.
25
+
26
+ Schemas live under `reference/schemas/mcp-gdd-tools.schema.json` (Draft-07). Tool sources are under `scripts/mcp-servers/gdd-mcp/tools/*.ts`.
27
+
28
+ ## Manual registration
29
+
30
+ ```bash
31
+ # Opt-in via installer (idempotent, detects Claude Code + Codex CLIs):
32
+ npx @hegemonart/get-design-done --register-mcp
33
+
34
+ # Or manual (Claude Code):
35
+ claude mcp add gdd-mcp -s user -- gdd-mcp
36
+
37
+ # Or manual (Codex):
38
+ codex mcp add gdd-mcp -- gdd-mcp
39
+ ```
40
+
41
+ Dismiss the gdd-health MCP-registration nudge by setting `.design/config.json` to `{"mcp_nudge": false}`.
42
+
43
+ ## When to prefer MCP vs file reads
44
+
45
+ | Scenario | Use MCP | Use file reads |
46
+ |----------|---------|----------------|
47
+ | Cold-boot priming (`/gdd:progress`, `/gdd:resume`, `/gdd:next`) | Yes — 3 calls, ~3 s, ~32k tokens | Fallback only when MCP unavailable |
48
+ | Mid-cycle context refresh in stage skills | Yes — 1–2 targeted calls | Fallback only |
49
+ | Editing STATE.md sections | No — use `mcp__gdd_state__*` (Phase 20) | N/A |
50
+ | Listing all skills | No — slash-skills + `scripts/list-skills.cjs` | N/A |
51
+ | Reading arbitrary untracked files | No | Yes — Read tool |
52
+
53
+ **Benchmark** (synthetic fixture at `test-fixture/baselines/phase-27-7/priming-benchmark.json`, modeled on Storybloq v1.2.0 measured numbers):
54
+
55
+ - MCP path: 3 calls, ~3 s, ~32k tokens
56
+ - File-read path: 5–10 reads, ~101 s, ~46.5k tokens
57
+ - Reduction: −31% tokens, ~34× wall-clock speedup
58
+
59
+ Per CONTEXT.md D-09 the benchmark is informational — failure to hit the −30% target surfaces as a Phase 27.7 success-criterion regression in closeout (Plan 27.7-07), NOT a CI hard-fail. After 5–10 real cycles, the synthetic fixture is replaced with measured GDD numbers (research-tail item).
60
+
61
+ ## See also
62
+
63
+ - `scripts/mcp-servers/gdd-state/` — Phase 20 STATE.md mutation MCP (write surface)
64
+ - `scripts/lib/mcp-tools-lint/` — Static analysis enforcing thin-wrapper discipline
65
+ - `reference/schemas/mcp-gdd-tools.schema.json` — Tool input/output schemas (Draft-07)
66
+ - `.planning/phases/27.7-gdd-mcp-server/CONTEXT.md` — Phase 27.7 decisions and rationale
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_cycle_recap",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "since_snapshot": { "type": "string" }
11
+ }
12
+ },
13
+ "output": {
14
+ "type": "object",
15
+ "required": ["since", "diff"],
16
+ "properties": {
17
+ "since": { "type": ["string", "null"] },
18
+ "diff": {
19
+ "type": "object",
20
+ "required": ["state_sections", "decisions_delta", "completed_plans_delta"],
21
+ "properties": {
22
+ "state_sections": { "type": "array", "items": { "type": "string" } },
23
+ "decisions_delta": { "type": "integer" },
24
+ "completed_plans_delta": { "type": "integer" }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,32 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_decisions_list",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "status": { "type": "string", "enum": ["locked", "tentative"] }
11
+ }
12
+ },
13
+ "output": {
14
+ "type": "object",
15
+ "required": ["decisions"],
16
+ "properties": {
17
+ "decisions": {
18
+ "type": "array",
19
+ "items": {
20
+ "type": "object",
21
+ "required": ["id", "text", "status"],
22
+ "properties": {
23
+ "id": { "type": "string" },
24
+ "text": { "type": "string" },
25
+ "status": { "type": "string" }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
31
+ }
32
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_events_tail",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "type": { "type": "string" },
11
+ "limit": { "type": "integer", "minimum": 1, "maximum": 10000 }
12
+ }
13
+ },
14
+ "output": {
15
+ "type": "object",
16
+ "required": ["events"],
17
+ "properties": {
18
+ "events": { "type": "array", "items": { "type": "object" } }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_health",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {}
10
+ },
11
+ "output": {
12
+ "type": "object",
13
+ "required": ["checks"],
14
+ "properties": {
15
+ "checks": {
16
+ "type": "array",
17
+ "items": {
18
+ "type": "object",
19
+ "required": ["name", "status", "detail"],
20
+ "properties": {
21
+ "name": { "type": "string" },
22
+ "status": { "type": "string", "enum": ["ok", "warn", "fail"] },
23
+ "detail": { "type": "string" }
24
+ }
25
+ }
26
+ }
27
+ }
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_intel_get",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "required": ["slice_id"],
10
+ "properties": {
11
+ "slice_id": { "type": "string", "minLength": 1 },
12
+ "shape": { "type": "array", "items": { "type": "string" } }
13
+ }
14
+ },
15
+ "output": {
16
+ "type": "object",
17
+ "required": ["slice_id", "data"],
18
+ "properties": {
19
+ "slice_id": { "type": "string" },
20
+ "data": { "type": "object" }
21
+ }
22
+ }
23
+ }
24
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_learnings_digest",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {
10
+ "cycles": { "type": "integer", "minimum": 1, "maximum": 50 }
11
+ }
12
+ },
13
+ "output": {
14
+ "type": "object",
15
+ "required": ["digest", "cycles_included"],
16
+ "properties": {
17
+ "digest": { "type": "string", "maxLength": 5120 },
18
+ "cycles_included": { "type": "integer", "minimum": 0 }
19
+ }
20
+ }
21
+ }
22
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "gdd_phase_current",
4
+ "type": "object",
5
+ "properties": {
6
+ "input": {
7
+ "type": "object",
8
+ "additionalProperties": false,
9
+ "properties": {}
10
+ },
11
+ "output": {
12
+ "type": "object",
13
+ "required": ["phase", "stage", "task_progress", "status"],
14
+ "properties": {
15
+ "phase": { "type": ["string", "null"] },
16
+ "stage": { "type": ["string", "null"] },
17
+ "task_progress": { "type": ["string", "null"] },
18
+ "status": { "type": ["string", "null"] }
19
+ }
20
+ }
21
+ }
22
+ }