View on GitHub →
← Back to Home

Go: Tool Contracts with ToolClad

Define .clad.toml manifests, validate typed arguments, execute tools safely, and generate MCP schemas with ToolClad.

The Problem: Unconstrained Tool Execution

Agentic runtimes let LLMs invoke CLI tools, but without constraints the model can generate arbitrary shell commands. Deny-list approaches are fragile — one missed metacharacter and you have command injection.

ToolClad inverts the model: instead of blocking dangerous commands, it constrains the LLM to fill typed parameters validated against a declarative manifest (allow-list). The dangerous action cannot be expressed because the interface doesn't permit it.

  • Typed parameters: string, integer, port, enum, scope_target, url, path, ip_address, cidr, boolean
  • Shell metacharacter rejection by default
  • Command template rendering with value mappings
  • Evidence envelopes with execution metadata
  • MCP schema generation from manifests
  • Full CLI for validation, testing, and execution

Step 1: Install ToolClad

Install the CLI binary:

go install github.com/ThirdKeyAI/ToolClad/go/cmd/toolclad@latest

Or import the packages in your Go project:

import (
    "github.com/thirdkeyai/toolclad/pkg/executor"
    "github.com/thirdkeyai/toolclad/pkg/manifest"
    "github.com/thirdkeyai/toolclad/pkg/validator"
)

Step 2: Create a Manifest

A .clad.toml manifest defines everything about a tool: its binary, typed arguments, command template, output format, and risk tier. Save this as whois_lookup.clad.toml:

[tool]
name = "whois_lookup"
version = "1.0.0"
binary = "whois"
description = "WHOIS domain/IP registration lookup"
timeout_seconds = 30
risk_tier = "low"

[tool.cedar]
resource = "PenTest::ScanTarget"
action = "execute_tool"

[args.target]
position = 1
required = true
type = "scope_target"
description = "Domain name or IP address to query"

[command]
template = "whois {target}"

[output]
format = "text"
envelope = true

[output.schema]
type = "object"

[output.schema.properties.raw_output]
type = "string"
description = "Raw WHOIS registration data"

Step 3: Load and Validate Programmatically

Load a manifest and inspect its contents. ToolClad validates the manifest structure on load.

package main

import (
    "fmt"
    "log"

    "github.com/thirdkeyai/toolclad/pkg/manifest"
    "github.com/thirdkeyai/toolclad/pkg/validator"
)

func main() {
    m, err := manifest.LoadManifest("tools/whois_lookup.clad.toml")
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Tool: %s v%s\n", m.Tool.Name, m.Tool.Version)
    fmt.Printf("Binary: %s\n", m.Tool.Binary)
    fmt.Printf("Risk tier: %s\n", m.Tool.RiskTier)
    fmt.Printf("Timeout: %ds\n", m.Tool.TimeoutSeconds)
    fmt.Printf("Args: %d (%d required)\n", len(m.Args), len(m.RequiredArgs()))

    // Validate an individual argument
    for _, argDef := range m.ArgsSorted() {
        validated, err := validator.ValidateArg(argDef, "example.com")
        if err != nil {
            fmt.Printf("  %s: FAIL - %v\n", argDef.Name, err)
        } else {
            fmt.Printf("  %s: OK (%s)\n", argDef.Name, validated)
        }
    }
}

Step 4: Execute a Tool

Execute a tool by passing argument values. ToolClad validates every argument, renders the command template, runs the binary with the configured timeout, and wraps the result in an evidence envelope.

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/thirdkeyai/toolclad/pkg/executor"
    "github.com/thirdkeyai/toolclad/pkg/manifest"
)

func main() {
    m, err := manifest.LoadManifest("tools/whois_lookup.clad.toml")
    if err != nil {
        log.Fatal(err)
    }

    args := map[string]string{
        "target": "example.com",
    }

    envelope, err := executor.Execute(m, args)
    if err != nil {
        log.Fatal(err)
    }

    data, _ := json.MarshalIndent(envelope, "", "  ")
    fmt.Println(string(data))
}

The executor constructs the command whois example.com from the template, runs it with a 30-second timeout, and returns a structured evidence envelope with status, timing, and output hash.

Step 5: Generate MCP Schema

Generate a Model Context Protocol-compatible JSON schema directly from a manifest. This bridges ToolClad manifests to any MCP-compatible agent runtime.

package main

import (
    "encoding/json"
    "fmt"
    "log"

    "github.com/thirdkeyai/toolclad/pkg/executor"
    "github.com/thirdkeyai/toolclad/pkg/manifest"
)

func main() {
    m, err := manifest.LoadManifest("tools/whois_lookup.clad.toml")
    if err != nil {
        log.Fatal(err)
    }

    schema := executor.GenerateMCPSchema(m)

    data, _ := json.MarshalIndent(schema, "", "  ")
    fmt.Println(string(data))
}

The generated schema includes inputSchema with typed properties and required fields, plus an outputSchema that wraps your declared results in the evidence envelope format when envelope = true.

Step 6: CLI Usage

The toolclad CLI provides four commands for working with manifests:

Validate a manifest

toolclad validate whois_lookup.clad.toml

Dry-run with argument validation

toolclad test whois_lookup.clad.toml --arg target=example.com

Execute a tool

toolclad run whois_lookup.clad.toml --arg target=example.com

Generate MCP schema

toolclad schema whois_lookup.clad.toml

Multiple arguments can be passed by repeating the --arg flag: --arg target=10.0.0.1 --arg scan_type=service.