Skip to main content
Glama
nano-mcp-termux

nano-mcp-termux

nano-mcp-termux

Zero-dependency, single-file MCP server for AI agents, natively running on Termux (Android), Raspberry Pi, and Linux.

GitHub Repository License: MIT MCP Version Docker Ready Version Tests CI Zero Dependencies

🚀 Quick Start

Local (Termux / Linux)

cd /path/to/nano-mcp-termux
python3 nano_mcp_termux.py

Docker

docker build -t nano-mcp-termux .
docker run -i --rm nano-mcp-termux

Health check (one-shot, no MCP handshake)

python3 nano_mcp_termux.py --health

Related MCP server: Python MCP Toolkit Server

🔌 Connect to your MCP client

This server does not run standalone — it's a sidecar that your AI client connects to via the MCP protocol.
Below are the three most common ways to mount it.

Option 1 — Zero‑install (uvx), best for desktop AI clients

uvx nano-mcp-termux

Then add this JSON to your AI client's mcpServers config (see client‑specific notes below):

{
  "mcpServers": {
    "nano-mcp-termux": {
      "command": "uvx",
      "args": ["nano-mcp-termux"]
    }
  }
}

Option 2 — Already pip installed, or you prefer a direct Python launch

pip install nano-mcp-termux

Config snippet for your AI client:

{
  "mcpServers": {
    "nano-mcp-termux": {
      "command": "nano-mcp-termux"
    }
  }
}

Option 3 — Run the single file directly (e.g. inside Termux on Android)

# Clone or download the repo, then:
cd /path/to/nano-mcp-termux
python3 nano_mcp_termux.py

Config snippet for your AI client:

{
  "mcpServers": {
    "nano-mcp-termux": {
      "command": "python3",
      "args": ["/full/path/to/nano_mcp_termux.py"]
    }
  }
}

How to use these snippets: client‑specific config files

AI client

Config file location (examples)

Note

Claude Desktop

macOS: ~/Library/Application Support/Claude/claude_desktop_config.jsonWindows: %APPDATA%\Claude\claude_desktop_config.json

∆ mcpServers key at top level

Cursor

~/.cursor/mcp.json

∆ mcpServers key at top level

Cline

$HOME/.cliner/mcp.json (or inside project folder as cline_mcp_settings.json)

∆ mcpServers key at top level

Cherry Studio

GUI → MCP section (typically auto‑creates ~/.cherrystudio/mcp/*.json)

JSON schema as above

Operit

~/.operit/mcp_config.json (or per‑workspace config in the workspace folder)

∆ mcpServers key at top level

Tip: after editing the config file, restart your AI client so the new server is detected. If the client supports hot‑reload, you may not need a restart.

Troubleshooting

Symptom

Likely cause

Fix

Client shows "server not found"

command path is wrong or not in PATH

Use absolute path (/usr/local/bin/nano-mcp-termux for pip installs, $HOME/.local/bin/nano-mcp-termux for pip --user)

Error: spawn uvx ENOENT

uvx not in PATH (not installed)

Use a custom shell like "shell": "uw uvx" or the direct Python launch (Option 2)

"Connection lost" / process dies immediately

No stdin from client

This server is stdio‑bound; don't run it directly like nano-mcp-termux — always launch it through your client's MCP config


🛠️ Features

  • ✅ Zero-dependency: No external packages required

  • ✅ Single-file: All logic in one Python script

  • ✅ Cross-platform: Works in Termux (Android), on Raspberry Pi, Linux, and inside Docker

  • ✅ Container-ready: Dockerfile at repo root for Glama introspection

  • ✅ Standard stdio framing: Newline-delimited JSON-RPC (official MCP transport)

  • ✅ Version negotiation: Echoes client protocolVersion (2024-11-05 / 2025-06-18 / 2025-11-25), falls back to highest supported

  • ✅ Graceful degradation: device_status never fails; unavailable inputs are marked, not crashed

  • ✅ [lifecycle] No orphaned processes: every command runs in its own process group; timeouts kill the whole tree (SIGTERM → SIGKILL), never just the shell

  • ✅ [lifecycle] Self-terminating: parent-process watchdog + stdin-EOF detection + optional idle timeout, so a dead client never leaves a zombie server behind

  • ✅ [lifecycle] Phantom Process Killer aware: reports Android 12+ child-count risk with concrete mitigations

  • ✅ [paths] Android/Termux path adapter: resolves /sdcard ↔ /storage/emulated/0 ↔ $HOME/storage/shared, detects noexec mounts, case-insensitive filesystems, and shell-quoting hazards

🧩 Tools Overview

Tool

Description

device_status

Aggregate device info: battery / storage / arch / memory (degraded fields marked unavailable)

server_health

Runtime health: pid/ppid/env, uptime, request counters, live children, reaping policy, RSS, Phantom Process Killer risk

resolve_path

Resolve an Android/Termux path: canonical form, mount point, noexec/read-only flags, case sensitivity, equivalent spellings, quoting warnings

path_adapter

Environment profile: runtime class, $HOME/$PREFIX/$TMPDIR, Android storage roots with mount summaries, noexec mount list

🔗 Protocol Surface

Method

Behavior

initialize

Echoes supported client version (2024-11-05 / 2025-06-18 / 2025-11-25), else responds with highest supported per MCP spec; declares {"tools": {}}

notifications/initialized

Silent (no response)

ping

Returns {}

tools/list

Returns tool catalog

tools/call

Returns {"content": [{"type": "text", ...}]}

Unknown method / tool

-32601 / -32602 JSON-RPC errors

Malformed JSON

-32700 parse error

🔄 Why 1.1.0: the orphan-process bug

Version 1.0.0 ran commands with subprocess.run(..., timeout=...). On timeout, Python terminates only the direct child — the sh -c wrapper. Anything the shell had already spawned is reparented to init and survives forever. On Android that is the exact leak class that trips the Phantom Process Killer and can get the whole app group killed.

1.1.0 fixes this at the root: commands are spawned with start_new_session=True and torn down with os.killpg(pgid, SIGTERM) → SIGKILL, so the entire process tree dies together.

The repository ships a reproducible A/B regression test for it:

python3 test_lifecycle_orphan.py
# current  nano_mcp_termux.py           leaked_grandchildren=0
# legacy   nano_mcp_termux_v10_backup.py leaked_grandchildren=1   <- the bug
# PASS: current server leaves 0 orphaned grandchildren after timeout

✅ Tests

python3 test_client.py            # 32 protocol / tool / lifecycle assertions
python3 test_lifecycle_orphan.py  # orphan-leak A/B regression (1.0.0 vs 1.1.0)
bash scripts/verify_glama_build.sh # Glama-build reproduction (see below)

verify_glama_build.sh answers one question: will Glama's builder be able to install and introspect this repo? It replays the same toolchain Glama uses (uv venv && uv pip install -e ., i.e. a newer setuptools than most dev machines have), then asserts the console entry point exists and that the server answers initialize + tools/list over stdio. Exit codes are deliberately three-valued so the script never lies about whose fault a failure is:

Exit

Meaning

0

Builds and introspects cleanly

1

Real problem in this repo (bad metadata, missing entry point, no tools)

2

Local environment problem (no uv, index unreachable, mirror returns 403)

The index is pinned to pypi.org on purpose — a local mirror inherited from your shell profile can return 403 and make a healthy repo look broken. Override with GLAMA_VERIFY_INDEX=https://your-mirror/simple only if you mean it.

Deprecation warnings about project.license are expected and non-fatal: the SPDX form that silences them needs setuptools>=77, which requires Python 3.9+, while this project still supports 3.8 source builds.

⚙️ Environment variables

Variable

Default

Purpose

NANO_IDLE_TIMEOUT

0 (off)

Seconds of inactivity before the server exits on its own

NANO_NO_WATCHDOG

unset

Set to 1 to disable the parent-process watchdog thread


This server is designed to pass Glama introspection checks and serve as a lightweight MCP implementation for mobile and edge devices.

Available Tools

4 tools
device_statusDevice status (read-only)A
Read-onlyIdempotent

Read-only snapshot of the host hardware and OS: battery level, storage usage, CPU architecture and memory. Use it when the answer depends on the physical device: free space before a download, battery before a long job, arch before picking a binary. Not for process or server state: use server_health for pid/uptime/counters of this MCP server, resolve_path to validate one path, and path_adapter for the runtime's path environment. Never raises: a field that cannot be read is reported with source unavailable. Returns {battery, storage, arch, memory}.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already declare readOnlyHint, idempotentHint, and destructiveHint=false, so safety is covered; the description adds a genuine contract beyond that: "Never raises: a field that cannot be read is reported with source unavailable." That partial-failure behavior is the key thing an agent needs, though it says nothing about freshness/caching of the snapshot or cost of the call.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the resource and its fields, then usage triggers, then exclusions, then the failure contract and return keys. Every clause carries routing or behavioral information; there is no restatement of the name or title.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

No output schema exists, and the description compensates by naming the return shape `{battery, storage, arch, memory}` plus the degraded-value convention. Combined with the annotations and an empty input schema, an agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Zero parameters, so there is nothing to disambiguate and the baseline is 4. The description correctly signals a no-argument call by framing the tool as a snapshot rather than implying any query/filter options.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("read-only snapshot of the host hardware and OS") and enumerates exactly which fields it covers: battery, storage, CPU architecture, memory. It explicitly separates itself from `server_health`, `resolve_path`, and `path_adapter`, so an agent can pick it without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives concrete when-to-use triggers tied to the agent's task ("free space before a download, battery before a long job, arch before picking a binary") and an explicit when-not clause naming all three siblings with their distinct scopes. Nothing about routing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

path_adapterPath environment adapter (read-only)A
Read-onlyIdempotent

Report the runtime's path environment so you can translate host paths correctly: detected env class (termux/proot/android/linux), HOME/PREFIX/TMPDIR, each Android shared-storage root with its real mount, all noexec mounts that block in-place script execution, and runtime-specific notes. Takes no arguments. Sibling boundary: this answers the environment question (env class, HOME/PREFIX/TMPDIR, mount roots); resolve_path answers the per-path question (expansion, mount, alternatives for one path). Use path_adapter when the environment itself is unknown, resolve_path to validate a single candidate path. Returns {env, home, prefix, tmpdir, python, platform, android_roots, noexec_mounts, notes}.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already cover the safety profile (read-only, idempotent, non-destructive, closed-world), and the description adds substantial context beyond them: it says the tool takes no arguments and enumerates the reported data, including env class, HOME/PREFIX/TMPDIR, Android roots, noexec mounts, and notes. It also lists the return fields, which is valuable given there is no output schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loads the core action, then adds the no-argument note, sibling boundary, and return shape without filler. The density is high but every sentence serves selection or invocation.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the lack of an output schema, the description helpfully lists the returned keys and explains what environment information is reported. It is complete for a zero-parameter read-only diagnostic tool, though it could mention whether the reported environment is cached or live.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the baseline is 4; the description confirms "Takes no arguments," aligning with the empty schema. There are no parameter semantics beyond that, and none are needed.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource: "Report the runtime's path environment," and immediately explains the practical purpose: translating host paths correctly. It also explicitly distinguishes itself from `resolve_path`, so an agent can identify the tool without opening sibling schemas.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit routing rule: use `path_adapter` when the environment itself is unknown, and use `resolve_path` to validate a single candidate path. This names the alternative and the condition that selects it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

resolve_pathResolve filesystem path (read-only)A
Read-onlyIdempotent

Resolve and validate one filesystem path in the runtime this server runs in: expands ~/$HOME/$PREFIX/$TMPDIR, follows symlinks, reports the mount backing the path, and lists every Android-equivalent spelling (/sdcard <-> /storage/emulated/0 <-> $HOME/storage/shared). Equivalent inputs: /sdcard/Download/x.txt, $HOME/storage/shared/Download/x.txt, ~/storage/shared/Download/x.txt, and relative docs/report.md (against cwd). Expansion: ~ and $HOME become the server's HOME; $PREFIX is the install prefix (e.g. /data/data/com.termux/files/usr); $TMPDIR is the server's own temp dir, never the client's. Useful when a path has to be handed to a shell command or another tool; not needed for paths already known-good on plain Linux. Sibling boundary: path_adapter reports the environment map itself (env class, HOME/PREFIX/TMPDIR, mount roots) and takes no arguments; resolve_path reports one concrete path. Use path_adapter for the environment, resolve_path for a candidate path. Returns {input, expanded, resolved, env, exists, is_dir, is_file, is_symlink, readable, writable, executable, mount, alternatives, warnings}.

NameRequiredDescriptionDefault
pathYesPath to resolve. May be absolute or relative and may contain ~, $HOME, $PREFIX or $TMPDIR. Required and non-empty; an empty string returns an error object instead of raising.

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The annotations already establish read-only, idempotent, non-destructive behavior, and the description adds substantial context beyond that: variable expansion rules, symlink following, mount detection, Android-equivalent path spellings, and the exact result fields returned. There is no contradiction with the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is front-loaded and every major sentence contributes useful information. It is somewhat dense and run-on in the first sentence, but the density is justified by the number of path-expansion rules being communicated.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

There is no output schema, so the description appropriately lists the return object fields. With only one parameter, explicit annotations, detailed expansion semantics, sibling differentiation, and return-shape disclosure, an agent has everything needed to call the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 100%, so the baseline is 3, but the description adds meaningful semantics beyond the schema: concrete equivalent input forms, relative-path handling against the working directory, and the specific meanings of `~`, `$HOME`, `$PREFIX`, and `$TMPDIR`. The schema already covers required/non-empty behavior, so this is not a full 5.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb and resource: resolving and validating one filesystem path, including expansion, symlink following, mount reporting, and alternative spellings. It explicitly distinguishes itself from the sibling `path_adapter`, so an agent can select it without inspecting either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit when-to-use guidance: useful when a path must be handed to a shell command or another tool, and not needed for paths already known-good on plain Linux. It also draws a clear sibling boundary: `path_adapter` reports the environment map, while `resolve_path` reports one concrete path.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

server_healthServer health (read-only)A
Read-onlyIdempotent

Read-only self-diagnostics for this MCP server process: pid/ppid, runtime class, uptime, request and tool-call counters, tracked live child processes, RSS, and the Android Phantom Process Killer risk assessment. Use it to verify the server is alive, to debug client-side disconnects, or to check whether orphaned children are accumulating on Android. Not for the device itself: use device_status for battery, storage, arch and memory. Returns {server, children, memory, phantom_process}.

NameRequiredDescriptionDefault

No parameters

TDQS

A4.7/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnly, idempotent, non-destructive and closed-world, so the safety profile is covered. The description adds real context beyond that: the scope is this server process rather than the device, it tracks live child processes, and it includes an Android-specific risk assessment. It stops short of saying anything about cost, frequency, or what an unhealthy result looks like, but the scope and content disclosure is solid.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with the scope ("Read-only self-diagnostics for this MCP server process"), then the returned signals, then the usage boundary against `device_status`. The field enumeration is dense but each item is a distinct diagnostic the caller may need, so nothing reads as padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no parameters, the description carries the full burden of telling the agent what comes back, and it does: it names the four top-level return keys {server, children, memory, phantom_process} and the highlights of each. An agent has everything it needs to decide to call it and to interpret the response shape.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool takes zero parameters, so the baseline is 4. The description correctly implies no configuration is needed by framing the call as pure self-diagnostics, and it supplies no misleading parameter hints.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb and resource ("read-only self-diagnostics for this MCP server process") and enumerates exactly what it reports: pid/ppid, uptime, counters, children, RSS, Phantom Process Killer risk. It explicitly distinguishes itself from the sibling `device_status`, so an agent can route correctly without opening either schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives three concrete triggers (verify the server is alive, debug client-side disconnects, check for accumulating orphaned children on Android) and an explicit exclusion with a named alternative: "Not for the device itself: use `device_status` for battery, storage, arch and memory."

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections.

  1. 4 tool updatesv1.1.1
    • First observeddevice_status
    • First observedpath_adapter
    • First observedresolve_path
    • First observedserver_health

TDQS

A4.6/5.0

Scored across 4 tools

Disambiguation5/5

Each tool targets a distinct question: device hardware (device_status), server process state (server_health), one candidate path (resolve_path), and the runtime path environment (path_adapter). Descriptions explicitly state sibling boundaries and even tell the agent which tool to use instead, eliminating misselection.

Naming Consistency4/5

All names are snake_case and self-describing, with a clear domain term per tool. The pattern is noun_noun for three tools (device_status, server_health, path_adapter) and verb_noun for one (resolve_path), a minor deviation that is still highly readable.

Tool Count4/5

Four tools is on the small side but each covers a distinct, non-overlapping diagnostic concern, so nothing feels padded or redundant. The surface is tightly scoped to introspection rather than trying to be a general shell wrapper.

Completeness4/5

The stated domain — read-only device, server, and path introspection — is well covered, including Android-specific concerns like phantom processes and shared-storage aliases. Minor gaps remain (no general process/network/log inspection), but agents can work around these via other tools or shell commands.

Maintenance

ActivityMaintained
ResponsivenessNo issues

Related MCP Connectors

Related MCP Servers

  • A
    license
    Not graded
    quality
    D
    maintenance
    A simple MCP Server with shell execution capabilities that can be run locally with Ngrok tunneling or hosted in an Ubuntu 24 Docker container.
    4
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Ultra-lightweight MCP server for Bun with full protocol compliance. Enables building MCP servers with tools, resources, and prompts using minimal dependencies and heap footprint.
    46,312 npm
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    A Python MCP server that allows ChatGPT to execute commands on your local PC via a secure Cloudflare tunnel.
    -