Install
$ agentstack add skill-sap-automation-pilot-agent-skills-automation-pilot-command-generation ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
SAP Automation Pilot Command Development
This skill provides guidance for creating SAP Automation Pilot commands by composing existing reference commands from the content library.
Command Release Policy
Deployed commands start in DRAFT state. Do not release automatically — only release when:
- The command has been tested and works correctly
- The user explicitly requests release
Draft state allows safe iteration without affecting production.
Overview
SAP Automation Pilot commands are JSON definitions that automate operations on SAP BTP. Commands can be:
- Atomic: Execute a single operation (e.g.,
HttpRequest,SendEmail) - Composite: Orchestrate multiple steps using executors
Reference Materials
examples/— Pattern examples (HTTP, ForEach, polling workflows)references/patterns.md— Common implementation patternsreferences/expressions.md— Expression language reference
Discovering Available Executors
Use the catalog-explorer skill to discover available executors via API:
# List available catalogs
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/catalogs?own=false"
# Find commands in a catalog
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands?catalog=applm-sapcp"
# Get full command definition (inputs, outputs, executors)
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/commands/applm-sapcp:RestartCfApp:1"
This ensures you always use valid executor names and correct parameters, even for newly added catalogs.
Dry Run
dryRun is always present on every executor. Set it to null unless you have a specific reason to provide mock output.
"dryRun": null
The dryRun: {output: {...}} form (with mock values) is only needed if a downstream executor references this executor's output AND you want dry-run mode to propagate realistic values through the chain. In practice, null is the standard.
IMPORTANT: Catalog Naming & ForEach Version
See references/catalogs.md for the complete catalog reference. Key rules:
- Generated commands: Use
>>suffix (e.g.,mycommands->>:MyCommand:1) - Built-in SAP commands: Use
sapcpsuffix (e.g.,http-sapcp:HttpRequest:1) - ForEach: Always use
ForEach:2, neverForEach:1(deprecated)
Command Structure
Naming Conventions
| Element | Convention | Example | |---------|------------|---------| | Command name | PascalCase | RestartCfApp, GetHanaInstance | | Input name | PascalCase | BtpCredentials, JiraConfig | | Input/output keys | camelCase | resourceName, subAccount | | Aliases | camelCase | getResource, createInstance | | Catalog ID | kebab-case | mycommands-xxx |
Names must not contain spaces (causes API issues). PascalCase matches SAP's built-in commands.
Basic Command Definition
{
"id": "mycommands->>:CommandName:1",
"catalog": "mycommands->>",
"name": "CommandName",
"description": "What the command does",
"version": 1,
"inputKeys": { },
"outputKeys": { },
"configuration": null,
"tags": { }
}
Input Keys
Define command parameters:
"inputKeys": {
"paramName": {
"type": "string", // string, number, boolean, array, object
"description": "Description",
"required": true,
"sensitive": false, // true masks in logs
"defaultValue": "value",
"minValue": 1, // for numbers
"maxValue": 100
}
}
Additional optional fields exist: allowedValues (fixed set of valid options), suggestedValues (hints shown in UI), allowedValuesFromInputKeys and suggestedValuesFromInputKeys (dynamic lists from an input reference). Omit them when not needed.
defaultValue is always a JSON string on the wire regardless of the declared type. For non-string types, stringify the JSON: "defaultValue": "5" for a number, "defaultValue": "true" for a boolean, "defaultValue": "[\"a\",\"b\"]" for an array, "defaultValue": "{\"key\":\"val\"}" for an object.
Region inputs must always use allowedValuesFromInputKeys to constrain the value to the SAP-provided region list:
- Cloud Foundry:
"allowedValuesFromInputKeys": ["metadata-sapcp:CfRegionData:1"] - Neo:
"allowedValuesFromInputKeys": ["metadata-sapcp:NeoRegionData:1"]
This ensures only valid Automation Pilot regions can be entered.
Output Keys
Define command outputs:
"outputKeys": {
"result": {
"type": "string",
"description": "The result",
"sensitive": false
}
}
Composite Commands
Commands with configuration orchestrate multiple steps:
"configuration": {
"values": [], // Pre-computed values from input references
"output": {}, // Final output mapping
"executors": [], // Steps to execute
"listeners": [] // Event handlers
}
Input References (values)
Load reusable credentials or metadata at execution time. Three forms of inputKey:
"values": [
{
"alias": "regionData",
"valueFrom": {
"inputReference": "metadata-sapcp:CfRegionData:1",
"inputKey": "$(.execution.input.region)"
}
},
{
"alias": "fullInputObject",
"valueFrom": {
"inputReference": "mycatalog->>:MyCredentials:1",
"inputKey": null
}
},
{
"alias": "ServiceAccountPassword",
"valueFrom": {
"inputReference": "mycatalog->>:MyCredentials:1",
"inputKey": "password"
}
}
]
- Expression (
"$(.execution.input.region)") — dynamically selects which key to load based on input null— loads the entire input reference as an object (access its fields via$(.alias.fieldName))- Plain string (
"password") — loads a single named key directly
Executors
Each executor runs a command with mapped inputs.
{
"execute": "http-sapcp:HttpRequest:1",
"alias": "getResource",
"input": {
"url": "$(.regionData.cfApiUrl)/v3/apps",
"method": "GET",
"user": "$(.execution.input.user)",
"password": "$(.execution.input.password)",
"tokenUrl": "$(.regionData.uaaTokenUrl)",
"clientId": "cf",
"timeout": "20"
},
"description": null,
"progressMessage": null,
"initialDelay": null,
"pause": null,
"when": null,
"validate": null,
"autoRetry": null,
"repeat": null,
"errorMessages": [],
"dryRun": null
}
Dynamic Expressions
Expressions use jq 1.6 syntax wrapped in $(). Access data with:
.execution.input.key- Input values.stepAlias.output.key- Previous step outputs.regionData.field- Values from input references$- Global scope (use inside pipes)
See [references/expressions.md](references/expressions.md) for the full expression reference — patterns, string/array/object operations, type conversions, and utilities.
IMPORTANT: Expression Complexity Limits
Expressions have a complexity limit. If you get "Expression contains too many elements" error, break complex object construction into intermediate Void steps:
Too complex — avoid this:
"output": {
"counts": "$({\"a\": .x, \"b\": .y, \"c\": .z, \"d\": .w, \"e\": .v, ...})"
}
Use a Void step instead:
"executors": [
{
"execute": "utils-sapcp:Void:1",
"alias": "buildCounts",
"input": {
"message": "{\"a\": $(.x), \"b\": $(.y), \"c\": $(.z)}"
}
}
],
"output": {
"counts": "$(.buildCounts.output.message | toObject)"
}
Rule of thumb: If building an object with more than 5-6 fields from different step outputs, use a Void step.
Conditional Execution (when)
Skip steps based on conditions:
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.execution.input.skipStep)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["false"]
}
]
}
]
}
Operators: EQUALS, NOT_EQUALS, CONTAINS, NOT_CONTAINS, STARTS_WITH, ENDS_WITH
Validation
Assert conditions on step output:
"validate": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.status)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["200", "201"]
}
]
}
]
}
Auto Retry
Retry on transient failures. Valid logic values: FIXED (constant delay) or INCREMENTAL (increasing delay).
"autoRetry": {
"maxCount": 3,
"delay": "5s",
"logic": "FIXED",
"applyOnValidation": false,
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$([408, 429, 500, 502, 503, 504, -1] | filter(. == $.step.output.status) | length)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["1"]
}
]
}
]
}
}
Repeat (Polling)
Poll until condition is met:
"repeat": {
"maxCount": 100,
"delay": "15s",
"failOnMaxCount": true,
"until": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.body | toObject.state)",
"operator": "EQUALS",
"semantic": "OR",
"values": ["COMPLETE"]
}
]
}
]
}
}
Error Messages
Provide custom error messages:
"errorMessages": [
{
"message": "Operation failed: $(.step.output.body | toObject.error)",
"when": {
"semantic": "OR",
"conditions": [
{
"semantic": "OR",
"cases": [
{
"expression": "$(.step.output.status)",
"operator": "NOT_EQUALS",
"semantic": "OR",
"values": ["200"]
}
]
}
]
}
}
]
Development Workflow
- Identify requirements - Inputs, outputs, steps needed
- Find reference commands - Use catalog-explorer skill or check
references/catalogs.md - Design the flow - Map out executors and data transformations
- Write expressions - Use jq syntax for data manipulation
- Add error handling - validate, autoRetry, errorMessages
- Test incrementally - Verify each step works (use
feature:dryRuntag)
Mandatory Description Patterns
⚠️ When creating commands, use these exact description patterns for standard parameters. No variations or creative rewording allowed!
See the complete authoritative list in [references/description-patterns.md](references/description-patterns.md).
Examples
Example 1: HTTP health check command
User: "Create a command that checks if an API endpoint is healthy"
- Create a composite command with one
http-sapcp:HttpRequest:1executor - Add
urlandexpectedStatusas input keys - Add
validateto assert response status matches expected - Add
autoRetryfor transient failures (429, 500, 502, 503, 504) - Include
dryRunwith a mock 200 response - Use
>>suffix for the catalog
Example 2: Batch processing with ForEach
User: "Build a command that processes a list of items in parallel batches of 5"
- Create a main composite command with an
itemsinput key (array type) - Create a sub-command for processing a single item
- Use
utils-sapcp:ForEach:2(neverForEach:1) withbatchSize: "5" - Pass items via the
inputsparameter - Include
dryRunon both the ForEach executor and the sub-command executors
Example 3: Trigger-and-poll workflow
User: "Create a command that starts an async operation and polls until it completes"
- Create a composite command with a trigger step (
HttpRequestPATCH/POST) - Add a polling step with
repeatconfiguration - Set
repeat.untilto check for completion states (e.g.,succeeded,failed) - Set
repeat.maxCountandrepeat.delayfor timeout protection - Set
failOnMaxCount: trueso the command fails if polling exceeds the limit - Extract operation ID from the trigger response and pass to the poll URL
Troubleshooting
Error: "Expression contains too many elements" Cause: A single expression builds an object with too many fields (>5-6 from different step outputs). Solution: Break into intermediate utils-sapcp:Void:1 steps to build partial objects, then combine.
Error: Command references ForEach:1 Cause: Using the deprecated version. Solution: Always use utils-sapcp:ForEach:2. Version 1 is deprecated and must never be used.
Error: Catalog suffix uses -sapcp for a generated command Cause: Applying the wrong suffix convention. Solution: Generated commands use >> suffix. Only SAP-provided built-in commands use -sapcp.
Error: Missing dryRun field on an executor Cause: Executor defined without a dryRun field. Solution: Always include "dryRun": null unless mock output propagation through a chain is needed.
Additional Resources
Reference Files
For detailed patterns and complete expression reference:
references/expressions.md- Complete dynamic expressions guidereferences/patterns.md- Common command patternsreferences/catalogs.md- Available commands by catalogreferences/supported-data-manipulation-expressions.pdf- Official SAP documentation
Example Files
Working command examples in examples/ (note PascalCase naming):
CreateResourceWithServiceKey.command.json- POST with service key / clientCert auth, autoRetry, errorMessages, and conditional initialDelayGetResourceWithRetry.command.json- HTTP GET with OAuth, retry, validate, and errorMessagesWaitForOperation.command.json- Polling pattern with repeatProcessAppsBatch.command.json- Batch processing with ForEach
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: SAP
- Source: SAP/automation-pilot-agent-skills
- License: Apache-2.0
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.