Skip to content

Ship np and nodepilot-mcp with both installers - #219

Merged
Sev7eNup merged 1 commit into
mainfrom
feat/ship-cli-and-mcp-with-installers
Aug 16, 2026
Merged

Ship np and nodepilot-mcp with both installers#219
Sev7eNup merged 1 commit into
mainfrom
feat/ship-cli-and-mcp-with-installers

Conversation

@Sev7eNup

Copy link
Copy Markdown
Owner

Problem

The server artifact contained exactly one executable — NodePilot.Api.exe. Neither operator client was ever packaged:

  • Build-Artifact.ps1 ran a single dotnet publish (the API).
  • NodePilotServer.iss [Files] took only the payload and deploy\.
  • The docs said so out loud: README.md told operators to dotnet publish src/NodePilot.Cli themselves, and the project tree called both "shipped via dotnet publish".

np is the documented way to drive an installation from a script, and nodepilot-mcp is the only way to point an AI agent at one. "Not shipped" therefore meant "not really available" — a lab install running 1.2.6 had neither binary anywhere on disk.

(To be clear about what this did not break: NodePilot's own AI features are unaffected. The workflow assistant and /ai-chat run in-process in NodePilot.Ai; nothing references NodePilot.Mcp. The missing piece was the opposite direction — an external agent connecting in.)

Change

Packaging. Both builds publish the clients into their own directories under tools\:

server artifact desktop package
tools\np\np.exe framework-dependent (matches the API) self-contained
tools\mcp\nodepilot-mcp.exe framework-dependent self-contained

Separate directories per client on purpose: they carry their own copies of shared dependencies, and merging publishes into one folder lets whichever ran last decide the assembly versions the service then loads. Desktop is self-contained because that package promises zero prerequisites — a framework-dependent apphost cannot borrow the runtime sitting next to the API.

Size: server ZIP +~10 MB. The desktop installer grows more (two self-contained publishes); worth flagging against its current 176 MB.

PATH. Install and update append <install>\tools\np to the machine PATH; uninstall removes it before deleting the directory it points at. Update carries the same step because an installation predating this change has no entry to keep. nodepilot-mcp is deliberately left off PATH — an MCP client is configured with an absolute path in .mcp.json and gains nothing from it.

Assert-NodePilotExtractedFiles is unaffected: the clients are inside the signed ZIP, so "exactly the signed contents" still holds.

Tests

  • deploy/Test-MachinePath.ps1 (new, wired into CI in both PS 5.1 and PS 7 lanes) — 20 assertions over the PATH helpers, covering what a manual run never reproduces: re-installing must not grow PATH (it has a real length limit and install/upgrade cycles repeat), and C:\NP\tools\np\ vs c:\np\TOOLS\np name the same directory while a naive compare disagrees — a mismatch there leaves a dead entry pointing at a deleted directory.
    Writing them immediately caught a real defect: PowerShell unwraps a single-element array on return, so appending to a one-entry PATH concatenated strings and produced C:\WindowsC:\...\tools\np — one unusable entry with no separator.
  • deploy/Test-DeploymentTemplates.ps1 — pins the packaging contract: both builds publish both clients, the desktop clients are self-contained, the Inno script ships tools\*, and install/update/uninstall all go through the one shared helper instead of three copies of the same string surgery.

Ran: Test-DeploymentTemplates.ps1, Test-MachinePath.ps1, Test-SetupAdapter.ps1 (213 assertions), Test-ArtifactSecurity.ps1 — all pass.

Docs

README.md (both client sections + project tree), CLAUDE.md, docs/claude-reference.md, docs/mcp-server.md, src/NodePilot.Cli/CLAUDE.md, deploy/README.md, and the docs site (content/cli.md, content/mcp-server.md) now say the clients ship with the installers, with dotnet publish demoted to the source-checkout case.

The server artifact contained exactly one executable, NodePilot.Api.exe. Neither
operator client was ever packaged: Build-Artifact.ps1 ran a single dotnet publish,
the Inno script took only the payload and deploy folders, and the documentation
told operators to build the clients themselves. `np` is the documented way to
drive an installation from a script and nodepilot-mcp is the only way to point an
AI agent at it, so "not shipped" meant "not really available" -- a lab install
running 1.2.6 had neither binary anywhere on disk.

Both builds now publish the clients into their own directories under tools\, kept
separate from the API so no publish decides another's shared assembly versions.
The server build publishes framework-dependent, matching the API; the desktop
build publishes self-contained, because that package promises zero prerequisites
and a framework-dependent apphost cannot borrow the runtime sitting next to the
API.

Install and update append <install>\tools\np to the machine PATH so operators can
type `np` without knowing where setup landed; uninstall removes it before
deleting the directory it points at. Update carries the same step because an
installation predating this change has no entry to keep. nodepilot-mcp is
deliberately left off PATH: an MCP client is configured with an absolute path in
.mcp.json and gains nothing from it.

The PATH string surgery lives in deploy/MachinePath.ps1 as pure functions rather
than three copies. Its tests cover what a manual run never reproduces:
re-installing must not grow PATH (which has a real length limit), and
"C:\NP\tools\np\" and "c:\np\TOOLS\np" name the same directory while a naive
compare disagrees -- a mismatch there leaves a dead entry pointing at a deleted
directory. Writing those tests immediately caught a real defect: PowerShell
unwraps a single-element array on return, so appending to a one-entry PATH
concatenated strings and produced one unusable entry with no separator.
@Sev7eNup
Sev7eNup merged commit 990aad2 into main Aug 16, 2026
9 checks passed
@Sev7eNup
Sev7eNup deleted the feat/ship-cli-and-mcp-with-installers branch August 16, 2026 16:00
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