Universal CLI Grammar
The Problem with Vendor-Specific Verbs
The current ISE CLI grew organically around ISE’s API surface:
netapi ise mnt sessions
netapi ise get-endpoint AA:BB:CC:DD:EE:FF
netapi ise ers endpoints list
netapi ise dc query "SELECT * FROM radius_auth"
These verbs are tightly coupled to ISE’s concepts.
get-endpoint means nothing to GitHub.
mnt sessions assumes ISE’s Monitoring persona.
Every new vendor would need its own vocabulary, and every user would need to learn it from scratch.
The fix is a universal grammar: one sentence structure that works for any API.
Universal Grammar
netapi <vendor> <resource> <verb> [args] [--flags]
| Segment | Role |
|---|---|
|
The API provider: |
|
The noun you are operating on: |
|
The action: |
|
Positional arguments the verb requires (an ID, a MAC, a zone name) |
|
Global or vendor-specific modifiers |
The grammar reads like English: netapi github repo list. Subject-object-verb, always in that order.
Verb Vocabulary
Seven standard verbs cover the CRUD lifecycle plus operational needs:
| Verb | HTTP Equivalent | Behavior |
|---|---|---|
|
|
Return all resources, paginated. Supports |
|
|
Return one resource by ID or unique key. |
|
|
Create a new resource. Accepts |
|
|
Modify an existing resource. Requires an ID argument. |
|
|
Remove a resource. Requires an ID argument. Honors |
|
|
Return only the count, without transferring full payloads. |
|
|
Stream all resources to a file. Supports |
Vendors may register additional verbs (ISE keeps coa for Change of Authorization, for example), but these seven are always available when the resource supports them.
Global Flags
These flags work identically across every vendor:
# Output format — default is table for humans, json for pipes
netapi ise endpoint list --format json
netapi github repo list --format yaml
netapi cloudflare dns list --format csv
# Authentication override
netapi custom get "https://api.example.com/v1/users" --auth bearer
# Write output to file instead of stdout
netapi ise endpoint export --output endpoints.json
# Verbose mode — print request/response headers
netapi github repo get anthropics/claude --verbose
# Dry run — show what would happen without executing writes
netapi ise endpoint delete ABC123 --dry-run
| Flag | Values | Default |
|---|---|---|
|
|
|
|
|
From vendor definition |
|
File path |
stdout |
|
Boolean |
|
|
Boolean |
|
|
Key=value expression |
None |
|
Integer |
Vendor default (typically 20) |
|
Integer or cursor token |
First page |
Cross-Vendor Examples
# ISE — list endpoints, filter by group
netapi ise endpoint list --filter "groupId=abc123"
# GitHub — list repos for an org, output as JSON
netapi github repo list --org anthropics --format json
# Cloudflare — list DNS records for a zone
netapi cloudflare dns list --zone example.com
# pfSense — get a specific firewall rule
netapi pfsense rule get 42
# Custom one-off — hit any URL directly
netapi custom get "https://api.example.com/v1/users" --auth bearer
# Chaining — ISE endpoints to CSV for spreadsheet import
netapi ise endpoint list --format csv --output endpoints.csv
# Piping — feed into jq for field extraction
netapi github repo list --org anthropics --format json | jq '.[].full_name'
# Piping — count endpoints matching a pattern
netapi ise endpoint list --format json | jq '[.[] | select(.mac | startswith("AA:BB"))] | length'
Backward Compatibility
Existing ISE commands become aliases. The old form continues to work; the new form is canonical:
| Legacy Command | Universal Equivalent |
|---|---|
|
|
|
|
|
|
|
|
Legacy aliases emit a deprecation notice to stderr on first use, directing the user to the new form. This keeps scripts working while nudging toward the standard grammar.
Plugin Architecture
Vendor support loads through a discovery chain:
-
Built-in vendors — Shipped with netapi. ISE, pfSense, WLC, IOS.
-
User-defined vendors — YAML definitions in
~/.config/netapi/vendors/. See Vendor Definition Format. -
--vendor-fileflag — Load a vendor definition from an arbitrary path for testing.
At startup, netapi scans built-in vendors first, then overlays user definitions.
If a user definition has the same name as a built-in, the user definition wins — this allows overriding default behavior without forking.
# Validate that a vendor definition loads correctly
netapi vendor validate github
# List all available vendors
netapi vendor list
# Show the resolved definition for a vendor
netapi vendor show ise --format yaml
Output Pipeline Design
Every netapi command produces structured output designed for composition with standard Unix tools:
# JSON output pipes cleanly into jq
netapi ise endpoint list --format json \
| jq '.[] | {mac: .mac, group: .groupName}'
# CSV output pipes into awk for field extraction
netapi ise endpoint list --format csv \
| awk -F',' 'NR>1 {print $2}'
# Table output is human-readable but not for parsing
netapi ise endpoint list --format table
When stdout is not a TTY (piped or redirected), netapi defaults to JSON.
This means netapi ise endpoint list | jq . works without --format json.
When stdout is a TTY, it defaults to table for readability.
The --format flag always overrides auto-detection.
Related
-
Vendor Definition Format — the YAML schema for declaring a new vendor
-
Layer Stack — how the CLI sits atop the composable architecture
-
Integrating Any API — end-to-end walkthrough of adding a new API