@lovinka/deployik-mcp 0.3.2 → 0.5.0

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
@@ -43,9 +43,37 @@ Or set `DEPLOYIK_URL` / `DEPLOYIK_TOKEN` env vars and pass `--yes`.
43
43
  | Command | What it does |
44
44
  |---|---|
45
45
  | `install` | MCP registration + skill files (default) |
46
+ | `install --daemon` | Long-lived launchd daemon — one HTTP process for every Claude window (macOS only, see below) |
46
47
  | `install-mcp` | MCP registration only |
47
48
  | `install-skill` | Skill files only |
48
49
  | `uninstall` | Removes the `deployik` MCP entry from every Claude config |
50
+ | `uninstall --daemon` | Stops + removes the launchd daemon and clears the HTTP entry from Claude configs |
51
+ | `daemon` | Runs the HTTP daemon in the foreground (for testing) |
52
+
53
+ ## Daemon mode (one process, every Claude window)
54
+
55
+ By default each Claude Code window spawns its own stdio child for every configured MCP. After a few open windows you can easily have 10+ idle `node` processes eating ~100 MB each. The daemon mode collapses this to a single long-lived HTTP MCP server bound to `127.0.0.1:8788`.
56
+
57
+ ```bash
58
+ npx -y @lovinka/deployik-mcp install --daemon --token=dpk_xxx
59
+ ```
60
+
61
+ What it does:
62
+
63
+ - Writes a launchd plist at `~/Library/LaunchAgents/com.lovinka.deployik-mcp.plist` with `KeepAlive=true`, `RunAtLoad=true`, and `DEPLOYIK_URL` / `DEPLOYIK_TOKEN` in `EnvironmentVariables` (file mode 0600 — token only readable by your user).
64
+ - Stages the runtime into `~/.deployik-mcp/runtime/` so launchd can read it on every macOS regardless of TCC (Transparency, Consent, Control) restrictions on `~/Documents`, `~/Desktop`, etc.
65
+ - Runs `launchctl bootstrap gui/$UID <plist>` to start the daemon immediately.
66
+ - Rewrites the `deployik` entry in `~/.claude.json` (and the Claude Desktop config) from a stdio command to `{ "type": "http", "url": "http://127.0.0.1:8788/mcp" }`.
67
+
68
+ After install, **restart any open Claude windows** so they pick up the HTTP entry. From then on, opening N windows still uses **one** daemon process.
69
+
70
+ Tools that need the client's filesystem (`init_in_repo`, `show_binding`) are skipped in HTTP mode since the daemon has no per-repo context. Project resolution still works via explicit `project_id` / `slug` / single-project workspace.
71
+
72
+ Logs: `~/Library/Logs/deployik-mcp.{out,err}.log`.
73
+
74
+ To go back to per-window stdio: `npx -y @lovinka/deployik-mcp uninstall --daemon`, then re-run `install` without `--daemon`.
75
+
76
+ > Linux note: launchd is macOS-only. On Linux, run the daemon under `systemd --user` pointing at `node <prefix>/lib/node_modules/@lovinka/deployik-mcp/dist/daemon.js` and add the matching HTTP entry to your client config manually.
49
77
 
50
78
  ### Manual install (if you prefer to edit JSON yourself)
51
79
 
@@ -68,7 +96,8 @@ Get a token at **Account → Access tokens** in Deployik. The token is shown onc
68
96
 
69
97
  ## What it does
70
98
 
71
- - **~37 thin tools** — one per Deployik HTTP endpoint (projects, deployments, env vars, secrets, domains, auto-build, password protection, volumes, analytics, email, dashboard groups, tokens, GitHub).
99
+ - **~50 thin tools** — one per Deployik HTTP endpoint (projects, deployments, env vars, secrets, domains, auto-build, password protection, volumes, analytics, email, dashboard groups, tokens, GitHub).
100
+ - **App bundles** — group several projects into one **app** and operate on it as a unit: `list_apps`, `create_app`, `get_app_health`, `update_app` (rename / toggle `deploy_ordered`), `delete_app`, `add_project_to_app`, `remove_project_from_app`; app-level env/secrets (`list_app_env_vars` / `set_app_env_var` / `delete_app_env_var` + secret variants, inherited by every member); and coordinated deploys: `deploy_app` (ordered, health-gated, fire-and-forget), `deploy_app_and_wait` (waits for the release outcome), `list_app_releases`, `rollback_app`. Member projects also gain `build_filter_enabled` / `watch_paths` (monorepo fan-out filtering) and `deploy_order` on create/update.
72
101
  - **12 workflow tools** — `setup_project_from_repo`, `deploy_project`, `set_secret`, `tail_latest_logs`, `debug_failed_deployment`, `get_project_health`, `init_in_repo`, `whats_my_url`, `whats_broken`, `redeploy`, and more.
73
102
  - **Dockerfile-aware project creation** — generated presets cover Next.js, Vite, Astro, static sites, and Node APIs; user-provided Dockerfiles are supported by choosing `framework: "static"`, setting `root_directory` to the Dockerfile folder, and setting `port` to the container listen port.
74
103
  - **Bundled knowledge** — Deployik's how-to recipes ship as MCP prompts (`deployik_recipe_*`), plus three tools: `list_recipes`, `get_recipe(topic)`, and **`find_help(question)`** which routes free-form English ("where do I set the Stripe key for the live site?") to the right recipe automatically.
package/dist/daemon.js ADDED
@@ -0,0 +1,119 @@
1
+ // HTTP daemon entrypoint.
2
+ //
3
+ // Runs ONE long-lived process bound to 127.0.0.1, so every Claude Code
4
+ // window/session shares the same server instead of each spawning its own
5
+ // stdio child. Wired up via `deployik-mcp install --daemon`, which writes
6
+ // a launchd plist and points Claude's MCP config at this URL.
7
+ //
8
+ // Stateless transport — the SDK requires a fresh transport per request in
9
+ // stateless mode (reusing one throws "Stateless transport cannot be reused").
10
+ // So each POST /mcp instantiates a new McpServer + transport pair. The build
11
+ // is in-memory only (no I/O), takes sub-millisecond, and avoids the session
12
+ // bookkeeping a stateful map would require. Identity comes from the daemon's
13
+ // process env, not per-session.
14
+ import { createServer } from "node:http";
15
+ import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
16
+ import { buildServer } from "./server.js";
17
+ import { ConfigError } from "./config/env.js";
18
+ import { VERSION } from "./version.js";
19
+ const DEFAULT_PORT = 8788;
20
+ const DEFAULT_HOST = "127.0.0.1";
21
+ const MCP_PATH = "/mcp";
22
+ const HEALTH_PATH = "/healthz";
23
+ export async function startDaemon() {
24
+ const port = parseInt(process.env.DEPLOYIK_DAEMON_PORT ?? "", 10) || DEFAULT_PORT;
25
+ const host = process.env.DEPLOYIK_DAEMON_HOST?.trim() || DEFAULT_HOST;
26
+ // Validate config up front so a bad token / missing URL fails the launchd
27
+ // bootstrap step (visible in the install output) instead of throwing on
28
+ // every request.
29
+ buildServer(process.env, process.cwd(), { mode: "http" });
30
+ const httpServer = createServer((req, res) => {
31
+ // Wrap in a promise so async errors land in the .catch — Node's HTTP
32
+ // request callback type is synchronous void, async throws otherwise
33
+ // become unhandled rejections.
34
+ void handleRequest(req, res).catch((err) => {
35
+ const message = err instanceof Error ? err.message : String(err);
36
+ if (!res.headersSent) {
37
+ sendJson(res, 500, { error: "internal", message });
38
+ }
39
+ else {
40
+ try {
41
+ res.end();
42
+ }
43
+ catch { /* socket already torn down */ }
44
+ }
45
+ });
46
+ });
47
+ async function handleRequest(req, res) {
48
+ // Localhost-only daemon, but cheap sanity check anyway: refuse anything
49
+ // not sourced from this machine. Catches stray docker bridges or a future
50
+ // misconfigured listener pointing at 0.0.0.0.
51
+ const remote = req.socket.remoteAddress ?? "";
52
+ if (!isLocal(remote)) {
53
+ sendJson(res, 403, { error: "forbidden", reason: `non-local remote: ${remote}` });
54
+ return;
55
+ }
56
+ const url = new URL(req.url ?? "/", `http://${host}:${port}`);
57
+ if (url.pathname === HEALTH_PATH) {
58
+ sendJson(res, 200, { ok: true, version: VERSION });
59
+ return;
60
+ }
61
+ if (url.pathname !== MCP_PATH) {
62
+ sendJson(res, 404, { error: "not_found", path: url.pathname });
63
+ return;
64
+ }
65
+ // Stateless: fresh server + transport per request. The whole thing is
66
+ // in-memory wiring (no network calls during buildServer), so the cost
67
+ // is the tool-registration loop — still sub-millisecond in practice.
68
+ const { server } = buildServer(process.env, process.cwd(), { mode: "http" });
69
+ const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
70
+ try {
71
+ await server.connect(transport);
72
+ await transport.handleRequest(req, res);
73
+ }
74
+ finally {
75
+ transport.close().catch(() => { });
76
+ server.close().catch(() => { });
77
+ }
78
+ }
79
+ await new Promise((resolve, reject) => {
80
+ httpServer.once("error", reject);
81
+ httpServer.listen(port, host, () => {
82
+ httpServer.off("error", reject);
83
+ resolve();
84
+ });
85
+ });
86
+ process.stderr.write(`deployik-mcp ${VERSION} daemon listening on http://${host}:${port}${MCP_PATH}\n`);
87
+ const shutdown = (signal) => {
88
+ process.stderr.write(`deployik-mcp daemon: received ${signal}, shutting down\n`);
89
+ httpServer.close();
90
+ process.exit(0);
91
+ };
92
+ process.once("SIGINT", () => shutdown("SIGINT"));
93
+ process.once("SIGTERM", () => shutdown("SIGTERM"));
94
+ process.once("SIGHUP", () => shutdown("SIGHUP"));
95
+ }
96
+ function sendJson(res, status, body) {
97
+ res.statusCode = status;
98
+ res.setHeader("Content-Type", "application/json");
99
+ res.end(JSON.stringify(body));
100
+ }
101
+ function isLocal(addr) {
102
+ // Strip IPv6-mapped-v4 prefix so "::ffff:127.0.0.1" compares cleanly.
103
+ const a = addr.replace(/^::ffff:/, "");
104
+ return a === "127.0.0.1" || a === "::1" || a === "localhost";
105
+ }
106
+ // Allow `node dist/daemon.js` to start directly without going through index.ts.
107
+ // `import.meta.url` check makes the file safe to import as a library too.
108
+ const invokedDirectly = import.meta.url === `file://${process.argv[1]}`;
109
+ if (invokedDirectly) {
110
+ startDaemon().catch((err) => {
111
+ if (err instanceof ConfigError) {
112
+ process.stderr.write(`deployik-mcp daemon: ${err.message}\n`);
113
+ process.exit(2);
114
+ }
115
+ process.stderr.write(`deployik-mcp daemon: ${err.message}\n`);
116
+ process.exit(1);
117
+ });
118
+ }
119
+ //# sourceMappingURL=daemon.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,21 +1,33 @@
1
1
  #!/usr/bin/env node
2
+ import { fileURLToPath } from "node:url";
3
+ import { dirname, resolve } from "node:path";
2
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
5
  import { buildServer } from "./server.js";
4
6
  import { ConfigError } from "./config/env.js";
5
7
  import { installSkill } from "./install-skill.js";
6
8
  import { installMcp, uninstallMcp } from "./install-mcp.js";
7
9
  import { installAll } from "./install.js";
10
+ import { installDaemon, uninstallDaemon, DEFAULT_DAEMON_PORT } from "./install-daemon.js";
11
+ import { startDaemon } from "./daemon.js";
8
12
  import { VERSION } from "./version.js";
9
13
  const HELP = `deployik-mcp ${VERSION}
10
14
 
11
15
  USAGE
12
16
  deployik-mcp Start the MCP server over stdio (default).
17
+ deployik-mcp daemon Run the HTTP daemon in the foreground
18
+ (binds 127.0.0.1:${DEFAULT_DAEMON_PORT} by default; for testing).
13
19
  deployik-mcp install Register the MCP server in Claude config
14
20
  AND copy the bundled skill (recommended).
21
+ deployik-mcp install --daemon Install as a long-lived launchd daemon
22
+ (one HTTP process, shared by every Claude
23
+ window). Rewrites Claude configs to use
24
+ http://127.0.0.1:${DEFAULT_DAEMON_PORT}/mcp. macOS only.
15
25
  deployik-mcp install-mcp Only register the MCP server in Claude config.
16
26
  deployik-mcp install-skill Only copy the Deployik skill files.
17
27
  deployik-mcp uninstall Remove the 'deployik' MCP server entry from
18
28
  every Claude config it lives in.
29
+ deployik-mcp uninstall --daemon Stop the launchd daemon, remove the plist,
30
+ and clear the HTTP entry from Claude configs.
19
31
  deployik-mcp --help, -h Show this message.
20
32
  deployik-mcp --version, -v Print version and exit.
21
33
 
@@ -61,6 +73,8 @@ function parseFlags(argv) {
61
73
  let yes = false;
62
74
  let url;
63
75
  let token;
76
+ let daemon = false;
77
+ let port;
64
78
  for (const arg of argv) {
65
79
  if (arg === "--global")
66
80
  scope = "global";
@@ -68,12 +82,19 @@ function parseFlags(argv) {
68
82
  scope = "local";
69
83
  else if (arg === "--yes" || arg === "-y")
70
84
  yes = true;
85
+ else if (arg === "--daemon")
86
+ daemon = true;
71
87
  else if (arg.startsWith("--url="))
72
88
  url = arg.slice("--url=".length);
73
89
  else if (arg.startsWith("--token="))
74
90
  token = arg.slice("--token=".length);
91
+ else if (arg.startsWith("--port=")) {
92
+ const n = parseInt(arg.slice("--port=".length), 10);
93
+ if (Number.isFinite(n) && n > 0)
94
+ port = n;
95
+ }
75
96
  }
76
- return { scope, yes, url, token };
97
+ return { scope, yes, url, token, daemon, ...(port !== undefined ? { port } : {}) };
77
98
  }
78
99
  async function main() {
79
100
  const argv = process.argv.slice(2);
@@ -87,8 +108,16 @@ async function main() {
87
108
  }
88
109
  const sub = argv[0];
89
110
  const rest = argv.slice(1);
111
+ if (sub === "daemon") {
112
+ await startDaemon();
113
+ return;
114
+ }
90
115
  if (sub === "install") {
91
116
  const flags = parseFlags(rest);
117
+ if (flags.daemon) {
118
+ const code = installDaemonCommand(flags);
119
+ process.exit(code);
120
+ }
92
121
  const code = await installAll(flags);
93
122
  process.exit(code);
94
123
  }
@@ -104,6 +133,10 @@ async function main() {
104
133
  }
105
134
  if (sub === "uninstall") {
106
135
  const flags = parseFlags(rest);
136
+ if (flags.daemon) {
137
+ uninstallDaemonCommand();
138
+ process.exit(0);
139
+ }
107
140
  const res = uninstallMcp({ scope: flags.scope });
108
141
  for (const w of res.written)
109
142
  process.stdout.write(` ✓ removed from ${w.path}${w.backupPath ? ` (backup → ${w.backupPath})` : ""}\n`);
@@ -125,6 +158,27 @@ async function main() {
125
158
  try {
126
159
  const { server } = buildServer();
127
160
  const transport = new StdioServerTransport();
161
+ // The SDK's StdioServerTransport only listens for stdin 'data' + 'error'
162
+ // (see node_modules/@modelcontextprotocol/sdk/.../server/stdio.js). When
163
+ // the MCP client (Claude Code, Claude Desktop) closes the stdio pipe on
164
+ // shutdown, stdin emits 'end' but nothing exits the process — the data
165
+ // listener keeps the event loop alive forever, and the npm exec wrapper
166
+ // stays parked waiting on its child. Result: zombie process per closed
167
+ // session. Wire our own exit triggers so the daemon dies cleanly.
168
+ let shuttingDown = false;
169
+ const shutdown = (code = 0) => {
170
+ if (shuttingDown)
171
+ return;
172
+ shuttingDown = true;
173
+ void transport.close().catch(() => { });
174
+ process.exit(code);
175
+ };
176
+ transport.onclose = () => shutdown(0);
177
+ process.stdin.once("end", () => shutdown(0));
178
+ process.stdin.once("close", () => shutdown(0));
179
+ process.once("SIGINT", () => shutdown(0));
180
+ process.once("SIGTERM", () => shutdown(0));
181
+ process.once("SIGHUP", () => shutdown(0));
128
182
  await server.connect(transport);
129
183
  process.stderr.write(`deployik-mcp ${VERSION} ready (stdio)\n`);
130
184
  }
@@ -137,6 +191,55 @@ async function main() {
137
191
  process.exit(1);
138
192
  }
139
193
  }
194
+ function installDaemonCommand(flags) {
195
+ const token = flags.token ?? process.env.DEPLOYIK_TOKEN ?? "";
196
+ if (!token) {
197
+ process.stderr.write(`deployik-mcp install --daemon: missing token.\n` +
198
+ ` Pass --token=<dpk_...> or set DEPLOYIK_TOKEN in your environment.\n`);
199
+ return 2;
200
+ }
201
+ const url = flags.url ?? process.env.DEPLOYIK_URL ?? "https://deployik.lovinka.com";
202
+ const sourceDir = resolveMcpSourceDir();
203
+ try {
204
+ const result = installDaemon({
205
+ sourceDir,
206
+ url,
207
+ token,
208
+ ...(flags.port !== undefined ? { port: flags.port } : {}),
209
+ });
210
+ process.stdout.write(`\n ✓ wrote plist ${result.plistPath}\n`);
211
+ process.stdout.write(` ${result.loaded ? "✓" : "✗"} launchctl bootstrap${result.loaded ? "" : ` failed: ${result.loadError}`}\n`);
212
+ process.stdout.write(` ✓ daemon URL ${result.url}\n`);
213
+ for (const w of result.configsWritten) {
214
+ process.stdout.write(` ✓ rewrote ${w.path}${w.backupPath ? ` (backup → ${w.backupPath})` : ""}\n`);
215
+ }
216
+ for (const s of result.configsSkipped) {
217
+ process.stdout.write(` · skipped ${s.path} — ${s.reason}\n`);
218
+ }
219
+ process.stdout.write(`\n Restart Claude Code to pick up the HTTP MCP entry.\n`);
220
+ process.stdout.write(` Logs: ~/Library/Logs/deployik-mcp.{out,err}.log\n`);
221
+ return result.loaded ? 0 : 1;
222
+ }
223
+ catch (err) {
224
+ process.stderr.write(`deployik-mcp install --daemon: ${err.message}\n`);
225
+ return 1;
226
+ }
227
+ }
228
+ function uninstallDaemonCommand() {
229
+ const result = uninstallDaemon();
230
+ process.stdout.write(` ${result.bootoutOk ? "✓" : "·"} launchctl bootout${result.bootoutError ? ` (${result.bootoutError})` : ""}\n`);
231
+ process.stdout.write(` ${result.plistRemoved ? "✓ removed plist" : "· plist already absent"}\n`);
232
+ for (const c of result.configsCleaned) {
233
+ process.stdout.write(` ${c.removed ? "✓" : "·"} ${c.path}${c.removed ? "" : " (no deployik entry)"}\n`);
234
+ }
235
+ }
236
+ function resolveMcpSourceDir() {
237
+ // We're running from dist/index.js — the mcp package root is one level up.
238
+ // Works for both local builds and a published npm install
239
+ // (node_modules/@lovinka/deployik-mcp/dist/index.js).
240
+ const here = dirname(fileURLToPath(import.meta.url));
241
+ return resolve(here, "..");
242
+ }
140
243
  // `installMcp` is exported from a sibling module for programmatic use; the
141
244
  // re-export here keeps the published `dist/index.js` self-contained for
142
245
  // scripts that prefer requiring the entry point.
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}