terminal-commands
v0.3.0
Published
A cross-platform, approval-gated terminal companion for ChatGPT, Claude, Codex, and any MCP-compatible AI client.
Readme
Terminal Commands companion
A companion that connects your Windows, macOS, or Linux machine to ChatGPT, Codex, Claude, or another MCP-compatible AI client. Browse approved folders, read text files, and approve commands and file writes on your own machine.
- Website and setup guide: terminalcommands.fr
- Your dashboard: mcp.terminalcommands.fr
- Hosted MCP server URL:
https://mcp.terminalcommands.fr/mcp - Help: Support
The companion runs on the machine you want to access. Your AI client connects to the hosted MCP server, which routes requests to machines paired with your Terminal Commands account. The companion makes an outbound connection; the hosted setup needs no public port, port forwarding, tunnel, or self-hosted server.
Quick start from npm
You need Node.js 20 or later, npm, and an existing folder to share. The npm package includes native helpers for Windows, macOS, and Linux on x64 and arm64; you do not need Go or a compiler.
1. Install and sign in
Run these commands on the machine you want to connect:
npm install --global terminal-commands@latest
terminal-commands loginOpen the URL printed in your terminal and approve the one-time code. Use the same Terminal Commands account when connecting your AI client and signing in to the dashboard. The hosted service's OAuth settings are built in; you do not need an OpenAI API key or to copy access tokens.
Prefer running without a global install? Use npx --yes terminal-commands@latest login
and npx --yes terminal-commands@latest connect with the same options shown below.
2. Connect a folder
Open a terminal in the project folder you want to share, then run:
terminal-commands connect --root . --allow-destructive--root . approves the current folder. --allow-destructive enables an interactive
approval prompt for each command or file write. Review the requested executable,
arguments, and working directory before approving. Omit that flag for read-only access;
commands and file writes will be refused.
You can also use an existing absolute path. For example, on macOS or Linux:
terminal-commands connect --root "$HOME/projects" --name "My laptop" --allow-destructiveOr in Windows PowerShell:
terminal-commands connect --root "$HOME\projects" --name "My PC" --allow-destructiveReplace the example path with your own folder. Repeat --root to approve more folders.
Keep the terminal open while using the connection. It shows connection activity,
approval requests, command stdout/stderr, exit status, and errors. Ctrl+C stops the
companion connection. Disconnecting is not a guarantee that an already-started program
has stopped; check the machine if a task's outcome is unknown.
3. Connect your AI client
Choose either the directory plugin or a manual MCP connection below. Both use the same hosted service and paired machines; installing the npm package alone does not add a plugin to your AI client.
Use the published plugin
When Terminal Commands is available in your ChatGPT or Codex Plugins directory:
- Open Plugins, search for Terminal Commands, and install it.
- Connect your account when prompted, using the same Terminal Commands account as
terminal-commands login. - Start a new chat, type
@, and select Terminal Commands. - Ask it to list your connected machines and approved folders.
Directory availability depends on publication, review, and workspace policies. If you cannot find the listing, add the hosted MCP server manually using the next section.
Add the MCP server manually
ChatGPT
- Open Plugins in ChatGPT.
- Select the plus button, then Add custom MCP server.
- Enter Terminal Commands as the name and a description such as “Access my paired machines with local command approval.”
- Choose a public server connection and enter the full server URL:
https://mcp.terminalcommands.fr/mcp. - Choose OAuth authentication. Review the warning, select I understand and want to continue, then Create as a plugin.
- Complete the sign-in using the same Terminal Commands account as the companion, review the discovered tools, and install the resulting plugin.
- Start a new Work chat, type
@, and select your plugin.
Custom MCP connections are subject to your account and workspace policies. See OpenAI's connection instructions for the current interface and metadata refresh steps.
Other MCP clients
In a client that supports remote MCP over Streamable HTTP and OAuth, add:
- Name: Terminal Commands
- Server URL:
https://mcp.terminalcommands.fr/mcp - Transport: Streamable HTTP
- Authentication: OAuth; sign in with the companion's Terminal Commands account
Use your client's remote-server setup instructions. Clients that only support stdio cannot connect directly to the hosted HTTPS endpoint.
The dashboard URL (https://mcp.terminalcommands.fr/) is a web page. Use the URL ending
in /mcp for MCP clients. The local http://127.0.0.1:3333/mcp service belongs to
the companion and is not the hosted endpoint to paste into ChatGPT.
Website and dashboard
Visit terminalcommands.fr for the product overview and setup guide. Open your dashboard and select Sign in to your workspace with the same account used by the companion.
The dashboard provides:
- Overview: a summary of your devices and command sessions.
- Devices: paired machines and their connection status, including offline devices.
- Sessions: command history, task status, approval details, errors, and exit codes.
- Live terminal: follow a selected session's bounded stdout/stderr and copy its output.
You can request cancellation of an active task from its session details. The companion's local approval policy still applies; cancellation is confirmed only when the machine reports that the process stopped. Device revocation is available with the required owner permission and fresh authentication.
The dashboard displays your account's activity; it does not replace the running companion or its local approval prompts. Output is bounded and retained for seven days by default. See the hosted service's privacy policy and terms.
Try your first request
With the companion running and the plugin selected, try:
List my connected machines and their approved folders.
Then, if Git is installed and you enabled command approval:
Run git status in my approved project folder. Show stdout, stderr, and the exit code.
Approve the request in the companion terminal. Commands return a task ID first; the AI
client uses wait_process_task or get_process_task with that same ID to retrieve
progress and results. If you only see a task ID, ask it to wait for that task and show
the output. You can also follow the session in the dashboard. Do not submit the command
again just because it is still running or its outcome is unknown.
Troubleshooting
- No machine appears, or it is offline: keep
terminal-commands connectrunning and check its connection messages. Confirm that the companion, dashboard, and MCP connection use the same account. Runterminal-commands statusin another terminal to inspect local sign-in state and settings; it is not a live connection check. - Commands or writes are refused: restart with
--allow-destructivein an interactive terminal and approve the request there. Without that flag the connection is read-only. A rejected approval or security error is reported in the task result. - A folder or program is blocked: choose an existing folder within an approved
--root, and check any--allowed-programrestriction. Shells,sudo, andsuare blocked by default. Ask for the executable and its arguments directly. - No output is visible yet: check the companion terminal and dashboard, then ask the AI client to wait for the existing task ID. A program may buffer output or simply produce none. Check the task status and exit code as well as stdout/stderr.
- Sign-in expired or authentication failed: stop the companion, run
terminal-commands login, and reconnect with your chosen roots and options. Reconnect the plugin's OAuth account if the client reports an authentication error. - The local service cannot start: check the printed error and whether port
3333is already in use. Choose another port withconnect --port 3334and your usual roots and approval options. - The directory listing is unavailable: use the manual hosted MCP setup above. A missing listing does not require hosting your own server.
To update, stop the companion, run npm install --global terminal-commands@latest,
then start connect again with your chosen roots and options. If you use npx, run
npx --yes terminal-commands@latest connect with those options instead.
For more help, visit Support. Include your OS, Node.js version, error text, and task ID when relevant. Keep credentials and sensitive command output private.
Security model
- Binds to
127.0.0.1by default and refuses public interfaces unless explicitly enabled. - The hosted connection uses OAuth and routes requests only to the authenticated user's paired machines.
- Marks command execution and file writes as destructive and also requires approval in the companion terminal.
- Runs programs without a shell. Shells,
sudo, andsuare blocked by default. - Passes only a small environment allowlist to child processes, excluding API keys and other secrets.
- Restricts dedicated filesystem tools to the approved roots.
File reads, bounded directory listings and writes use a bundled native helper
with Go's handle-based os.Root API. Approved roots are pinned and checked
against their original file identities; a concurrent parent-directory or root
symlink swap cannot redirect an operation into another directory. File writes
truncate only an already safely opened regular file. The helper is required:
missing, crashed or unsupported helpers refuse file IO, with no path-string
fallback. Relative paths and the originally configured root aliases remain
supported. Roots should be ordinary directories on a trusted local filesystem;
this boundary does not prevent privileged mount changes or hard-link aliases.
Process working directories use the same checked root identities and native
directory handles during startup. Unix wrappers use fchdir followed by exec,
preserving the supervised PID/process group. Windows holds canonical ancestors
against rename through process creation; existing taskkill /T supervision
includes the wrapper's child. An approved executable still has the user's
ordinary OS permissions; the directory boundary is not a process sandbox.
See Go's traversal-resistant APIs for the native filesystem boundary and its mount/hard-link limitations.
Command-line programs still execute with your operating-system user permissions. A program can access more than the dedicated filesystem tools, so review every command approval carefully.
The local MCP server exposes the low-level process supervisor (start_process,
read_process_output, list_processes, and terminate_process) only to the local
companion over HTTP. The public gateway exposes submit_process, get_process_task,
wait_process_task, and terminate_process_task to submit, follow, and request cancellation
of tasks with bounded output.
There is no legacy public direct-execution API.
Build from source
Building from source requires Node.js 20+ and Go 1.27.2+. npm test builds the
host's helper, while npm pack builds all six supported release targets. The
published package includes them, so installed users need only Node.js. Helpers
are built from the reviewed native/fs-helper source without CGO or downloaded
runtime executables. Unsupported OS/architecture combinations fail closed.
npm install
npm run build
MACHINE_TERMINAL_ROOTS="$HOME" npm startThis starts only the local HTTP service. It does not connect a machine to the hosted
gateway. For the complete companion, use npm run cli -- login and
npm run cli -- connect --root . --allow-destructive after building.
The local health endpoint is http://127.0.0.1:3333/health. Its /mcp endpoint is
private to the companion's local capability, as described below.
Command line
terminal-commands login
terminal-commands connect --root ~/Documents/GitHub --root ~/workFrom a checkout, the same commands run through npm. Everything after -- reaches the CLI:
npm run cli -- connect --root ~/Documents/GitHub --allow-destructive
npm run cli -- connect --helpCommands: login, connect, status, logout, paired-devices, revoke-device, help.
Add --help to a command to see the options it accepts. logout removes saved login
credentials; it does not revoke a paired device or stop an already-running companion.
Options
| Option | Applies to | Environment variable | Meaning |
| -------------------------- | ---------------------------------------------- | ------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
| -r, --root <path> | connect, status | MACHINE_TERMINAL_ROOTS | Approved filesystem root. Repeat the flag, or separate paths with the platform path delimiter. Defaults to the current directory. |
| --name <name> | connect, status | MACHINE_TERMINAL_DEVICE_NAME | Device name shown in the client. Defaults to the hostname. |
| --device-id <id> | connect, status, revoke-device | MACHINE_TERMINAL_DEVICE_ID | Defaults to the saved local device ID for connect/status; required explicitly for revoke-device. |
| --port <port> | connect, status | MACHINE_TERMINAL_PORT | Port for the local MCP service. Defaults to 3333. |
| --host <host> | connect, status | MACHINE_TERMINAL_HOST | Bind address for the local MCP service. Defaults to 127.0.0.1. |
| --gateway-url <url> | connect, status, paired-devices, revoke-device | MACHINE_TERMINAL_GATEWAY_URL | Defaults to wss://mcp.terminalcommands.fr/device/connect. Change only for a self-hosted gateway. |
| --allow-destructive | connect | MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE | Ask in the terminal before each command and file write. Without it both are refused. |
| --auto-approve | connect | MACHINE_TERMINAL_AUTO_APPROVE | Approve every command and file write automatically, without a terminal prompt. Implies --allow-destructive. Use with caution. |
| --allow-shell | connect | MACHINE_TERMINAL_ALLOW_SHELL | Permit shells and privilege tools such as sudo. |
| --allowed-program <name> | connect | MACHINE_TERMINAL_ALLOWED_PROGRAMS | Executable allowlist. Repeat the flag or separate names with commas. |
| --allow-public-bind | connect | MACHINE_TERMINAL_ALLOW_PUBLIC_BIND | Permit a non-loopback bind. Only behind authenticated HTTPS. |
| --auth-issuer <url> | all | MACHINE_TERMINAL_AUTH_ISSUER | OAuth issuer, for a self-hosted gateway. |
| --auth-client-id <id> | all | MACHINE_TERMINAL_AUTH_CLIENT_ID | OAuth client ID. |
| --auth-audience <url> | all | MACHINE_TERMINAL_AUTH_AUDIENCE | OAuth audience. |
| --dry-run | connect | — | Print the resolved settings and exit without starting anything. |
| --json | connect, status | — | Print machine-readable JSON. |
A flag beats the environment variable, and the environment beats the default. --root replaces
MACHINE_TERMINAL_ROOTS rather than adding to it. On/off options accept --no- forms, so
--no-allow-shell overrides an environment variable that enabled shells.
Roots are expanded (~ included), resolved to absolute paths, de-duplicated, and checked before
anything starts, so a typo fails immediately instead of silently approving the wrong folder.
connect --dry-run prints exactly what would be approved:
terminal-commands connect --root . --dry-runConfiguration
Options with an environment variable in the table can also be configured that way, for example when running the companion through a service manager.
MACHINE_TERMINAL_ROOTS: path-delimited filesystem roots. Defaults to the directory where the companion starts.MACHINE_TERMINAL_HOST: bind host. Defaults to127.0.0.1.MACHINE_TERMINAL_PORT: bind port. Defaults to3333.MACHINE_TERMINAL_ALLOWED_PROGRAMS: optional comma-delimited executable allowlist.MACHINE_TERMINAL_ALLOW_SHELL=1: permits shells and privilege tools. Avoid enabling this unless necessary.MACHINE_TERMINAL_ALLOW_PUBLIC_BIND=1: permits a non-loopback bind. Do not enable without authenticated HTTPS and OAuth 2.1.MACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE=1: enables a terminal prompt for every command or file write. Non-interactive sessions deny these actions.MACHINE_TERMINAL_AUTO_APPROVE=1: approves every command and file write automatically, without prompting. ImpliesMACHINE_TERMINAL_AGENT_ALLOW_DESTRUCTIVE=1. Use with caution, and only when you trust the connected client.
Advanced connections
terminal-commands connect starts the local service and outbound gateway companion
together. This is also the normal setup for a headless server: complete login in a
browser on another device using the printed URL and code, then keep connect running
in an interactive terminal if you want per-command approvals. Non-interactive sessions
refuse approval-gated actions unless an administrator explicitly enables --auto-approve.
terminal-commands login stores OAuth credentials in a private local configuration
file. terminal-commands status shows its directory. Do not copy credentials into
MCP configuration, dashboard fields, chat messages, or shell arguments.
For a self-hosted gateway, pass its --auth-issuer, --auth-client-id, and
--auth-audience to login. Pass the same authentication options plus its
--gateway-url to connect. Remote clients need its public HTTPS /mcp URL
with OAuth support. Keep the local companion service bound to loopback.
For clients running directly on the same machine, the package also includes
dist/stdio.js. Configure your client to launch it with Node.js, using the absolute
path inside the installed package and MACHINE_TERMINAL_ROOTS for approved folders.
This local-only mode does not pair a machine with the hosted gateway or populate the
dashboard. Stdio is read-only by default; see the local approval boundary below.
Protocol-v2 production companion
The hosted gateway requires a protocol-v2 companion (0.2.0 or later). Install
terminal-commands@latest and reconnect if an older companion's handshake is rejected.
A local process follows the companion's approval policy, and a repeated task ID returns its existing session instead of spawning a duplicate. The supervisor enforces a bounded process limit (32 running and 512 retained by default) and removes old completed sessions after one hour. Task-ID tombstones remain in the live companion to prevent replay after session history expires.
The local supervisor is currently memory-backed. If the local server itself
restarts, an already-running OS process may become unsupervised; a gateway task
will remain unknown until it can be reconciled, never automatically rerun.
Local approval boundary and replay safety
The CLI generates a fresh 256-bit MACHINE_TERMINAL_LOCAL_AGENT_TOKEN for its local
MCP child and gateway companion. Only the approved companion can use destructive
tools over HTTP. Direct HTTP clients (including tunnels) are denied by default, even for
reads; stdio clients are read-only by default. Never expose the
local agent token in logs or shell arguments. Trusted local stdio administrators
can explicitly set MACHINE_TERMINAL_TRUST_STDIO=1; this grants local execution
without per-call companion confirmation and must not be enabled for untrusted clients.
The CLI also creates a private, persistent device-secret paired with the device ID.
The gateway stores only its hash, requires the matching secret on reconnect, and closes
device WebSockets when their OAuth access token expires. Losing the secret requires an
explicit re-pair: remove the server-side device registration with authorized
administrative access, then connect with a new secret. Signing out alone does not
rotate the device identity.
Process startup writes replay-resistant task receipts before executing commands.
The CLI saves them in task-receipts.json under its private config directory.
Receipts survive local-service restarts, expire after 30 days, and are bounded to
20,000 recent task IDs. The gateway retains idempotency metadata beyond output
retention, so old cloud requests still cannot be silently dispatched again.
On Unix the process supervisor runs commands in their own process groups; termination sends SIGTERM and escalates to SIGKILL after a short grace period. On Windows it uses process termination plus taskkill /T /F for forced descendant cleanup. Programs that create independent/detached processes may still escape group cleanup; Terminal Commands is not a full OS sandbox.
Revoke a lost or compromised device
From a trusted terminal, use:
terminal-commands revoke-device --device-id <paired-device-id>The command requires an interactive confirmation and a fresh Auth0 device login
with the separate terminal:devices:revoke permission. It never saves the
elevated management token. The Auth0 API and device-code client must first be
configured to grant this permission to authorized account owners.
Revocation persists on the gateway, closes the current WebSocket and rejects future connections using the revoked device ID even with its original secret. Revocation cannot terminate processes already running on the machine, revoke stolen Auth0 tokens, or block registration under another ID. Immediately also revoke compromised Auth0 credentials and investigate the host. Restoring the same ID requires a reviewed administrative reset, not a fresh login.
Use terminal-commands paired-devices to list paired device IDs even when
machines are offline, including revoked status. The CLI fetches paginated,
owner-scoped metadata with the same fresh elevated OAuth permission; secrets
and pairing credential hashes are never returned.
Saved projects and the ChatGPT workspace (v0.3.0 preview)
The workspace preview requires the v0.3.0 companion and an enabled workspace MCP connection. It is a development preview until the regular ChatGPT host test and release are complete; installing an older npm release does not enable this view.
In ChatGPT, ask Terminal Commands to open the workspace, then choose Parcourir…, your connected machine, and a folder inside its approved roots. Give it a unique name such as ABC Project. Next time, say “Terminal Commands, work on ABC Project and fix bug ABCD.” Saved projects belong to your account and remember the machine and folder identity. They do not grant access to new paths. If a folder moves or is replaced, browse it again and save a new project. Change approved roots in the companion's local configuration when necessary, then reconnect.
Starting a run stores a bounded source-text baseline on the gateway for seven days. Known credential files, dependencies and generated folders are excluded, but source files may still contain sensitive information. Coverage limits are shown in the view. The Activity tab follows approved commands, local approval, stdout/stderr and exit status. Changes compares captured source files with their state at the start of the run, preserving the distinction from existing edits. Reopen the run to recover logs. An offline or unknown status is not proof that a command stopped; reconnect and inspect the existing task before doing anything again. The dashboard remains available for older command history and device management; the website provides setup instructions and support.
