Skip to content
 
 

Repository files navigation

AI Server Commander

CI Release License: MIT

AI Server Commander is a self-hosted bridge that lets approved AI assistant clients run bounded terminal commands on a machine you control. OpenAI/ChatGPT and Anthropic/Claude are first-class client families; additional clients and protocols are welcome when they do not compromise functionality, reliability, or performance for those two.

It exposes the same execution core through two primary client adapters:

  • REST/OpenAPI for ChatGPT Custom GPT Actions and automation clients.
  • Remote MCP + OAuth for Claude and other MCP-capable clients.

The server does not provide model access or credits. It receives authenticated tool calls, applies local policy and limits, executes the requested command on the host, and returns a structured result.

Caution

AI Server Commander can execute real shell commands with the permissions of its operating-system user. It is not a sandbox. Run it as a dedicated unprivileged user, keep it behind HTTPS, enable SAFE_MODE, and expose it only to clients and users you trust.

Features

  • One bounded command executor shared by REST and MCP.
  • Inline commands and multi-line script mode.
  • Per-command timeouts and output limits.
  • Independent activity IDs for concurrent commands.
  • Targeted interruption by activityId.
  • Optional SAFE_MODE denylist for obviously destructive commands.
  • Process-group termination on timeout or interruption on POSIX hosts.
  • Structured results with output, exit code, timeout, interruption, blocking and truncation metadata.
  • Lightweight activity logs and scoped notices.
  • Generated OpenAPI document at /openapi.json.
  • Remote MCP endpoint at /mcp.
  • OAuth discovery, dynamic client registration, authorization code + PKCE, access tokens and refresh tokens.
  • MCP tool title, input/output schemas, risk annotations, OAuth security schemes and structured content.
  • Backward-compatible legacy REST endpoint for existing Custom GPT Actions.

Architecture

ChatGPT Custom GPT / REST client
              │ HTTPS + Bearer token
              ▼
        REST / OpenAPI adapter ─────┐
                                    │
Claude / remote MCP client          ├── shared bounded executor ── host shell
              │ HTTPS + OAuth       │             │
              ▼                     │             ├── SAFE_MODE
          MCP adapter ──────────────┘             ├── timeout/output caps
                                                   ├── activity log
                                                   └── notices

See docs/architecture.md for request flows, trust boundaries and the module map.

Requirements

  • Node.js 20 or newer.
  • Linux, macOS or another host with a compatible shell.
  • A public HTTPS URL for ChatGPT or remote MCP clients.
  • A dedicated, minimally privileged operating-system account for production use.

Windows may work for basic commands, but process-group termination is POSIX-specific.

Quick start

1. Clone and install

git clone https://github.com/Jhacarreiro/ai-server-commander.git
cd ai-server-commander
npm install

2. Create configuration

The first npm start launches an interactive setup and writes config.json.

npm start

For non-interactive deployment:

cp config.example.json config.json
chmod 600 config.json

Minimal configuration:

{
  "port": 3000,
  "productionDomain": "https://commander.example.com",
  "authToken": "replace-with-a-long-random-secret",
  "mcpToken": "replace-with-a-separate-long-random-secret"
}

Generate tokens with a cryptographically secure tool:

openssl rand -hex 32

3. Start with safer defaults

SAFE_MODE=true npm start

Local checks:

curl http://127.0.0.1:3000/openapi.json
npm run check
npm test

4. Put it behind HTTPS

Use a reverse proxy such as Nginx, Caddy or a managed tunnel. Forward the original host and scheme so OAuth metadata contains the correct public URL.

See docs/deployment.md for systemd, Nginx, upgrades and rollback.

Configuration

config.json

Key Required Purpose
port Yes Local TCP port used by the Node server.
productionDomain Yes Exact public origin, such as https://commander.example.com. Required for correct remote OAuth metadata behind a proxy.
authToken Yes Bearer token for REST and approval code for the built-in OAuth consent page.
mcpToken No Separate pre-shared token for MCP clients that support token auth. Falls back to authToken when omitted.

config.json contains secrets and is ignored by Git. Keep it mode 600 and never paste it into issues or logs.

LocalTunnel support was removed in v1.0.8 because its pinned HTTP dependency chain could not be updated safely. Existing configurations with useLocalTunnel: true now fail with migration guidance. Use a maintained HTTPS reverse proxy or tunnel and set productionDomain explicitly.

Environment variables

Variable Default Purpose
SAFE_MODE false Enables the built-in destructive-command denylist. Recommended for production.
COMMAND_TIMEOUT_MS 120000 Server-wide maximum command duration. Client requests can ask for less, not more.
MAX_OUTPUT_CHARS 12000 Server-wide maximum returned output.
MAX_SCRIPT_BODY_BYTES 524288 Maximum script/request body size.
OAUTH_STATE_PATH runtime/oauth-state.json Persistent OAuth client and token-hash state.
OAUTH_AUTH_CODE_TTL_SECONDS 300 Authorization-code lifetime.
OAUTH_ACCESS_TOKEN_TTL_SECONDS 3600 Access-token lifetime.
OAUTH_REFRESH_TOKEN_TTL_SECONDS 2592000 Refresh-token lifetime.
SHELL Host default Shell used for inline execution and as script-mode fallback.
NODE_ENV unset Standard Node environment label.

See .env.example. The application does not automatically load .env; set variables through your shell, process manager or service unit.

ChatGPT Custom GPT Actions

Custom GPT Actions use the REST/OpenAPI adapter and remain the most broadly compatible ChatGPT path.

  1. Deploy AI Server Commander on a public HTTPS origin.
  2. In the Custom GPT builder, add an Action.
  3. Import https://commander.example.com/openapi.json.
  4. Configure API-key authentication as a Bearer token.
  5. Use the authToken value from config.json.
  6. Add or adapt prompt.md as the GPT instructions.
  7. Test first with a read-only command such as pwd && hostname.

Legacy GET request:

GET /api/runTerminalScript?command=pwd%20%26%26%20hostname
Authorization: Bearer <authToken>

Preferred POST request:

POST /v1/commands/execute
Authorization: Bearer <authToken>
Content-Type: application/json

{
  "mode": "inline",
  "command": "pwd && hostname",
  "cwd": "/srv/project",
  "timeoutMs": 45000,
  "maxOutputChars": 12000
}

Remote MCP clients

The remote MCP endpoint is:

https://commander.example.com/mcp

The server implements MCP protocol version 2025-03-26. initialize always returns that version and does not echo a different client-requested version. Clients that cannot continue on 2025-03-26 disconnect during negotiation, which matches the MCP lifecycle rules.

An empty JSON-RPC batch ([]) is invalid and returns HTTP 400 with JSON-RPC -32600. A POST that contains only notifications (no id) still returns HTTP 202 with no body.

The primary tool is run_terminal_command.

Field Type Notes
command string Exact command for inline mode.
script string Multi-line script body. Supplying it defaults the mode to script.
mode inline or script Optional explicit mode.
cwd string Must be an existing readable directory. Invalid paths are rejected.
shell string Script-mode shell, for example /bin/sh.
timeoutMs integer Requested timeout, capped by server policy.
maxOutputChars integer Requested output limit, capped by server policy.

OAuth discovery

The server publishes:

/.well-known/oauth-protected-resource
/.well-known/oauth-protected-resource/mcp
/.well-known/oauth-authorization-server
/.well-known/openid-configuration
/oauth/register
/oauth/authorize
/oauth/token
/oauth/revoke

The built-in flow supports dynamic client registration, authorization code + PKCE, refresh-token rotation and RFC-style token revocation. The authorization page asks for the server authToken as the approval code.

OAuth state is persisted atomically at OAUTH_STATE_PATH. Client secrets, authorization codes, access tokens and refresh tokens are stored only as SHA-256 hashes; raw values are returned to the client only when issued. The state file is forced to mode 600 on supported POSIX filesystems. After upgrading from an in-memory-only release, existing clients must authorize once; credentials issued by v1.0.8 or later survive normal restarts.

ChatGPT MCP readiness

The MCP descriptor includes OAuth security schemes, a compatibility mirror in _meta, risk annotations, an output schema and structuredContent. Whether a specific ChatGPT account or surface can add a custom remote MCP server depends on the current ChatGPT plan and client capabilities. Keep the REST Action path available until the target workflow is validated.

Command modes

Inline

curl -sS \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{"mode":"inline","command":"pwd && hostname","timeoutMs":5000}' \
  https://commander.example.com/v1/commands/execute

Multi-line script

curl -sS \
  -H 'Authorization: Bearer YOUR_TOKEN' \
  -H 'Content-Type: application/json' \
  -d '{
    "mode":"script",
    "shell":"/bin/sh",
    "script":"set -e\npwd\nhostname\n",
    "timeoutMs":5000
  }' \
  https://commander.example.com/v1/commands/execute

Response

{
  "message": "Command executed successfully.",
  "activityId": "cmd_...",
  "output": "...",
  "exitCode": 0,
  "timedOut": false,
  "interrupted": false,
  "blocked": false,
  "outputTruncated": false,
  "maxOutputChars": 12000,
  "mode": "inline",
  "notices": []
}

Interrupt a command

When exactly one command is active:

POST /api/interrupt
Authorization: Bearer <authToken>

When several commands may be active, target one explicitly:

POST /api/interrupt
Authorization: Bearer <authToken>
Content-Type: application/json

{
  "activityId": "cmd_..."
}

Activity log and notices

Activity endpoints:

GET  /api/activity
GET  /api/activity/status
GET  /api/activity/index
POST /api/activity/context

Notice endpoints:

POST /api/notices
GET  /api/notices/pending
POST /api/notices/:id/ack

Activity records use command hashes, byte counts and redacted previews rather than complete script bodies by default. Treat generated runtime/ data as potentially sensitive operational metadata.

Security model

AI Server Commander provides controls, not isolation:

  • Authentication gates the HTTP interfaces.
  • The shared executor caps duration and returned output.
  • SAFE_MODE blocks a small set of obviously destructive patterns.
  • Invalid working directories fail closed.
  • Temporary script files are mode-restricted and deleted after execution.
  • Activity previews redact common token and secret patterns.
  • MCP risk annotations tell compatible clients that terminal execution may be destructive and open-world.

It does not provide:

  • a container or VM sandbox;
  • a complete shell parser or comprehensive command policy;
  • per-user operating-system isolation;
  • a path allowlist;
  • protection from every form of command composition or shell indirection;
  • encrypted OAuth metadata at rest (secret and token values are hashed, while non-secret client metadata remains readable);
  • rate limiting.

Recommended production controls:

  1. Run as a dedicated unprivileged user.
  2. Do not add the service user to docker, sudo or other privileged groups unless explicitly required.
  3. Enable SAFE_MODE=true, but do not treat it as a sandbox.
  4. Restrict network exposure with a firewall, access proxy or VPN where client requirements permit.
  5. Use separate high-entropy authToken and mcpToken values.
  6. Rotate tokens after accidental disclosure.
  7. Review activity and system service logs.
  8. Require human confirmation for commands that write, delete, restart services, change permissions or access credentials.

See SECURITY.md for vulnerability reporting.

Testing

npm run check
npm test

The smoke suite covers:

  • bounded executor behavior;
  • REST GET/POST compatibility;
  • multi-line scripts and request-size limits;
  • invalid working directories;
  • concurrency and targeted interruption;
  • MCP initialization, advertised protocol version, empty-batch rejection and execution;
  • OAuth metadata, PKCE, persistent state, restart continuity, refresh rotation and revocation;
  • native setup and Firestore service-account compatibility;
  • SAFE_MODE results;
  • OpenAPI generation and version alignment.

CI runs checks on supported Node versions for every push and pull request.

Troubleshooting

Public URLs use http:// or the wrong hostname

Set productionDomain to the exact external HTTPS origin and forward Host and X-Forwarded-Proto from the reverse proxy.

The MCP client asks to reconnect after an upgrade or restart

Confirm that every release uses the same OAUTH_STATE_PATH and that the service user can read and write it. Upgrading from v1.0.7 or earlier requires one new authorization because those releases kept OAuth state only in memory. A missing, moved or deleted state file also requires reauthorization.

The MCP client disconnects during initialize

The server only implements MCP protocol version 2025-03-26 and always returns that version from initialize. A client that cannot continue on 2025-03-26 is expected to disconnect. Confirm the client supports that version rather than expecting the server to echo an older or newer request.

A command runs in the wrong directory

Pass an explicit cwd. It must exist and be readable by the service user.

A request times out earlier than expected

The effective timeout is the lower of the client request and COMMAND_TIMEOUT_MS.

Output is incomplete

Check outputTruncated. Increase the requested maxOutputChars and, if needed, the server-wide MAX_OUTPUT_CHARS cap. Prefer commands that filter output before returning it.

OAuth discovery fails

Check these URLs from outside your network:

curl -i https://commander.example.com/.well-known/oauth-protected-resource/mcp
curl -i https://commander.example.com/.well-known/oauth-authorization-server
curl -i https://commander.example.com/mcp

The unauthenticated /mcp request should return 401 with a WWW-Authenticate challenge pointing to protected-resource metadata.

Documentation

Project status

AI Server Commander is a small self-hosted project maintained on a best-effort basis. The command-execution surface is intentionally narrow. New capabilities should normally be implemented once in shared core code and exposed through both REST and MCP adapters with matching safety semantics.

Contributing

Contributions are welcome. Read CONTRIBUTING.md before opening a pull request. Do not include deployment secrets, private hostnames, personal paths, access tokens, logs or production state.

License

Licensed under the MIT License. See NOTICE.md for project attribution.

About

Self-hosted REST/OpenAPI and remote MCP bridge for bounded AI-assisted terminal execution.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

9 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages