This guide builds a small but complete configuration: a calculator tool, an agent that calls it, and a test that exercises the agent. It mirrors the declarative model end to end: declare resources, reference them with ${kind.ref_name}, then run the pipeline. The three resources live in one file, math.yaml, separated by ---. The tool’s Python implementation lives beside it in a calculator/ directory that wxctl scaffolds for you:
.
├── math.yaml
└── calculator/
    ├── calculator.py     # the implementation
    ├── schema.yaml       # input / output schema
    └── requirements.txt  # Python dependencies

1. Declare the tool

A tool is a callable function an agent can invoke. The only schema-required field is permission. To actually run, a tool also needs a binding and a source_path pointing at its implementation. The input_schema describes the parameters the agent passes.
math.yaml
kind: tool
ref_name: calculator
name: calculator
display_name: Calculator
description: A calculator tool that adds, multiplies, and divides two numbers.
permission: read_only
source_path: ./calculator
binding:
  python:
    function: calculator:main
input_schema:
  type: object
  properties:
    operation:
      type: string
      enum: [add, multiply, divide]
    a:
      type: number
    b:
      type: number
  required: [operation, a, b]
binding.python.function uses module:function format: here, main() in calculator.py. source_path is a directory that must hold that Python file plus a schema.yaml; paths resolve relative to the config file’s directory. You don’t write those files by hand; step 4 generates them from this input_schema.

2. Declare the agent

The agent references the tool by ref with ${tool.calculator}. wxctl turns that reference into a dependency edge, so the tool is created before the agent and the agent receives the tool’s real ID at execution time. An agent requires name, description, style, and llm.
math.yaml
---
kind: agent
ref_name: math_assistant
name: math_assistant
display_name: Math Assistant
description: An assistant that performs arithmetic using the calculator tool.
instructions: Use the calculator tool for any arithmetic the user requests.
llm: groq/openai/gpt-oss-120b
style: default
tools:
  - ${tool.calculator}
The llm field accepts a plain model name (no dependency) or a ${model.<ref>} reference to a model resource you also declare. style controls reasoning: default is standard conversational, react enables step-by-step Reasoning + Acting, planner enables multi-step task decomposition.

3. Declare a test

A kind: test resource exercises a deployed agent. Each entry in turns sends a chat message. expect_tools asserts which tools the agent should call, and expect_answer describes the answer to look for. Tests run only under wxctl test; they are ignored by plan, apply, and destroy.
math.yaml
---
kind: test
ref_name: math_smoke_test
agent: ${agent.math_assistant}
turns:
  - message: "What is 2 plus 3?"
    expect_tools:
      - calculator
    expect_answer: "5"

4. Scaffold the source directory

source_path: ./calculator names a directory that doesn’t exist yet. Rather than create it by hand, materialize it from the tool’s input_schema with the CLI or the compose_scaffold MCP tool:
wxctl compose scaffold -f math.yaml
Writes the stubs into ./calculator/, the tool’s declared source_path, resolved relative to math.yaml. Add --dry-run to print the manifest without writing.
Either way, three files land in the tool’s source directory; scaffolding never overwrites a file that already exists:
  • schema.yaml: the input_schema (plus an output_schema stub), lifted from math.yaml. This is the file the tool loads its schema from at apply time (see the note below).
  • requirements.txt: a placeholder header, ready for any Python dependencies.
  • calculator.py: a typed stub whose signature matches the schema (numberfloat, parameters sorted by name), with the entry point named by the binding:
calculator/calculator.py
def main(a: float, b: float, operation: str) -> dict:
    """TODO: describe what this tool does."""
    # TODO: implement
    return {"a": a, "b": b, "operation": operation}
The inline input_schema in math.yaml is a scaffold seed, not part of the deployed tool. Scaffolding lifts it into calculator/schema.yaml, and the tool loads its schema from there at apply time; the inline copy is ignored from then on. Once schema.yaml exists you can delete the input_schema block from math.yaml, and the finished examples in the repo do. Keeping both works but lets the two drift out of sync.

5. Implement the tool

Fill in the stub with the real logic. main() returns a JSON-serializable dict:
calculator/calculator.py
"""Calculator tool for watsonx Orchestrate: add, multiply, and divide."""


def add(a: float, b: float) -> float:
    """Return the sum of a and b."""
    return a + b


def multiply(a: float, b: float) -> float:
    """Return the product of a and b."""
    return a * b


def divide(a: float, b: float) -> float:
    """Return a divided by b; raise on divide-by-zero."""
    if b == 0:
        raise ValueError("Cannot divide by zero")
    return a / b


def main(operation: str, a: float, b: float) -> dict:
    """Entry point named by `binding.python.function`.

    Args:
        operation: One of "add", "multiply", or "divide".
        a: The first operand.
        b: The second operand.

    Returns:
        The operation, its operands, and the result, or an `error` message.
    """
    if operation == "add":
        result = add(a, b)
    elif operation == "multiply":
        result = multiply(a, b)
    elif operation == "divide":
        try:
            result = divide(a, b)
        except ValueError as e:
            return {"error": str(e)}
    else:
        return {"error": f"Unknown operation: {operation}"}

    return {"operation": operation, "a": a, "b": b, "result": result}

6. Validate, plan, apply, test

1

Validate the schema

Check the file against the resource schemas before touching any service:
wxctl validate -f math.yaml
2

Preview the plan

A dry run: see what would be created, in dependency order, without making changes:
wxctl plan -f math.yaml
3

Apply

Create the tool and the agent remotely:
wxctl apply -f math.yaml
4

Test

Run the test turns against the deployed agent:
wxctl test -f math.yaml
When you are done, wxctl destroy -f math.yaml tears the resources down in reverse-dependency order.

Next steps

Resource kinds

Every kind wxctl supports, with fields and endpoints.

Troubleshooting

Logging, concurrency, and color environment variables.