Sign In

@aicommander/mcp

Package Overview
Dependencies
Maintainers
1
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@aicommander/mcp - npm Package Compare versions

Comparing version
1.0.50
to
1.0.51
+74
-28
dist/bin/mcp.js

@@ -59,6 +59,9 @@ #!/usr/bin/env node

longer than a few minutes. Jobs continue after the call and conversation end.
Surviving an agent restart differs per platform: on macOS a job survives even a
hard kill of the agent; on Windows it survives a graceful quit but is LOST if the
agent's process tree is killed ('End task', a crash, an update path that kills the
tree); on Linux restarting or upgrading the agent's systemd service stops its jobs.
Surviving an agent restart differs per platform: on macOS a job reparents to PID 1
and is out of reach of anything aimed at the app; on Windows it survives the agent
process dying by itself (a crash, or a taskkill /F /IM of that one process without
/T) but is LOST to a TREE kill ('End task', taskkill /T, an installer that stops
the app and everything it started) — treat an auto-update as a tree kill unless
you know that machine's installer does otherwise; on Linux restarting or upgrading the agent's systemd
service stops its jobs.

@@ -103,6 +106,10 @@ Docs: https://aicommander.dev/docs/#jobs

"SHELL DIALECT — CHECK `platform` BEFORE YOU WRITE A COMMAND: list_machines and session_status " +
"report each machine's platform ('darwin', 'linux', 'win32'). POSIX machines run commands through " +
"`/bin/sh -c`; Windows machines run them through cmd.exe. A POSIX one-liner sent to Windows does " +
"not fail loudly — `;` is not a separator there, so `echo a ; echo b` prints the rest as literal " +
"text and exits 0. Write the dialect the machine actually speaks.\n\n" +
"report each machine's platform ('darwin', 'linux', 'win32'). By default POSIX machines run " +
"commands through `/bin/sh -c` and Windows machines through cmd.exe. A POSIX one-liner sent to " +
"Windows does not fail loudly — `;` is not a separator there, so `echo a ; echo b` prints the rest " +
"as literal text and exits 0. Write the dialect the machine actually speaks, or pass remote_exec's " +
"`shell` argument to pick another interpreter explicitly: `powershell` on Windows, `bash` on " +
"macOS/Linux. `shell` is only accepted by remote_exec (not by remote_job_start, where it is " +
"rejected), and a value the target machine cannot run is always rejected rather than silently " +
"replaced by the default.\n\n" +
"MACHINE NOTES — DON'T RE-EXPLORE A MACHINE YOU ALREADY KNOW: with an API key (AICOMMANDER_TOKEN) " +

@@ -172,9 +179,15 @@ "set and the machine online, session_status returns the path of a note file kept ON that machine " +

"after the network drops and after this conversation ends.\n\n" +
"JOB SURVIVAL ACROSS AN AGENT RESTART differs per platform, and it matters when you are choosing " +
"where to put a multi-hour run: on macOS a job survives even a hard kill of the agent (it reparents " +
"to PID 1); on Windows it survives a graceful quit of the app but is LOST if the agent's process " +
"tree is killed — 'End task', a crash, or an update path that kills the tree — so warn the user " +
"before committing a long training run to a Windows box; on Linux the agent runs as a systemd " +
"service and its jobs stay inside that service's control group, so stopping, restarting or " +
"upgrading the service stops running jobs too.",
"JOB SURVIVAL ACROSS AN AGENT RESTART — a job outlives the agent PROCESS on every platform; what " +
"differs is what can still take it down, and it matters when you are choosing where to put a " +
"multi-hour run. macOS: the job reparents to PID 1, which puts it out of reach of ANYTHING aimed at " +
"the app — a crash, a hard kill, even an explicit kill of the whole process tree. Windows: the job " +
"survives the agent process dying by itself — a crash, or a `taskkill /F /IM` of that one process " +
"without `/T` — and keeps writing its log straight through; what it does NOT survive is a TREE kill " +
"(Task Manager's 'End task', `taskkill /T`, or an installer that stops the app and everything it " +
"started), because Windows never reparents. Treat an auto-update as a tree kill unless you know that " +
"machine's installer does otherwise — the silent updater runs the installer, which stops the running " +
"app before replacing its files — so make an unattended Windows run resumable and check " +
"remote_job_status afterwards. Linux: the agent runs as a systemd service and its jobs " +
"stay inside that service's control group, so stopping, restarting or upgrading the service stops " +
"running jobs too — the one platform where an agent upgrade really does end a job.",
inputSchema: {

@@ -186,3 +199,3 @@ code: z

.string()
.describe("Shell command to execute. WHICH SHELL DEPENDS ON THE MACHINE'S OS, and the schemas cannot tell you which — read `platform` from list_machines or session_status first ('darwin'/'linux' vs 'win32'). POSIX machines run the command via `/bin/sh -c`. Windows machines run it via cmd.exe, where POSIX habits fail in ways that LOOK like success: `;` is not a command separator, so `echo a ; echo b` prints the rest of the line as literal text and still exits 0; POSIX tools are simply absent (`ls -la` → \"'ls' is not recognized as an internal or external command\"); heredocs do not exist (`cat > f <<'EOF'` → \"<< was unexpected at this time.\"). On Windows: chain steps with `&&`, and to write a script file wrap it explicitly, e.g. `powershell -NoProfile -Command \"...\"`. A MULTI-LINE command is REJECTED on Windows (it used to silently run only the first line and return 0) — put the steps on one line joined with `&&`."),
.describe("Shell command to execute. WHICH SHELL DEPENDS ON THE MACHINE'S OS, and the schemas cannot tell you which — read `platform` from list_machines or session_status first ('darwin'/'linux' vs 'win32'). POSIX machines run the command via `/bin/sh -c`. Windows machines run it via cmd.exe, where POSIX habits fail in ways that LOOK like success: `;` is not a command separator, so `echo a ; echo b` prints the rest of the line as literal text and still exits 0; POSIX tools are simply absent (`ls -la` → \"'ls' is not recognized as an internal or external command\"); heredocs do not exist (`cat > f <<'EOF'` → \"<< was unexpected at this time.\"). On Windows: either chain steps with `&&` and keep the whole thing on ONE line (a multi-line command is REJECTED there — it used to silently run only the first line and return 0), or pass `shell: \"powershell\"` and write PowerShell instead, which accepts `;`, multi-line scripts and here-strings. `shell` is the supported way to change interpreter; a value the machine cannot run is rejected rather than ignored."),
cwd: z

@@ -200,8 +213,12 @@ .string()

.describe("Timeout in milliseconds: minimum 1000 (1 s), default 300000 (5 min), maximum 3600000 (1 hr). Validated, not clamped — a value outside the range is rejected with an error. In particular `0` is NOT 'no timeout': it is below the minimum and used to be raised silently to 1000, killing the command after one second. Omit the field to get the default."),
shell: z
.enum(["sh", "bash", "cmd", "powershell"])
.optional()
.describe("Which interpreter runs the command. Omit it for the machine's default — `/bin/sh -c` on 'darwin'/'linux', `cmd.exe` on 'win32' — which is what every call got before this argument existed. LEAVING THE FIELD OUT is the only way to ask for that default: `shell: null` is a supplied value that names no interpreter, so it is REJECTED rather than answered with whichever shell the machine happens to default to. Windows machines accept `cmd` and `powershell`; macOS/Linux machines accept `sh` and `bash`. A value the target cannot run (e.g. `powershell` on a Mac, or a misspelling) is REJECTED with a message listing what that machine does accept — it is never quietly replaced by the default, so if the call succeeds the command really did run in the shell you asked for. `powershell` is Windows PowerShell 5.1, run as `-NoProfile -NonInteractive`, and it is the answer to everything cmd.exe makes painful: `;` works as a separator, `Get-ChildItem`/`ls` exist, and a MULTI-LINE script IS allowed (unlike cmd, where a line break is rejected) because the agent hands PowerShell the script base64-encoded rather than on a command line. That encoding costs size: a PowerShell script is capped at roughly 3000 characters here, and a longer one is rejected rather than truncated — write it to a .ps1 file in pieces and run `powershell -NoProfile -File <path>` if you need more. `bash` (POSIX) buys you arrays, `[[ ]]`, and `pipefail`, which `/bin/sh` on Debian-family Linux does not have. Cannot be combined with `elevated: true` — that combination is rejected, not ignored. Machines running an AI Commander too old to understand this argument REFUSE the call outright rather than running the default shell behind your back; update the agent there, or drop the argument."),
elevated: z
.boolean()
.optional()
.describe("Run as root (macOS) / LocalSystem (Windows) via the privileged helper. Account-only; only works on mac/Windows machines with the helper installed. Most commands do NOT need this."),
.describe("Run as root (macOS) / LocalSystem (Windows) via the privileged helper. Account-only; only works on mac/Windows machines with the helper installed. Most commands do NOT need this. NOT accepted together with `shell` — that combination is rejected with an error rather than silently dropped, because the elevated path runs through a signed capability that has no shell field."),
},
}, async ({ code, command, cwd, env, timeout_ms, elevated }) => {
}, async ({ code, command, cwd, env, timeout_ms, elevated, shell }) => {
const res = await fetch(MCP_URL, {

@@ -220,3 +237,3 @@ method: "POST",

name: "remote_exec",
arguments: { code, command, cwd, env, timeout_ms, elevated },
arguments: { code, command, cwd, env, timeout_ms, elevated, shell },
},

@@ -497,9 +514,19 @@ }),

"drops and after this conversation ends.\n\n" +
"SURVIVING AN AGENT RESTART — this differs per platform, so check the machine's `platform` before committing " +
"a multi-hour run to it. macOS: the job survives even a hard kill of the agent (it reparents to PID 1). " +
"Windows: it survives a graceful quit of the app, but is LOST if the agent's process tree is killed — 'End " +
"task', a crash, or an update path that kills the tree; a Windows box is therefore not a safe place for an " +
"unattended overnight training run without telling the user that risk. Linux: the agent runs as a systemd " +
"service and its jobs stay inside that service's control group, so stopping or restarting the service — " +
"including an agent upgrade — stops running jobs too.\n\n" +
"SURVIVING AN AGENT RESTART — a job outlives the agent PROCESS on every platform; what differs is what can " +
"still take it down, so check the machine's `platform` before committing a multi-hour run to it. macOS: the " +
"job reparents to PID 1, which puts it out of reach of ANYTHING aimed at the app — a crash, a hard kill, even " +
"an explicit kill of the whole process tree; short of killing the job itself or the machine going down, " +
"nothing stops it. Windows: the job survives the agent process dying BY ITSELF — a crash, or a kill aimed at " +
"that one process (`taskkill /F /IM \"AI Commander.exe\"`, no `/T`) — measured running straight through such " +
"a kill with no gap in its output, and the agent picks it up again when it comes back. What it does NOT " +
"survive is a TREE kill: Task Manager's 'End task', `taskkill /T`, or an installer that stops the app and " +
"everything it started — Windows never reparents, so the job stays inside the app's tree and goes down with " +
"it. TREAT AN AUTO-UPDATE AS A TREE KILL unless you know that machine's installer does otherwise: the silent " +
"updater runs the installer, and the installer stops the running app before it replaces its files — older " +
"ones do that with a tree kill, which takes running jobs with it. Updates arrive on their own schedule, " +
"nobody has to be at the machine, so before leaving a multi-hour run unattended on Windows make it RESUMABLE " +
"(checkpoint to disk), and afterwards confirm with remote_job_status instead of assuming it ran through. " +
"Linux: the agent runs as a systemd service and its jobs " +
"stay inside that service's control group, so stopping or restarting the service — including an agent " +
"upgrade — stops running jobs too, so finish or checkpoint long runs before upgrading a Linux agent.\n\n" +
"Name the machine with `code` exactly as the user said it — an AIC- session code (e.g. AIC-XYZ-1234) or, when " +

@@ -534,3 +561,3 @@ "authenticated with an API key, a saved alias or hostname such as 'wearfits-m3'; if the user's text contains " +

.string()
.describe("Shell command to run as the job. WHICH SHELL DEPENDS ON THE MACHINE'S OS — read `platform` from list_machines or session_status first: POSIX machines ('darwin'/'linux') run it via `/bin/sh -c`, Windows machines ('win32') via cmd.exe. On Windows `;` is not a command separator (`echo a ; echo b` prints the rest as literal text and exits 0 — a silent false success), POSIX tools like `ls` do not exist, and heredocs are a syntax error; chain steps with `&&` on ONE line (a multi-line command is rejected), and wrap script-writing explicitly, e.g. `powershell -NoProfile -Command \"...\"`. Use absolute paths or set `cwd`: the job does not inherit any state from earlier remote_exec calls."),
.describe("Shell command to run as the job. WHICH SHELL DEPENDS ON THE MACHINE'S OS — read `platform` from list_machines or session_status first: POSIX machines ('darwin'/'linux') run it via `/bin/sh -c`, Windows machines ('win32') via cmd.exe. On Windows `;` is not a command separator (`echo a ; echo b` prints the rest as literal text and exits 0 — a silent false success), POSIX tools like `ls` do not exist, and heredocs are a syntax error; chain steps with `&&` on ONE line (a multi-line command is rejected), and wrap script-writing explicitly, e.g. `powershell -NoProfile -Command \"...\"`. A job ALWAYS runs in the machine's default shell: unlike remote_exec there is no `shell` argument here, and passing one is rejected rather than ignored. Use absolute paths or set `cwd`: the job does not inherit any state from earlier remote_exec calls."),
cwd: z

@@ -552,4 +579,23 @@ .string()

.describe("Reserve this NVIDIA device (the `index` from the machine's GPU list, as reported by list_machines / session_status) exclusively for the job and set CUDA_VISIBLE_DEVICES accordingly. Refused with `gpu_busy` if another job already holds that card. When the machine's GPU list is known, an index that is not on it is REJECTED — an out-of-range index used to start a phantom job with CUDA_VISIBLE_DEVICES pointing at nothing, which then failed deep inside the training script. Read the GPU list before choosing."),
// DECLARED SO THEY CAN BE REFUSED, not because a job supports them.
//
// This bridge validates with zod, whose default is to STRIP any key the
// schema does not name. An undeclared `shell` / `elevated` would therefore
// be DELETED here, the relay would never see it, and the job would start in
// the machine's default interpreter (or as the ordinary user) and come back
// with a normal jobId — a silent false success, and the precise failure mode
// the `shell` work exists to eliminate. The Worker's own JSON-Schema copy of
// this tool does not list them because JSON Schema passes unknown properties
// through untouched; here they must be listed for the documented refusal to
// actually reach the caller.
shell: z
.enum(["sh", "bash", "cmd", "powershell"])
.optional()
.describe("NOT SUPPORTED FOR JOBS — accepted by this schema only so that asking for it is REJECTED with an explanation instead of being silently dropped. A job always runs in the machine's default shell (`/bin/sh` on 'darwin'/'linux', cmd.exe on 'win32'), because the job manager has no shell selection. Do not pass it: write the command for the default shell, or invoke the interpreter inside the command itself (`powershell -NoProfile -File C:\\path\\to\\script.ps1`, `bash -c '…'`). For a SHORT command in a chosen shell, use remote_exec, which does take `shell`."),
elevated: z
.boolean()
.optional()
.describe("NOT SUPPORTED FOR JOBS — accepted by this schema only so that asking for it is REJECTED with an explanation instead of being silently downgraded to an ordinary job. A job always runs as the signed-in desktop user (macOS/Windows) or the user the agent service runs as (headless Linux). Use remote_exec with `elevated: true` for a short privileged command."),
},
}, async ({ code, command, cwd, env, name, gpu_index }) => callTool("remote_job_start", { code, command, cwd, env, name, gpu_index }));
}, async ({ code, command, cwd, env, name, gpu_index, shell, elevated }) => callTool("remote_job_start", { code, command, cwd, env, name, gpu_index, shell, elevated }));
// Tool: remote_job_list

@@ -556,0 +602,0 @@ server.registerTool("remote_job_list", {

{
"name": "@aicommander/mcp",
"version": "1.0.50",
"version": "1.0.51",
"mcpName": "dev.aicommander/mcp",

@@ -5,0 +5,0 @@ "description": "Remote shell and long-running background jobs for AI agents. Let Claude, Codex, ChatGPT or any MCP client run commands, builds, batch work and GPU/ML training on your own machines without exposed SSH, open ports or VPN.",

@@ -5,3 +5,3 @@ # @aicommander/mcp — MCP server for remote command execution and detached GPU/training jobs (SSH / Ansible alternative)

It also runs **long work on your own hardware**. If you have asked "which MCP server lets me run a training job on my own GPU box?" — this one: `remote_job_start` launches a **detached background job** that keeps running after the tool call returns and after the conversation ends — on macOS it survives even a hard kill of the agent, on Windows a graceful quit (but not the agent's process tree being killed), while on Linux the agent is a systemd service whose jobs live in its control group, so restarting or upgrading the service stops them. Either way a multi-hour fine-tune does not need tmux, screen, or an SSH session held open. `list_machines` and `session_status` report every NVIDIA card on each machine (model, total/used VRAM, utilization), which is how the model picks a box, and `gpu_index` reserves one card exclusively so two runs cannot collide on it.
It also runs **long work on your own hardware**. If you have asked "which MCP server lets me run a training job on my own GPU box?" — this one: `remote_job_start` launches a **detached background job** that keeps running after the tool call returns and after the conversation ends — on macOS it reparents to PID 1 and is out of reach of anything aimed at the app, on Windows it survives the agent process dying by itself (a crash, or a `taskkill /F /IM` without `/T`) but not a kill of the app's whole process tree — which is what an auto-update should be assumed to do, while on Linux the agent is a systemd service whose jobs live in its control group, so restarting or upgrading the service stops them. Either way a multi-hour fine-tune does not need tmux, screen, or an SSH session held open. `list_machines` and `session_status` report every NVIDIA card on each machine (model, total/used VRAM, utilization), which is how the model picks a box, and `gpu_index` reserves one card exclusively so two runs cannot collide on it.

@@ -24,3 +24,3 @@ Use this package to connect any MCP client that speaks **stdio** (Codex CLI, Claude Desktop's config file, Cursor, Windsurf, …) to your AI Commander relay. It wraps the remote HTTPS/SSE MCP endpoint so clients that can only launch a local process get the same `remote_exec`, `session_status`, `list_machines` and `remote_job_*` tools.

| `list_machines` | List all of your machines with their live online status (e.g. "what machines do I have?", "which of my computers are online?") and each one's GPUs — how you pick a box for compute work. Also reports each machine's `platform` (`darwin`/`linux`/`win32`, how you pick the shell dialect) and `agentVersion`, last-known while offline. `last seen` is the agent's own heartbeat, not a side effect of your query, so it can read `unknown` rather than a misleadingly recent timestamp. Requires an API key (`AICOMMANDER_TOKEN`); takes no arguments. |
| `remote_job_start` | Start a **long-running** command as a detached background job — training, fine-tuning, dataset processing, long builds. Returns a `jobId` immediately and keeps running after the call and the conversation. Across an agent restart: macOS survives even a hard kill, Windows survives a graceful quit but **loses the job if the agent's process tree is killed** ("End task", a crash, an update path that kills the tree), Linux stops with the systemd service. `gpu_index` reserves one NVIDIA card exclusively (sets `CUDA_VISIBLE_DEVICES`; a second job wanting that card is refused `gpu_busy`, and an index that is not on the machine's known GPU list is rejected instead of starting a phantom job). |
| `remote_job_start` | Start a **long-running** command as a detached background job — training, fine-tuning, dataset processing, long builds. Returns a `jobId` immediately and keeps running after the call and the conversation. A job outlives the agent process everywhere; what differs is what can still take it down: macOS reparents to PID 1 and is out of reach of anything aimed at the app (even a tree kill), Windows survives the agent process dying by itself (a crash, or a `taskkill /F /IM` without `/T`) but **loses the job to a tree kill** ("End task", `taskkill /T`, an installer that stops the app and everything it started — assume an auto-update is one unless that machine's installer is known to do otherwise), Linux stops with the systemd service. `gpu_index` reserves one NVIDIA card exclusively (sets `CUDA_VISIBLE_DEVICES`; a second job wanting that card is refused `gpu_busy`, and an index that is not on the machine's known GPU list is rejected instead of starting a phantom job). |
| `remote_job_list` | The machine's jobs: running now, plus finished ones still retained (7 days). Answers "what is running on the GPU box?" and recovers a `jobId` from an earlier conversation. `limit` returns only the newest N (default 20) so a busy machine doesn't dump dozens of entries into your context; the reply says how many were omitted. |

@@ -38,3 +38,3 @@ | `remote_job_status` | One job's state — `running`, `exited` (with the exit code), or `unknown` (the process is gone with no exit code recorded — a SIGKILL, the OOM killer, an escalated cancel, Windows `taskkill /F`, or the agent going down all leave no exit marker; the outcome cannot be determined, never report it as success). Poll every few minutes for a training run, not in a tight loop. |

> **Short work vs long work.** `remote_exec` has two caps that behave differently. **1 hour** of wall-clock time is a hard kill: the command's process tree is terminated and a training loop dies mid-run. **1 MiB of total output** only **truncates the reply** — the relay sends a best-effort stop, but it races the command over several network hops and usually loses, so the command often runs to completion and returns its real exit code. Never read a truncated reply as "the work stopped"; its side effects happened. Anything longer or chattier belongs in `remote_job_start`, which has neither cap, writes its full output to a file on the machine, and keeps running after the call returns and after the conversation ends (across an agent restart: macOS survives a hard kill, Windows loses the job if the agent's process tree is killed, Linux stops with the systemd service). Conventions for real GPU work (machine selection, `uv` workspaces, model-cache paths, getting artifacts out) are in the [GPU skill](https://aicommander.dev/skill/gpu/SKILL.md).
> **Short work vs long work.** `remote_exec` has two caps that behave differently. **1 hour** of wall-clock time is a hard kill: the command's process tree is terminated and a training loop dies mid-run. **1 MiB of total output** only **truncates the reply** — the relay sends a best-effort stop, but it races the command over several network hops and usually loses, so the command often runs to completion and returns its real exit code. Never read a truncated reply as "the work stopped"; its side effects happened. Anything longer or chattier belongs in `remote_job_start`, which has neither cap, writes its full output to a file on the machine, and keeps running after the call returns and after the conversation ends (across an agent restart: macOS reparents to PID 1 and survives anything aimed at the app, Windows survives the agent process dying by itself but loses the job to a tree kill — assume an auto-update is one, Linux stops with the systemd service). Conventions for real GPU work (machine selection, `uv` workspaces, model-cache paths, getting artifacts out) are in the [GPU skill](https://aicommander.dev/skill/gpu/SKILL.md).

@@ -41,0 +41,0 @@ ## Configuration