Skip to main content
Glama

BoteX: Execution Harness for AI Coding Agents

License: Apache-2.0 Python 3.10+ CI BoteX MCP server – quality and maintenance score on Glama

Giving an LLM direct, unrestricted access to your repository usually ends the same way: it hallucinates line numbers, corrupts indentation, writes broken syntax, eats your token budget reading thousands of irrelevant lines, or gets stuck in an infinite fix-break loop.

BoteX is a guardrail harness designed specifically for autonomous code-editing agents. It sits between an MCP client (such as Devin, Antigravity, Claude Desktop, or Cursor) and your codebase, enforcing deterministic safeguards before any code ever touches your disk.


Why BoteX?

Instead of trusting the model to behave, BoteX wraps every action in safety gates:

  • Outline-first inspection: Agents inspect symbol skeletons (get_outline) before asking for specific line ranges. This prevents dumping massive files into context and slashes token waste.

  • Prompt Injection & Untrusted Data Isolation (OWASP LLM01: Prompt Injection): External content retrieved by tools (file contents, web pages, memory notes) is automatically wrapped in XML-like untrusted data boundary tags (<untrusted_file_content>, <untrusted_web_content>, <untrusted_memory_content>). System prompt rules instruct the LLM to treat bounded content strictly as data, significantly reducing prompt injection risks.

  • Sensitive Information Disclosure & Secret Guardrails (OWASP LLM02: Sensitive Information Disclosure): Prevents accidental leaks by blocking access to environment files (.env*), SSH keys, credentials, and local configurations (botex.config.local.json). Automatically redacts API keys, Bearer tokens, private keys, and provider secrets in tool outputs and logs.

  • Read-before-write gate: Models cannot patch a file they haven't inspected in the current session. No blind edits, no guessing.

  • Pre-write syntax validation: Code modifications are validated in memory (e.g. ast.parse for Python) before saving. If the patch breaks syntax, it gets rejected on the spot.

  • Multi-tier fuzzy patching: Patches are whitespace- and newline-resilient (CRLF/LF agnostic with indentation detection), so minor formatting mismatches don't break execution.

  • Unbounded Consumption Protection (OWASP LLM06: Unbounded Consumption): Prevents "Denial of Wallet" and infinite loop resource exhaustion. BoteX evaluates USD spend dynamically mid-loop to enforce the budget cap instantly, and aborts with an automatic rollback if the agent falls into a mutation streak or stagnation loop.

  • Misinformation Protection (OWASP LLM07: Misinformation): Generated code undergoes rigorous in-memory AST and syntax validation (including ruff for Python and bash -n for shell scripts) to reject hallucinated or invalid code before touching disk. Post-write verify_command acts as a mandatory final test gate.

  • Improper Output Handling (OWASP LLM10: Improper Output Handling): Strict Read-Before-Write gates ensure no blind writes occur, and every touched file is backed by a pre-mutation snapshot. If an agent hits an error, the automatic snapshot rollback reverts the workspace cleanly.

  • Hard budget & pricing caps: Pre-flight checks verify model pricing against live provider catalogs. If a task exceeds its budget or a model is overpriced, BoteX aborts before spending a cent.

  • Strict privacy & Zero Data Retention (ZDR): Enforces provider.data_collection: deny on OpenRouter calls, blocks .env/secrets, masks API keys in logs, and prevents path traversal attacks. Free (:free) models that cannot guarantee ZDR are rejected locally. Opt out explicitly per run (allow_non_zdr) or via the openrouter-free provider preset.

  • Proactive Rate Limiting & Cooldown Tracking: Token-bucket rate limiter tracks RPM and TPM limits per provider with burst buffers and debt queues. When providers return rate-limit headers or 429 status codes, BoteX enters a cooldown window instead of repeatedly burning retries.

  • In-Flight State Checkpointing & Provider Fallbacks: Failed model runs take a snapshot of planned steps, observations, and read files before rolling back workspace changes. The next model or fallback provider receives this context directly, avoiding redundant exploration.

  • Enhanced Permission Controls (OWASP LLM03: Excessive Agency): BoteX protects against Excessive Agency via a centralized Agency Middleware. Tools are strictly defined with Risk Levels (LOW, MEDIUM, HIGH) and required scopes. High-risk destructive actions automatically require explicit interactive operator approval (or pre-authorized task scopes in headless mode), limiting the potential damage of prompt injections or hallucinations.


Related MCP server: dsh-codex-bridge

Quickstart

1. Installation

Requires Python 3.10+:

pip install -r requirements.txt

(Optional: install globally via pip install . to get the botex CLI command).

2. Add your API key

BoteX handles provider keys internally, so calling MCP clients don't need to pass credentials on every request. Create a .env file next to server.py:

OPENROUTER_API_KEY=sk-or-v1-...
# GEMINI_API_KEY=AIza...        # only for provider "google" (GOOGLE_API_KEY works as fallback)

Key lookup order: OPENROUTER_API_KEY env var → local .env → custom paths.env_file → botex.config.local.json (gitignored). For provider google, the key resolves via GEMINI_API_KEY → GOOGLE_API_KEY → secrets.google_api_key in config.

3. Connect to your MCP client

You can run BoteX as a local stdio process or as a remote service over SSE.

Option A: Local Process (stdio)

Register BoteX using the included zero-install launchers (botex.cmd on Windows, botex.sh on Linux/macOS) or directly with Python:

{
  "mcpServers": {
    "botex": {
      "command": "C:/path/to/harness/botex.cmd"
    }
  }
}

Option B: Remote MCP via SSE (Cloud or VPS)

Start the gateway with botex cloud and connect via Server-Sent Events (SSE):

{
  "mcpServers": {
    "botex-cloud": {
      "url": "http://127.0.0.1:8000/mcp/sse",
      "headers": {
        "Authorization": "Bearer <your_token>"
      }
    }
  }
}

How It Works

Every delegated task goes through an explicit contract-driven lifecycle:

Delegated Task
     │
     ▼
[Pre-flight check] ──► Validate budget, model capabilities & price cap
     │
     ▼
[Tool Loop]        ──► Symbol outline ─► Target read ─► In-memory patch ─► Syntax check
     │
     ├─► Success   ──► Verify contract (file exists, syntax valid, verify_command passed) ─► DONE
     │
     └─► Failure   ──► Rollback snapshots ─► Try next candidate (model_fallbacks) or abort

A task returns ok: true only when its completion is verified on disk, not just because an LLM said "I'm done". For background tasks (start_task / get_task_status), status reports the execution lifecycle (QUEUED, RUNNING, DONE, FAILED, CANCELLED); lifecycle DONE means the run finished, not success. Success requires result_data.ok == true and result_data.status == "DONE".


MCP Tools Reference

BoteX exposes a focused set of MCP tools:

Task Execution

  • run_subagent(task, ...): Runs a full coding task synchronously and blocks until finished. Returns both human-readable text and detailed structured metadata.

  • start_task(task, ...): Launches a task asynchronously in the background and returns a task_id.

  • get_task_status(task_id): Polls progress, logs, and results for a background task.

Two-Tier Memory Vault

  • save_memory(title, content, workspace_dir, kind, tags, memory_id, scope): Saves persistent notes, decisions, handoffs, or lessons. Scope can be "local" (<workspace_dir>/.botex/memory/) or "global" (~/.botex/memory/).

  • read_memory(memory_id, workspace_dir, scope) / search_memory(workspace_dir, query, tag, kind, scope): Retrieves saved memory across local or global storage (scope="all" searches both).

  • patch_memory_section(memory_id, section, new_content, workspace_dir, scope, append): Replaces or appends one Markdown section in a memory note without touching frontmatter, other sections, or the Timeline trail.

  • delete_memory(memory_id, workspace_dir, scope): Deletes a specific memory note from local or global storage.

  • get_backlinks(memory_id, workspace_dir, scope): Lists memories that link to the given note through [[wikilinks]].

  • find_graph_health(workspace_dir, scope): Audits the vault's wikilink graph — reports broken links, orphan notes, and cycles.

  • init_obsidian_vault(workspace_dir, scope, override_existing, export_index): Writes .obsidian/ config (per-kind graph colors) and optionally regenerates _GRAPH_INDEX.md.

  • read_scratchpad(memory_id, workspace_dir): Reads an execution or terminal log offloaded to .botex/scratchpad/<memory_id>.log.

Diagnostics & Utility

  • get_outline(path, workspace_dir): Returns a high-level symbol outline of a file without reading all lines.

  • get_stats(period): Generates token usage and cost reports from the local analytics ledger.

  • check_health(): Checks readiness of providers, models, keys, and budget limits.

  • clean_snapshots(max_age_days, max_total_mb): Prunes old workspace backup snapshots.

  • recommend_models(task_type, limit, include_free, provider, profile, tier): Queries /api/v1/models with authenticated headers, classifies models into high / standard / budget tiers, filters for tool-calling support (require_tools=true) and interactive endpoints (excluding :batch), and ranks models by task aptitude and token cost. include_free=true admits :free variants.

  • fetch_url(url, max_bytes): Safe, caller-side URL fetching with strict host and size filters.


Task Delegation & Parameters

run_subagent and start_task expose the exact same caller parameters, including approval_policy. Example payload:

{
  "task": "Add email validation to auth/validators.py and cover it with unit tests",
  "files": ["auth/validators.py"],
  "workspace_dir": "./my-project",
  "profile": "coding"
}

Key Parameters

Parameter

Default

Description

task

(required)

Clear description of what needs to be implemented or fixed.

files

[]

List of primary target files to nudge the agent toward (target hints, not a security allowlist; workspace_dir is the hard boundary).

workspace_dir

""

Root directory the agent is restricted to. Empty = engine.default_workspace from config, else the server's current directory. For Remote SSE (botex cloud), workspace_dir is on the BoteX host.

provider

""

API provider from botex.config.json (openrouter, nvidia, google, openrouter-free, ollama; empty = configured default or BOTEX_PROVIDER).

model

""

Explicit provider model ID; overrides profile.

profile

""

Model profile from botex.config.json (default, coding, auto-beta, review, fast). Empty = picked per mode via engine.mode_profiles.

api_key

""

Provider API key per-request (keep empty in examples; do not pass secrets in task or memory).

mode

"edit"

Capability preset: readonly, edit, destructive, full. readonly is hard read-only (no local write, delete/move, or exec; flags cannot widen it).

recipe

""

Specialized workflow persona (planner, code-explorer, reviewer, security-reviewer, security-taint-hardener, build-resolver, tdd, architecture-adr, ui-craftsman).

max_turns

0

Max tool-loop iterations (0 uses engine.max_turns, 45 in the shipped config, overridable per mode via engine.mode_max_turns).

max_tokens

0

Per-turn completion cap (0 = config value; reasoning models need ~8000+).

max_duration_s

0

Total wall-clock limit in seconds (0 = config value).

budget_limit_usd

-1

Hard spend limit for this task (-1 uses global config, 0 = unlimited).

allow_destructive

false

Explicit opt-in for delete_file and move_file.

allow_exec

false

Explicit opt-in for run_command (requires exec.enabled: true in config).

allow_net

false

Explicit opt-in for web access via read_url.

net_allowed_hosts

[]

Per-run host authorization for read_url (interacts with net.policy).

net_allowed_urls

[]

Per-run URL authorization; a trailing / authorizes that subtree.

allow_non_zdr

false

Per-run consent to relax the ZDR gate; required for :free models (provider may log/train on prompts). The result carries zdr_enforced: false.

output_path

""

File-output contract: enforce that this exact file must be written and non-empty for DONE status. Requires mutating mode (edit, destructive, full); MUST NOT be used with readonly (readonly reports in summary).

approval_policy

"high_risk"

Human-in-the-Loop policy: none, high_risk, mutations, all_risky. Shared by run_subagent and start_task.

verify_command

""

Verification command (e.g. pytest tests/test_auth.py) that must pass before completion. Requires exec.enabled: true and exec authorization.


Recipes & System Prompts

BoteX builds the API System Prompt dynamically for each run. It fuses a hardcoded operational core (tool rules, syntax validation checks, output formats) with an optional Recipe (a markdown file). This means that providing a --recipe <name> (or the "recipe": "<name>" JSON parameter) acts as an injection mechanism for your custom System Prompts, giving the agent specialized personas or specific constraints.

Recipes live in the botex/recipes/ directory.

Examples of custom System Prompts for Modding and Analysis

If you are using BoteX as an MCP Server to analyze game mods or reverse-engineer engine behavior, you can create custom recipes like botex/recipes/ue4.md or botex/recipes/lua_audit.md.

  • Unreal Engine 4 Modding (--recipe ue4)

    You are an expert C++ and UE4 Blueprint reverse engineer. Your primary goal is to identify memory leaks in object instantiation and unsafe cast operations. Always prioritize stability over performance optimizations. Adhere strictly to Epic Games naming conventions.
  • Security Audit (--recipe lua_audit)

    You are a strict security auditor analyzing game mods. Review this Lua codebase for potential sandbox escapes, arbitrary file read/write vulnerabilities, and networking exploits. Do not attempt to fix logic bugs; focus only on security vulnerabilities.
  • Code Refactoring & Optimization (--recipe optimize)

    You are an optimization expert. The target codebase runs in a heavily restricted environment. Identify inefficient loops, excessive global variable usage, and unnecessary memory allocations. Suggest changes using only standard libraries.

When provided, BoteX automatically appends your recipe to the system instructions before starting the LLM loop.


Safety & Capability Modes

Permissions follow the principle of least privilege. Modes are presets over core capabilities:

Mode

Allowed Operations

Typical Use Case

readonly

Outline inspection, ranged file reads, memory search.

Architecture reviews, exploratory passes.

edit (default)

Read tools + apply_patch + create_file.

Standard feature work and bug fixing.

destructive

Edit tools + delete_file + move_file.

Refactoring, renaming, file reorganizations.

full

Destructive tools + gated run_command execution.

Full autonomous loop (edit → test → fix).

NOTE

readonly and chat are hard read-only modes: no local writes, deletions, moves, or command execution. Capability flags cannot widen readonly to write or exec; allow_net adds only read-only network access (read_url). Chat is a separate CLI/REST interface (botex chat, /api/chat), not the normal task delegation API. Network access (allow_net) is never enabled by default in any preset. It must be explicitly enabled per request or configured via net.allowed_hosts.

Command Execution (run_command) Safety

BoteX is not a full OS sandbox. When allow_exec is granted, processes run under the operator's account. However, BoteX guards execution with multiple layers:

  1. Disabled by default: Requires exec.enabled: true in configuration.

  2. Binary allowlist: Only pre-approved executables (pytest, python, ruff, git, etc.) can be called.

  3. Deny rules: Blocks dangerous arguments (python -c, git push, rm, shell piping, network downloads).

  4. Isolated environment: Executes directly (shell=False), workspace set as cwd, secret environment variables stripped, and execution timeouts strictly enforced.

Human-in-the-Loop (HITL) Approvals

BoteX integrates an interactive approval manager for high-risk operations. Configure the policy in botex.config.json under approval.policy:

  • NONE: Auto-approves operations permitted by the capability mode.

  • HIGH_RISK (default): Blocks destructive actions (delete_file, move_file) and shell execution (run_command) until an operator approves.

  • MUTATIONS: Requires operator approval for any file modifications (create_file, apply_patch).

  • ALL_RISKY: Intercepts all mutating tools and system execution commands.

In REPL mode, BoteX prompts for interactive confirmation. In Cloud Gateway mode, actions enter a pending approval queue and can be approved or rejected via REST API (POST /api/approvals/{id}/decide).


Model Fallbacks & OpenRouter Live Routing

If a model refuses a task, burns its token limit without output, or hits provider rate limits, BoteX falls back to alternative models defined in botex.config.json:

"model_fallbacks": {
  "default": ["anthropic/claude-sonnet-4", "qwen/qwen3-coder", "deepseek/deepseek-v3.2"],
  "auto-beta": ["anthropic/claude-sonnet-4", "qwen/qwen3-coder", "deepseek/deepseek-v3.2"],
  "coding": ["anthropic/claude-sonnet-4", "qwen/qwen3-coder", "deepseek/deepseek-v3.2"],
  "review": ["openai/gpt-5.4", "google/gemini-2.5-pro", "deepseek/deepseek-r1"],
  "fast": ["qwen/qwen3-coder-flash", "openai/gpt-4o-mini", "deepseek/deepseek-v3.2"]
}

BoteX uses two complementary failover layers:

  1. In-Payload OpenRouter Failovers (extra_body.models): Before sending the first turn to OpenRouter, BoteX runs each fallback candidate through all five safety gates (Denylist, Decisions, ZDR, Price, Tools) and passes up to three validated slugs in extra_body["models"]. If the primary endpoint is overloaded on the initial request, OpenRouter switches to the next validated model on the wire without an extra client round-trip. Meta-routers (openrouter/auto, openrouter/pareto-code, openrouter/free) are excluded from models: [...], and once a run modifies any file (files_touched) or executes a command (exec_ran), BoteX strips models from subsequent turns so a different model never takes over a mutated workspace mid-task.

  2. Engine-Level Rollback & Retry: If an attempt fails mid-run, BoteX verifies that all partial file changes from the failed attempt were rolled back before starting the next candidate model. A fallback model never inherits a dirty workspace.

Before a candidate can fail at all, a shared retry layer (botex/retry.py, configured per provider via providers.<name>.retry) waits out unbilled transient rejections: HTTP 429 and connection errors (Google additionally retries 503 "overloaded"), honoring server wait hints (Retry-After header, retryDelay/quotaResetDelay body fields). Timeouts and other 5xx are never retried — the request may already have executed, so a retry could double-bill it — and daily-quota errors skip the wait entirely so the run falls back immediately. While the layer is enabled, SDK-internal retries are disabled (max_retries=0) so exactly one layer owns the backoff policy; setting "retry": false on a provider hands control back to the SDK. Config keys: enabled (default true), max_waits (2), max_wait_s (60.0), base_delay_s (1.0), jitter_s (1.5). A persistent 429 surfaces as failure_kind: "provider_rate_limited", which is eligible for the model_fallbacks chain.

For OpenRouter runs, BoteX also sets extra_body.provider (data_collection: "deny" when ZDR is active, require_parameters: true when tools or response_format are present, allow_fallbacks: true, and sort: "throughput"), automatically stripping reasoning parameters for non-reasoning model slugs so require_parameters: true never triggers HTTP 404 rejections. On DONE, BoteX queries GET /api/v1/generation?id=... in parallel (with a short 404 retry) to record official billing (total_cost), queue latency (queue_time_ms), inference time (generation_time_ms), and prompt cache read/write tokens in .agent_analytics.json and .botex/memory/ task receipts.


Free Models (Non-ZDR Opt-in)

ZDR is on by default and :free slugs are rejected pre-flight with ZDR_VIOLATION. Operators without a paid subscription (or anyone who accepts that free endpoints may log/train on prompts) can opt in two ways; both leave an audit trail (zdr_enforced: false in the result and a stderr warning):

# Persistent: switch to the bundled free-tier provider preset
# (all profiles resolve to tool-capable :free models)
botex config provider openrouter-free          # or BOTEX_PROVIDER=openrouter-free

# or relax ZDR on the current provider (keeps paid models, widens routing)
botex config zdr off                            # 'on' restores enforcement

# Per-run only, no config change:
botex run "task" --provider openrouter-free
botex run "task" --model qwen/qwen3.8-27b:free --allow-non-zdr
{"task": "...", "provider": "openrouter-free"}
{"task": "...", "model": "qwen/qwen3.8-27b:free", "allow_non_zdr": true}
NOTE

:free variants also require the OpenRouter account-level free-model opt-in (privacy settings); without it requests surface as provider_policy_denied. Free models carry per-day rate limits. The model_fallbacks chains in the preset stay entirely on :free slugs.


Configuration

Settings are resolved hierarchically (lowest to highest priority):

  1. botex.config.json: committed defaults (provider profiles, engine limits, security lists).

  2. botex.config.local.json: machine-local overrides (gitignored; place custom keys or limits here).

  3. BOTEX_* environment variables.

Config files are re-read automatically when they change (file mtime check on every access), so edits to botex.config.local.json - manual or via botex config ... - take effect on the next call, including in a running MCP server; no restart is needed. Environment variables are read from the server process environment, so changing them requires a restart.

Example botex.config.local.json:

{
  "engine": {
    "budget_limit_usd": 1.0,
    "max_turns": 20,
    "default_workspace": "C:/projects/my-project"
  },
  "pricing": {
    "max_input_per_mtok": 3.0,
    "max_output_per_mtok": 6.0
  }
}

Standalone CLI & REPL

BoteX can be run directly from your terminal without any MCP client:

Interactive REPL

botex repl -w ./my-project --profile coding

Prompts for [y/N] confirmation whenever the agent wants to delete or move files.

The startup banner shows the resolved absolute workspace and where it came from (explicit, default_workspace or cwd), and warns when it is the BoteX repository itself. Inside the REPL, workspace (or cd) prints the current workspace, and workspace <DIR> / cd <DIR> switches the target project for the rest of the session (relative paths are resolved against the current workspace; the directory must exist).

Set a persistent default so that running botex without -w never targets the harness itself:

botex config workspace C:/projects/my-project   # writes engine.default_workspace
botex config workspace none                      # clear it (falls back to cwd)

Workspace resolution: explicit -w / workspace_dir > engine.default_workspace > current directory. BOTEX_DEFAULT_WORKSPACE overrides the config value.

Headless One-Shot Tasks

# Run a specific task
botex run "Add --verbose flag to cli.py" -w ./my-project --profile coding

# Fix a bug with a tight turn budget
botex run "Fix the failing auth test" --files tests/test_auth.py --max-turns 15

# Refactoring with destructive permissions
botex run "Clean up legacy modules" -w ./my-project --allow-destructive

Conversational Chat Mode

Multi-turn discussion with token streaming, read-only tools, and automatic context pruning:

# Start interactive chat in the terminal
botex chat --model openrouter/auto

# With a custom system prompt and temperature
botex chat --system "Focus on security analysis" --temp 0.2

In-chat commands: /clear (reset session), /history (review transcript), /help, /exit.

Cloud Gateway & Remote MCP (FastAPI)

Run BoteX as a background cloud daemon with SSE transport, WebSocket chat, and REST endpoints:

# Start cloud server on default port 8000
botex cloud --host 0.0.0.0 --port 8000

Zero-Friction Local mode runs unauthenticated when BOTEX_ACCESS_TOKEN is unset. Set BOTEX_ACCESS_TOKEN in .env to enforce Bearer token verification on all protected endpoints.

REST surface (all under /api/): health, wallet, key/status, analytics (+POST analytics/digest), models, models/recommended, recipes, skills (+/{name}), tasks (+/{id}, /{id}/cancel, /{id}/followup, /{id}/events SSE), approvals (+/{id}/decide), memory (+/graph, /backlinks), curator/status, curator/run, rate_limits, delegation/devin, chat (+/abort, /sessions, /sessions/{id}, /sessions/{id}/save_to_memory), and WebSocket /api/chat/ws. /api/health, /api/delegation/devin, and the PWA shell stay public.

Maintenance & Observability

botex health               # Verify API keys, provider connectivity, and model pricing
botex wallet               # Check OpenRouter balance, key limits, and daily query caps
botex config               # Inspect or modify local configuration defaults
botex --stats              # View weekly token consumption and spend per model
botex --stats --month      # Monthly cost report
botex --history <task_id>  # Inspect exact prompts, tool calls, and diffs for a run
botex --snapshots          # Check disk usage of backup snapshots
botex --clean-snapshots    # Prune old snapshot backups
botex models [--refresh]   # List configured provider models (refresh catalog)
botex memory <sub>         # Memory vault CLI (save, search, list, ...)
botex serve                # Force MCP stdio server mode explicitly

Project Structure

server.py              MCP server entry point & CLI dispatcher
botex.cmd / botex.sh   Zero-install launchers (auto-detects python environment)
pyproject.toml         Package configuration (exposes `botex` CLI command)
requirements.txt       Runtime dependencies
botex.config.json      Committed defaults (providers, engine limits, exec/net policy)
ROADMAP.md             Deferred features & technical rationale
botex/
  approval.py          Human-in-the-loop approval manager, async actions, and rollback integration
  chat.py              Conversational chat sessions, token streaming, and read-only sandbox
  server_cloud.py      FastAPI cloud gateway, Remote MCP SSE, WebSocket chat, and REST API
  catalog.py           Dynamic model catalog, TTL caching, and live provider query
  denylist.py          Hierarchical model exclusions (globs and regex)
  wallet.py            Key credits, spend analytics, and daily query limits
  engine.py            Autonomous execution loop, context management & rollback triggers
  state.py             In-flight task state checkpointing across model and provider fallbacks
  rate_limiter.py      Asynchronous token-bucket rate limiter (RPM/TPM) and cooldown tracking
  curator.py           Memory Vault periodic curator and Telegram dispatch
  delegation.py        Subagent delegation bridge (Devin ACP)
  events.py            Engine event emitter taxonomy
  file_tools.py        Safe outline-first reading, file creation, moving & deletion
  patch_engine.py      Fuzzy patch matching, syntax validation & snapshot management
  security.py          Path traversal defenses, secret masking & gitignore filtering
  exec_tools.py        Policy-controlled command execution & argument filtering
  capabilities.py      Permission presets (readonly, edit, destructive, full)
  config.py            Layered config loader (defaults < local < env)
  context_guard.py     Context compaction & verification output pruning
  net_tools.py         Net policy enforcement (read_url / fetch_url)
  memory.py            Two-tier memory vault with wikilink graph & scratchpad offload
  embeddings.py        Local embedding helpers for memory/graph features
  obsidian.py          Obsidian PKM vault export & graph index generation
  meta_harness.py      Task taxonomy, patch probing & meta-lesson recommendations
  analytics.py         Cost/token ledger, budgeting & summary reporting
  pricing.py           Live model price guardrail & catalog checks
  providers.py         Provider adapters (OpenRouter, NVIDIA, Google AI Studio, etc.) & request normalizer
  retry.py             Shared transient-failure backoff (429 / connection errors / Google 503 overload) & provider circuit breaker
  benchmarks.py        Live model recommendations (recommend_models) & task-tier estimator
  contracts.py         Task completion verification, status taxonomy & canonical handoff schema
  tracing.py           OTel-style span helpers (landed; wiring in progress)
  skills.py            Server-side Skill Vault, safe install/update & L0 task triage
  recipes.py           Recipe loading & system-prompt injection
  i18n.py / ui.py      CLI language & status rendering
  recipes/             Specialized agent personas (planner, reviewer, tdd,
                       code-explorer, build-resolver, security-reviewer,
                       security-taint-hardener, architecture-adr, ui-craftsman)
ops/curator/           Nightly Knowledge-Graph curator service & Telegram dispatch
skills/                Caller-side SKILL.md (botex) + server-side skill packs
tests/                 Comprehensive test suite (615 unit, cloud gateway, chat, approvals)
deploy/oracle-vps/     systemd units & one-click installer for Oracle A1.Flex ARM64
docs/
  TUTORIAL.md          Architecture deep dive and technical walkthrough

Testing

Run the full local test suite (no API key required):

python -m pytest tests/

All unit, approval, chat, memory, and cloud gateway tests run hermetically in offline mode.


License & Trademark

BoteX is licensed under the Apache License 2.0. See LICENSE for details. Derivative works must retain attribution notices as specified in NOTICE.

The BoteX name and logo are trademarks of Jakub Grzesiak (jg-webtech.pl). You are free to fork and modify the code under Apache-2.0, but forks or derived distributions should not use the name "BoteX" or suggest official endorsement without prior written consent.

Available Tools

18 tools
check_healthA

Checks BoteX environment readiness (provider keys, snapshot dir, disk).

Inputs: no named parameters. Returns: result

clean_snapshotsC

Purges old or excess backup snapshots from disk.

Inputs: max_age_days, max_total_mb. Returns: result

delete_memoryA
Deletes a specific memory entry by ID from the workspace or global Memory Vault
and removes its index/graph links.

Args:
    memory_id: The ID of the memory entry to delete (e.g. 'mem_a1b2c3d4').
    workspace_dir: Workspace root directory. Empty = engine.default_workspace
           from the config, else the server's current directory.
    scope: Lookup scope ('local', 'global', or 'all'). Default is 'local'.

Inputs: memory_id (required), scope, workspace_dir. Returns: object

fetch_urlA
Fetch one public http(s) text document for caller-side research.

This tool is intentionally separate from BoteX's internal read_url: the
orchestrating agent chooses the URL, so arbitrary public hosts do not have
to be configured in net.allowed_hosts. SSRF protections, redirect
revalidation, content-type checks, byte limits, and secret masking still
apply. In net.policy='allowlist', only configured hosts may be fetched.

Inputs: url (required), max_bytes. Returns: object

find_graph_healthB
Audits Memory Vault knowledge graph integrity: reports broken links, orphan notes, and cycles.

Args:
    workspace_dir: Workspace root directory.
    scope: Lookup scope ('local', 'global', or 'all').

Inputs: scope, workspace_dir. Returns: object

get_outlineA
Returns a lightweight file skeleton (classes, methods, functions, headers,
line numbers) without reading the whole file. Saves ~90% of tokens.

Inputs: path (required), workspace_dir. Returns: object

get_statsB
Returns an analytics summary of executed BoteX tasks (USD costs, token
usage, code-line balance).
Args:
    period: 'week' (last 7 days) or 'month' (last 30 days).

Inputs: period. Returns: result

get_task_statusA

Return status and result for a task started with start_task.

`status` is the task lifecycle (QUEUED | RUNNING | DONE | FAILED |
CANCELLED) — DONE here means "finished", not "succeeded"; check
`result_data.status` / `result_data.ok` for the engine outcome.
`result` is the pretty-text report; `result_data` is the structured
engine result (status, failure_kind, ok, files_touched, exec_ran,
rollback_verified, attempts[], cost_usd, ...).

Inputs: task_id (required). Returns: object

init_obsidian_vaultB
Initializes or syncs Obsidian PKM vault configuration (.obsidian/graph.json, app.json)
with preset color profiles for #decision, #lesson, #handoff, #context.

Args:
    workspace_dir: Workspace root directory.
    scope: Target scope ('local' or 'global').
    override_existing: Whether to overwrite existing .obsidian config files.
    export_index: Also generate dynamic _GRAPH_INDEX.md note.

Inputs: scope, export_index, workspace_dir, override_existing. Returns: object

patch_memory_sectionA
Surgically updates a Markdown section (e.g. '## Compiled Truth') in a memory note
without modifying frontmatter, other sections, or the Timeline audit trail.

Args:
    memory_id: Target memory ID to patch.
    section: Section heading to replace or create (default 'Compiled Truth').
    new_content: New Markdown text for the section.
    workspace_dir: Workspace root directory.
    scope: Lookup scope ('local' or 'global').
    append: Whether to append to existing section content instead of replacing.

Inputs: memory_id (required), scope, append, section, +2 more. Returns: object

read_memoryA
Reads the full content and metadata of a specific memory entry by ID from the Memory Vault.

Args:
    memory_id: The ID of the memory entry to retrieve (e.g. 'mem_a1b2c3d4').
    workspace_dir: Workspace root directory. Empty = engine.default_workspace
           from the config, else the server's current directory.
    scope: Lookup scope ('local', 'global', or 'all'). Default is 'local'.

Inputs: memory_id (required), scope, workspace_dir. Returns: object

read_scratchpadA
Reads the full execution or terminal log offloaded to the scratchpad (.botex/scratchpad/<memory_id>.log).

Args:
    memory_id: The ID of the scratchpad entry (e.g. 'mem_exec_a1b2c3d4').
    workspace_dir: Workspace root directory. Empty = engine.default_workspace
           from the config, else the server's current directory.

Inputs: memory_id (required), workspace_dir. Returns: object

recommend_modelsA
[EN] Fetches the most cost-effective and capable models from OpenRouter based on live benchmarks.
USE THIS TOOL BEFORE calling `run_subagent` if you are unsure which model ID to use or want to optimize for cost/quality.
include_free=True also admits ':free' variants (non-ZDR — prompts may be
logged/trained on); they require allow_non_zdr or the openrouter-free
provider to actually run.

[PL] Pobiera rekomendacje modeli z OpenRouter na podstawie benchmarków.
Użyj tego narzędzia ZANIM wywołasz `run_subagent`, jeśli nie znasz dokładnego ID modelu.
include_free=True dopuszcza warianty ':free' (non-ZDR — prompty mogą być
logowane/trenowane); do uruchomienia wymagają allow_non_zdr albo
providera openrouter-free.

Inputs: tier, limit, recipe, profile, +3 more. Returns: result

run_subagentA
Runs BoteX — an autonomous code execution engine (agent-agnostic harness).
BoteX performs multi-step tasks on local files inside workspace_dir.

TIP: If the user didn't specify a model, DO NOT guess. Use the `recommend_models` tool first to find the best model for this task!

Key engine features:
- Outline-First and Context Pruning: ~90% token savings.
- Pre-write Syntax Check: in-memory code validation before touching disk.
- Multi-tier Fuzzy Patching: tolerant of CRLF/LF and indentation drift.
- Hard Zero-Retention on OpenRouter (provider: data_collection=deny)
  and DLP secret censoring.
- Automatic Snapshot Rollback on loops or critical failures.

Args:
    task: Precise description of the coding or refactoring task.
    files: Optional list of primary files affected by the task.
    workspace_dir: Project directory path. Empty = engine.default_workspace
           from the config, else the server's current directory.
    model: Any model from the provider's catalog. Empty = model from config
           (see 'profile' and botex.config.json).
    profile: Model profile from botex.config.json (e.g. 'default', 'coding',
           'auto-beta', 'fast'). When both 'model' and 'profile' are empty,
           the capability mode picks the tier via engine.mode_profiles
           (readonly -> 'fast', destructive/full -> 'coding'). Ignored when
           'model' is given explicitly.
    provider: API provider from the 'providers' section of botex.config.json
           ('openrouter', 'nvidia', 'google', 'openrouter-free', 'ollama').
           Empty = provider marked 'default: true' in the configuration.
    mode: Capability preset: 'readonly' (read only — the contract is an
           analysis report, DONE requires non-empty findings), 'edit'
           (read + edit — default), 'destructive' (+delete/move),
           'full' (+run_command). Empty = engine.default_mode from
           config. Explicit allow_* flags may only WIDEN a preset —
           they never narrow it (readonly can never gain file writes).
           In headless mode this is pre-authorization — grant it
           consciously.
    max_turns: Maximum tool-loop steps (0 = config value).
    max_tokens: Per-turn completion cap (0 = config value; reasoning
           models need ~8000+ since thinking shares this budget).
    max_duration_s: Total wall-clock limit in seconds (0 = config value,
           0 disables only when config is also 0). Checked between turns.
    budget_limit_usd: Daily spend limit in USD (negative = config value,
           0 = no limit).
    allow_destructive: Authorize destructive operations (delete_file/
           move_file) for this task. Headless mode cannot confirm
           mid-run — grant ONLY with the user's consent.
    allow_exec: Authorize run_command for this task (also requires
           exec.enabled=true in config). WARNING: this is NOT a sandbox —
           commands run with operator privileges. Grant only for trusted
           workspaces with the user's consent.
    api_key: Optional provider API key passed per-request (highest
           priority — overrides env/.env/config). Empty = resolved
           internally by the harness.
    output_path: Optional required output file (workspace-relative).
           Sets the file_output contract: DONE is accepted only when
           the file exists on disk, is non-empty, and passes the
           syntax gate; a DONE response carrying the payload as text
           is salvaged to disk. Requires a mutating mode.
    allow_net: Explicitly authorizes the read-only public web tool for
           this run. It is never enabled by a capability mode.
    net_allowed_hosts: Optional per-run host authorization. In
           net.policy='caller' these replace config defaults; in 'public'
           they narrow public access; in 'allowlist' they must stay inside
           net.allowed_hosts.
    net_allowed_urls: Optional per-run URL authorization rules. A URL
           ending in '/' authorizes that subtree; otherwise it authorizes
           the exact URL including its query string.
    verify_command: Optional allowlisted command that must pass before
           DONE is accepted — it runs against the workspace as it
           stands, including when the task made no writes (a passing
           verifier validates a legitimate no-change result). Requires
           exec.enabled=true in config and exec authorization.
    recipe: Optional operational persona / workflow prompt (e.g. 'planner',
           'code-explorer', 'reviewer', 'security-reviewer', 'build-resolver', 'tdd').
    allow_non_zdr: Per-run consent to relax the Zero Data Retention
           gate. Required for free (:free) models whose endpoints may
           log or train on prompts — it also switches the wire
           provider.data_collection flag to 'allow'. Grant only with
           the operator's consent; the result carries
           zdr_enforced=false. The openrouter-free provider preset is
           the persistent config-level alternative.
    approval_policy: Operator approval policy for risky operations: 'none',
           'high_risk' (default), 'mutations', 'all_risky'.

Returns:
    A pretty-text report in the text content (unchanged format for legacy
    clients) plus the full engine result as structuredContent (status,
    failure_kind, ok, files_touched, exec_ran, rollback_verified,
    attempts[], cost_usd, ...). `ok` is true only when status == DONE —
    i.e. the task contract was verified, not merely claimed by the model.

Inputs: task (required), mode, files, model, +18 more. Returns: Structured result of a finished BoteX run. `status` is one of the engine status…

save_memoryA
Saves a persistent context note, architectural decision, task handoff, or lesson
into the workspace or global shared Memory Vault.
Masks secrets and provides directory isolation.

Args:
    title: Short descriptive title of the memory entry.
    content: Detailed markdown notes, architectural rationale, or handoff context.
    workspace_dir: Workspace root directory. Empty = engine.default_workspace
           from the config, else the server's current directory.
    kind: Entry category ('context' | 'decision' | 'handoff' | 'lesson').
    tags: Optional list of keyword tags for filtering and discovery.
    memory_id: Optional custom identifier (alphanumeric/hyphen/underscore). Auto-generated if omitted.
    scope: Storage scope ('local' for workspace, 'global' for cross-workspace). Default is 'local'.

Inputs: title (required), content (required), kind, tags, +3 more. Returns: object

search_memoryA
Searches the Memory Vault by keyword query, tag, or entry kind
(context, decision, handoff, lesson) across local, global, or all scopes.

Args:
    workspace_dir: Workspace root directory. Empty = engine.default_workspace
           from the config, else the server's current directory.
    query: Free-text search string matched against title, content, and tags.
    tag: Filter by specific tag.
    kind: Filter by entry kind ('context', 'decision', 'handoff', 'lesson').
    scope: Search scope ('local', 'global', or 'all'). Default is 'local'.

Inputs: tag, kind, query, scope, +1 more. Returns: object

start_taskA

Start a detached BoteX task and return its task_id immediately.

Runs the same engine as `run_subagent`, but returns instantly with a
`task_id` for polling via `get_task_status`. Concurrency is bounded by
engine.max_concurrent_tasks and the queue by engine.max_queued_tasks —
a full queue returns status TOO_MANY_QUEUED.

The capability/limits arguments are identical to `run_subagent` —
same contracts, same pre-authorization semantics:

- mode: capability preset — 'readonly' (read tools only; the contract
  is an analysis report), 'edit' (read + write; default), 'destructive'
  (+ delete/move), 'full' (+ run_command). Empty = engine.default_mode.
- allow_destructive / allow_exec / allow_net only ever WIDEN the
  preset, never narrow it — a readonly task cannot gain file writes.
  allow_exec additionally requires exec.enabled=true in the config.
  None of these is a sandbox: grant them only with the user's consent.
- output_path makes the contract file_output: DONE requires the file
  to exist, be non-empty, and pass the syntax gate (mutating mode).
- verify_command is an allowlisted command that must pass before DONE
  is accepted — it runs even when the task made no writes. Requires
  exec authorization.
- recipe: Optional operational persona / workflow prompt (e.g. 'planner',
  'code-explorer', 'reviewer', 'security-reviewer', 'build-resolver', 'tdd').
- max_turns / max_tokens / max_duration_s / budget_limit_usd:
  0 (negative for budget) = config value; a non-positive max_turns is
  clamped to 1.
- net_allowed_hosts / net_allowed_urls narrow or replace the net
  scope per run according to net.policy (see run_subagent).
- allow_non_zdr: per-run consent to relax Zero Data Retention — needed
  for free (:free) models whose endpoints may log or train on prompts.
  Same semantics as in run_subagent.

Returns:
    {"task_id": str, "status": "QUEUED"} — poll with get_task_status.
    The finished task's `result_data` carries the full engine result
    (status, failure_kind, ok, files_touched, exec_ran,
    rollback_verified, attempts[], cost_usd, ...).

Inputs: task (required), mode, files, model, +17 more. Returns: object

Tool Schema Changelog

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

Loading tool schema changelog…

    TDQS

    A3.6/5.0

    Scored across 18 tools

    Disambiguation4/5

    Most tools have clearly distinct targets: memory CRUD (save/search/read/delete/patch), graph tools (get_backlinks, find_graph_health), admin (check_health, clean_snapshots, get_stats), and file/web access (get_outline, fetch_url). The main friction point is run_subagent vs start_task, which expose nearly identical argument surfaces and differ only in sync vs detached execution — well documented, but an easy misselection. search_memory vs read_memory vs read_scratchpad are separable but require reading descriptions to distinguish.

    Naming Consistency5/5

    Every tool follows a consistent snake_case verb_noun pattern (fetch_url, get_task_status, save_memory, patch_memory_section, init_obsidian_vault). Verbs are used predictably (get_* for retrieval, *_memory for vault ops, run_/start_ for engine invocation). No camelCase or stylistic outliers.

    Tool Count4/5

    18 tools is on the heavier side but each maps to a real capability across three sub-domains (engine execution, memory vault, environment/admin). Nothing looks like filler, though the admin trio (check_health, clean_snapshots, get_stats) plus Obsidian init could plausibly be consolidated. Slightly over-scoped rather than bloated.

    Completeness4/5

    Memory vault coverage is strong (create/read/search/patch/delete/backlinks/graph-health/Obsidian export) and the task lifecycle covers start, poll, and synchronous run. The notable gap is the absence of a cancel_task tool even though CANCELLED is an advertised status, and there is no explicit list_memories or full-content update (only section patching) — agents can mostly work around these via search_memory and max_duration_s.

    Maintenance

    ActivityActive
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables MCP-capable coding assistants to delegate repository investigation, bounded implementation work, and noisy command runs (tests, builds, linters) to sandboxed OpenCode agents. Each role can use an independently selected model, and only concise results are returned to the parent agent, which keeps responsibility for architecture and high-risk operations.
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables MCP clients like Codex and DSH to invoke DeepSeek subagents for inspect, plan, review, and implement tasks, with write protection, rollback, and status reporting.
      1,160 npm
      MIT
    • A
      license
      Not graded
      quality
      C
      maintenance
      Enables delegating tasks to agents you configure yourself through OpenAI-compatible Chat Completions backends, with automatic or explicit routing, synchronous calls, persistent asynchronous jobs, and multi-step reviewed workflows. It returns proposals and analyses only, leaving file changes, command execution, and approvals under the MCP client's control.
      18 npm
      MIT