Skip to content

Latest commit

 

History

History
547 lines (440 loc) · 26.2 KB

File metadata and controls

547 lines (440 loc) · 26.2 KB

Tools reference

This is the canonical list of every tool available to the bot, organised by what's on by default and what's opt-in. Each opt-in section names the env var(s) that flip the gate.

For Claude Code's full upstream tool catalogue (some of which hamroh doesn't currently expose) see https://code.claude.com/docs/en/tools-reference. The "Other CC tools you can wire in" section at the bottom of this page points at the ones a fork might want to add.


Always on — hamroh built-ins

These are the bot's core surface, served by the local hamroh MCP server. Auto-discovered from hamroh/tools/*.py (each tool is a BaseTool subclass). All available every turn; no env flag needed.

Messaging

Tool What it does
telegram_send_message Send a text message to a chat. The only way the user sees anything — a plain text content block produces no Telegram output.
telegram_reply_to_message Reply to a specific user message (threads in groups).
telegram_edit_message Edit one of the bot's previous messages. No push notification — good for in-progress updates on long tasks.
telegram_delete_message Delete a bot message. Use sparingly.
telegram_add_reaction React to a message with an emoji. Prefer over "ok"/"👍" replies in groups.
telegram_create_poll Send a poll. Supports regular/quiz, multi-answer, anonymity toggle, auto-close (open_period or close_date), and reply-to.
telegram_stop_poll Close a live poll early and return final tallies.
telegram_read_attachment Read a photo or document the user sent. The dispatcher saves inbound attachments under data/attachments/ and surfaces them as [attachment: <path> ...] markers — pass that path here. Images come back as image content blocks (you actually see them); text-like files (md/txt/log/csv/json/yaml/code) come back as UTF-8; PDFs are extracted via pypdf and returned as text with --- page N --- markers. Path traversal is rejected. GIFs/videos are unsupported.
telegram_send_memory_document Send a memory file (under memories/) to a chat as a downloadable document. Path-locked to the memory root. Optional caption + reply-to.
render_html Render an HTML snippet to PNG via headless Chromium → data/renders/. Use for tables/charts/diffs that markdown can't fit. Network blocked — inline any CSS/JS. Returns the relative path.
render_latex Render a LaTeX expression to PNG via KaTeX (loaded from cdn.jsdelivr.net only — narrow allow-list). Pass the LaTeX without surrounding $$. Optional title. Returns the relative path; pair with telegram_send_photo.
telegram_send_photo Send a rendered photo (from data/renders/) as an inline Telegram photo with preview. Pair with render_html or render_latex.

Browser

Drive a real headless Chromium for pages WebFetch can't reach (JS-rendered, multi-step, form-driven). One warm browser is reused for the whole session; browser_navigate opens a page and the rest of the tools act on that same page across the turn — so flows like search → open the images tab → grab the first image → send it work. Live network is allowed here (unlike renders), but localhost / RFC1918 / link-local / file:// targets are refused. On by default; disable by listing the tools in builtin_tools_disabled.

Tool What it does
browser_navigate Open a URL in the shared Chromium page and wait for the DOM. Start here, then use the other browser_* tools on the same page. ~30s load budget.
browser_back Go back one entry in the page history. Returns the URL landed on.
browser_reload Reload the current page (content changed or didn't finish loading).
browser_reset Close the page and clear cookies/state; the next browser_navigate opens a fresh isolated tab.
browser_click Click an element. Waits up to ~10s for it to be actionable.
browser_fill Type a value into an input/textarea (replaces existing content). Pair with browser_click to submit.
browser_press_key Press a key (Enter to submit, Tab to move focus); optional selector targets a field.
browser_select_option Choose an option in a native <select> dropdown (use instead of browser_fill).
browser_scroll Scroll the page (or a selector into view) to reveal lazy-loaded content.
browser_get_text Return the visible text of the page, or one element via CSS selector. Truncated.
browser_get_html Return the page HTML, or one element's inner HTML. Use when structure/attributes matter. Truncated.
browser_get_attribute Read one HTML attribute of an element — e.g. an image's src, a link's href.
browser_list List elements matching a selector with their text and href/src — pick which link/image to act on.
browser_wait_for Wait for an element to appear (after an async-loading click) before reading or acting.
browser_screenshot Screenshot the page (or one element) to a PNG under data/renders/; pair with telegram_send_photo.
browser_download Download the original file at a URL (typically an image) into data/renders/ and return its path; pair with telegram_send_photo. Get the URL first with browser_get_attribute.

Memory (memories/)

Tool What it does
memory_list List existing memory files, each with its frontmatter description (progressive disclosure, like skill_list). Legacy files without frontmatter show just path + size.
memory_search Search the text inside memory files for keywords; returns matching lines, best matches first.
memory_read Read a memory file by relative path.
memory_write Create or overwrite a memory file. Content must begin with name/description frontmatter (the template); writes without it are rejected. Read-before-write rail enforced; 64 KiB cap.
memory_append Append text to a memory file's body and refresh its frontmatter description (so memory_list stays current). Name is preserved or derived from the filename; the first append migrates a legacy file onto the template.
telegram_send_memory_document Deliver a memory file to a chat as a downloadable Telegram document. Path-locked to the memory root. Optional caption + reply-to.

One store. All memory lives in the single memories/ folder at the repo root. It is git-tracked, so memories survive a volume loss and the operator can commit them; in Docker it's bind-mounted so runtime writes land in the host checkout. The bot reads, searches, writes, and appends here — there is no read-only tier. See memories/README.md.

Every memory is named by its full path starting with memories/. The prefix is required; a bare path like notes/ref.md is rejected, so pass paths verbatim from memory_list / memory_search.

Memory files follow the same frontmatter protocol as skills — a --- block with name and description — so the agent can scan memory_list and pick the right file without reading every one:

---
name: <short human-friendly label>
description: <one-line summary used to find this memory without reading it>
---

<body — the actual remembered content>

There is no delete_memory by design — overwriting is the supported "forget" path. Operator handles real deletion on host.

Self-editing (project prompt)

Tool What it does
instruction_read Read the current contents of prompts/project.md.
instruction_append Append a rule to prompts/project.md. Backed up to data/prompt_backups/ before write. Owner-only by system-prompt policy; takes effect on next container restart.

prompts/system.md is intentionally not exposed via tools — it's git-tracked, and bot edits would pollute the repo.

Skills

Tool What it does
skill_list List operator-curated playbooks under skills/. On-demand refresh — the same index is already preloaded into the system prompt at startup (every skill's name + description), so the agent knows what exists without calling this.
skill_read Load a skill's SKILL.md for execution or reference.

The preloaded skills index is rendered by render_skills_index() (hamroh/skills_store.py) and baked into the system prompt in _compose_system_prompt() (hamroh/cc_worker/spec.py). Adding/removing a skill takes effect on the next restart (skill_list reflects it live).

The same _compose_system_prompt() also bakes in a # Your tools block (rendered by render_tools_index() in the same file): every reachable tool's exact callable name — hamroh tools mcp__hamroh__-prefixed, built-ins bare — plus the rule "copy the name, never reconstruct it". Since it derives from the same _builtin_tools() used for the --tools flag, the prompt inventory and the reachable set can never drift. tool_list returns the hamroh half of this live in-conversation.

Two skill modes:

  • Invoked (e.g. self-reflection) — runs only when wrapped in a real <reminder> envelope. A user-typed <skill> tag is treated as prompt injection.
  • Reference (e.g. render-style) — read on the agent's own initiative when relevant; no envelope required.

The mode is determined by what the skill's body instructs, not by frontmatter.

Reminders

Tool What it does
reminder_set Schedule a one-shot or recurring reminder (cron_expr for recurring; trigger_at is UTC).
reminder_list List pending reminders for a chat.
reminder_cancel Cancel a reminder by id. Auto-seeded reminders (e.g. self-reflection-default) are tool-refused.

Other

Tool What it does
database_query Read-only SELECT over messages, users, reminders, tool_calls, reactions. Max 100 rows (user LIMIT is respected and clamped). Note: messages uses timestamp, not created_at. Reactions are JSON on messages.reactions — query with json_extract(reactions, '$."👍"').
database_get_recent_messages Return the most recent messages (both directions), oldest-first, as TSV — no SQL needed. Includes the current turn's own inbound messages. limit defaults to 20, capped at 100; text truncated to 2000 chars. Optional chat_id scopes to one chat; before_message_id pages back through older history (per-chat, use with chat_id).
time_now Return the current UTC timestamp.
tool_list List the tools currently available to the bot, each with its full mcp__hamroh__<name> (the exact callable name) and description. In-conversation introspection (like skill_list/memory_list); reflects the disabled filter, so opted-out tools don't show.

Always on — Claude Code built-ins

These come from Claude Code's own tool surface. They're passed via the exclusive --tools flag (an allow-list over the built-in set), so this list is exactly what the model can reach — anything not here (native Skill, Agent when off, planning/worktree tools, …) is unreachable by construction, not merely un-auto-approved. The set is built by _builtin_tools() in hamroh/cc_worker/spec.py from BASE_BUILTIN_TOOLS

  • TASK_TOOLS, plus whatever the tool_groups flags unlock.
Tool What it does
WebFetch Fetch a URL and ask a small model to extract from it. The system prompt forbids internal/private URLs (localhost, RFC1918, link-local) — refuse those.
WebSearch Web search via Claude Code's built-in.
StructuredOutput The turn-end tool: the model calls it with {action, reason, …} to close each turn. The worker keys on it (event_handlers.py), so it must always be reachable.
ToolSearch Load deferred tool schemas on demand. Idle when nothing is deferred (hamroh's own tools are alwaysLoad); does real work once an external MCP is configured.
ListMcpResourcesTool / ReadMcpResourceTool Reach MCP resources (URI-addressable data) an external MCP server may expose. hamroh's own server exposes none, so these are no-ops until a resource-bearing MCP is enabled.
WaitForMcpServers Block on an external MCP server that is still connecting.
TaskCreate / TaskGet / TaskList / TaskUpdate Session task-checklist, so the bot can track a multi-step turn (e.g. a research digest fanning out over many sources). No permission required.

The MCP-discovery and task tools are read-only / no-permission and harmless when idle. TaskStop/TaskOutput are deliberately omitted (background-task control the fire-and-forget bot doesn't need; TaskOutput is deprecated).


Opt-in tool groups

Two configuration layers feed the bot's tool surface:

  • plugins.json at repo root — single source of truth for tool-group toggles, the list of external MCP servers, the builtin_tools_disabled list, and skills_disabled. The operator copies the shipped plugins.json.example once (cp plugins.json.example plugins.json), edits, and restarts. plugins.json is gitignored so customisations stay local.
  • .env — credentials for external services (Jira, GitLab, GitHub) only. Referenced from plugins.json via ${VAR}.

Changes to either take effect on container restart.

plugins.json shape

{
  "tool_groups": {
    "bash": false,
    "code": false,
    "subagents": false
  },
  "mcps": [
    {
      "name": "mcp-atlassian",
      "type": "sse",
      "url": "https://mcp.atlassian.com/v1/sse",
      "allowed_tools": ["mcp__mcp-atlassian"],
      "enabled": true
    }
  ],
  "skills_disabled": []
}
  • tool_groups — flips for the Claude-Code built-ins below (bash, code, subagents). Edit and restart to flip.
  • mcps[].name is load-bearing — it becomes the mcp__<name>__<tool> namespace the model sees. Renaming breaks operator memory, prompts, and tests. The defaults match today's keys exactly (mcp-atlassian, mcp-gitlab, github).
  • mcps[].type selects the transport: stdio (default), http, or sse. Mirrors what Claude Code's --mcp-config accepts. See "MCP transports" below for the per-transport field shape.
  • mcps[].allowed_tools is one flat list — exact tool name (mcp__mcp-atlassian__jira_search) or a server-prefix shorthand (mcp__mcp-gitlab). Both forms are accepted by Claude Code's --allowedTools.
  • ${VAR} interpolation runs over args (each element), env values, url, and headers values — pulling from the process env (i.e. .env). Concatenation works (${GITLAB_URL}/api/v4). If any referenced ${VAR} resolves empty, that MCP is silently skipped at boot — preserving today's "credentials missing → MCP not spawned" semantics.
  • enabled: false skips the MCP even if its ${VAR} refs resolve.
  • A missing plugins.json boots with empty plugins (locked-down). A malformed plugins.json crashes boot loudly with a PluginsConfigError.

MCP transports

Three transports are supported, exactly as the MCP spec and Claude Code's --mcp-config define them. Mixing fields across transports (e.g. command on an http entry) crashes boot.

stdio — local subprocess (default). hamroh spawns the command, talks over stdin/stdout. Auth via the subprocess env block.

{
  "name": "github",
  "type": "stdio",          // optional; default when omitted
  "command": "npx",
  "args": ["-y", "@modelcontextprotocol/server-github"],
  "env": { "GITHUB_PERSONAL_ACCESS_TOKEN": "${GITHUB_PERSONAL_ACCESS_TOKEN}" },
  "allowed_tools": ["mcp__github"],
  "enabled": true
}

http — remote streamable-HTTP server. Auth via static headers. Use this for hosted MCPs (Linear, Notion-cloud, GitHub's remote MCP, etc.) where you've already issued a PAT or OAuth token.

{
  "name": "linear",
  "type": "http",
  "url": "https://mcp.linear.app/mcp",
  "headers": { "Authorization": "Bearer ${LINEAR_API_KEY}" },
  "allowed_tools": ["mcp__linear"],
  "enabled": true
}

sse — Server-Sent Events transport. Same field shape as http. Some hosted MCPs use this; check vendor docs.

{
  "name": "events",
  "type": "sse",
  "url": "https://example.com/sse",
  "headers": { "X-API-Key": "${EVENTS_KEY}" },
  "allowed_tools": ["mcp__events"],
  "enabled": true
}

Auth. Hamroh doesn't manage OAuth flows — supply an already-issued token via ${VAR} interpolation. For interactive OAuth-managed servers, see Claude Code's MCP docs (it can run the flow on your behalf when configured outside plugins.json).

Tool groups

These three groups default to off. Flip in plugins.json (tool_groups.<name>: true).

bash — shell execution

Tool What it does
Bash Run shell commands.
PowerShell Run PowerShell commands (Windows / opt-in via CLAUDE_CODE_USE_POWERSHELL_TOOL).
Monitor Watch a long-running process and stream output back to the model.

These all share Claude Code's "permission required" risk class — same trust class. Off by default for safety.

code — code work

Tool What it does
Edit Targeted edits to a file.
Write Create or overwrite a file.
Read Read a file.
NotebookEdit Edit Jupyter notebook cells.
Glob Find files by glob pattern.
Grep Search file contents.
LSP Code intelligence — definitions, references, type errors. Requires a code-intelligence plugin to be installed.

Useful unit when you want the bot to do real code work (not just chat). The Telegram-assistant deployment leaves this off and relies on memory

  • project.md for everything it needs to remember.

subagents

Tool What it does
Agent Spawn a subagent with its own context window for an isolated task. Token-heavy — leave off unless you need it. When off, the subagent docs (prompts/subagents.md) aren't even loaded, so the model doesn't know the capability exists.
SendMessage Resume/steer a background subagent (or message an agent-team teammate). Unlocked together with Agent.

Adding a new external MCP

  1. Append an entry to mcps in plugins.json with a unique name, the command to spawn (and any args), the env it needs (use ${VAR} to pull credentials from .env rather than committing them), and allowed_tools listing the exact tool names or mcp__<name> prefix to advertise.
  2. Add any referenced env vars to .env.
  3. Restart: docker compose up -d --force-recreate.
  4. Tail logs — you should see mcp <name> configured (...). If it says skipped (unresolved ${VAR} ...), an env var is empty.

Disabling a built-in hamroh tool

The built-ins under "Always on — hamroh built-ins" are auto-discovered from hamroh/tools/*.py and registered every boot. To hide one (e.g. you don't use polls, or you don't want LaTeX rendering eating context), list its name in builtin_tools_disabled:

{
  "builtin_tools_disabled": [
    "telegram_create_poll", "telegram_stop_poll",
    "render_latex", "render_html", "telegram_send_photo"
  ]
}

The tool is skipped at MCP registration time — it's never instantiated, never advertised, and the model has no way to invoke it. Names must match an exact tool name (the name class attribute on the BaseTool subclass — also the cell text in this doc's tables). A typo crashes boot with the available list.

There is no curated "essential" set — disabling telegram_send_message mutes the bot, disabling telegram_read_attachment makes it blind to inbound photos and documents. The operator owns this trade-off.

Disabling a skill

Add the skill's directory name to skills_disabled:

{ "skills_disabled": ["render-style"] }

Restart. skill_list no longer surfaces it and skill_read raises "not found", so envelope-driven invocations (<skill name="...">) can't bypass the toggle either.

Jira — Atlassian's remote MCP over SSE

The default plugins.json ships an mcp-atlassian entry pointed at Atlassian's official remote MCP (https://mcp.atlassian.com/v1/sse, type: sse). It's enabled: false by default — flip it to true to advertise the mcp__mcp-atlassian tools on --allowedTools.

Auth is OAuth, not env vars. The remote server authenticates via OAuth, which hamroh does not manage. Establish the grant once on the host with Claude Code (claude mcp add --transport sse atlassian https://mcp.atlassian.com/v1/sse, then complete the browser login); Claude Code reuses the stored token. The headless bot can't run the browser flow itself, so the OAuth grant must already exist on the host.

For the tool list and capabilities see Atlassian's remote MCP docs.

GitLab — derived from GITLAB_URL + GITLAB_TOKEN

The default plugins.json ships an mcp-gitlab entry that references both vars. When they're set in .env, the mcp-gitlab server spawns and the mcp__mcp-gitlab prefix is added to --allowedTools. Unlike the Jira server, mcp-gitlab is GitLab-only — the prefix match is safe.

For the canonical GitLab tool list see upstream https://github.com/zereight/mcp-gitlab.

GitHub — derived from GITHUB_PERSONAL_ACCESS_TOKEN

The default plugins.json ships a github entry that references the token. When set in .env, the github MCP server spawns (via npx -y @modelcontextprotocol/server-github) and the mcp__github prefix is added to --allowedTools. Single-vendor server, blanket prefix match is safe.

GitHub.com is the default. For GitHub Enterprise, add a "GITHUB_HOST": "${GITHUB_HOST}" line to the github plugin's env block in plugins.json and set GITHUB_HOST (e.g. github.example.com) in .env. (The default omits this line so github.com users aren't blocked by an unset var.)

How to generate the token (fine-grained PAT — recommended):

  1. Go to https://github.com/settings/tokens?type=beta.
  2. Token name: hamroh (or your bot's name).
  3. Expiration: 90 days (max for fine-grained). Set a calendar reminder to rotate.
  4. Resource owner: your account, or the org if the bot acts on org repos.
  5. Repository access: "Only select repositories" → pick exactly the repos the bot should touch. Never "All repositories" unless that's truly what you want.
  6. Repository permissions — grant only what the bot needs:
    • Contents — Read & write (read code, push branches, commit)
    • Issues — Read & write (file bug tickets from chat)
    • Pull requests — Read & write (open PRs, comment)
    • Metadata — Read (mandatory; auto-granted)
    • Actions — Read & write (only if the bot should trigger or read CI)
    • Everything else: "No access"
  7. Click Generate token. Copy the github_pat_... string — you won't see it again.
  8. Paste into .env as GITHUB_PERSONAL_ACCESS_TOKEN=github_pat_... and restart: docker compose up -d --force-recreate.

Avoid classic PATs. They grant scopes per-org with no per-repo limit, so a leaked classic token has a much bigger blast radius. The ?type=beta URL above gets you the fine-grained kind.

GitHub Enterprise: generate the PAT on your Enterprise instance and also set GITHUB_HOST=github.your-company.com in .env.

Swapping the MCP server. If you'd rather use the official Go-based github/github-mcp-server instead of the npm package, edit the github entry in plugins.json: swap command/args and adjust the env block. The rest of the plumbing (allowlist, credential interpolation) stays as-is.


How to enable / disable

Tool groups — flip in plugins.json:

{ "tool_groups": { "bash": true, "code": true, "subagents": false } }

External MCPs — credentials in .env, advertise/disable in plugins.json:

# Jira (set all three; mcp-atlassian spawns when present)
JIRA_URL=https://your-site.atlassian.net
JIRA_USERNAME=you@example.com
JIRA_API_TOKEN=...

# GitLab (set both)
GITLAB_URL=https://gitlab.example.com
GITLAB_TOKEN=...

# GitHub
GITHUB_PERSONAL_ACCESS_TOKEN=github_pat_...

To stop advertising an MCP without removing credentials, flip enabled: false on its entry in plugins.json. To remove entirely, delete the entry.

Skills — list directory names in plugins.json skills_disabled.

Restart the container after any edit: docker compose up -d --force-recreate. Tail logs at startup — plugins loaded: N enabled mcp(s), M disabled skill(s), tool_groups={...} summarises the active config; mcp <name> configured (...) / mcp <name> skipped (...) lines explain each MCP's outcome.


Other CC tools you can wire in

hamroh doesn't expose every Claude Code built-in. The remaining omissions are deliberate (see the full-catalog audit in the BASE_BUILTIN_TOOLS comment in hamroh/cc_worker/spec.py). A fork that wants any of these can add it to BASE_BUILTIN_TOOLS (always on) or a gated set (BASH_TOOLS, CODE_TOOLS, SUBAGENT_TOOLS) in that same file; _builtin_tools() assembles the final --tools list.

Already exposed (don't re-add): the task-checklist tools (TaskCreate, TaskGet, TaskList, TaskUpdate) and MCP-discovery tools (ToolSearch, ListMcpResourcesTool, ReadMcpResourceTool, WaitForMcpServers) are on by default; SendMessage unlocks with subagents.

Notable upstream tools still off on purpose:

  • Skill — hamroh runs skills through its own <reminder><skill> envelope + mcp__hamroh__skill_read, not CC's native Skill tool; leaving it on is a dead-end (empty registry).
  • Scheduled tasksCronCreate, CronDelete, CronList. Would duplicate hamroh's own reminder_* tools.
  • PushNotification / SendUserFile — hamroh delivers over Telegram; these need Anthropic push infra / Remote Control.
  • AskUserQuestion — interactive multi-choice UI, dead in --print headless mode; the bot asks via Telegram instead.
  • Planning & worktreesEnterPlanMode, ExitPlanMode, EnterWorktree, ExitWorktree. Dev-loop tools; useful for code-work forks.
  • Artifact, RemoteTrigger, ScheduleWakeup, ShareOnboardingGuide, Workflow, TodoWrite, ReportFindings — claude.ai / Team-plan / deprecated / not applicable to a chat bot.
  • TaskStop / TaskOutput — background-task control the fire-and-forget bot doesn't need (TaskOutput is deprecated).

For each, the upstream https://code.claude.com/docs/en/tools-reference is authoritative — it lists permission requirements and behaviour notes that may change between CC versions.