${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:
1. Declare the tool
Atool 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
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
Theagent 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
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
Akind: 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
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:
- CLI
- MCP
./calculator/, the tool’s declared source_path, resolved relative to math.yaml. Add --dry-run to print the manifest without writing.schema.yaml: theinput_schema(plus anoutput_schemastub), lifted frommath.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 (number→float, parameters sorted by name), with the entry point named by the binding:
calculator/calculator.py
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
6. Validate, plan, apply, test
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.

