sully-termux-mcp
sully-termux-mcp
sully-termux-mcp is a Node.js 20+ TypeScript ESM gateway for running MCP tools in Termux. The production default is deliberately small: it listens on a loopback address only, requires a 32-byte Bearer token, and exposes a separate authenticated admin API.
Quick start
npm ci
npm run build
node dist/cli.js setup
node dist/cli.js start
node dist/cli.js status
node dist/cli.js pair # explicit user action; prints both tokensThe generated configuration is ~/.config/sully-mcp/config.json with mode 0600; data, job output, and the redacted audit log are under ~/.local/share/sully-mcp with mode 0700. Never paste pairing JSON into a log or issue.
For phone controls, install the Termux:API command package (pkg install termux-api) and install the Termux:API companion Android app from the same signing source (for example, both from F-Droid, or both from GitHub). Android will not deliver API calls when the two packages come from different signing sources. Restart the gateway after installing or updating Termux:API so its command inventory is rebuilt.
The MCP endpoint is http://127.0.0.1:8765/mcp. This release supports the stable 2025-11-25 and 2025-06-18 session-based protocols using initialize, notifications/initialized, tools/list, and tools/call. The 2026-07-28 transport is intentionally not advertised until its required routing headers and response envelope are implemented. DELETE /mcp closes a 2025 session.
Administration
POST /admin/v1 uses the admin Bearer token and accepts { "action": "status", "params": {} }. Actions include status, doctor, permissions, capabilities, jobs.list, jobs.output, jobs.cancel, jobs.cleanup, profiles.list, profiles.upsert, profiles.remove, upstreams.list, upstreams.import, upstreams.update, upstreams.remove, gateway.stop, tokens.rotate, and pair.revoke. All responses have {ok:true,result} or {ok:false,error}. Secrets are only returned for an explicit tokens.rotate response or the explicit pair CLI command.
permissions and doctor include a centralized Termux:API checklist grouped by device, communication, media, interaction, and automation. Command availability is detected at startup; Android runtime permission state is reported as unknown, and the same-signature-source requirement for Termux:API is explicit.
Tools and limits
Built-ins include bounded echo_safe, fs_read, fs_write, fs_list, fs_metadata, fs_find, fs_copy, fs_move, command profiles, shell jobs, job status/output/cancel/cleanup, and detected Termux:API commands (including typed location, camera, notification, toast, clipboard, SMS, TTS, volume, and torch inputs). Less common API commands expose bounded raw args with a usage/help hint; consult the installed command's --help for exact options. shell_exec always spawns bash -lc with shell:false; command profiles and stdio upstreams use an executable plus argument array with shell:false. Jobs survive restarts as interrupted, capture at most 8 MiB, and return at most 64 KiB per output request. Requests are limited to 1 MiB and responses to 4 MiB; tool concurrency defaults to four.
HTTP upstreams must use HTTPS, or HTTP on the exact literal loopback host 127.0.0.1/::1 (no DNS aliases or whitespace). HTTPS destinations resolving to private, link-local, or metadata ranges are rejected. Redirects, URL credentials, fragments, and non-allowlisted tools are rejected. HTTP and stdio upstream tools are prefixed (id__tool) and can be restricted with allowedTools, readOnly, and prefix.
Termux lifecycle
The normal sully-mcp start/restart lifecycle acquires termux-wake-lock when available and stop releases it. Install termux-boot/sully-termux-mcp as ~/.termux/boot/sully-termux-mcp and make it executable to start after reboot. Android battery optimisation can still stop Termux; the application should treat an unavailable gateway as recoverable.
Supported CLI commands: setup, start, stop, restart, status, logs, doctor, permissions, pair, update, and uninstall --yes.
Release integrity
npm run release -- --version 0.2.0 requires MCP_RELEASE_ED25519_PRIVATE_KEY and runs a build before writing sully-termux-mcp-0.2.0.tar.gz, its SHA-256, manifest, and Ed25519 signature. The App command in scripts/install-command.txt downloads the fixed GitHub Release archive, hash, manifest, and signature into a temporary directory, compares the archive to its pinned expected hash, verifies the Ed25519 signature with its inline public key, and only then runs the verified archive's scripts/install-local.sh. The local installer checks Node 20+, runs npm ci --omit=dev and the build, and swaps an atomic versions/<version>/current symlink while preserving the previous current target on failure. It also creates a real sully-mcp wrapper in Termux's $PREFIX/bin (or ~/.local/bin) and never uses an unverified curl | sh pipeline. The private signing key is supplied transiently through MCP_RELEASE_ED25519_PRIVATE_KEY by the release operator and is never committed.
Security model
Only loopback hosts are accepted for the local listener. MCP and admin credentials are independent 64-hex random values and compared with a constant-time comparison. Configuration is written as UTF-8 with restrictive permissions. The server does not return tokens from /health, tool lists, normal errors, audit records, or job metadata. Audit entries are redacted and bounded. Side-effect tools are never automatically retried. Destructive upstream tools must be explicitly allowlisted by the caller.
This repository is licensed under the PolyForm Noncommercial License 1.0.0 carried over from SULLYTEST2.