# Managing Tools and Validations in CleverAgents

## Overview

CleverAgents provides a unified tool registry where you can register, inspect,
and manage two kinds of callable operations: **tools** (general-purpose
functions) and **validations** (pass/fail quality gates). This example walks
through the complete lifecycle — registering tools from YAML config files,
listing them with filters, inspecting details, and removing them — all from
the CLI.

## Prerequisites

- CleverAgents installed (`pip install cleveragents`)
- Python 3.13 or higher

## What You'll Learn

- How to **register a custom tool** from a YAML configuration file
- How to **register validations** — both standalone (custom code) and wrapped
  (delegating to an existing tool)
- How to **list all tools and validations** with optional type/namespace filters
- How to **inspect a specific tool** to see its capability flags, resource
  slots, and validation mode
- How to **remove a tool** from the registry
- How to use `--format json` for machine-readable output in scripts

---

## Step-by-Step Walkthrough

### Step 1: Register a custom tool from YAML

Create a YAML file describing your tool (see `examples/tools/custom-tool.yaml`
for the full schema):

```yaml
# examples/tools/custom-tool.yaml
name: local/line-counter
description: Count lines in a file
source: custom
code: |
  import pathlib
  path = pathlib.Path(inputs["file_path"])
  count = len(path.read_text().splitlines())
  return {"line_count": count}

input_schema:
  type: object
  required:
    - file_path
  properties:
    file_path:
      type: string
      description: Path to the file to count lines in

output_schema:
  type: object
  properties:
    line_count:
      type: integer

capability:
  read_only: true
  writes: false
  idempotent: true

resource_slots:
  - name: target_file
    resource_type: fs-mount
    access: read_only
    description: File system containing the target file
    binding: contextual

timeout: 60
```

Register it with:

```bash
$ agents tool add --config examples/tools/custom-tool.yaml
```

**Expected Output:**
```
╭─────────── Tool Registered ───────────╮
│ Name: local/line-counter              │
│ Namespace: local                      │
│ Short Name: line-counter              │
│ Description: Count lines in a file    │
│ Source: custom                        │
│ Type: tool                            │
│ Timeout: 60s                          │
│ Capability:                           │
│   read_only: True                     │
│   writes: False                       │
│   checkpointable: False               │
│   idempotent: True                    │
│   unsafe: False                       │
│   human_approval_required: False      │
│ Resource Slots:                       │
│   - target_file (fs-mount, read_only) │
│ Lifecycle:                            │
│   (none)                              │
╰───────────────────────────────────────╯
```

**What's Happening:**

The `tool add` command reads the YAML file, validates the schema, and persists
the tool definition to the local tool registry (SQLite database). The
`capability` block is especially important — it tells the plan executor whether
the tool is safe to run without human approval, whether it can be checkpointed,
and whether it has side effects.

The namespaced name `local/line-counter` uses the `local` namespace, which is
the conventional namespace for project-specific tools. Built-in tools use
provider namespaces like `devops/` or `qa/`.

---

### Step 2: Register a required validation (custom code)

Validations are pass/fail quality gates. A `required` validation must pass
before a plan step can proceed. Register one from YAML:

```yaml
# examples/validations/required-validation.yaml
name: qa/coverage-check
description: Verify test coverage meets the required threshold
source: custom
mode: required
code: |
  import json
  report = json.loads(inputs["coverage_json"])
  total = report.get("totals", {}).get("percent_covered", 0)
  passed = total >= inputs.get("threshold", 80)
  return {
    "passed": passed,
    "data": {"coverage_percent": total},
    "message": f"Coverage {total}% {'meets' if passed else 'below'} threshold"
  }
```

```bash
$ agents validation add --config examples/validations/required-validation.yaml
```

**Expected Output:**
```
╭──────────────────── Validation Registered ─────────────────────╮
│ Name: qa/coverage-check                                        │
│ Description: Verify test coverage meets the required threshold │
│ Source: custom                                                 │
│ Mode: required                                                 │
╰────────────────────────────────────────────────────────────────╯
```

**What's Happening:**

The validation is stored in the same tool registry as regular tools, but with
`tool_type = validation`. The `mode: required` field means this validation
blocks plan execution if it fails. The inline `code` block is a Python snippet
that receives `inputs` and must return a dict with a `passed` boolean.

---

### Step 3: Register a wrapped validation

A **wrapped validation** delegates execution to an existing tool and transforms
its output into a pass/fail result:

```yaml
# examples/validations/wrapped-validation.yaml
name: qa/lint-check
description: Validate that linting passes by wrapping the lint tool
wraps: devops/run-linter
mode: required
transform: |
  def transform(result):
    errors = result.get("errors", [])
    passed = len(errors) == 0
    return {
      "passed": passed,
      "data": {"error_count": len(errors), "errors": errors[:5]},
      "message": f"{len(errors)} lint errors found" if errors else "No lint errors"
    }
```

```bash
$ agents validation add --config examples/validations/wrapped-validation.yaml
```

**Expected Output:**
```
╭─────────────────────────── Validation Registered ────────────────────────────╮
│ Name: qa/lint-check                                                          │
│ Description: Validate that linting passes by wrapping the lint tool          │
│ Source: wrapped                                                              │
│ Mode: required                                                               │
│ Wraps: devops/run-linter                                                     │
│ Transform: def transform(result):                                            │
│   errors = result.get("errors", [])                                          │
│   passed = len(errors) == 0                                                  │
│   return {                                                                   │
│     "passed": passed,                                                        │
│     "data": {"error_count": len(errors), "errors": errors[:5]},              │
│     "message": f"{len(errors)} lint errors found" if errors else "No lint    │
│ errors"                                                                      │
│   }                                                                          │
╰──────────────────────────────────────────────────────────────────────────────╯
```

**What's Happening:**

The `wraps` field points to an existing tool (`devops/run-linter`). When the
validation runs, CleverAgents calls that tool and passes the result through the
`transform` function. This lets you reuse existing tools as quality gates
without duplicating logic.

---

### Step 4: List all tools and validations

```bash
$ agents tool list
```

**Expected Output:**
```
                                Tools (3 total)
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ Name              ┃ Type       ┃ Source  ┃ Description             ┃ Timeout ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ local/line-counter│ tool       │ custom  │ Count lines in a file   │      60 │
│ qa/coverage-check │ validation │ custom  │ Verify test coverage    │     120 │
│                   │            │         │ meets the requir...     │         │
│ qa/lint-check     │ validation │ wrapped │ Validate that linting   │     180 │
│                   │            │         │ passes by wrapp...      │         │
└───────────────────┴────────────┴─────────┴─────────────────────────┴─────────┘
```

**What's Happening:**

`tool list` shows **both tools and validations** in a single unified view. The
`Type` column distinguishes them. The `Source` column shows how each was
defined: `custom` (inline Python code), `wrapped` (delegates to another tool),
or `mcp` (delegates to an MCP server tool).

---

### Step 5: Filter by type

Show only validations:

```bash
$ agents tool list --type validation
```

**Expected Output:**
```
                                Tools (2 total)
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ Name              ┃ Type       ┃ Source  ┃ Description             ┃ Timeout ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ qa/coverage-check │ validation │ custom  │ Verify test coverage    │     120 │
│                   │            │         │ meets the requir...     │         │
│ qa/lint-check     │ validation │ wrapped │ Validate that linting   │     180 │
│                   │            │         │ passes by wrapp...      │         │
└───────────────────┴────────────┴─────────┴─────────────────────────┴─────────┘
```

Show only tools (not validations):

```bash
$ agents tool list --type tool
```

**Expected Output:**
```
                              Tools (1 total)
┏━━━━━━━━━━━━━━━━━┳━━━━━━┳━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ Name            ┃ Type ┃ Source ┃ Description                 ┃ Timeout ┃
┡━━━━━━━━━━━━━━━━━╇━━━━━━╇━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ local/line-counter│ tool │ custom │ Count lines in a file     │      60 │
└─────────────────┴──────┴────────┴─────────────────────────────┴─────────┘
```

Filter by namespace:

```bash
$ agents tool list --namespace qa
```

**Expected Output:**
```
                                Tools (2 total)
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┓
┃ Name              ┃ Type       ┃ Source  ┃ Description             ┃ Timeout ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━┩
│ qa/coverage-check │ validation │ custom  │ Verify test coverage    │     120 │
│ qa/lint-check     │ validation │ wrapped │ Validate that linting   │     180 │
└───────────────────┴────────────┴─────────┴─────────────────────────┴─────────┘
```

**What's Happening:**

The `--type` flag accepts `tool` or `validation`. The `--namespace` flag
filters by the prefix before the `/` in the tool name. You can also pass a
regex pattern as a positional argument: `agents tool list "line-.*"`.

---

### Step 6: Inspect a specific tool

```bash
$ agents tool show local/line-counter
```

**Expected Output:**
```
╭─────────── Tool Details ───────────╮
│ Name: local/line-counter           │
│ Namespace: local                   │
│ Short Name: line-counter           │
│ Description: Count lines in a file │
│ Source: custom                     │
│ Type: tool                         │
│ Timeout: 60s                       │
│ Capability:                        │
│   (default)                        │
│ Resource Slots:                    │
│   (none)                           │
│ Lifecycle:                         │
│   (none)                           │
╰────────────────────────────────────╯
```

Inspect a validation to see its mode:

```bash
$ agents tool show qa/coverage-check
```

**Expected Output:**
```
╭───────────────────────── Tool Details ─────────────────────────╮
│ Name: qa/coverage-check                                        │
│ Namespace: qa                                                  │
│ Short Name: coverage-check                                     │
│ Description: Verify test coverage meets the required threshold │
│ Source: custom                                                 │
│ Type: validation                                               │
│ Timeout: 120s                                                  │
│ Capability:                                                    │
│   (default)                                                    │
│ Resource Slots:                                                │
│   (none)                                                       │
│ Lifecycle:                                                     │
│   (none)                                                       │
│ Validation Mode: required                                      │
╰────────────────────────────────────────────────────────────────╯
```

**What's Happening:**

`tool show` works for both tools and validations — use the full namespaced name
(`namespace/short-name`). For validations, the panel includes an extra
**Validation Mode** field showing `required` or `informational`.

> **Why do capabilities show as `(default)`?**
> The detail view collapses the default capability flags and omits resource slot
> bindings when they match the defaults stored in the registry. The values you
> set during registration are still persisted and enforced when the tool runs,
> even though the summary view shows `(default)` / `(none)`.

---

### Step 7: Get machine-readable JSON output

```bash
$ agents tool list --format json
```

**Expected Output:**
```json
{
  "command": "",
  "status": "ok",
  "exit_code": 0,
  "data": [
    {
      "name": "local/line-counter",
      "description": "Count lines in a file",
      "source": "custom",
      "tool_type": "tool",
      "namespace": "local",
      "short_name": "line-counter",
      "capability": {},
      "timeout": 60
    },
    {
      "name": "qa/coverage-check",
      "description": "Verify test coverage meets the required threshold",
      "source": "custom",
      "tool_type": "validation",
      "namespace": "qa",
      "short_name": "coverage-check",
      "capability": {},
      "timeout": 120
    },
    {
      "name": "qa/lint-check",
      "description": "Validate that linting passes by wrapping the lint tool",
      "source": "wrapped",
      "tool_type": "validation",
      "namespace": "qa",
      "short_name": "lint-check",
      "capability": {},
      "timeout": 180
    }
  ],
  "timing": {
    "duration_ms": 0
  },
  "messages": [
    {
      "level": "ok",
      "text": "ok"
    }
  ]
}
```

Show a single tool in JSON:

```bash
$ agents tool show local/line-counter --format json
```

**Expected Output:**
```json
{
  "command": "",
  "status": "ok",
  "exit_code": 0,
  "data": {
    "name": "local/line-counter",
    "description": "Count lines in a file",
    "source": "custom",
    "tool_type": "tool",
    "namespace": "local",
    "short_name": "line-counter",
    "capability": {},
    "timeout": 60
  },
  "timing": {
    "duration_ms": 0
  },
  "messages": [
    {
      "level": "ok",
      "text": "ok"
    }
  ]
}
```

**What's Happening:**

All tool commands support `--format json` (or `-f json`). The response uses the
standard CleverAgents JSON envelope: `data` holds the payload, `status` is
`"ok"` on success, and `timing.duration_ms` shows elapsed time. For list
commands, `data` is an array; for `show`, `data` is a single object.

---

### Step 8: Update an existing tool or validation

If you modify the YAML and want to re-register without removing first:

```bash
$ agents validation add --config examples/validations/required-validation.yaml --update
```

**Expected Output:**
```
╭──────────────────── Validation Updated ────────────────────────╮
│ Name: qa/coverage-check                                        │
│ Description: Verify test coverage meets the required threshold │
│ Source: custom                                                 │
│ Mode: required                                                 │
╰────────────────────────────────────────────────────────────────╯
```

**What's Happening:**

Without `--update`, re-registering an existing name raises an error. The
`--update` flag performs an upsert — it updates the existing record in place.
The same flag is available on `tool add --update`.

---

### Step 9: Remove a tool

```bash
$ agents tool remove --yes local/line-counter
```

**Expected Output:**
```
Removed tool: local/line-counter
```

**What's Happening:**

`tool remove` soft-deletes the tool from the registry. The `--yes` flag skips
the interactive confirmation prompt, which is useful in scripts. Without
`--yes`, the CLI will ask you to confirm before deleting.

---

## Scripting with JSON Output

Because all commands support `--format json`, you can integrate tool management
into CI/CD pipelines:

```bash
# Check if a specific tool is registered
$ agents tool list --format json | jq -r '.data[] | select(.name == "qa/coverage-check") | .name'
qa/coverage-check

# List all validation names
$ agents tool list --type validation --format json | jq -r '.data[].name'
qa/coverage-check
qa/lint-check

# Check the timeout for a tool
$ agents tool show local/line-counter --format json | jq '.data.timeout'
60

# Count registered tools
$ agents tool list --format json | jq '.data | length'
3

# List all tools in the qa namespace
$ agents tool list --namespace qa --format json | jq -r '.data[].name'
qa/coverage-check
qa/lint-check
```

---

## Tool vs Validation: When to Use Each

| Aspect | Tool | Validation |
|--------|------|------------|
| **Purpose** | General-purpose callable operation | Pass/fail quality gate |
| **Return value** | Any structured data | Must include `passed: bool` |
| **Modes** | N/A | `required` (blocks) or `informational` (advisory) |
| **Source types** | `custom`, `mcp` | `custom`, `wrapped` |
| **Used in plans** | As action steps | As pre/post conditions |
| **Namespace convention** | `local/`, `devops/` | `qa/` |

---

## Complete Interaction Log

<details>
<summary>Click to see the full verified command sequence</summary>

```
# 1. Register a custom tool
$ agents tool add --config examples/tools/custom-tool.yaml
╭─────────── Tool Registered ───────────╮
│ Name: local/line-counter              │
│ Source: custom                        │
│ Type: tool                            │
│ Timeout: 60s                          │
│ Capability: read_only: True, ...      │
╰───────────────────────────────────────╯

# 2. Register a required validation (custom code)
$ agents validation add --config examples/validations/required-validation.yaml
╭──────────────────── Validation Registered ─────────────────────╮
│ Name: qa/coverage-check                                        │
│ Mode: required                                                 │
╰────────────────────────────────────────────────────────────────╯

# 3. Register a wrapped validation
$ agents validation add --config examples/validations/wrapped-validation.yaml
╭─────────────────────────── Validation Registered ────────────────────────────╮
│ Name: qa/lint-check                                                          │
│ Source: wrapped                                                              │
│ Wraps: devops/run-linter                                                     │
╰──────────────────────────────────────────────────────────────────────────────╯

# 4. List all (tools + validations unified)
$ agents tool list
                                Tools (3 total)
┃ Name              ┃ Type       ┃ Source  ┃ Description             ┃ Timeout ┃
│ local/line-counter│ tool       │ custom  │ Count lines in a file   │      60 │
│ qa/coverage-check │ validation │ custom  │ Verify test coverage... │     120 │
│ qa/lint-check     │ validation │ wrapped │ Validate that linting...│     180 │

# 5. Filter by type
$ agents tool list --type validation
# → shows only qa/coverage-check and qa/lint-check

$ agents tool list --type tool
# → shows only local/line-counter

# 6. Filter by namespace
$ agents tool list --namespace qa
# → shows qa/coverage-check and qa/lint-check

# 7. Inspect a tool
$ agents tool show local/line-counter
# → shows capability flags, resource slots, lifecycle

# 8. Inspect a validation (shows Validation Mode)
$ agents tool show qa/coverage-check
# → shows Validation Mode: required

# 9. JSON output for scripting
$ agents tool list --format json
# → {"status": "ok", "data": [...], "timing": {...}}

$ agents tool show local/line-counter --format json
# → {"status": "ok", "data": {...}, "timing": {...}}

# 10. Update without removing
$ agents validation add --config examples/validations/required-validation.yaml --update
# → Validation Updated panel

# 11. Remove a tool
$ agents tool remove --yes local/line-counter
# → Removed tool: local/line-counter
```
</details>

---

## Key Takeaways

- **Tools and validations share a unified registry** — `tool list` shows both;
  use `--type tool` or `--type validation` to filter.
- **Namespaced names** (`namespace/short-name`) uniquely identify every tool.
  Use `local/` for project-specific tools and `qa/` for quality gates.
- **Validations have a `mode`** — `required` blocks plan execution on failure;
  `informational` is advisory only.
- **Wrapped validations** reuse existing tools as quality gates via a
  `transform` function — no code duplication needed.
- **`--update` flag** enables safe re-registration when you modify a YAML
  config without needing to remove and re-add.
- **`--format json`** works on all tool commands — use it to integrate tool
  management into CI/CD scripts with `jq`.

## Try It Yourself

Now that you've seen the full tool and validation lifecycle, try these
variations:

- **Register the MCP tool example**: `agents tool add --config examples/tools/mcp-tool.yaml`
- **Filter by source**: `agents tool list --source wrapped`
- **Regex filter**: `agents tool list "qa/.*"`
- **CI check**: `agents tool list --format json | jq '.data | map(select(.tool_type == "validation")) | length'`
- **Override mode at registration**: `agents validation add --config examples/validations/required-validation.yaml --informational`

## Related Examples

- See [`cli-tools/output-format-flags.md`](output-format-flags.md) for a deep
  dive into all six output formats (`json`, `yaml`, `plain`, `table`, `rich`,
  `color`)
- See `docs/showcase/cli-tools/` for more CLI tool examples

---
*This example was automatically generated and verified by the CleverAgents UAT system.*
*Feature area: Tool and validation management | Test cycle: 1 | Generated: 2026-04-07*
