From itential-builder
Builds command templates with validation rules, runs CLI checks on network devices, and manages analytic templates for pre/post comparisons.
How this command is triggered — by the user, by Claude, or both
Slash command
/itential-builder:SKILL [action or template-name]skills/itential-mop/The summary Claude sees in its command listing — used to decide when to auto-load this command
# MOP (Method of Procedure) - Developer Skills Guide MOP manages command templates and analytic templates for running CLI commands against network devices with validation rules. Command templates execute show commands and evaluate the output against rules. Analytic templates compare command output before and after a change. **MOP is for read-only validation only -- never use it to push configuration to devices.** Use Jinja2 templates and workflow tasks for config changes. ## Concepts - **Command template** = a set of CLI commands + validation rules, run against one or more devices - **A...
MOP manages command templates and analytic templates for running CLI commands against network devices with validation rules. Command templates execute show commands and evaluate the output against rules. Analytic templates compare command output before and after a change.
MOP is for read-only validation only -- never use it to push configuration to devices. Use Jinja2 templates and workflow tasks for config changes.
<!variable_name!> in both commands and rules (NOT {{ var }} or $var)All /mop/* endpoints:
| Method | Endpoint | Description |
|---|---|---|
| POST | /mop/createTemplate | Create a command template |
| GET | /mop/listTemplates | List all command templates |
| GET | /mop/listATemplate/{name} | Get a command template by name |
| POST | /mop/updateTemplate/{mopID} | Update a command template (full replacement) |
| POST | /mop/deleteTemplate/{id} | Delete a command template |
| POST | /mop/exportTemplate | Export template (body: {"_id": "..."} or {"name": "..."}) |
| POST | /mop/importTemplate | Import a template |
| POST | /mop/RunCommandTemplate | Run a command template against devices |
| POST | /mop/RunCommand | Run a single ad-hoc command on one device (workflow task) |
| POST | /mop/RunCommandDevices | Run a single ad-hoc command on multiple devices |
| POST | /mop/RunCommandTemplateSingleCommand | Run one command from a template by index |
| POST | /mop/GetBootFlash | Get boot flash image name from a device |
| POST | /mop/reattempt | Retry/delay mechanism for workflows |
| POST | /mop/createAnalyticTemplate | Create an analytic template |
| GET | /mop/listAnalyticTemplates | List all analytic templates |
| GET | /mop/listAnAnalyticTemplate/{name} | Get an analytic template by name (path param) |
| POST | /mop/updateAnalyticTemplate/{id} | Update an analytic template |
| POST | /mop/deleteAnalyticTemplate/{id} | Delete an analytic template |
| POST | /mop/runAnalyticsTemplate | Run an analytic template (workflow task) |
Create with POST /mop/createTemplate. The body uses a {"mop": {...}} wrapper.
{
"mop": {
"name": "Port_Turn_Up_Pre_Check",
"description": "Validates interface and VLAN before port turn-up",
"os": "",
"passRule": true,
"ignoreWarnings": false,
"commands": [
{
"command": "show interface <!interface!>",
"passRule": true,
"rules": [
{
"rule": "line protocol is",
"eval": "contains",
"severity": "error"
}
]
},
{
"command": "show vlan brief",
"passRule": true,
"rules": [
{
"rule": "<!vlan_id!>",
"eval": "contains",
"severity": "error",
}
]
}
]
}
}
Field reference:
name -- template name (required, must be unique)description -- human-readable descriptionos -- target OS filter (empty string = any OS)passRule (template-level) -- true = ALL commands must pass (AND), false = ONE command must pass (OR)ignoreWarnings -- see ignoreWarnings section belowcommands[] -- array of commands to execute
command -- the CLI command string. Variables use <!variable_name!> syntaxpassRule (command-level) -- true = ALL rules must pass (AND), false = ONE rule must pass (OR)rules[] -- validation rules applied to the command output
rule -- the string or pattern to match against. Can contain <!variables!>eval -- evaluation operator (case-sensitive, see Rule Evaluation below)severity -- "error", "warning", or "info"flags -- optional evaluation flags (see Flags below)Only "name" is required -- template validation uses AJV with strict=false, so minimal templates are accepted.
passRule: true = ALL commands must pass (AND logic)passRule: false = at least ONE command must pass (OR logic)passRule: true = ALL rules in this command must pass (AND logic)passRule: false = at least ONE rule must pass (OR logic)Template-level field, default false. When true: only rules with severity: "error" count as real failures. Rules with severity: "warning" or "info" that fail are treated as passing. When false (default): all severity levels count.
{
"mop": {
"name": "...",
"passRule": true,
"ignoreWarnings": true,
"commands": [...]
}
}
The eval field determines how rule matching works. Eval types are case-sensitive.
| Eval | Purpose | Example Rule |
|---|---|---|
contains | String exists in output | "line protocol is" |
!contains | String does NOT exist in output | "ERROR" |
contains1 | String exists exactly once | "Active" |
RegEx | Regex matches output (capital R and E!) | "/\\d+\\.\\d+/" |
!RegEx | Regex does NOT match | "/ERROR/" |
#comparison | Extract + compare two values | See details below |
Optional flags object on each rule:
case: true = case-INSENSITIVE matching (confusing name -- case: true does NOT mean case-sensitive)global: true = global search (RegEx only)multiline: true = ^/$ match start/end of lines, not just start/end of string (RegEx only)case is available for all eval types. global and multiline are only meaningful for RegEx and !RegEx.
Extract two values from command output using regex, then compare numerically.
{
"rule": "/Available: (\\d+)/",
"ruleB": "/Total: (\\d+)/",
"eval": "#comparison",
"evaluator": ">=",
"severity": "error"
}
rule / ruleB -- regex patterns (in /pattern/ format) to extract values from the command outputevaluator -- comparison operator: =, !=, <, >, <=, >=, %% operator -- passes if ruleB/rule * 100 <= percentage. Set "percentage": 80 to pass if ruleB is at most 80% of rule.Example with percentage:
{
"rule": "/Total: (\\d+)/",
"ruleB": "/Used: (\\d+)/",
"eval": "#comparison",
"evaluator": "%",
"percentage": 80,
"severity": "error"
}
<!variable_name!> in both commands and rules{{ var }}) and workflow variable references ($var.job.x)Example command with variables:
{
"command": "show running-config interface <!interface!>",
"passRule": true,
"rules": [
{
"rule": "switchport access vlan <!vlan_id!>",
"eval": "contains",
"severity": "error",
"evaluation": "pass"
}
]
}
POST /mop/RunCommandTemplate
{
"template": "Port_Turn_Up_Pre_Check",
"variables": {
"interface": "GigabitEthernet0/1",
"vlan_id": "100"
},
"devices": ["IOS-CAT8KV-1"]
}
template -- template name (string)variables -- object with values for <!variable!> substitutionsdevices -- array of device names (or single device name string)Use the MOP.RunCommandTemplate task. See /itential-studio for full workflow task wiring patterns.
{
"incoming": {
"template": "$var.job.templateName",
"variables": "$var.job.templateVariables",
"devices": "$var.job.devices"
},
"outgoing": {
"mop_template_results": null
}
}
template -- name of the command template (string or $var reference)variables -- object with values for <!variable!> substitutionsdevices -- array of device names to run againstSee /itential-builder for running the workflow via POST /operations-manager/jobs/start.
Run a single command directly without creating a template first:
POST /mop/RunCommand
{
"command": "show version",
"variables": {},
"device": "IOS-CAT8KV-1"
}
Returns: {raw, evaluated, device, response, result} — same shape as one entry in commands_results.
For multiple devices: POST /mop/RunCommandDevices with "devices": ["dev1", "dev2"] (array instead of singular device).
To run a single command from an existing template by index: POST /mop/RunCommandTemplateSingleCommand with {"templateId": "name", "commandIndex": 0, "variables": {}, "devices": ["dev1"]}.
{
"all_pass_flag": true,
"result": true,
"name": "Port_Turn_Up_Pre_Check",
"commands_results": [
{
"raw": "show interface <!interface!>",
"evaluated": "show interface GigabitEthernet0/1",
"all_pass_flag": true,
"device": "IOS-CAT8KV-1",
"response": "...command output...",
"result": true,
"parameters": {"interface": "GigabitEthernet0/1"},
"rules": [
{"rule": "line protocol is", "eval": "contains", "result": true, "severity": "error"}
]
}
]
}
result (top-level) -- overall template pass/fail (boolean)all_pass_flag (top-level) -- the template's passRule settingcommands_results[] -- one entry per command per device
raw -- original command string (before variable substitution)evaluated -- command with variables substitutedresponse -- raw device outputresult -- whether this command passed (boolean)all_pass_flag -- this command's passRule settingdevice -- the device this command ran againstparameters -- the variables that were substitutedrules[].result -- true/false for each individual rulePOST /mop/updateTemplate/{mopID}
The mopID is the template name (URL-encoded). Uses the same {"mop": {...}} body wrapper as create. The body is a full replacement -- include ALL fields, not just changed ones.
Response on success:
{
"n": 1,
"ok": 1,
"nModified": 1
}
Analytic templates compare command output before and after a change to detect drift or validate results. Endpoints are listed in the API Reference table above.
POST /mop/createAnalyticTemplate
{
"name": "Interface_Change_Validation",
"os": "cisco-ios",
"passRule": true,
"prepostCommands": [
{
"preRawCommand": "show interface GigabitEthernet0/1",
"postRawCommand": "show interface GigabitEthernet0/1",
"passRule": true,
"rules": [
{
"type": "matches",
"preRegex": "/line protocol is (\\w+)/",
"postRegex": "/line protocol is (\\w+)/",
"evaluator": "="
}
]
}
]
}
name -- template nameos -- target OSpassRule -- true = ALL prepostCommands must pass (AND), false = ONE must pass (OR)prepostCommands[] -- array of pre/post command pairs
preRawCommand -- CLI command to run before the changepostRawCommand -- CLI command to run after the changepassRule -- true = ALL rules must pass, false = ONE must passrules[] -- comparison rules
type -- matches, !matches, regex, or tablepreRegex -- regex to extract value from pre-change outputpostRegex -- regex to extract value from post-change outputevaluator -- comparison operator: =, !=, <, >, <=, >=, %| Type | Purpose |
|---|---|
matches | Pre and post extracted values must match per evaluation operator |
!matches | Pre and post extracted values must NOT match |
regex | Regex-based extraction and comparison |
table | Table-based comparison of structured output |
In a workflow, use the MOP.runAnalyticsTemplate task:
{
"incoming": {
"pre": "$var.preCheckTaskId.mop_template_results",
"post": "$var.postCheckTaskId.mop_template_results",
"analytic_template_name": "Interface_Change_Validation",
"variables": {}
},
"outgoing": {
"analytic_result": null
}
}
Critical: The pre and post inputs must be the full RunCommandTemplate output object (which contains a commands_results property). Do NOT pass just the commands_results array — pass the entire result object.
Gotcha: Pre and post commands must have exactly 1 match each in the collected results. If 0 or >1 match, it produces an error. The matching compares against both the raw and evaluated command strings — if variables were used, the evaluated string (with variables replaced) is what will match.
Missing variable = skip = PASS (not fail) -- if a <!var!> token has no value, the command is silently skipped and counts as PASSED. Verify variables are passed correctly.
case: true = case-INsensitive -- confusing naming. "flags": {"case": true} enables case-insensitive matching. It does NOT mean case-sensitive.
Empty rules = auto-pass -- a command with no rules ("rules": []) always passes. Add at least one rule if you want validation.
RegEx 5-second timeout -- complex regex patterns run in a sandboxed VM with a 5-second limit. Patterns prone to catastrophic backtracking will timeout.
contains does substring matching -- "100" matches "1002". For exact matching, use RegEx with multiline flag:
{"rule": "^<!vlanId!>\\s+", "eval": "RegEx", "severity": "error", "flags": {"multiline": true}}
Eval types are case-sensitive -- "RegEx" not "regex" or "REGEX". "#comparison" not "Comparison".
Only "name" is required -- template validation uses AJV with strict=false. Minimal templates are accepted.
Update is full replacement -- POST /mop/updateTemplate/{mopID} replaces the entire template. Include ALL fields when updating, not just changed ones.
MOP is read-only -- command templates run show commands and evaluate output. Never use MOP to push configuration changes. Use Jinja2 templates and workflow adapter tasks for config changes.
_id equals name -- the engine sets _id = name on create. They are always identical. Use either for lookups.
Rule-level missing variable ≠ command-level skip -- if a command has <!var!> missing, the whole command is skipped (passes). But if a rule has <!var!> missing, it gets eval: "missing_parameters" and returns "Invalid Rule: Missing Parameters" with result: false. The rule fails, not skips.
Template name change on update = delete + create -- if you update with a different name, the engine deletes the old template and creates a new one. This is destructive — the old _id is gone.
Import renames on collision -- importTemplate does not fail on duplicate names. It appends (N) to the name (e.g., My_Template becomes My_Template (1)).
Cannot set namespace directly -- providing namespace in the create body throws an error. Namespaces are managed through project membership.
Always start from a helper template when creating assets. Read the helper file first, then modify it.
| File | API Call | Purpose |
|---|---|---|
${CLAUDE_PLUGIN_ROOT}/helpers/create-command-template.json | POST /mop/createTemplate | Command template with rules |
${CLAUDE_PLUGIN_ROOT}/helpers/update-command-template.json | POST /mop/updateTemplate/{mopID} | Update template (full replacement) |
show interface, show vlan brief)${CLAUDE_PLUGIN_ROOT}/helpers/create-command-template.json as a starting templatename, description, add commands with <!variable!> placeholderscontains for simple checks, RegEx for pattern matchingpassRule at template and command level (AND vs OR logic)POST /mop/createTemplatePOST /mop/RunCommandTemplate providing variables and devicesresult (top-level) and commands_results[].rules[].result for pass/fail detailsAfter standalone testing passes:
/itential-studio to build a workflowMOP.RunCommandTemplate task to the workflowtemplate, variables, devices using $var.job.* referencesmop_template_results$var.taskName.result (true/false)/itential-builder to run via POST /operations-manager/jobs/startPOST /mop/createAnalyticTemplateprepostCommands with pre/post command pairspreRegex/postRegex to extract values for comparisonevaluation operator (= to verify values match, != to verify they changed)npx claudepluginhub itential/builder-skills/SKILLResolves GitHub issue via isolated worktree, TDD workflow, and auto-closing PR creation.
/SKILLCreates conventional git commit from conversation intent using git-agent and pushes to remote. Accepts optional Claude model name for co-author.
/SKILLSurfaces current session task from state file, evaluates clarity (prompts for clarification if needed), assesses completion, and verifies if fully done.