Adding a Tool¶
This guide walks through adding a new neuroimaging tool to niBuild. Adding a tool requires two files: a CWL definition and an annotation entry.
Step 1: Write the CWL File¶
Create a new .cwl file in public/cwl/<library>/ (e.g., public/cwl/fsl/my_tool.cwl).
cwlVersion: v1.2
class: CommandLineTool
baseCommand: my_tool
hints:
DockerRequirement:
dockerPull: brainlife/fsl:latest
stdout: $(inputs.output_prefix).log
stderr: $(inputs.output_prefix).log
inputs:
input_image:
type: File
label: Input NIfTI image
inputBinding:
position: 1
output_prefix:
type: string
label: Output filename prefix
inputBinding:
position: 2
# Optional parameters use nullable types
threshold:
type: ['null', double]
label: Intensity threshold
inputBinding:
prefix: -t
position: 3
outputs:
result:
type: File
outputBinding:
glob: $(inputs.output_prefix).nii.gz
log:
type: File
outputBinding:
glob: $(inputs.output_prefix).log
Key Conventions¶
cwlVersion: v1.2— always use CWL v1.2hints.DockerRequirement— specify the Docker image (usehintsnotrequirementsso the tool can run without Docker)- Required inputs — use plain types (
File,string,int, etc.) - Optional inputs — wrap in nullable array:
['null', double] - Mutually exclusive options — use a record type (see
bet.cwlfor an example) - Output globs — reference
$(inputs.output_prefix)or similar to match output filenames
Step 2: Add the Annotation¶
Add an entry to src/utils/toolAnnotations.js in the TOOL_ANNOTATIONS object:
"my_tool": {
"cwlPath": "cwl/fsl/my_tool.cwl",
// UI display metadata
"fullName": "My Tool Full Name",
"function": "One-sentence description of what the tool does.",
"modality": "Expected input type and format.",
"keyParameters": "-t (threshold), -o (output prefix)",
"keyPoints": "Usage notes and best practices.",
"typicalUse": "Where this tool fits in a typical pipeline.",
"docUrl": "https://link-to-official-docs",
// File extension validation (optional)
"inputExtensions": {
"input_image": [".nii", ".nii.gz"]
},
"outputExtensions": {
"result": [".nii", ".nii.gz"]
},
// Parameter constraints (optional)
"bounds": {
"threshold": [0, 1]
},
// Conditional outputs (optional)
// Maps output name to the boolean parameter that enables it
"requires": {
"optional_output": "enable_flag"
},
// Enum hints for string parameters (optional)
"enumHints": {
"cost_function": ["mutualinfo", "corratio", "normcorr"]
}
},
Annotation Fields¶
| Field | Required | Description |
|---|---|---|
cwlPath |
Yes | Path to CWL file relative to public/ |
fullName |
Yes | Human-readable tool name |
function |
Yes | What the tool does (one sentence) |
modality |
Yes | Expected input data description |
keyParameters |
Yes | Important command-line flags |
keyPoints |
Yes | Usage notes and gotchas |
typicalUse |
Yes | Where this fits in a pipeline |
docUrl |
Yes | Link to official documentation |
inputExtensions |
No | Valid file extensions per input |
outputExtensions |
No | Expected file extensions per output |
bounds |
No | [min, max] for numeric parameters |
requires |
No | Output → boolean flag dependency |
enumHints |
No | Fixed valid values for string params |
Step 3: Add to Menu Organization¶
Add the tool to MODALITY_ASSIGNMENTS in toolAnnotations.js:
export const MODALITY_ASSIGNMENTS = {
'Structural MRI': {
FSL: {
'Brain Extraction': ['bet', 'my_tool'], // ← add here
// ...
},
},
};
Choose the appropriate modality → library → category. Create a new category if none fits.
Step 4: Validate¶
- Run
npm run devand verify the tool appears in the menu - Drag it onto the canvas and check that parameters render correctly
- Connect it to other tools and verify type compatibility
- Check the CWL Preview panel for correct output
How It Works¶
niBuild uses a three-layer system for tool configuration:
- CWL files (ground truth) — define inputs, outputs, types, Docker images, command-line bindings
toolAnnotations.js— adds non-CWL metadata: UI labels, extensions, bounds, menu organizationtoolRegistry.js— merges both viagetToolConfigSync()into a single runtime config object
On app mount, preloadAllCWL() fetches and parses all CWL files into a sync cache. When a tool is first used, getToolConfigSync() merges the parsed CWL data with annotations and caches the result.