mcpill 1.7.0 → 1.8.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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.8.0
4
+
5
+ - `mcpill run` (stdio) now auto-registers the server in Claude config on start and auto-unregisters on SIGINT, SIGTERM, and process exit — no manual step required
6
+ - `--register` flag and `mcpill unregister` command removed; lifecycle management is now automatic
7
+
3
8
  ## 1.7.0
4
9
 
5
10
  - `mcpill run` is now side-effect-free by default — stopping the process disconnects the server; no Claude config is written
package/README.md CHANGED
@@ -20,8 +20,7 @@ Describe your server in plain English, let Claude generate the source files.
20
20
  mcpill init # scaffolds PILL.md + example source files
21
21
  # fill PILL.md # describe your server: tools, resources, prompts
22
22
  # tell Claude: "Build this PILL.md" # agent generates source files + runs compile
23
- mcpill run # start the server (transient stops when you do)
24
- mcpill run --register # or: register so Claude manages it persistently
23
+ mcpill run # start the server — registered while running, removed on exit
25
24
  ```
26
25
 
27
26
  `PILL.md` embeds the agent instructions — Claude knows exactly what to generate and how.
@@ -88,23 +87,11 @@ Validates all pill directories (`.<name>/`) in the project root.
88
87
  Starts the MCP server from the pill artifact.
89
88
 
90
89
  ```bash
91
- mcpill run # stdio (default)
90
+ mcpill run # stdio (default) — registered while running, removed on exit
92
91
  mcpill run --transport http --port 3333
93
- mcpill run --register # also write server entry to Claude config
94
92
  ```
95
93
 
96
- `--register` is opt-in. Without it, running the server has no side effects on Claude's configuration stopping the process fully disconnects it.
97
-
98
- ### `mcpill unregister`
99
-
100
- Removes the server entry from all Claude config files — the inverse of `mcpill run --register`.
101
-
102
- ```bash
103
- mcpill unregister
104
- mcpill unregister --dir <path>
105
- ```
106
-
107
- Removes from `.claude/settings.json` (project scope), `~/.claude.json` (user scope), and the Claude Desktop config. Prints what was removed, or reports if the server was not registered.
94
+ For stdio transport, the server entry is automatically written to Claude's config on start and removed on exit (SIGINT, SIGTERM, or normal process exit). HTTP transport is self-hosted — Claude connects to a URL, so no registration occurs.
108
95
 
109
96
  ### `mcpill pack`
110
97
 
package/dist/cli.js CHANGED
@@ -548,6 +548,7 @@ async function runInit(opts) {
548
548
  // src/commands/run.ts
549
549
  import path7 from "path";
550
550
  import fs6 from "fs";
551
+ import os from "os";
551
552
  import { z as z2 } from "mcpill-runtime";
552
553
  import { createServer, applySetup } from "mcpster";
553
554
 
@@ -1106,6 +1107,32 @@ async function runValidate(baseDir) {
1106
1107
  }
1107
1108
 
1108
1109
  // src/commands/run.ts
1110
+ function patchJson(filePath, patcher) {
1111
+ if (!fs6.existsSync(filePath)) return;
1112
+ try {
1113
+ const obj = JSON.parse(fs6.readFileSync(filePath, "utf-8"));
1114
+ patcher(obj);
1115
+ fs6.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n", "utf-8");
1116
+ } catch {
1117
+ }
1118
+ }
1119
+ function desktopConfigPaths() {
1120
+ const home = os.homedir();
1121
+ if (process.platform === "win32") {
1122
+ const appData = process.env.APPDATA ?? path7.join(home, "AppData", "Roaming");
1123
+ return [path7.join(appData, "Claude", "claude_desktop_config.json")];
1124
+ }
1125
+ return [path7.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
1126
+ }
1127
+ function unregisterSync(name, baseDir) {
1128
+ const remove = (obj) => {
1129
+ const servers = obj.mcpServers;
1130
+ if (servers) delete servers[name];
1131
+ };
1132
+ patchJson(path7.join(baseDir, ".claude", "settings.json"), remove);
1133
+ patchJson(path7.join(os.homedir(), ".claude.json"), remove);
1134
+ for (const p of desktopConfigPaths()) patchJson(p, remove);
1135
+ }
1109
1136
  async function runServer(opts) {
1110
1137
  const baseDir = path7.resolve(opts.dir ?? process.cwd());
1111
1138
  await runValidate(baseDir);
@@ -1157,7 +1184,7 @@ async function runServer(opts) {
1157
1184
  resolver: async () => content
1158
1185
  });
1159
1186
  }
1160
- if (opts.register) {
1187
+ if (transport === "stdio") {
1161
1188
  applySetup(name, tools.map((t) => t.name), {
1162
1189
  projectPath: baseDir,
1163
1190
  permissions: "restrictive",
@@ -1167,6 +1194,16 @@ async function runServer(opts) {
1167
1194
  args: ["run", "--dir", baseDir]
1168
1195
  }
1169
1196
  });
1197
+ const cleanup = () => unregisterSync(name, baseDir);
1198
+ process.on("exit", cleanup);
1199
+ process.on("SIGINT", () => {
1200
+ cleanup();
1201
+ process.exit(0);
1202
+ });
1203
+ process.on("SIGTERM", () => {
1204
+ cleanup();
1205
+ process.exit(0);
1206
+ });
1170
1207
  }
1171
1208
  try {
1172
1209
  await server.start();
@@ -1561,72 +1598,6 @@ async function runPublish(dir, access) {
1561
1598
  }
1562
1599
  }
1563
1600
 
1564
- // src/commands/unregister.ts
1565
- import path10 from "path";
1566
- import fs8 from "fs";
1567
- import os from "os";
1568
- function patchJson(filePath, patcher) {
1569
- if (!fs8.existsSync(filePath)) return false;
1570
- try {
1571
- const obj = JSON.parse(fs8.readFileSync(filePath, "utf-8"));
1572
- patcher(obj);
1573
- fs8.writeFileSync(filePath, JSON.stringify(obj, null, 2) + "\n", "utf-8");
1574
- return true;
1575
- } catch {
1576
- return false;
1577
- }
1578
- }
1579
- function desktopConfigPaths() {
1580
- const home = os.homedir();
1581
- if (process.platform === "win32") {
1582
- const appData = process.env.APPDATA ?? path10.join(home, "AppData", "Roaming");
1583
- return [path10.join(appData, "Claude", "claude_desktop_config.json")];
1584
- }
1585
- return [path10.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")];
1586
- }
1587
- async function runUnregister(opts) {
1588
- const baseDir = path10.resolve(opts.dir ?? process.cwd());
1589
- const mcpillDir = path10.join(baseDir, ".mcpill", "server");
1590
- const config = await loadConfig(mcpillDir);
1591
- const { name } = config;
1592
- let removed = 0;
1593
- const settingsPath = path10.join(baseDir, ".claude", "settings.json");
1594
- const removedProject = patchJson(settingsPath, (obj) => {
1595
- const servers = obj.mcpServers;
1596
- if (servers && name in servers) {
1597
- delete servers[name];
1598
- removed++;
1599
- }
1600
- });
1601
- if (removedProject) process.stderr.write(`removed "${name}" from .claude/settings.json
1602
- `);
1603
- const claudeJsonPath = path10.join(os.homedir(), ".claude.json");
1604
- const removedUser = patchJson(claudeJsonPath, (obj) => {
1605
- const servers = obj.mcpServers;
1606
- if (servers && name in servers) {
1607
- delete servers[name];
1608
- removed++;
1609
- }
1610
- });
1611
- if (removedUser) process.stderr.write(`removed "${name}" from ~/.claude.json
1612
- `);
1613
- for (const configPath of desktopConfigPaths()) {
1614
- const removedDesktop = patchJson(configPath, (obj) => {
1615
- const servers = obj.mcpServers;
1616
- if (servers && name in servers) {
1617
- delete servers[name];
1618
- removed++;
1619
- }
1620
- });
1621
- if (removedDesktop) process.stderr.write(`removed "${name}" from Claude Desktop config
1622
- `);
1623
- }
1624
- if (removed === 0) {
1625
- process.stderr.write(`"${name}" was not registered \u2014 nothing to remove
1626
- `);
1627
- }
1628
- }
1629
-
1630
1601
  // src/cli.ts
1631
1602
  var pkgDir = dirname(fileURLToPath(import.meta.url));
1632
1603
  var pkg = JSON.parse(
@@ -1637,7 +1608,7 @@ program.name("mcpill").version(pkg.version);
1637
1608
  program.command("init").description("Scaffold a new .mcpill/ directory").option("--dir <path>", "Target directory").action(async (opts) => {
1638
1609
  await runInit(opts);
1639
1610
  });
1640
- program.command("run").description("Start the MCP server").option("--transport <type>", "Transport: stdio (default) or http").option("--port <n>", "Port number (HTTP only)", parseInt).option("--dir <path>", "Project root containing .mcpill/").option("--register", "Write server entry to Claude config so Claude manages the process").action(
1611
+ program.command("run").description("Start the MCP server").option("--transport <type>", "Transport: stdio (default) or http").option("--port <n>", "Port number (HTTP only)", parseInt).option("--dir <path>", "Project root containing .mcpill/").action(
1641
1612
  async (opts) => {
1642
1613
  await runServer(opts);
1643
1614
  }
@@ -1649,9 +1620,6 @@ program.command("validate").description("Validate .mcpill/ configuration").optio
1649
1620
  program.command("compile").description("Compile server.md \u2194 .mcpill/ files").option("--dir <path>", "Directory containing server.md and .mcpill/").option("--to-md", "Reverse: generate server.md from .mcpill/ files").option("--strict", "Error on missing tool handlers instead of generating stubs").option("--no-hooks", "Skip writing the PreToolUse hook to .claude/settings.json").action(async (opts) => {
1650
1621
  await runCompile({ ...opts, noHooks: opts.hooks === false });
1651
1622
  });
1652
- program.command("unregister").description("Remove server entry from Claude config (undo mcpill run --register)").option("--dir <path>", "Project root containing .mcpill/").action(async (opts) => {
1653
- await runUnregister(opts);
1654
- });
1655
1623
  program.command("pack").description("Prepare pill for npm distribution").option("--dir <path>", "pill project root", ".").action(({ dir }) => runPack(dir));
1656
1624
  program.command("publish").description("Pack and publish pill to npm").option("--dir <path>", "pill project root", ".").option("--access <level>", "npm access level", "public").action(({ dir, access }) => runPublish(dir, access));
1657
1625
  program.parseAsync(process.argv).catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcpill",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "type": "module",
5
5
  "description": "CLI for building, validating, and publishing MCP servers using the pill format.",
6
6
  "homepage": "https://mcpill.ruco.dev",