{"id":48084561,"url":"https://github.com/fcannizzaro/exocommand","last_synced_at":"2026-04-04T15:07:58.454Z","repository":{"id":339854796,"uuid":"1163606746","full_name":"fcannizzaro/exocommand","owner":"fcannizzaro","description":"An MCP server that exposes user-defined shell commands as tools for AI coding assistants","archived":false,"fork":false,"pushed_at":"2026-02-21T23:17:33.000Z","size":990,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-02-22T04:20:05.116Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fcannizzaro.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-02-21T21:57:50.000Z","updated_at":"2026-02-21T23:17:36.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fcannizzaro/exocommand","commit_stats":null,"previous_names":["fcannizzaro/exocommand"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/fcannizzaro/exocommand","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcannizzaro%2Fexocommand","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcannizzaro%2Fexocommand/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcannizzaro%2Fexocommand/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcannizzaro%2Fexocommand/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fcannizzaro","download_url":"https://codeload.github.com/fcannizzaro/exocommand/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fcannizzaro%2Fexocommand/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31403953,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T10:20:44.708Z","status":"ssl_error","status_checked_at":"2026-04-04T10:20:06.846Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T15:07:57.798Z","updated_at":"2026-04-04T15:07:58.443Z","avatar_url":"https://github.com/fcannizzaro.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"media/logo.webp\" width=\"256\" alt=\"exocommand\" /\u003e\n\u003c/p\u003e\n\n\u003ch1 align=\"center\"\u003e@fcannizzaro/exocommand\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  An MCP server that exposes user-defined shell commands as tools for AI coding assistants.\n\u003c/p\u003e\n\n[![Publish Package](https://github.com/fcannizzaro/exocommand/actions/workflows/publish.yaml/badge.svg)](https://github.com/fcannizzaro/exocommand/actions/workflows/publish.yaml)\n[![npm](https://img.shields.io/npm/v/@fcannizzaro/exocommand?label=exocommand)](https://www.npmjs.com/package/@fcannizzaro/exocommand)\n\n## Overview\n\nExocommand is a centralized [MCP](https://modelcontextprotocol.io) server that manages multiple projects, each with its own set of shell commands defined in a `.exocommand` YAML file. Instead of giving an AI agent unrestricted terminal access, you register projects with the server and control exactly which commands each agent can discover and execute.\n\n## Features\n\n- **Multi-project registry** -- Register multiple projects, each with a unique access key. The server manages them all from a single process.\n- **TUI dashboard** -- When running in a terminal, the server displays a real-time dashboard with project tabs, execution cards, status indicators, and animated spinners.\n- **YAML configuration** -- Define commands per project in a `.exocommand` file with a name, description, and shell command.\n- **Live reload** -- The server watches each project's config file for changes and notifies connected clients automatically.\n- **Streaming output** -- By default, stdout and stderr are streamed line-by-line to the client via SSE in real time. If the server crashes mid-execution, the client retains all lines already received.\n- **Task mode** -- Opt-in execution mode backed by the MCP experimental tasks API for crash-resilient, independently-pollable command execution.\n- **Cancellation** -- Long-running commands can be cancelled by the client; the spawned process is killed immediately.\n- **Multi-session** -- Uses the Streamable HTTP transport, supporting multiple concurrent MCP sessions across projects.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"media/tui.webp\" alt=\"TUI Dashboard\" /\u003e\n\u003c/p\u003e\n\n## Quick Start\n\n1. **Create a config file** in your project directory (skip if you already have one):\n\n```bash\nbunx @fcannizzaro/exocommand init\n```\n\n2. **Register the project** by pointing to a directory containing a `.exocommand` file (or to the file directly):\n\n```bash\nbunx @fcannizzaro/exocommand add .\n```\n\nThis validates the config and prints an access key:\n\n```\n  ✓ Project registered\n\n    Key     a1b2c3d4e5f6\n    Header  exocommand-project: a1b2c3d4e5f6\n    Config  /path/to/project/.exocommand\n```\n\n3. **Start the server:**\n\n```bash\nbunx @fcannizzaro/exocommand\n```\n\n\u003e Use `@fcannizzaro/exocommand@latest` to always run the latest version from npm.\n\nThe server starts on `http://127.0.0.1:5555/mcp` by default.\n\n4. **Connect an MCP client** with the `exocommand-project` header set to the access key (see [Connecting an AI Client](#connecting-an-ai-client)).\n\n## CLI Commands\n\n| Command | Description |\n| --- | --- |\n| `exocommand` | Start the MCP server. |\n| `exocommand init` | Create a sample `.exocommand` config file in the current directory. Errors if the file already exists. |\n| `exocommand add \u003cpath\u003e` | Register a project. Accepts a directory (auto-resolves `.exocommand` inside it) or a direct file path. Validates the config and prints the access key. |\n| `exocommand ls` | List all registered projects with their access keys and config paths. Missing configs are flagged. |\n| `exocommand rm \u003ckey-or-path\u003e` | Remove a project by its access key or filesystem path. |\n\n- The registry is stored at `~/.exocommand/exocommand.db.json`.\n- Access keys are 12-character hex strings.\n- Re-adding an already registered path returns the existing key (no duplicates).\n\n## Configuration\n\nCreate a `.exocommand` file in the project root:\n\n```yaml\nbuild:\n  description: \"Run the production build\"\n  command: \"cargo build --release\"\n\nclippy:\n  description: \"Run clippy linter\"\n  command: \"cargo clippy\"\n\nlist-external:\n  description: \"List files in parent directory\"\n  command: \"ls -a\"\n  cwd: ../\n```\n\nEach top-level key is a command name. Names may contain letters, numbers, hyphens, and underscores.\n\n| Field | Required | Description |\n| --- | --- | --- |\n| `description` | Yes | What the command does. |\n| `command` | Yes | The shell command to run. |\n| `cwd` | No | Working directory for the command. Relative paths are resolved from the config file's directory. |\n\n### Environment Variables\n\n| Variable | Description | Default |\n| --- | --- | --- |\n| `EXO_PORT` | Server port | `5555` |\n| `EXO_TASK_MODE` | Enable task mode (`true` or `1`) | `false` |\n\n### Execution Modes\n\nThe `execute` tool supports two execution modes:\n\n**Streaming (default)** -- Each output line is sent as an SSE event on the response stream as it happens. The client receives lines in real time. If the server crashes mid-execution, all lines sent up to that point are already with the client. Cancellation works through the standard MCP request signal (client disconnect or `notifications/cancelled`).\n\n**Task mode** -- Enabled via `EXO_TASK_MODE=true`. Uses the MCP experimental tasks API. The server creates a background task, and clients can poll its status independently. Task-aware clients get full crash resilience (disconnect, reconnect, and resume polling). Supports structured cancellation via `tasks/cancel`.\n\n## Connecting an AI Client\n\nPoint any MCP-compatible client at the server's `/mcp` endpoint. Clients must include the `exocommand-project` header with the project's access key on the initialize request.\n\nFor example, with [OpenCode](https://opencode.ai):\n\n```json\n{\n  \"mcp\": {\n    \"exocommand\": {\n      \"enabled\": true,\n      \"type\": \"remote\",\n      \"url\": \"http://host.docker.internal:5555/mcp\",\n      \"headers\": {\n        \"exocommand-project\": \"\u003caccess-key\u003e\"\n      }\n    }\n  }\n}\n```\n\nThe server exposes two tools:\n\n| Tool | Description |\n| --- | --- |\n| `listCommands()` | Returns all available commands from the project's config file. |\n| `execute(name, timeout?)` | Executes a command by name, streaming output back to the client. An optional `timeout` (in seconds) kills the process after the given duration and returns the buffered output. |\n\nRemember to tell the agent that they can use these tools to run commands on the project. For example:\n\n```\nYou can run predefined shell commands using the `listCommands()` and `execute(name, timeout?)` tools. Use `listCommands()` to see all available commands, and `execute(name, timeout?)` to run a specific command, with an optional timeout in seconds.\n```\n\n## Safety\n\nWhen running agents in Docker, mount `.exocommand` files as read-only volumes to prevent the agent from modifying them.\n\nIf you use a script to start containerized agents, you can automatically mount the `.exocommand` file when present in the launch directory:\n\n```bash\ndocker run \\\n  # ...\n  $([ -f \"$PWD/.exocommand\" ] \u0026\u0026 echo \"-v $PWD/.exocommand:$PWD/.exocommand:ro\") \\\n  # ...\n```\n\n## License\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffcannizzaro%2Fexocommand","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffcannizzaro%2Fexocommand","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffcannizzaro%2Fexocommand/lists"}