Documentation
¶
Overview ¶
Package core — AgentNew MCP tool. Mirrors `clawtool agent new` so a model can scaffold a Claude Code subagent persona from inside a conversation. Both surfaces share the same template renderer (internal/agentgen) so the output is byte-identical.
Terminology reminder (operator's 2026-04-27 ruling):
- **agent** = a USER-DEFINED PERSONA (this tool scaffolds one)
- **instance** = a configured upstream CLI bridge (claude / codex / gemini / opencode / hermes / openclaw / ...)
Don't confuse this with the legacy AgentList tool (agents_tool.go), which currently still surfaces *instances* under the legacy "agent" name. That rename is tracked separately.
Package core — SendMessage and AgentList MCP tools (ADR-014 Phase 1).
SendMessage routes a prompt to the resolved agent's transport and buffers the streaming reply for the MCP response. Full HTTP-grade streaming arrives with `clawtool serve` in Phase 2; the MCP wire here is request/response so we accept the buffer cap.
AgentList exposes the supervisor's registry snapshot — same shape as `clawtool send --list` and `GET /v1/agents`. Mirrors the v0.9 `RecipeList` pattern (read-only, structured, BaseResult-shaped).
Package core — atomic.go: shared file-mutation primitives for Edit and Write. These helpers guarantee no partial-write artifacts on crash and give us a single place to add safety polish (line-ending preservation, BOM handling) consistently across both tools.
Per ADR-007 we do not use a third-party "atomic write" library — Go's stdlib (`os.WriteFile` + `os.Rename`) is enough when used correctly. The platform invariant we rely on: rename(2) on the same filesystem is atomic, so writers never see a half-written file at the target path.
Package core implements clawtool's canonical core tools per ADR-005.
Quality bar reminder: each core tool must measurably beat the corresponding native built-in across the major agents. For Bash:
- timeout-safe: output is captured even when the process is killed
- predictable cwd: defaults to $HOME, never the daemon's cwd
- structured result: stdout/stderr/exit_code/duration_ms/timed_out returned as JSON even on timeout
v0.1 implements timeout-safe + structured result + cwd. Secret redaction and per-session command history land in v0.2.
Package core — Bash background-mode task registry (ADR-021 phase B, Codex's "long-running" recommendation). Mirrors BIAM's task vocabulary (pending / active / done / failed / cancelled) without reusing the SQLite store: bash subprocess output is volatile, signing every stdout chunk via Ed25519 (which BIAM would do) is the wrong default. Process-local in-memory registry, lifetime = clawtool serve process.
Package core — MCP surface for Bash background tasks. The underlying registry is in bash_bg.go; this file is the wiring layer mapping {BashOutput, BashKill} onto Get/Kill helpers and rendering the snapshot under the standard core-tool envelope.
Package core — Bridge* MCP tools (ADR-014 Phase 1).
Mirrors `clawtool bridge add/list/remove/upgrade` over MCP so a model can install / inspect / uninstall bridges mid-conversation ("kanka gemini bridge'i kur"). Same dispatch path as the CLI — both end up calling setup.Apply on the bridge's recipe.
Package core — BrowserFetch retrieves a URL through a real browser engine (Obscura, Chromium-via-CDP) so SPA / JS-rendered content lands in the agent's context. Sister tool to WebFetch (server-side via Mozilla Readability), which can't render React / Next.js / hydrated SPAs.
Per ADR-007 we wrap mature engines: Obscura (V8 + Chrome DevTools Protocol, Apache 2.0). We never re-implement page loading. clawtool adds: agent-friendly polish (size cap, structured result, optional JS evaluator, optional CSS-selector wait, post-render readability pass for clean prose).
Stateless: each call spins a fresh browser context. For interactive multi-step flows (login + cookie + click + capture) use BrowserAction.
Package core — BrowserScrape parallelises BrowserFetch across many URLs by wrapping `obscura scrape <url...> --concurrency N --eval ... --format json`. Use case: "give me the rendered headline from these 50 SPA blog posts", "bulk-snapshot a competitor's site map", etc.
Per ADR-007 we wrap Obscura's scrape subcommand (Apache-2.0 Rust engine, V8 + CDP) — clawtool never re-implements parallel fetching. Stateless: each URL gets its own browser context, no cookies, no shared session. For interactive work use BrowserAction.
Package core — Commit MCP tool. Wraps internal/checkpoint's Commit primitive (ADR-022) so an agent can land a Conventional Commits-validated, Co-Authored-By-blocked commit through one tool call instead of three Bash invocations.
This tool is what closes the operator's earlier gap: agents shell out to `Bash git commit -m "feat: …"` because there's no Commit tool, the messages aren't always conventional-shaped, and Bash has no way to refuse a Co-Authored-By trailer. Commit makes the right path the easy path.
Pre-commit guardrails layered through (in order):
- Repo check — bails with a clear error if cwd isn't a Git repo.
- internal/rules.Evaluate at EventPreCommit — operator's declarative invariants (e.g. "skill routing-map row updated" when a core tool changed). A Verdict.IsBlocked() = true is a hard refusal.
- internal/checkpoint.ValidateMessage — Conventional Commits + Co-Authored-By block.
- Optional dirtiness guard — refuses to commit if the working tree still has unstaged changes after staging (catches "you forgot to stage X" mid-flight).
Package core — Edit performs a precise search-and-replace on an existing file with safety polish:
- uniqueness check by default (refuses ambiguous edits)
- atomic temp+rename so a crash never leaves a half-written file
- line-ending preserve (LF / CRLF / CR detected from current content)
- BOM preserve
- binary refusal (symmetric with Read)
Per ADR-007 we wrap stdlib `os` for I/O and add our own polish layer. The search-and-replace shape mirrors what Claude Code's native Edit uses today (old_string / new_string / replace_all) — agents that learnt that interface get the same affordances against clawtool's stronger invariants.
Package core — Glob is the canonical wrapper around bmatcuk/doublestar.
Per ADR-007 we don't write a glob engine. doublestar is the de-facto double-star (`**`) glob library in Go, used by GoReleaser, k6, etc. This file's value is the polish layer: cwd-aware path resolution, uniform structured output, hard cap to protect agent context, and platform-stable separators (the wrapper always returns forward-slash paths regardless of OS — agents expect that).
ADR-021 phase B added .gitignore-aware traversal — when cwd is a Git worktree we ask `git ls-files --cached --others --exclude-standard -z` for the candidate set then run doublestar over it, which gives us the same ignore semantics as ripgrep (and keeps the operator's expected ".git/, vendor/, node_modules/ ignored by default" behaviour).
Package core — Grep wraps ripgrep when present, falls back to system grep.
Per ADR-007 we curate ripgrep as the default engine: it has the correct .gitignore semantics, --type aliases, fast Aho-Corasick matching, and a stable JSON output that is straightforward to parse. The fallback is system grep (-rn) so clawtool's Grep is never unavailable.
Output is uniform across engines so the agent never has to know which one ran. The `engine` field in the result lets users / tests verify.
Package core — typed manifest of clawtool's MCP tools (#173 Step 2 of Codex's #1 ROI refactor).
BuildManifest assembles a *registry.Manifest with one ToolSpec per shipped tool. Step 2 (this commit) ONLY adds entries for the youngest six tools — Commit, RulesCheck, AgentNew, BashOutput, BashKill, TaskNotify. server.go still calls each tool's RegisterX directly; the manifest is not yet consumed at boot. That hookup lands in Step 3, after the older tools (Bash / Read / Edit / Write / Grep / Glob / WebFetch / WebSearch / ToolSearch) get the same treatment.
Why incremental: a single big-bang manifest migration carries the risk that one register-fn signature mismatch (or one missed gate) breaks every tool at once. Doing it six tools at a time, with the surface_drift_test guarding cross-plane invariants, makes each step audit-able and rollback-able.
Why the youngest first: they have the freshest test coverage and the smallest blast radius if a migration mistake slips through. By the time we reach the older core (Bash / Read / Edit / Write) the registry harness is battle-tested.
Package core — Mcp* MCP tools (ADR-019). v0.17 fills in `McpNew` (real generator wrapper), `McpList` (real walker), and keeps thin stubs for `McpRun` / `McpBuild` / `McpInstall` that point at the CLI shortcut (those are inherently filesystem-side operations the model doesn't usually drive).
Package core — Portal* MCP tools (ADR-018). Read-only surface in v0.16.1: PortalList, PortalUse, PortalWhich, PortalUnset, PortalRemove, plus a deferred-feature stub for PortalAsk so the shape is discoverable before the v0.16.2 CDP driver lands.
PortalAdd is intentionally CLI-only — it spawns $EDITOR which has no meaning in an MCP context. Operators add portals from the terminal; agents discover and use them through MCP.
Package core — Read returns file content with stable line cursors, deterministic line counts, and per-format dispatch.
Per ADR-007 we wrap mature engines instead of writing parsers:
- text → stdlib bufio (line-walked, single-pass)
- pdf → pdftotext (poppler-utils) shell-out
- ipynb → native JSON cell parse
- docx → pandoc shell-out (universal office converter)
- xlsx → github.com/xuri/excelize/v2 (Microsoft/Alibaba/Oracle in prod)
- csv / tsv → stdlib encoding/csv (header + bounded preview)
- html → github.com/go-shiori/go-readability (Mozilla Readability port)
- json / yaml / toml / xml → text passthrough with format tag
- binary → refused with structured error
Adding a new format means: extend detectFormat, add a reader function, update CoreToolDocs's description, ship tests for the format. The readResult shape stays uniform so the agent never has to branch on which engine ran.
Package core — Recipe* MCP tools mirror the `clawtool recipe …` CLI surface so a model can run setup tasks from inside a conversation. Same registry, same Detect/Apply/Verify cycle.
Three tools per ADR-013:
RecipeList — enumerate recipes, optionally filter by category.
RecipeStatus — Detect output for one recipe or all of them.
RecipeApply — full Detect→Prereqs→Apply→Verify against a repo,
with structured options.
All three return BaseResult-shaped output (pretty text + structured JSON) so chat UIs render them the same way as Bash/Read/etc.
Package core — RulesCheck MCP tool. Surfaces the rules engine (internal/rules) so an agent can ask "are the operator's invariants satisfied right now?" without first having to call the unattended-mode supervisor or wait for pre_commit time.
This tool is read-only: it loads .clawtool/rules.toml (or the XDG fallback), evaluates against a caller-supplied Context, and returns the Verdict (results + warnings + blocked). It does NOT hook into Edit/Write/Bash automatically — rule enforcement at tool-call time lands when the Tool Manifest Registry refactor (#173) gives us a middleware seam.
Package core — Sandbox* MCP tools (ADR-020). v0.18 ships the read-only surface (List / Show / Doctor) so models can discover the profile catalog and recommend the right one to operators. SandboxRun is intentionally CLI-only — letting a model spawn sandboxed commands has the wrong default.
Package core — SemanticSearch MCP tool (ADR-014 T6, design from the 2026-04-26 multi-CLI fan-out).
Concept queries ("how is auth rotated?") that Grep can't reach because the literal token isn't there. We wrap chromem-go's in-memory vector store + the configured embedding provider (OpenAI default, Ollama override). One Store per repo, lazily built on first Search call so cold-boot doesn't pay the embedding cost when the tool isn't being used.
Coexistence with Grep: Grep stays the literal regex tool; this is the conceptual one. Tool descriptions carry the routing hint so ToolSearch ranks each correctly per query.
Package core — session-scoped read tracking for the Read-before-Write guardrail (ADR-021). MCP session id is the key; we look it up via server.ClientSessionFromContext, never from a tool argument (Codex flagged this — model-supplied session ids can't be trusted).
Package core — file-IO helpers extracted to keep session_state.go pure. Mirrors the read.go IO surface but stays small enough to embed.
Package core — SkillNew MCP tool. Mirrors `clawtool skill new` so a model can scaffold a Claude Code skill from inside a conversation, without the user dropping to a shell. Both surfaces share the same template renderer (passed in at registration so we don't reach across packages — this file stays a leaf).
Spec compliance: agentskills.io. SKILL.md gets YAML frontmatter with `name` + `description` (required) plus optional `triggers`, followed by the body template. Same shape as the CLI emits.
Package core — TaskNotify MCP tool. Edge-triggered completion push that pairs with SendMessage(bidi=true). Subscribes to the in-process biam.Notifier so the caller wakes the instant ANY of the watched tasks reaches a terminal state — no SQLite poll, no external CLI hooks.
Architecture: the runner publishes a *biam.Task to Notifier when it flips a row to a terminal state (see internal/agents/biam/ runner.go). Here we register one channel per task_id, then `select` across all of them + the timeout context. First task wins; the rest stay subscribed until the caller polls them with TaskGet (their slot decays at next Publish or process exit).
Already-terminal tasks: we eagerly check the store BEFORE blocking, so a TaskNotify call against a task that already finished returns immediately rather than waiting for a Publish that already happened.
Package core — TaskGet / TaskWait / TaskList MCP tools (ADR-015 Phase 1). Surface the BIAM SQLite store the supervisor's async runner persists into, so a calling model can:
- Fire SendMessage with bidi=true → receive task_id immediately.
- Continue its own work without blocking on the upstream.
- Pull back via TaskGet (snapshot) / TaskWait (block until terminal) when it actually needs the result.
All three tools are read-only and stateless beyond the BIAM store.
Package core — ToolSearch is the discovery primitive that makes a 50+ tool catalog usable per ADR-005. The agent calls ToolSearch with a natural-language query, gets ranked candidates, then binds to the right tool with its full schema via the regular tools/list output.
Package core — Verify MCP tool (ADR-014 T4, design from the 2026-04-26 multi-CLI fan-out).
Verify runs a repo's tests / lints / typechecks via whichever runner the repo declares (Make, pnpm, npm, go, pytest, ruby, cargo, just) and returns one structured pass/fail per check. Per ADR-007 we wrap maintained runners — `go test -json`, `pytest --json-report`, `cargo test --message-format json` — and fall back to the runner's plain output when the structured form isn't available on this host.
Buffered single payload (not stream): callers want the full pass/fail summary, not the live log fire hose. Bash already streams when that's what's wanted.
Package core — WebFetch retrieves a URL and renders the body in the best shape for an AI agent: clean article text for HTML, raw text for plain-text MIME types, structured rejection for binaries.
Per ADR-007 we wrap two mature engines:
- net/http stdlib client for transport (proxy, TLS, redirect handling are all stock — battle-tested across Go's user base);
- github.com/go-shiori/go-readability for HTML extraction (Mozilla Readability port, the same algorithm Firefox Reader View ships).
What clawtool adds: agent-friendly polish — a hard body cap so a runaway page can't blow context, structured result with content-type-aware `format`, citation metadata (final URL after redirects, fetched_at).
Package core — SSRF guard for WebFetch (ADR-021 phase B).
Without this, an agent could ask WebFetch for `http://169.254.169.254/` (AWS metadata), `http://localhost:5432/` (the operator's local Postgres), or any RFC1918 address (the operator's internal network). The guard blocks resolution to those address ranges BEFORE the GET is issued, and re-checks every redirect target so a public 302→private redirect chain is rejected too.
Per ADR-007 we don't ship our own DNS resolver — net.LookupIP is canonical. We only own the address-range allow/deny logic.
Package core — WebSearch is a pluggable web-search primitive. clawtool itself does no crawling or ranking; it adapts whichever search backend the user has configured (Brave today; Tavily / SearXNG planned) into a uniform `{results, backend, …}` shape.
Per ADR-007 we wrap, never reinvent. The backend interface is small on purpose so adding a new provider is one file (see websearch_brave.go for the canonical example).
Package core — Write creates or overwrites a whole file with given content. Sister tool to Edit; the boundary is intentional:
- Edit modifies a substring of an existing file. Refuses if the file is missing.
- Write replaces or creates the whole file. If the file already exists and has detectable line endings, those endings are preserved by default so existing tooling (CR LF on Windows, etc.) keeps working.
Per ADR-007 we wrap stdlib `os` for I/O. The polish layer is the same atomic-write primitive Edit uses, plus a parent-directory auto-create so agents don't need a separate `mkdir` step before writing.
Index ¶
- Variables
- func BuildManifest() *registry.Manifest
- func CoreToolDocs() []search.Doc
- func HashFile(path string) (string, error)
- func HashString(s string) string
- func RegisterAgentNew(s *server.MCPServer)
- func RegisterAgentTools(s *server.MCPServer)
- func RegisterBash(s *server.MCPServer)
- func RegisterBashKill(s *server.MCPServer)
- func RegisterBashOutput(s *server.MCPServer)
- func RegisterBridgeTools(s *server.MCPServer)
- func RegisterBrowserFetch(s *server.MCPServer)
- func RegisterBrowserScrape(s *server.MCPServer)
- func RegisterCommit(s *server.MCPServer)
- func RegisterEdit(s *server.MCPServer)
- func RegisterGlob(s *server.MCPServer)
- func RegisterGrep(s *server.MCPServer)
- func RegisterMcpTools(s *server.MCPServer)
- func RegisterPortalAliases(s *server.MCPServer, cfg config.Config)
- func RegisterPortalTools(s *server.MCPServer)
- func RegisterRead(s *server.MCPServer)
- func RegisterRecipeTools(s *server.MCPServer)
- func RegisterRulesCheck(s *server.MCPServer)
- func RegisterSandboxTools(s *server.MCPServer)
- func RegisterSemanticSearch(s *server.MCPServer)
- func RegisterSkillNew(s *server.MCPServer)
- func RegisterTaskNotify(s *server.MCPServer)
- func RegisterTaskTools(s *server.MCPServer)
- func RegisterToolSearch(s *server.MCPServer, idx *search.Index)
- func RegisterVerify(s *server.MCPServer)
- func RegisterWebFetch(s *server.MCPServer)
- func RegisterWebSearch(s *server.MCPServer, store *secrets.Store)
- func RegisterWrite(s *server.MCPServer)
- func ResetBashTasksForTest()
- func ResetEngineCache()
- func ResetSemanticSearchCache()
- func ResetSessionsForTest()
- func SetAutoLintEnabled(enabled bool)
- func SetBiamStore(s *biam.Store)
- func SetLintRunner(r lint.Runner)
- func SubmitBackgroundBash(parent context.Context, command, cwd string, timeoutMs int) (string, error)
- type Backend
- type BaseResult
- type BashTask
- type BashTaskSnapshot
- type BashTaskStatus
- type BashTaskStore
- type BrowserFetchResult
- type BrowserScrapeResult
- type BrowserScrapeRow
- type EditResult
- type Engine
- type GlobResult
- type GrepMatch
- type GrepResult
- type LineEndings
- type ReadRecord
- type ReadResult
- type Renderer
- type SearchOptions
- type SemanticSearchResult
- type SessionKey
- type SessionState
- type ToolSearchResult
- type VerifyCheck
- type VerifyResult
- type WebFetchResult
- type WebSearchHit
- type WebSearchResult
- type WriteResult
Constants ¶
This section is empty.
Variables ¶
var BashTasks = &BashTaskStore{tasks: map[string]*BashTask{}}
BashTasks is the singleton. Tests use ResetBashTasksForTest.
var ErrBlockedAddress = errors.New("WebFetch refused: target resolves to a private / loopback / link-local / cloud-metadata address (SSRF guard, ADR-021)")
ErrBlockedAddress is the sentinel returned when the resolved IP falls into a deny range. Caller surfaces it verbatim.
var ErrMissingAPIKey = errors.New("missing API key")
ErrMissingAPIKey is returned by backends when their required API key is not present in either the secrets store or process env.
var Sessions = &SessionState{ reads: map[SessionKey]map[string]ReadRecord{}, }
Sessions is the process-wide singleton. Tests reset via ResetSessionsForTest.
Functions ¶
func BuildManifest ¶ added in v0.21.0
BuildManifest returns the typed manifest of every clawtool MCP tool. Caller (server.go in Step 3) walks it via manifest.Apply(s, runtime, cfg.IsEnabled).
Step 2 scope: 6 specs (Commit, RulesCheck, AgentNew, BashOutput, BashKill, TaskNotify). Each spec's Register fn adapts the existing RegisterX(s) signature to the registry.RegisterFn shape (s, runtime).
Specs added but Register-not-wired-yet are LEGAL — Apply silently skips them. We use that to document the older tools in the same manifest BEFORE migrating them, so search-index consumers (Step 4 work) can already see the canonical entry.
func CoreToolDocs ¶
CoreToolDocs returns search.Doc descriptors for every clawtool core tool. Step 4 of #173 collapsed the duplicated entry list into a delegate over BuildManifest().SearchDocs(nil) so the manifest is now the single source of truth. Kept as a public shim so the surface_drift_test (which iterates by spec name) stays a one-liner; internal callers go to the manifest directly.
func HashFile ¶ added in v0.20.0
HashFile returns SHA-256 of the file's raw bytes as hex. Helper used by Read / Write / Edit; centralised so the format stays consistent across tools.
func HashString ¶ added in v0.20.0
HashString computes SHA-256 of a string. Used for range_hash after format-aware decoding (PDF / DOCX / XLSX) so the hash captures the canonical text we returned to the agent, not the raw bytes.
func RegisterAgentNew ¶ added in v0.20.0
RegisterAgentNew adds the AgentNew tool to s. Template + helpers come from internal/agentgen so this MCP surface and the `clawtool agent new` CLI emit byte-identical files.
func RegisterAgentTools ¶ added in v0.20.0
RegisterAgentTools adds SendMessage + AgentList to the MCP server.
func RegisterBash ¶
RegisterBash adds the Bash tool to the given MCP server.
func RegisterBashKill ¶ added in v0.20.0
RegisterBashKill exposes KillBashTask over MCP as BashKill. The snapshot is returned post-cancel so the caller sees the terminal status (or `cancelled` if the kill won the race against a quick exit).
func RegisterBashOutput ¶ added in v0.20.0
RegisterBashOutput exposes GetBashTask over MCP as BashOutput.
func RegisterBridgeTools ¶ added in v0.20.0
RegisterBridgeTools adds BridgeList/Add/Remove/Upgrade to s.
func RegisterBrowserFetch ¶ added in v0.20.0
RegisterBrowserFetch wires the BrowserFetch MCP tool.
func RegisterBrowserScrape ¶ added in v0.20.0
RegisterBrowserScrape wires the BrowserScrape MCP tool.
func RegisterCommit ¶ added in v0.20.0
RegisterCommit wires the Commit MCP tool. Idempotent.
func RegisterEdit ¶
RegisterEdit adds the Edit tool to the given MCP server.
func RegisterGlob ¶
RegisterGlob adds the Glob tool to the given MCP server.
func RegisterGrep ¶
RegisterGrep adds the Grep tool to the given MCP server.
func RegisterMcpTools ¶ added in v0.20.0
RegisterMcpTools wires the Mcp* surface (ADR-019). McpNew runs the real generator. McpList walks the on-disk markers. McpRun / McpBuild / McpInstall are CLI-side filesystem operations and surface a hint to use the shell command — that's the natural path for a model giving advice rather than driving the build.
func RegisterPortalAliases ¶ added in v0.20.0
RegisterPortalAliases scans cfg.Portals and binds a thin wrapper `<name>__ask` for each one. Same wire-naming convention as internal/sources/manager.go aggregation. Each alias forwards to PortalAsk with the portal name pre-bound, so the calling model can do `my_deepseek__ask({"prompt":"..."})` without remembering the generic shape.
func RegisterPortalTools ¶ added in v0.20.0
RegisterPortalTools wires the Portal* MCP surface. Always registered; missing config produces empty results, not boot failure.
func RegisterRead ¶
RegisterRead adds the Read tool to the given MCP server.
func RegisterRecipeTools ¶ added in v0.9.0
RegisterRecipeTools adds the three Recipe* MCP tools to s. The registry must already be populated (typically via blank import of internal/setup/recipes).
func RegisterRulesCheck ¶ added in v0.20.0
RegisterRulesCheck wires the RulesCheck tool. Idempotent.
func RegisterSandboxTools ¶ added in v0.20.0
func RegisterSemanticSearch ¶ added in v0.20.0
RegisterSemanticSearch wires the tool. Always registered; missing embedding key surfaces as a per-call error, not a boot failure.
func RegisterSkillNew ¶ added in v0.9.0
RegisterSkillNew adds the SkillNew tool to s. Template + helpers come from internal/skillgen so this MCP surface and the `clawtool skill new` CLI emit byte-identical output.
func RegisterTaskNotify ¶ added in v0.20.0
RegisterTaskNotify wires the TaskNotify tool. Idempotent.
func RegisterTaskTools ¶ added in v0.20.0
RegisterTaskTools wires TaskGet / TaskWait / TaskList. Idempotent — safe to call when the BIAM store wasn't initialised; per-call handlers surface the "not configured" error.
func RegisterToolSearch ¶
RegisterToolSearch adds the ToolSearch tool to the given MCP server, closing over the index built at startup.
func RegisterVerify ¶ added in v0.20.0
RegisterVerify wires the Verify MCP tool.
func RegisterWebFetch ¶
RegisterWebFetch adds the WebFetch tool to the given MCP server.
func RegisterWebSearch ¶
RegisterWebSearch adds the WebSearch tool to the given MCP server. The secrets-store reference is captured so per-call backend resolution can pick up updated API keys without restart.
func RegisterWrite ¶
RegisterWrite adds the Write tool to the given MCP server.
func ResetBashTasksForTest ¶ added in v0.20.0
func ResetBashTasksForTest()
ResetBashTasksForTest wipes the registry. Test-only.
func ResetEngineCache ¶
func ResetEngineCache()
ResetEngineCache forces a re-detection on next LookupEngine call. Used by tests that manipulate $PATH.
func ResetSemanticSearchCache ¶ added in v0.20.0
func ResetSemanticSearchCache()
ResetSemanticSearchCache lets tests drop the cached stores. No-op in production.
func ResetSessionsForTest ¶ added in v0.20.0
func ResetSessionsForTest()
ResetSessionsForTest clears the registry. Test-only escape hatch matching the pattern in agents/supervisor.go.
func SetAutoLintEnabled ¶ added in v0.20.0
func SetAutoLintEnabled(enabled bool)
SetAutoLintEnabled lets server.go's boot path flip the flag based on config.AutoLint.Enabled. Idempotent.
func SetBiamStore ¶ added in v0.20.0
SetBiamStore registers the process-wide BIAM store. Idempotent.
func SetLintRunner ¶ added in v0.20.0
SetLintRunner replaces the package-level Runner — used by tests to inject deterministic findings.
func SubmitBackgroundBash ¶ added in v0.20.0
func SubmitBackgroundBash(parent context.Context, command, cwd string, timeoutMs int) (string, error)
SubmitBackgroundBash spawns the command, registers a task, and returns the task_id. The goroutine reading stdout/stderr keeps running after the call returns; consumers poll via TaskGet until status is terminal.
Types ¶
type Backend ¶
type Backend interface {
Name() string
Search(ctx context.Context, query string, limit int, opts SearchOptions) ([]WebSearchHit, error)
}
Backend abstracts a web-search provider. Implementations must be safe to invoke from multiple goroutines and complete within the supplied context's deadline.
SearchOptions carries the optional, provider-neutral filters from ADR-021 phase B. Backends translate what they support and ignore the rest — the operator sees a uniform request shape across providers, the unsupported ones degrade silently to "behave as though the filter wasn't supplied".
type BaseResult ¶ added in v0.9.0
type BaseResult struct {
Operation string `json:"-"`
Engine string `json:"engine,omitempty"`
DurationMs int64 `json:"duration_ms,omitempty"`
ErrorReason string `json:"error_reason,omitempty"`
}
BaseResult holds the fields and rendering helpers every tool result shares. Each tool's *Result struct embeds this so the JSON shape stays uniform (timing, error, engine surfaced the same way everywhere) and Render() implementations stay short — each one is just "header + body + footer" composed from BaseResult helpers and the tool-specific data.
Operation is JSON-omitted; it's a presentation concern (the header verb), not a wire field.
func (BaseResult) ErrorLine ¶ added in v0.9.0
func (b BaseResult) ErrorLine(target string) string
ErrorLine renders the canonical failure one-liner. Every tool that fails uses this — keeps "✗ <verb> — <reason>" consistent across the whole catalog.
func (BaseResult) FooterLine ¶ added in v0.9.0
func (b BaseResult) FooterLine(extras ...string) string
FooterLine joins extras with " · " and appends the duration. Used at the bottom of multi-line results (after content).
func (BaseResult) HeaderLine ¶ added in v0.9.0
func (b BaseResult) HeaderLine(title string) string
HeaderLine renders the canonical multi-line header used by tools that return content (Bash, Read, Grep). Engine — when set — is shown in brackets so the caller always knows which backend ran.
func (BaseResult) IsError ¶ added in v0.9.0
func (b BaseResult) IsError() bool
IsError is the universal failure predicate.
func (BaseResult) SuccessLine ¶ added in v0.9.0
func (b BaseResult) SuccessLine(target string, extras ...string) string
SuccessLine is the canonical single-line success format used by stateless tools (Edit, Write). Variadic extras are joined with " · " and the duration is appended automatically.
type BashTask ¶ added in v0.20.0
type BashTask struct {
ID string
Command string
Cwd string
StartedAt time.Time
FinishedAt time.Time
TimeoutMs int
// contains filtered or unexported fields
}
BashTask carries one background bash invocation's state. Output buffers grow without bound by design — the operator can always kill the task when the live tail gets noisy. We cap at 4 MiB per stream to match the BIAM body cap.
func (*BashTask) Snapshot ¶ added in v0.20.0
func (t *BashTask) Snapshot() BashTaskSnapshot
Snapshot returns the current state under the task's lock.
type BashTaskSnapshot ¶ added in v0.20.0
type BashTaskSnapshot struct {
ID string `json:"task_id"`
Command string `json:"command"`
Cwd string `json:"cwd,omitempty"`
Status BashTaskStatus `json:"status"`
Stdout string `json:"stdout"`
Stderr string `json:"stderr"`
ExitCode int `json:"exit_code"`
TimedOut bool `json:"timed_out"`
StartedAt time.Time `json:"started_at"`
FinishedAt time.Time `json:"finished_at,omitempty"`
}
snapshot returns a read-only view safe to ship over MCP.
func GetBashTask ¶ added in v0.20.0
func GetBashTask(id string) (BashTaskSnapshot, bool)
GetBashTask returns the snapshot for id. ok=false when no task matches.
func KillBashTask ¶ added in v0.20.0
func KillBashTask(id string) (BashTaskSnapshot, bool)
KillBashTask cancels the task's context, which propagates SIGKILL to the whole process group via ApplyGroupWithCtxCancel. No-op when the task is already terminal. Returns ok=false for unknown IDs.
func ListBashTasks ¶ added in v0.20.0
func ListBashTasks(limit int) []BashTaskSnapshot
ListBashTasks returns every recorded task, newest first. Bounded by limit (0 = no cap).
type BashTaskStatus ¶ added in v0.20.0
type BashTaskStatus string
BashTaskStatus mirrors BIAM's lifecycle so an agent that knows TaskGet's vocabulary doesn't need a second mental model.
const ( BashTaskActive BashTaskStatus = "active" BashTaskDone BashTaskStatus = "done" BashTaskFailed BashTaskStatus = "failed" BashTaskCancelled BashTaskStatus = "cancelled" )
type BashTaskStore ¶ added in v0.20.0
type BashTaskStore struct {
// contains filtered or unexported fields
}
BashTaskStore is the process-wide registry. Concurrent reads + writes are guarded by an RWMutex so TaskGet / TaskList stay fast under load.
type BrowserFetchResult ¶ added in v0.20.0
type BrowserFetchResult struct {
BaseResult
URL string `json:"url"`
FinalURL string `json:"final_url,omitempty"`
Format string `json:"format"` // "html" | "text" | "eval"
Title string `json:"title,omitempty"`
Byline string `json:"byline,omitempty"`
SiteName string `json:"site_name,omitempty"`
Content string `json:"content"`
EvalResult string `json:"eval_result,omitempty"`
SizeBytes int `json:"size_bytes"`
FetchedAt string `json:"fetched_at"`
Truncated bool `json:"truncated"`
}
BrowserFetchResult mirrors WebFetchResult so an agent can swap one for the other without rewriting downstream parsing. Adds EvalResult for callers that pass `eval` (raw stdout slice from obscura).
func (BrowserFetchResult) Render ¶ added in v0.20.0
func (r BrowserFetchResult) Render() string
Render keeps parity with WebFetchResult: framed body + footer.
type BrowserScrapeResult ¶ added in v0.20.0
type BrowserScrapeResult struct {
BaseResult
Results []BrowserScrapeRow `json:"results"`
Total int `json:"total"`
Failed int `json:"failed"`
Truncated bool `json:"truncated"`
FetchedAt string `json:"fetched_at"`
}
BrowserScrapeResult lists per-URL outcomes plus aggregate counts.
func (BrowserScrapeResult) Render ¶ added in v0.20.0
func (r BrowserScrapeResult) Render() string
Render lists one row per URL.
type BrowserScrapeRow ¶ added in v0.20.0
type BrowserScrapeRow struct {
URL string `json:"url"`
Result string `json:"result,omitempty"`
Error string `json:"error,omitempty"`
}
BrowserScrapeRow is one URL's outcome. `Result` carries the eval'd value (or rendered text); `Error` is set on per-URL failure so the rest of the batch keeps going.
type EditResult ¶
type EditResult struct {
BaseResult
Path string `json:"path"`
Replaced bool `json:"replaced"`
OccurrencesReplaced int `json:"occurrences_replaced"`
SizeBytesBefore int64 `json:"size_bytes_before"`
SizeBytesAfter int64 `json:"size_bytes_after"`
LineEndings string `json:"line_endings"`
LintFindings []lint.Finding `json:"lint_findings,omitempty"`
// HashBefore / HashAfter let the model verify exactly what
// changed (ADR-021). Both are SHA-256 hex of the file's raw
// bytes — pre-edit and post-edit.
HashBefore string `json:"hash_before,omitempty"`
HashAfter string `json:"hash_after,omitempty"`
// DiffUnified is a tiny `diff -u`-style patch of the change.
// Always populated on a successful edit; empty when the edit
// was a no-op or failed.
DiffUnified string `json:"diff_unified,omitempty"`
}
EditResult is the uniform shape returned to the agent.
func (EditResult) Render ¶ added in v0.9.0
func (r EditResult) Render() string
Render satisfies the Renderer contract. Single-line success/failure; stateless tools don't need a multi-line body.
type Engine ¶
type Engine struct {
Name string // canonical name, e.g. "ripgrep"
Bin string // resolved absolute path, empty if absent
}
Engine describes one detected upstream binary that clawtool wraps.
Per ADR-007, clawtool curates and wraps best-in-class engines. Detection runs once on startup so wrapper tools can pick the best available engine without re-shelling-out for every call.
func LookupEngine ¶
LookupEngine returns the cached engine entry for a given binary name. The Bin field is empty if the engine is not present on this system.
type GlobResult ¶
type GlobResult struct {
BaseResult
Matches []string `json:"matches"`
MatchesCount int `json:"matches_count"`
Truncated bool `json:"truncated"`
Cwd string `json:"cwd"`
Pattern string `json:"pattern"`
RespectGitignore bool `json:"respect_gitignore"`
IncludeHidden bool `json:"include_hidden"`
}
GlobResult is the uniform shape returned to the agent.
func (GlobResult) Render ¶ added in v0.9.0
func (r GlobResult) Render() string
Render satisfies the Renderer contract. One match per line so the chat looks like running `find` or `fd` in a terminal.
type GrepMatch ¶
type GrepMatch struct {
Path string `json:"path"`
Line int `json:"line"`
Column int `json:"column"`
Text string `json:"text"`
Before []string `json:"before,omitempty"`
After []string `json:"after,omitempty"`
}
GrepMatch is a single hit. Line and column are 1-indexed for human readability and to match conventional editor jumping. Before/After arrive populated only when the caller asked for context lines.
type GrepResult ¶
type GrepResult struct {
BaseResult
Matches []GrepMatch `json:"matches"`
MatchesCount int `json:"matches_count"`
Truncated bool `json:"truncated"`
Cwd string `json:"cwd"`
Pattern string `json:"pattern"`
}
GrepResult is the uniform shape returned regardless of engine.
func (GrepResult) Render ¶ added in v0.9.0
func (r GrepResult) Render() string
Render satisfies the Renderer contract. Output mirrors ripgrep's standard `path:line:col: text` so a developer reading the chat sees the same shape they'd see in a terminal.
type LineEndings ¶
type LineEndings string
LineEndings identifies the dominant line-ending convention of a file.
const ( LineEndingsLF LineEndings = "lf" LineEndingsCRLF LineEndings = "crlf" LineEndingsCR LineEndings = "cr" // ancient Mac LineEndingsUnknown LineEndings = "unknown" )
type ReadRecord ¶ added in v0.20.0
type ReadRecord struct {
Path string `json:"path"`
FileHash string `json:"file_hash"` // SHA-256 of raw bytes
RangeHash string `json:"range_hash,omitempty"` // SHA-256 of returned line range
LineStart int `json:"line_start,omitempty"`
LineEnd int `json:"line_end,omitempty"`
ReadAt time.Time `json:"read_at"`
}
ReadRecord captures what a Read tool call observed about a path at a single point in time. Edit + Write consult these to verify the agent has seen the file AND the file hasn't drifted since.
type ReadResult ¶
type ReadResult struct {
BaseResult
Path string `json:"path"`
Content string `json:"content"`
LineStart int `json:"line_start"`
LineEnd int `json:"line_end"`
TotalLines int `json:"total_lines"`
SizeBytes int64 `json:"size_bytes"`
Format string `json:"format"`
Truncated bool `json:"truncated"`
// FileHash is SHA-256 of the file's raw bytes (hex). Edit /
// Write check this against the recorded read-time hash to
// detect "file changed since you last looked" (ADR-021).
FileHash string `json:"file_hash,omitempty"`
// RangeHash is SHA-256 of the canonical returned content
// (after format-aware decoding for PDF / DOCX / XLSX). Lets
// range-based Edits prove they're operating on the slice
// the model just saw.
RangeHash string `json:"range_hash,omitempty"`
// Sheets is populated only for spreadsheet formats; lets the agent
// page through workbook structure without re-reading the file.
Sheets []string `json:"sheets,omitempty"`
}
ReadResult is the uniform shape across all formats. Embeds BaseResult so common fields (engine, duration, error) and their rendering helpers are inherited.
func (ReadResult) Render ¶ added in v0.9.0
func (r ReadResult) Render() string
Render satisfies the Renderer contract. The body is the file content framed by horizontal rules; header carries path and engine, footer carries cursor + size.
type Renderer ¶ added in v0.9.0
type Renderer interface {
Render() string
}
Renderer is the contract every tool result implements. The MCP dispatch helper below relies on it exclusively; tools that don't implement it would fall through to JSON marshaling, but every core tool overrides it.
type SearchOptions ¶ added in v0.20.0
type SearchOptions struct {
IncludeDomains []string // e.g. ["docs.python.org", "go.dev"]
ExcludeDomains []string // e.g. ["pinterest.com"]
Recency string // "24h" | "1w" | "1m" | "1y" | "" (empty = no filter)
Country string // ISO 3166-1 alpha-2 (e.g. "US", "TR"); empty = backend default
Topic string // free-form classifier the backend may honour
}
SearchOptions are the optional filters layered on top of (query, limit). Each backend maps these to its own API: Brave uses goggles for site filters + freshness for recency; Tavily uses include_domains / exclude_domains / topic / time_range; Google CSE uses sort=date + as_sitesearch.
type SemanticSearchResult ¶ added in v0.20.0
type SemanticSearchResult struct {
BaseResult
Repo string `json:"repo"`
Query string `json:"query"`
Results []index.Result `json:"results"`
}
SemanticSearchResult is the MCP response shape.
func (SemanticSearchResult) Render ¶ added in v0.20.0
func (r SemanticSearchResult) Render() string
Render satisfies Renderer. One result per line in the human form, score in parentheses. Path:lines: snippet first 80 chars.
type SessionKey ¶ added in v0.20.0
type SessionKey string
SessionKey is the trusted MCP session identifier. "anonymous" when the transport doesn't supply one (typical stdio).
func SessionKeyFromContext ¶ added in v0.20.0
func SessionKeyFromContext(ctx context.Context) SessionKey
SessionKeyFromContext extracts the trusted MCP session id from a tool handler's ctx. Falls back to "anonymous" so unit tests (and stdio sessions without a transport-supplied id) still get a meaningful key.
type SessionState ¶ added in v0.20.0
type SessionState struct {
// contains filtered or unexported fields
}
SessionState is the process-local read registry. Concurrent callers share one instance via Sessions.
func (*SessionState) ReadOf ¶ added in v0.20.0
func (s *SessionState) ReadOf(sid SessionKey, path string) (ReadRecord, bool)
ReadOf returns the latest record for (session, path).
func (*SessionState) RecordRead ¶ added in v0.20.0
func (s *SessionState) RecordRead(sid SessionKey, r ReadRecord)
RecordRead stores a Read observation. Idempotent — re-reading the same path overwrites the prior record.
type ToolSearchResult ¶
type ToolSearchResult struct {
BaseResult
Query string `json:"query"`
Results []search.Hit `json:"results"`
TotalIndexed int `json:"total_indexed"`
TypeFilter string `json:"type_filter,omitempty"`
}
ToolSearchResult is the JSON envelope returned to the agent.
func (ToolSearchResult) Render ¶ added in v0.9.0
func (r ToolSearchResult) Render() string
Render satisfies the Renderer contract. Each hit is rendered as "[score] name (type) — description" so the agent and the user see a plausible top-N list rather than raw JSON.
type VerifyCheck ¶ added in v0.20.0
type VerifyCheck struct {
Name string `json:"name"`
Status string `json:"status"` // "pass" | "fail" | "timeout" | "skipped"
DurationMs int64 `json:"duration_ms"`
Summary string `json:"summary,omitempty"`
DetailsLogExcerpt string `json:"details_log_excerpt,omitempty"`
}
VerifyCheck is one per-runner result. `DetailsLogExcerpt` is the last verifyMaxLogExcerpt bytes of combined stdout+stderr — enough for an agent to read the last failing assertion without blowing the response budget.
type VerifyResult ¶ added in v0.20.0
type VerifyResult struct {
BaseResult
Repo string `json:"repo"`
Checks []VerifyCheck `json:"checks"`
Overall string `json:"overall"` // "pass" | "fail"
}
VerifyResult is the uniform response. `Overall` is "pass" iff every check passed; one fail flips the whole result.
func (VerifyResult) Render ¶ added in v0.20.0
func (r VerifyResult) Render() string
Render satisfies the Renderer contract. One line per check + a final overall verdict.
type WebFetchResult ¶
type WebFetchResult struct {
BaseResult
URL string `json:"url"`
FinalURL string `json:"final_url"`
Status int `json:"status"`
ContentType string `json:"content_type"`
Format string `json:"format"` // "html" | "text" | "binary-rejected"
Title string `json:"title,omitempty"`
Byline string `json:"byline,omitempty"`
SiteName string `json:"site_name,omitempty"`
Content string `json:"content"`
SizeBytes int `json:"size_bytes"`
FetchedAt string `json:"fetched_at"`
Truncated bool `json:"truncated"`
}
WebFetchResult is the uniform shape returned to the agent.
func (WebFetchResult) Render ¶ added in v0.9.0
func (r WebFetchResult) Render() string
Render satisfies the Renderer contract. Header carries the URL + status + format; body is the extracted article (or raw text); footer notes truncation when applicable.
type WebSearchHit ¶
type WebSearchHit struct {
Title string `json:"title"`
URL string `json:"url"`
Snippet string `json:"snippet,omitempty"`
}
WebSearchHit is one ranked result from any backend.
type WebSearchResult ¶
type WebSearchResult struct {
BaseResult
Query string `json:"query"`
Results []WebSearchHit `json:"results"`
ResultsCount int `json:"results_count"`
Truncated bool `json:"truncated"`
}
WebSearchResult is the uniform result envelope the agent receives. Backend lives in BaseResult.Engine because the engine concept is the same — which backend ran this query — and consolidating in the embedded struct keeps every tool's "who did the work" field in one place across the catalog.
func (WebSearchResult) Render ¶ added in v0.9.0
func (r WebSearchResult) Render() string
Render satisfies the Renderer contract. Header carries query + backend; body lists `[N] title — url` rows so a developer scans results the same way they would in a browser results page.
type WriteResult ¶
type WriteResult struct {
BaseResult
Path string `json:"path"`
BytesWritten int64 `json:"bytes_written"`
Created bool `json:"created"`
LineEndings string `json:"line_endings"`
LintFindings []lint.Finding `json:"lint_findings,omitempty"`
}
WriteResult is the uniform shape returned to the agent.
func (WriteResult) Render ¶ added in v0.9.0
func (r WriteResult) Render() string
Render satisfies the Renderer contract. The Operation field switches between "Write" and "Create" based on whether the file existed beforehand — agents glance-read whether something was clobbered.
Source Files
¶
- agent_tool.go
- agents_tool.go
- atomic.go
- bash.go
- bash_bg.go
- bash_bg_tool.go
- bridges_tool.go
- browser_fetch.go
- browser_scrape.go
- commit_tool.go
- edit.go
- engines.go
- exec.go
- exec_unix.go
- glob.go
- grep.go
- manifest.go
- mcp_tool.go
- portal_tool.go
- pretty.go
- read.go
- read_html.go
- read_legacy.go
- read_office.go
- read_structured.go
- recipes_tool.go
- rules_tool.go
- sandbox_tool.go
- semsearch.go
- session_state.go
- session_state_helpers.go
- skill_tool.go
- tasknotify_tool.go
- tasks_tool.go
- toolsearch.go
- verify.go
- webfetch.go
- webfetch_ssrf.go
- websearch.go
- websearch_brave.go
- write.go