shellmcp: tool description lists installed modern tools

This commit is contained in:
2026-11-05 10:17:18 +01:00
parent b95c3d662b
commit 60219ef8f8
2 changed files with 36 additions and 0 deletions
+25
View File
@@ -56,3 +56,28 @@ def sandbox(cwd, hide=()):
elif os.path.exists(path): elif os.path.exists(path):
argv += ["--ro-bind", "/dev/null", path] argv += ["--ro-bind", "/dev/null", path]
return argv + ["--chdir", cwd] return argv + ["--chdir", cwd]
PREFERRED = { # named in the tool description when installed, in this order
"rg": "search file contents (instead of grep)", "fd": "find files (instead of find)",
"eza": "list files (instead of ls)", "sd": "find and replace (instead of sed)",
"dust": "disk usage (instead of du)", "procs": "processes (instead of ps)",
"jq": "query JSON", "ast-grep": "search code by syntax", "tokei": "count lines of code"}
def describe(mode, cwd, timeout, limit):
"""The tool description: where commands run, the limits, and the modern tools installed."""
text = (f"Run a bash command on {socket.gethostname()} as {getpass.getuser()} in {cwd}. "
f"Returns its exit code and combined stdout and stderr (at most {limit} characters; "
f"the middle of longer output is cut). Commands are killed after {timeout} s. "
"There is no terminal: interactive programs, pagers and prompts fail.")
if mode == "readonly":
text += (" Read-only sandbox: nothing can be written except /tmp, which is emptied "
"after every command; no network; other processes are not visible. To change "
"something, give the user the command to run instead.")
else:
text += (" Commands run with the user's full permissions: they can change files, "
"processes and the network.")
tools = [f"{name}: {what}" for name, what in PREFERRED.items() if shutil.which(name)]
if tools:
text += " Installed, prefer these: " + "; ".join(tools) + "."
return text
+11
View File
@@ -44,3 +44,14 @@ class Sandbox(unittest.TestCase):
self.assertNotIn("key", out) self.assertNotIn("key", out)
self.assertIn("tmp-ok", out) self.assertIn("tmp-ok", out)
self.assertFalse(os.path.exists(os.path.join(d, "new"))) self.assertFalse(os.path.exists(os.path.join(d, "new")))
class Describe(unittest.TestCase):
def test_names_only_installed_tools_and_the_mode(self):
with mock.patch("shutil.which", lambda name: name in ("fd", "rg")):
text = shellmcp.describe("readonly", "/srv", 60, 8000)
self.assertIn("in /srv.", text)
self.assertIn("Read-only sandbox", text)
self.assertIn("prefer these: rg: search file contents (instead of grep); "
"fd: find files (instead of find).", text)
self.assertNotIn("jq", text)
self.assertIn("full permissions", shellmcp.describe("all", "/srv", 60, 8000))