@akms/mcp-ssh 0.1.5 → 0.1.6

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
@@ -84,7 +84,7 @@ Only entries that launch this package are listed or touched. An edit keeps every
84
84
 
85
85
  `SSH_HOST` + `SSH_USER` is the whole minimum, and the **private key stays a file on disk** — only its path goes in the config. Because each server fronts one host, the agent never names it: `ssh_exec({ command: "df -h" })` is a complete call. The server name (`ssh-prod`) is what the agent sees in its tool list, so name it after the host.
86
86
 
87
- The server speaks MCP over **stdio**: stdout carries the JSON-RPC stream and all logging goes to stderr. Running it by hand in a terminal only prints the startup line and waits.
87
+ The server speaks MCP over **stdio**: stdout carries the JSON-RPC stream and all logging goes to stderr. Running it by hand in a terminal only prints the startup line and waits. When the client goes away — stdin ends, or the process is simply gone — the server closes its sessions and exits within a few seconds on its own; a stderr nobody reads is never a reason to stay. Its log says `mcp server ready on stdio` with its `pid`, for when one has to be found among a client's processes.
88
88
 
89
89
  ## 🗂️ Host configuration
90
90
 
package/lib/cli.cjs CHANGED
@@ -31,11 +31,12 @@ var DEFAULT_MAX_READ_FILE_BYTES = 2e5;
31
31
  var KEEPALIVE_INTERVAL_MS = 15e3;
32
32
  var SESSION_IDLE_TIMEOUT_MS = 15 * 60 * 1e3;
33
33
  var SESSION_SWEEP_INTERVAL_MS = 6e4;
34
+ var SHUTDOWN_DEADLINE_MS = 5e3;
34
35
  var CWD_MARKER = "__AKMS_SSH_CWD__";
35
36
  var MAX_DIRECTORY_ENTRIES = 1e3;
36
37
  var SERVER_NAME = "akms-mcp-ssh";
37
38
  var PACKAGE_NAME = "@akms/mcp-ssh";
38
- var SERVER_VERSION = "0.1.5";
39
+ var SERVER_VERSION = "0.1.6";
39
40
 
40
41
  // src/_defs/command-rules.ts
41
42
  var CRITICAL_PATH_PATTERN = new RegExp(
@@ -498,6 +499,10 @@ var LEVEL_WEIGHTS = {
498
499
  error: 4
499
500
  };
500
501
  var SECRET_META_KEYS = ["password", "passphrase", "privatekey", "privatekeypath", "secret", "token"];
502
+ var stderrBroken = false;
503
+ process.stderr.on("error", () => {
504
+ stderrBroken = true;
505
+ });
501
506
  function resolveThreshold() {
502
507
  const configured = process.env[LOG_LEVEL_ENV];
503
508
  if (configured == null) {
@@ -521,6 +526,9 @@ function maskMeta(meta) {
521
526
  return masked;
522
527
  }
523
528
  function write(level, message, meta) {
529
+ if (stderrBroken == true) {
530
+ return;
531
+ }
524
532
  const threshold = resolveThreshold();
525
533
  if (threshold === "silent") {
526
534
  return;
@@ -534,8 +542,12 @@ function write(level, message, meta) {
534
542
  if (Object.keys(masked).length > 0) {
535
543
  line += ` ${JSON.stringify(masked)}`;
536
544
  }
537
- process.stderr.write(`${line}
545
+ try {
546
+ process.stderr.write(`${line}
538
547
  `);
548
+ } catch {
549
+ stderrBroken = true;
550
+ }
539
551
  }
540
552
  function createLogger(boundMeta) {
541
553
  return {
@@ -5774,6 +5786,12 @@ async function main() {
5774
5786
  instance.shutdown();
5775
5787
  process.exit(exitCode);
5776
5788
  }
5789
+ process.on("uncaughtException", (error) => {
5790
+ _logger.error(error, "uncaught exception; the server keeps running");
5791
+ });
5792
+ process.on("unhandledRejection", (reason) => {
5793
+ _logger.error(reason, "unhandled rejection; the server keeps running");
5794
+ });
5777
5795
  let shuttingDown = false;
5778
5796
  const shutdown = (signal) => {
5779
5797
  if (shuttingDown == true) {
@@ -5781,6 +5799,10 @@ async function main() {
5781
5799
  }
5782
5800
  shuttingDown = true;
5783
5801
  _logger.info("shutting down", { signal });
5802
+ setTimeout(() => {
5803
+ _logger.warn("shutdown did not finish in time; exiting anyway", { deadline_ms: SHUTDOWN_DEADLINE_MS });
5804
+ process.exit(0);
5805
+ }, SHUTDOWN_DEADLINE_MS).unref();
5784
5806
  instance.shutdown();
5785
5807
  void instance.server.close().finally(() => {
5786
5808
  process.exit(0);
@@ -5800,7 +5822,8 @@ async function main() {
5800
5822
  _logger.info("mcp server ready on stdio", {
5801
5823
  server: SERVER_NAME,
5802
5824
  version: SERVER_VERSION,
5803
- profile: instance.profile?.name ?? null
5825
+ profile: instance.profile?.name ?? null,
5826
+ pid: process.pid
5804
5827
  });
5805
5828
  }
5806
5829
  main().catch((error) => {