Skip to content

feat(mcp-setup): extend client detection to 6 clients + monorepo dev workflow + GUI-PATH fix#394

Open
Jurij89 wants to merge 9 commits intochore/v10-mcp-consolidationfrom
feat/mcp-setup-extend-clients-monorepo
Open

feat(mcp-setup): extend client detection to 6 clients + monorepo dev workflow + GUI-PATH fix#394
Jurij89 wants to merge 9 commits intochore/v10-mcp-consolidationfrom
feat/mcp-setup-extend-clients-monorepo

Conversation

@Jurij89
Copy link
Copy Markdown
Contributor

@Jurij89 Jurij89 commented May 5, 2026

Summary

  • Adds 4 new auto-detected MCP clients to dkg mcp setup — Claude Desktop (per-platform: macOS ~/Library/Application Support/Claude/..., Windows %APPDATA%\Claude\..., Linux ~/.config/Claude/...), Windsurf (~/.codeium/windsurf/mcp_config.json), VSCode + GitHub Copilot Chat (per-platform Code user-settings dir + mcp.json; uses servers.dkg shape), and Cline (deep-nested under VSCode's per-extension globalStorage). The auto-detect set goes from 2 (Cursor + Claude Code) to 6 clients. Continue (YAML) and Codex CLI (TOML) are deferred to a follow-up — phase-1's architecture supports both formats but their config shapes need a live-install verify pass.
  • Adds a contributor monorepo-dev workflow. When dkg mcp setup runs from inside a dkg-v9 checkout, it auto-detects the workspace via findDkgMonorepoRoot() and writes a different entry that points at the local build ({ command: "node", args: ["<repo>/packages/cli/dist/cli.js", "mcp", "serve"] }) instead of the globally-installed dkg. Two mutually-exclusive overrides — --installed (force the published-CLI form even from a monorepo cwd, for dogfooding) and --monorepo (force the local-build form, errors if no monorepo root is locatable).
  • Fixes MCP dkg: spawn dkg ENOENT for GUI MCP clients (F30). Claude Desktop and other GUI-spawned MCP clients don't inherit the shell PATH where npm install -g writes the dkg bin, so the bare-"dkg" form crashed at spawn time. dkg mcp setup now resolves the absolute bin path via which dkg (POSIX) / where.exe dkg (Windows) at registration time and writes that absolute path into the client config — bypassing PATH inheritance entirely. Best-effort: a null resolution falls back silently to the bare-"dkg" form, so setup never crashes on a which-missing or non-globally-installed CLI.

Related


Files changed (by area)

The 9-commit history walks phases 1 → 7. The groupings below regroup by logical scope so reviewers can read the changes without committing to chronological order.

Phase 1 — ClientTarget architecture refactor

Commit 62a8bcab (refactor(cli): generalise mcp-setup ClientTarget with format + entryPath dispatch).

  • packages/cli/src/mcp-setup.tsClientTarget interface gains two optional fields:
    • format?: 'json' | 'toml' | 'yaml' (defaults to 'json') — per-client config-file format. Phase 1 only wires 'json'; the 'toml' and 'yaml' branches are guards that throw a clean error so phase-1 commits don't silently pass for clients that need them. Codex CLI's TOML format and Continue's YAML format land via this hook in deferred follow-ups.
    • entryPath?: string (defaults to 'mcpServers.dkg') — dot-path to the entry under the config root. Lets VSCode + Copilot's servers.dkg shape register without per-client write logic.
  • readConfigBody / writeConfigBody dispatch helpers added so the per-format branching is one site instead of being repeated across classify / register / refresh paths.

Phase 2 — Monorepo context detection + flag overrides

Commit 508fced7 (feat(cli): mcp-setup monorepo context detection + --installed/--monorepo flags).

  • packages/cli/src/mcp-setup.tsdetectContext() walks ancestors from the CLI's compiled location via findDkgMonorepoRoot() (re-exported from @origintrail-official/dkg-core). A hit returns { context: 'monorepo', monorepoRoot }; a miss returns { context: 'installed', monorepoRoot: null }. canonicalEntry(context, monorepoRoot, resolvedBin) takes the context-tagged result and emits the right shape:
    • installed: { command: "<resolved-bin-or-dkg>", args: ["mcp", "serve"] }
    • monorepo: { command: "node", args: ["<root>/packages/cli/dist/cli.js", "mcp", "serve"] }
  • --installed / --monorepo flags wired in cli.ts and validated in mcpSetupAction as mutually exclusive (commander's option() doesn't enforce exclusivity, so we throw at runtime if both are passed).
  • --monorepo errors if findDkgMonorepoRoot() returns null — the user explicitly asked for monorepo mode, so we fail loudly rather than silently fall back.

Phase 3 — Claude Desktop + Windsurf

Commit 6cd3a1e5 (feat(cli): mcp-setup detects Claude Desktop + Windsurf).

  • packages/cli/src/mcp-setup.tsclaudeDesktopPaths(home) returns the per-platform path: macOS ~/Library/Application Support/Claude/claude_desktop_config.json, Windows %APPDATA%\Claude\claude_desktop_config.json, Linux ~/.config/Claude/claude_desktop_config.json. Both clients use the canonical mcpServers.dkg shape, so no entryPath override.

Phase 4 — VSCode + GitHub Copilot Chat

Commit 17944d86 (feat(cli): mcp-setup detects VSCode + Copilot Chat).

  • packages/cli/src/mcp-setup.tsvscodeMcpPaths(home) returns the per-platform Code user-settings dir + mcp.json. entryPath: 'servers.dkg' override because Copilot Chat keys MCP servers under servers, not the canonical mcpServers. The phase-1 dispatch helpers route the write to the right key without per-client logic.
  • This is the only client in the auto-detect set with a non-mcpServers.dkg entry path. Reviewers comparing JSON shapes across clients will spot the asymmetry; it's intentional and documented inline.

Phase 5 — Cline

Commit 3afba8a7 (feat(cli): mcp-setup detects Cline at VSCode globalStorage).

  • packages/cli/src/mcp-setup.tsclineMcpPaths(home) returns the per-platform Code user-settings dir + the Cline-extension-specific suffix: globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json. Path mirrors vscodeMcpPaths for the Code-user-data root with the per-extension globalStorage append. Uses canonical mcpServers.dkg shape (only the path is unusual; the entry shape is standard).

Phase 6 — JSDoc refresh + READMEs

Commits eb698b2c (docs(cli): refresh mcp-setup JSDoc header for post-phase-5 surface), 2298d22f (docs(readme): expand client list to 6 + add monorepo dev workflow (Part 6)), dde551f7 (docs(mcp-dkg): mirror client-list expansion + monorepo dev workflow (Part 6)).

  • packages/cli/src/mcp-setup.ts — file-level JSDoc rewritten to enumerate the 6 detection targets, the monorepo-vs-installed dispatch, and the --installed / --monorepo flag semantics. Authoritative reference for any future contributor adding a 7th client.
  • README.md (+38 / -7) — 7 stale sites updated (routing table, MCP section opener, bundled-flow step 4, round-trip step 4, troubleshooting "no clients detected", CLI cheat-sheet, Setup-guides table) plus a new "Contributor (monorepo dev) workflow" subsection covering the auto-detect, the rebuild prereq, the flag overrides, and the moved-checkout --force caveat.
  • packages/mcp-dkg/README.md (+31 / -10) — opening sentence's bolded client list expanded; bundled-flow step 4 carries the full per-platform path list with the VSCode + Copilot servers.dkg shape carve-out; Manual config block expanded from 3 generic bullets to 7 explicit per-client entries; Troubleshooting bullet enumerates the actual 6; "Contributor (monorepo dev) workflow" subsection replaces the prior pnpm exec tsx workspace-relative form (which was a pre-W6-pre workaround).

Phase 7 — F30 absolute-bin-path resolution

Commit f22b3877 (fix(cli): resolve absolute dkg bin path on mcp setup to fix GUI-client PATH inheritance (F30)).

  • packages/cli/src/mcp-setup.ts — exported resolveDkgBin() shells out to which dkg (POSIX) / where.exe dkg (Windows), takes the first match (Windows can return multiple hits when shadowed across PATH; first-wins matches shell behaviour), and returns the absolute path. Returns null on any failure (dkg not on PATH, exec error, empty output) so callers fall back to the bare-"dkg" form silently. stdio: ['ignore', 'pipe', 'ignore'] keeps which's stderr out of the operator-facing setup log.
  • canonicalEntry(context, monorepoRoot, resolvedBin) — third parameter; when installed context and resolvedBin is non-null, command is the absolute path. Bare-"dkg" falls back when resolution fails, so setup never crashes.
  • mcpSetupAction — calls deps.resolveDkgBin() only in installed context (monorepo mode hard-codes the local CLI dist absolute path; no resolution needed). Tests inject their own stub.
  • cli.ts (+6 / -…) — wires resolveDkgBin through mcpSetupAction's deps surface in production.

Test fixtures (full coverage of the new surface)

Commit 62a8bcab (phase 1 baseline) plus per-phase additions across 508fced7, 6cd3a1e5, 17944d86, 3afba8a7, f22b3877.

  • packages/cli/test/mcp-setup.test.ts (+552 / -1) — extends the branch-base 13-test surface (W6-pre e78d8027 baseline 9 + F14 ab380b63 +2 + F26 polish +2, settling at the PR feat(mcp): V10 MCP consolidation — single keeper, 21-tool surface, 2-command setup #381 branch-tip fc2b375d with 13) to 34 tests at f22b3877 (intermediate datapoint: 28 at dde551f7 after phases 1–5 docs-mirror; +6 F30 cases land in phase 7). Coverage:
    • ClientTarget format + entryPath dispatch helpers (phase 1)
    • Monorepo context detection: clean-machine, monorepo cwd, --installed from monorepo, --monorepo from non-monorepo (errors), --installed + --monorepo together (rejects) (phase 2)
    • Per-client path resolution per platform (phase 3 / 4 / 5)
    • F30 bin-path resolution: which-success, which-not-found (falls back to bare dkg), Windows where.exe multi-line output (phase 7)
    • The format: 'yaml' / format: 'toml' clean-throw guards (phase 1 — verify the deferred-client posture)

Test plan

Co-authored by qa-engineer from two verification passes against the branch — initial brief verification at dde551f7 (post-Part-6 docs mirror) and F30-specific re-check at f22b3877 (post-phase-7).

Pre-merge gates — all green

  • Build green (turbo full + cli filter). pnpm run build → 19/19 turbo tasks; pnpm --filter @origintrail-official/dkg build → tsc + network/json copy steps clean. Phase 1's ClientTarget architecture refactor + phase 7's F30 changes don't break the umbrella CLI's tsc step.
  • packages/cli/test/mcp-setup.test.ts 34/34 passing at f22b3877 (was 28 at dde551f7; was 11 at the W6-pre baseline e78d8027). Distribution: phase 1 dispatch + phase 2 monorepo detection + phase 3 Claude Desktop + Windsurf + phase 4 VSCode + Copilot Chat (incl. servers.dkg asymmetry) + phase 5 Cline globalStorage + phase 7 F30 bin resolution all covered. Test file diff: +552 lines net.
  • dkg mcp setup --print-only byte-correct in monorepo mode. Run from inside the dkg-v9 checkout: output emits { "command": "node", "args": ["C:\\Projects\\dkg-v9\\packages\\cli\\dist\\cli.js", "mcp", "serve"] }. Absolute path to cli.js (not global dkg); confirms findDkgMonorepoRoot() correctly identifies the monorepo context.
  • dkg mcp setup --print-only --installed byte-correct in installed mode (F30 fix verified live on Windows). Output emits the resolved absolute Windows path: { "command": "C:\\Users\\jurij\\AppData\\Local\\fnm_multishells\\38776_1777447566844\\dkg", "args": ["mcp", "serve"] }. Matches which dkg output exactly. NOT the bare "dkg" string anymore — this is the F30 fix smoke. The fnm-shell-managed bin path resolves correctly on Windows under the npm-version-manager indirection layer (nvm-windows users would hit the same code path).
  • --installed override smoke from monorepo cwd — same probe as above forces installed-mode entry from inside the monorepo checkout; --installed flag correctly overrides the auto-detected monorepo context, escape hatch works as designed.
  • README ↔ source byte-aligned for the 6-client detection set. Both repo-root README.md and packages/mcp-dkg/README.md document Cursor, Claude Code, Claude Desktop, Windsurf, VSCode + Copilot Chat, Cline. mcp-setup.ts:406-437 detectClients() source enumeration matches: same 6 names. Deferred clients (Continue YAML, Codex CLI TOML) explicitly called out in both READMEs as not-auto-detected with manual-paste fallback. No drift.
  • VSCode + Copilot servers.dkg vs mcpServers.dkg asymmetry covered. mcp-setup.test.ts:600-728 has 3 phase-4 cases pinning the asymmetry: (a) detected + writes under servers.dkg, (b) sibling preservation under servers.<other> on merge, (c) staleness reclassification on installed→monorepo flip (servers.dkg.command flips from dkg to node). Per-client serializer dispatch correctly handles the only client that doesn't use the canonical mcpServers.dkg shape.

F30 re-check verification (post-phase-7 at f22b3877)

Independent confirmation on Windows via the qa checkout, providing the second cross-platform datapoint after mcp-lead's local Windows smoke. Live results captured above in checkboxes #4 and #5; no regressions on the monorepo-auto-detect path (#3 unchanged across pre/post-F30).

Pre-existing failures noted (NOT introduced by this PR)

Release-readiness gates explicitly deferred

  • Live npm install -g @origintrail-official/dkg against the production registry, then dkg mcp setup in Claude Desktop / Windsurf / VSCode / Cline / Cursor / Claude Code on macOS / Windows / Linux. Combinatorial; runs as part of the broader release cycle, not pre-merge here.
  • Cross-platform path-resolution smoke (Mac + Linux equivalents to the Windows F30 smoke at the consensus is hardcoded #4) — release-readiness step. The unit tests at mcp-setup.test.ts stub homedir() and platform() so per-platform path resolution is covered at the contract level; the real-OS smoke catches OS-version-specific edge cases.

QA sign-off

qa-engineer signs off the Test-plan section based on both verification passes. Pre-merge gates green at f22b3877. Release-readiness gates deferred and documented; they do not block PR-open.


Migration notes

For users with existing dkg MCP entries written by an older / pre-F30 setup:

  • Bare-"dkg" entries always stay registered. A pre-existing bare-"dkg" entry is treated as equivalent to whatever absolute path resolution produces (or to bare-"dkg" itself when resolution fails) — both invoke the currently-installed bin, so the classify logic at mcp-setup.ts:618-625 makes them register-equivalent regardless of resolution outcome. Pre-F30 entries from older setup runs do NOT trigger a spurious refresh on a routine dkg mcp setup re-run.
  • F30 fix kicks in on --force re-run. Pre-F30 users with bare-"dkg" entries who want the GUI-friendly absolute-path form have two options: pass --force to refresh all detected clients (writes the absolute path on every entry), or manually delete the existing entry (the next un-forced setup writes the absolute-path form against the now-not-registered slot). Without --force, re-runs are no-ops by design — the bare-form is treated as equivalent.
  • Real divergence (different absolute paths) IS reclassified as stale. A pre-existing entry that pins one absolute path while the currently-resolved bin lives at a different absolute path (e.g. an old /Users/me/.npm-global/dkg after a Node-version-manager rotation, vs the current /opt/homebrew/bin/dkg) is genuine drift, classifies as stale, and gets refreshed without --force. Bare-form vs absolute-form is not in this category — only absolute-vs-different-absolute is.
  • Cross-platform diagnostic. If dkg mcp setup continues writing the bare-"dkg" form, check which dkg (POSIX) or where.exe dkg (Windows). An empty output means the bin isn't on PATH; the bare-"dkg" fallback is correct in that case but won't help GUI MCP clients (Claude Desktop, etc.) that don't inherit the shell PATH. Either install the umbrella CLI globally so the resolver finds it, or manually edit the client config to point at the absolute path you want.
  • No action required for users on any of the 6 detected clients today — the bare-"dkg" ↔ resolved-absolute-path equivalence is uniform across all of them, so existing entries continue working unchanged. In practice the absolute-path form only matters for GUI-spawned clients (Claude Desktop, etc.) that don't inherit the shell PATH; CLI-spawned clients (Cursor, Claude Code) work either way. --force is the deliberate opt-in to upgrade legacy entries to the GUI-friendly absolute-path form.

Deferred (post-this-PR follow-ups)

Two clients deferred per the plan's defer-rule: their detection paths are speculative until verified against a live install, and their config formats need a worked-shape spec before we wire them up.

  • Continue — YAML config + uncertain in-YAML entry shape. Phase 1's architecture supports the format via ClientTarget.format = 'yaml'; a clean-throw guard is in place so a future commit only needs to add the YAML write helper + the live-install-verified path/entry-shape. No architectural blocker.
  • Codex CLI — TOML config requires @iarna/toml (or equivalent) workspace dep, which deserves a separate commit so the dependency boundary is reviewable. Phase 1's ClientTarget.format = 'toml' clean-throw guard is in place; same posture as Continue.

Both follow-ups inherit the phase-1 architecture and don't require any further refactor — only the per-format writer + the per-client path/entry-shape.


🤖 Generated with Claude Code

}
return { context: 'monorepo', monorepoRoot: root };
}
const root = findRoot();
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: findDkgMonorepoRoot() is called without a start directory, so it walks up from the installed @origintrail-official/dkg-core package location rather than from the user's current checkout. A globally-installed dkg run inside dkg-v9 will therefore stay in installed mode and never pick up the local build, which breaks the new contributor workflow. Please plumb process.cwd() (or another explicit invocation dir) into the lookup.

): Record<string, unknown> {
if (context === 'monorepo' && monorepoRoot) {
return {
command: 'node',
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: monorepo mode hard-codes command: "node", but this PR's own installed-mode change exists because GUI MCP clients often do not inherit the shell PATH. In that environment the generated monorepo entry will still fail with spawn node ENOENT. Use an absolute Node executable such as process.execPath when writing this form.

if (context === 'monorepo' && monorepoRoot) {
return {
command: 'node',
args: [join(monorepoRoot, 'packages', 'cli', 'dist', 'cli.js'), 'mcp', 'serve'],
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Bug: this always points clients at packages/cli/dist/cli.js without checking that the checkout has actually been built. On a fresh or cleaned monorepo checkout, dkg mcp setup will overwrite a working installed registration with a path every client fails to spawn. Validate the built CLI exists before selecting monorepo mode, and fail with a rebuild hint or fall back to installed mode.

Comment thread packages/cli/src/cli.ts
.option('--force', 'Refresh every detected client regardless of current registration state')
.option('--print-only', 'Print the canonical JSON to stdout; skip every other step')
.option('--yes', 'Auto-confirm all registrations (default; reserved for future interactive prompts)')
.option('--installed', 'Force the installed-mode command form ({ command: "dkg", args: ["mcp", "serve"] }) even when invoked from inside a monorepo dev checkout. Mutually exclusive with --monorepo.')
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Issue: this help text no longer matches the implementation. In installed mode mcp-setup.ts now prefers resolveDkgBin() and will usually emit an absolute path, not the literal { command: "dkg", ... } shape promised here. Please update the flag description (and matching docs) so operators know what config they will actually get.

Jurij Skornik and others added 9 commits May 6, 2026 17:52
…ath dispatch (phase 1 of follow-up)

Phase 1 of the follow-up plan at
`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` (§Part 3).
Architecture refactor only — no behaviour change for the existing
two clients (Cursor + Claude Code). Subsequent phases extend
`detectClients()` with the 6 new clients (Claude Desktop, VSCode +
Copilot, Windsurf, Continue, Cline, Codex CLI) and the monorepo
context detection — all of which need this generalised dispatch
to land safely.

This commit:

- `ClientTarget` interface gains two optional fields:
  - `format?: 'json' | 'toml' | 'yaml'` — defaults to 'json'
  - `entryPath?: string` — defaults to 'mcpServers.dkg'
  Existing entries don't need to declare either; their behaviour is
  byte-identical post-refactor.

- New helpers:
  - `splitEntryPath` — split a dotted path ('mcpServers.dkg' /
    'servers.dkg' / 'mcp_servers.dkg') into head segments + leaf.
  - `ensurePathContainer` — write-side traversal that lazy-creates
    intermediate `Record` containers.
  - `readEntryAt` — read-side traversal that returns `undefined` for
    any missing intermediate.
  - `readConfigBody` / `writeConfigBody` — per-format dispatch shape
    (JSON wired today; TOML / YAML branches throw clean
    "land phase 4/5 first" errors so a target declaring those
    formats trips fast at registration time, not at runtime).

- `classify(target)` and `writeRegistration(target)` refactored to
  use the helpers. JSON output preserves the pre-refactor formatting
  (2-space indent, trailing newline) byte-for-byte.

- `mcp-setup.test.ts:324` — pre-existing post-F14+F26 test
  (`faucet failure logs manual instructions`) now stubs `fetch` so
  the daemon-reachability probe succeeds and the throwing-faucet
  mock is actually reached. Without this stub the funding step
  short-circuited on the unreachable-path log line and the test
  asserted on a code path that never ran. Confirmed pre-existing
  by running `vitest mcp-setup.test.ts` against `fc2b375d` (the
  branch base, pre-phase-1) — same single failure.

Verification:
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts integrations.test.ts` → 54/54 ✓
- `pnpm --filter @origintrail-official/dkg build` → clean

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…epo flags (phase 2 of follow-up)

Phase 2 of the follow-up plan
(`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` §Part 1).
Adds setup-context detection so contributors running `dkg mcp setup`
from inside a dkg-v9 dev checkout get an absolute-path command form
that targets THEIR local CLI dist, not a stale globally-installed
`dkg`. npm-installed CLIs continue to write the standard
`{ command: "dkg", args: ["mcp", "serve"] }` shape.

Reuses `findDkgMonorepoRoot()` from `@origintrail-official/dkg-core`
byte-for-byte — same primitive `resolveDkgConfigHome()` already
uses to switch `~/.dkg` ↔ `~/.dkg-dev`. Injectable via the deps
surface so tests can stub without touching the real filesystem.

This commit:

- `packages/cli/src/mcp-setup.ts`:
  - New `SetupContext` type ('installed' | 'monorepo') + new
    `detectContext(findRoot, { force? })` helper.
  - `canonicalEntry(context, monorepoRoot)` is now context-aware:
    monorepo + a real root produces
    `{ command: "node", args: ["<repo>/packages/cli/dist/cli.js",
    "mcp", "serve"] }`; installed (or monorepo with no root, which
    can only happen on the force path and surfaces a clear error)
    produces the standard `{ command: "dkg", args: ["mcp", "serve"] }`.
  - `classify(target, expectedEntry)` and
    `writeRegistration(target, entry)` now take the context-aware
    expected entry as a parameter so staleness comparisons compare
    apples-to-apples (an installed-form entry in a monorepo
    invocation classifies as `stale`, gets refreshed under
    `--force` to the local-bin form).
  - `mcpSetupAction` validates the `--installed` / `--monorepo`
    mutual-exclusion at the boundary and threads the resolved
    context through every downstream call site.
  - `McpSetupActionDeps` gains `findDkgMonorepoRoot` for testability.

- `packages/cli/src/cli.ts`:
  - Two new commander flags on `dkg mcp setup`:
    `--installed` (force installed-mode form even from inside a
    monorepo cwd; escape hatch for testing the published-CLI shape)
    and `--monorepo` (force monorepo-mode form; errors if no
    monorepo root locatable). Mutually exclusive.
  - `findDkgMonorepoRoot` now passed into `mcpSetupAction`'s deps.

- `packages/cli/test/mcp-setup.test.ts`:
  - `makeDeps()` extended with a stub `findDkgMonorepoRoot` that
    defaults to returning `null` (installed-mode); tests that
    exercise the monorepo path override.
  - 6 new phase-2 tests:
    * monorepo auto-detect → local-cli-dist absolute-path form
    * no monorepo detected → standard `dkg` installed form
    * `--installed` from inside a monorepo forces standard form
    * `--monorepo` from outside any monorepo throws the canonical error
    * `--installed` + `--monorepo` together throws mutual-exclusion error
    * Pre-existing installed-form entry classifies as `stale` when
      run in monorepo mode, gets rewritten to monorepo form
  - New `parseStdoutJson` helper extracts the JSON object emitted
    by `--print-only` from the spied writes (vitest's progress
    reporter can interleave non-JSON writes; first-`{` to last-`}`
    is the tight bracket).

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts integrations.test.ts` → 60/60 ✓ (was 54; +6 phase-2)
- `dkg mcp setup --help` shows both new flags
- Live smoke from inside this monorepo: `node packages/cli/dist/cli.js mcp setup --print-only`
  emits the local-cli-dist `node /abs/path/cli.js mcp serve` form. Confirmed
  monorepo auto-detect works end-to-end.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…llow-up)

Phase 3 of the follow-up plan
(`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` §Part 2,
"Easy clients" tier). Both new clients use the canonical
`mcpServers.dkg` JSON shape, so only `detectClients()` needs to
extend — the phase-1 dispatch handles writes uniformly.

This commit:

- `packages/cli/src/mcp-setup.ts`:
  - New `claudeDesktopPaths(home)` resolver. macOS:
    `~/Library/Application Support/Claude/claude_desktop_config.json`;
    Windows: `%APPDATA%\Claude\claude_desktop_config.json` (with a
    `~/AppData/Roaming` fallback when APPDATA isn't set); Linux +
    other: `~/.config/Claude/claude_desktop_config.json`. Per-platform
    `displayPath` tildifies the home prefix so the operator log
    reads consistently across platforms.
  - `detectClients()` extended with two new candidates: Claude
    Desktop (per-platform via the resolver above) and Windsurf
    (`~/.codeium/windsurf/mcp_config.json`). Detection-permissive
    heuristic unchanged: parent-dir existence is sufficient.

- `packages/cli/test/mcp-setup.test.ts`:
  - `beforeEach` / `afterEach` extended with `process.env.APPDATA`
    override so the Claude Desktop Win32 path resolves inside the
    `tmpHome` sandbox. macOS + Linux ignore APPDATA so this is a
    no-op there.
  - 4 new phase-3 tests:
    * Claude Desktop detected when its config dir exists; canonical
      entry written at the per-platform path
    * Windsurf detected at `~/.codeium/windsurf/`; canonical entry
      written
    * Clients without their config dir are not detected — test
      pre-creates only `.cursor/` and asserts Claude Desktop +
      Windsurf paths stay absent
    * Pre-existing Claude Desktop entry on a sibling key is
      preserved on merge (real-world shape: a user has other MCP
      servers; setup must not clobber)

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts integrations.test.ts` → 64/64 ✓ (was 60; +4 phase-3)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-up)

Phase 4 of the follow-up plan
(`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` §Part 2,
"Medium clients" tier). Ships VSCode + Copilot Chat in this commit.

**Continue deferred to a follow-up PR per the plan's defer rule.**
The plan calls out Continue's `~/.continue/config.yaml` (newer) /
`config.json` (legacy) detection plus uncertainty about the
in-YAML MCP entry shape ("if shape diverges, add a per-client
adapter"). Two open questions there:
  1. The exact in-YAML shape Continue accepts isn't pinned by
     the standard MCP spec and would need verification against
     a live Continue install.
  2. We don't yet have a YAML serializer wired (phase 1's
     `writeConfigBody` throws "phase 4 first" for `format:
     'yaml'`). Adding `js-yaml` is on-pattern but pulls in a
     test-shape question (YAML round-trip equivalence) that's
     out of scope for the "medium clients" tier as the plan
     framed it.
The architecture from phase 1 makes Continue cheap to add
incrementally once both questions are resolved.

VSCode + Copilot Chat is the load-bearing phase-4 add: it's the
first non-`mcpServers.dkg`-shape client (entries key under
`servers.dkg`), so it actually exercises the phase-1 entryPath
dispatch end-to-end.

This commit:

- `packages/cli/src/mcp-setup.ts`:
  - New `vscodeMcpPaths(home)` resolver. macOS:
    `~/Library/Application Support/Code/User/mcp.json`; Windows:
    `%APPDATA%\Code\User\mcp.json`; Linux:
    `~/.config/Code/User/mcp.json`. User-scoped (cross-workspace),
    not the per-workspace `.vscode/mcp.json`.
  - `detectClients()` extended with the VSCode candidate. Uses
    `entryPath: 'servers.dkg'` instead of the canonical default —
    Copilot Chat keys under `servers`, not `mcpServers`. Phase-1
    entryPath dispatch handles this without per-client write logic.

- `packages/cli/test/mcp-setup.test.ts`:
  - 3 new phase-4 tests:
    * VSCode detected; canonical entry written under `servers.dkg`,
      not `mcpServers.dkg` — pins the alternate-shape contract.
    * Pre-existing VSCode `servers.<other>` siblings preserved on
      merge (real-world shape: a user has other MCPs already wired).
    * VSCode staleness across context flip — pre-existing
      installed-form `servers.dkg` reclassifies as `stale` when run
      in monorepo mode, gets rewritten to the local-cli-dist form.
      Pins that the cross-shape staleness comparison works.

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts` → 26/26 ✓ (was 23; +3 phase-4)
- `pnpm --filter @origintrail-official/dkg exec vitest run integrations.test.ts` → 41/41 ✓

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…f follow-up, partial)

Phase 5 of the follow-up plan
(`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` §Part 2,
"Hard clients" tier). Ships Cline; **defers Codex CLI to a
follow-up PR per the plan's defer rule.**

Codex CLI deferred because:
  1. Adding `@iarna/toml` (or any TOML serializer) is a workspace-
     level dependency change that warrants its own commit/PR for
     review focus on the dependency choice + transitive surface.
  2. TOML round-trip equivalence testing (parse → write → parse
     → assert structural equality) is a meaningfully different
     test shape than the JSON-roundtrip pattern this PR's other
     fixtures use.
  3. The `format: 'toml'` dispatch branch in phase-1's
     `readConfigBody` / `writeConfigBody` already throws a clean
     "land phase 5 first" error if a `format: 'toml'` candidate
     ever gets added pre-implementation, so deferring won't
     silently misbehave.
The architecture from phase 1 makes Codex CLI a pure additive
change once the TOML dep lands.

Cline ships now: it's the canonical `mcpServers.dkg` JSON shape,
deeply nested under VSCode's per-extension globalStorage path
(`<userDataDir>/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`).
The phase-1 dispatch already handles JSON; the new work is a
per-platform path resolver that mirrors `vscodeMcpPaths` for
the Code-user-data root, with the per-extension suffix appended.

This commit:

- `packages/cli/src/mcp-setup.ts`:
  - New `clineMcpPaths(home)` resolver. Same Mac / Win32 / Linux
    triad as Claude Desktop / VSCode but with the deeply-nested
    `globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json`
    suffix appended.
  - `detectClients()` extended with the Cline candidate. Uses an
    IIFE to bind the resolver result without polluting the outer
    scope (single-use; would otherwise force three locals for one
    candidate). Default `entryPath` (`mcpServers.dkg`) wins, no
    override needed.

- `packages/cli/test/mcp-setup.test.ts`:
  - 2 new phase-5 tests:
    * Cline detected at the per-platform globalStorage path;
      canonical entry written under `mcpServers.dkg`.
    * Cline siblings preserved — pre-existing `mcpServers.<other>`
      entries (e.g. a `github` MCP) survive the merge unchanged.

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts` → 28/28 ✓ (was 26; +2 phase-5)
- Live smoke: `node packages/cli/dist/cli.js mcp setup --print-only --installed`
  emits the standard `dkg` form even from inside this monorepo.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…hase 6)

Phase 6 of the follow-up plan
(`C:\Users\jurij\.claude\plans\ethereal-dazzling-scott.md` §Part 6).
Code-comment / JSDoc updates only — docs-lead handles the repo-root
README + mcp-dkg README in their parallel cycle.

The pre-phase-6 module header at `mcp-setup.ts:1-39` was written
when only Cursor + Claude Code were detected and the canonical
entry was hardcoded `{ command: "dkg", args: ["mcp", "serve"] }`.
Phases 1-5 added six more clients (one deferred), per-client
format / entryPath dispatch, monorepo context detection, and two
new CLI flags. The header now describes that surface end-to-end:

- Step 4 of the bundled flow now enumerates all 6 detected
  clients (Cursor, Claude Code, Claude Desktop, Windsurf,
  VSCode + Copilot Chat, Cline) plus the deferred ones
  (Continue, Codex CLI) with a pointer to the phase-4 / phase-5
  commit bodies for the defer rationale.
- New "Context-awareness (phase 2)" paragraph documents the
  installed-vs-monorepo command form selection + the
  `--installed` / `--monorepo` overrides.
- New "Per-client format / entry-shape dispatch (phase 1)"
  paragraph explains why VSCode keys under `servers.dkg` while
  the rest use canonical `mcpServers.dkg`, and how phase-1's
  ClientTarget fields handle that without per-client write logic.
- Flags block extended with `--installed` + `--monorepo`.

JSDoc-only; zero runtime behaviour change.

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts` → 28/28 ✓

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rt 6)

mcp-lead's `feat/mcp-setup-extend-clients-monorepo` phases 1-6 ship
detection for Cursor, Claude Code, Claude Desktop, Windsurf,
VSCode + GitHub Copilot Chat, and Cline. Continue and Codex CLI are
deferred to a follow-up (Continue's YAML shape needs a live install
verify; Codex CLI's TOML format requires a workspace dep). README
references to "Cursor, Claude Code, Continue, or Cline" were stale
before phase 1 and triply wrong after — updated all five sites:

- Quick Start routing table (line 70)
- MCP section opener (line 81)
- Bundled flow step 4 (line 93) — full per-platform path list with
  the VSCode `servers.dkg` shape carve-out
- Round-trip step 4 (line 121)
- Troubleshooting "no clients detected" (line 132) — Continue +
  Codex CLI carved out as `--print-only` paste-manually candidates
- CLI cheat-sheet (line 306)
- Setup-guides table (line 347)

New "Contributor (monorepo dev) workflow" subsection under MCP
Setup. Covers `findDkgMonorepoRoot()` auto-detect, the local
`node /abs/path/cli.js mcp serve` written form, the rebuild prereq
(`pnpm --filter @origintrail-official/dkg build`), and the two
flag overrides (`--installed` to dogfood the published CLI from a
monorepo cwd; `--monorepo` to fail loudly if the workspace lookup
goes sideways). Includes the moved-checkout caveat: absolute paths
mean `dkg mcp setup --force` is required after a checkout move.

Source-of-truth check: `detectClients()` array at
`packages/cli/src/mcp-setup.ts:404-446` — 6 clients verified
verbatim. The plan's table was best-knowledge; the COMMITTED list
is what these edits reflect.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…Part 6)

Parallel to `2298d22f` in the repo-root README. Updates the
`packages/mcp-dkg/README.md` Install + Manual config + Troubleshooting
sections to reflect the actual `detectClients()` array landed by
mcp-lead's phases 1-6 on `feat/mcp-setup-extend-clients-monorepo`.

- Opening sentence (line 3) — bolded client list expanded to the 6
  auto-detected clients.
- Install bundled-flow step 4 — full per-platform path list, with
  the VSCode + Copilot `servers.dkg` shape carve-out.
- Manual config block (was 3 generic bullets, now 7 explicit
  per-client entries) — each carries config path + entry shape.
- Continue + Codex CLI carved out as `--print-only` paste-manually
  candidates, since they're not auto-detected today.
- Troubleshooting "no clients detected" bullet — same enumeration.
- "Contributor (monorepo dev) workflow" subsection replaces the prior
  `pnpm exec tsx` workspace-relative form (which was a pre-W6-pre
  workaround). New form is the actual auto-detected `node /abs/path/
  cli.js mcp serve` shape with rebuild prereq, `--installed` /
  `--monorepo` mode overrides, and the moved-checkout caveat.

Source-of-truth: `detectClients()` array at
`packages/cli/src/mcp-setup.ts:404-446`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t PATH inheritance (F30)

User real-world signal: a Claude Desktop user got
`MCP dkg: spawn dkg ENOENT` after running `dkg mcp setup`. Root
cause: GUI MCP clients (Claude Desktop, Windsurf, etc.) don't
inherit the shell PATH that `npm install -g` writes the `dkg` bin
into. The bare-`"dkg"` command in the registered config can only
be spawned by clients that DO inherit shell PATH (Cursor, Claude
Code CLI, terminal-launched VSCode + Copilot Chat). For everyone
else, the entry is dead on arrival.

Fix: in installed mode, resolve the absolute path of the `dkg`
bin at setup time via `which dkg` (POSIX) / `where.exe dkg`
(Windows) and write THAT into the client config. GUI clients can
spawn an absolute path without PATH inheritance. Monorepo mode
unchanged — already absolute (`node <repo>/packages/cli/dist/cli.js`).

This commit:

- `packages/cli/src/mcp-setup.ts`:
  - New `resolveDkgBin()` helper. Uses `execSync` + platform-
    specific `which`/`where.exe`, returns the first line of output
    (matches shell PATH-precedence on Windows, where `where.exe`
    can return multiple shadowed hits). Returns `null` on any
    failure (`dkg` not on PATH, exec error, empty output) so
    callers fall back to the bare-`"dkg"` form without crashing.
    `stdio: ['ignore', 'pipe', 'ignore']` silences `which`'s
    not-found stderr so the operator-facing setup log stays clean.
    Exported for the cli.ts wiring.
  - `canonicalEntry(context, monorepoRoot, resolvedBin)` extended
    with the new third parameter. Installed mode prefers the
    resolved-path form when available; falls back to bare-`"dkg"`
    when the resolver returns null. Monorepo mode unchanged.
  - `mcpSetupAction` calls `deps.resolveDkgBin()` once at the top,
    only in installed mode (monorepo mode hard-codes its own
    absolute path; no need to spawn a child process).
  - `McpSetupActionDeps` extended with `resolveDkgBin: () => string | null`
    so tests can inject deterministic stubs.
  - `classify()` staleness contract updated: bare-`"dkg"` and the
    currently-resolved absolute path are equivalent for the
    currently-installed bin (both spawn the same process). A
    pre-existing bare-`"dkg"` entry classifies as `registered`
    against a resolved-path canonical when args match — avoids
    spurious `--force` prompts on re-runs after PATH-state
    changes or after upgrading from pre-F30 setup. Asymmetric:
    a pre-existing DIFFERENT absolute path (e.g. `/old/path/dkg`
    vs current `/usr/local/bin/dkg`) IS real divergence and
    classifies as `stale`.

- `packages/cli/src/cli.ts`:
  - Pass `resolveDkgBin` through to `mcpSetupAction`'s deps.

- `packages/cli/test/mcp-setup.test.ts`:
  - `makeDeps()` extended with a `resolveDkgBin` stub that
    defaults to returning null (preserves pre-F30 test behaviour
    for the existing 28 tests).
  - 6 new F30 tests:
    * Installed mode + resolved bin → canonical entry uses
      absolute path; `resolveDkgBin` called exactly once
    * `resolveDkgBin` → null falls back to bare `"dkg"`
    * Monorepo mode does NOT call `resolveDkgBin` (no spurious
      child-process spawn during dev setup)
    * Pre-existing bare-`"dkg"` entry stays `registered` against
      a resolved-path canonical (no rewrite, mtime unchanged) —
      pins the re-run resilience contract
    * Pre-existing different absolute path classifies as `stale`
      and gets refreshed — pins the asymmetric divergence contract
    * `--print-only` with resolved bin emits the absolute path
      so the manual-paste form matches what setup actually writes

Verification:
- `pnpm --filter @origintrail-official/dkg build` → clean
- `pnpm --filter @origintrail-official/dkg-mcp test` → 88/88 ✓
- `pnpm --filter @origintrail-official/dkg exec vitest run mcp-setup.test.ts` → 34/34 ✓ (was 28; +6 F30)

Live smoke (qa-engineer's verification asks):
- `dkg mcp setup --print-only` from inside this monorepo → emits
  `node /abs/path/cli.js mcp serve` (monorepo form, unchanged)
- `dkg mcp setup --print-only --installed` from same cwd →
  emits the resolved absolute `dkg` bin path with `["mcp", "serve"]`,
  not the bare-`"dkg"` form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Jurij89 Jurij89 force-pushed the chore/v10-mcp-consolidation branch from fc2b375 to 1190795 Compare May 6, 2026 15:53
@Jurij89 Jurij89 force-pushed the feat/mcp-setup-extend-clients-monorepo branch from f22b387 to 14d2992 Compare May 6, 2026 15:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant