writing-skills
writing-skills
Use when creating new skills, editing existing skills, or verifying skills work before deployment
automation-pilot-mcp-server-generationCreate, configure, and deploy SAP Automation Pilot MCP server definitions and tool configurations. Use when building MCP servers, defining tools, setting up Automation Pilot MCP configs, or deploying MCP server definitions via API.
Independent GitHub snapshot · Automated classification · Original documentation governs
This skill guides the creation of MCP server definitions for SAP Automation Pilot. MCP server definitions are JSON files that expose Automation Pilot commands as MCP tools, allowing AI assistants to invoke them directly.
Every MCP server definition follows this structure:
{
"name": "kebab-case-server-name",
"enabled": true,
"instructions": "<Natural-language description for the AI>",
"mcpTools": [ ...tool objects... ]
}
| Field | Type | Description |
|---|---|---|
name | string | Kebab-case identifier (e.g., cf-app-management). Must match filename without .json. No spaces. |
enabled | boolean | Activates/deactivates the entire server |
instructions | string | Natural-language guidance for the AI on what this server does and when to use it |
mcpTools | array | Array of tool definitions (1 or more) |
Each entry in mcpTools uses exactly this schema — all fields are required:
{
"commandId": "<catalog>-<suffix>:<CommandName>:<version>",
"name": "<snake_case_tool_name>",
"enabled": true,
"inputReferences": [
"<catalog>-<<<TENANT_ID>>>:<InputName>:<version>"
],
"tags": {},
"title": "<Title Case Label>",
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true,
"readOnlyHint": false
}
| Field | Type | Description |
|---|---|---|
commandId | string | Reference to an Automation Pilot command: <catalog>-<suffix>:<CommandName>:<version> |
name | string | Snake_case identifier exposed to the MCP protocol (e.g., list_orgs, create_snapshot) |
enabled | boolean | Enable/disable individual tool (set false to include but not activate) |
inputReferences | string[] | Array of credential/input references injected at runtime. Use [] if credentials are embedded in the command. |
tags | object | Always {} (reserved for future use) |
title | string | Title Case human-readable label (e.g., "List Transport Nodes") |
destructiveHint | boolean | Signals the tool deletes or permanently alters state |
idempotentHint | boolean | Signals the tool is safe to call repeatedly with the same result |
openWorldHint | boolean | Signals the tool discovers across a landscape vs. targeting a known resource |
readOnlyHint | boolean | Signals the tool only reads data, never modifies state |
Command IDs follow the pattern: <catalog>-<suffix>:<CommandName>:<version>
| Suffix | Usage | Example |
|---|---|---|
-sapcp | SAP-provided (shared/public) commands | cf-sapcp:ListCfOrgs:1 |
-<<<TENANT_ID>>> | Tenant-specific (custom) commands | hanalm-<<<TENANT_ID>>>:GetHanaCloudInstance:1 |
The <<<TENANT_ID>>> is a placeholder substituted at deployment time with the actual tenant identifier.
| Prefix | Domain |
|---|---|
ans | Alert Notification Service |
applm | Application Lifecycle Management |
calmhm | Cloud ALM Health Monitoring |
capops | Custom operations (tenant-specific) |
cf | Cloud Foundry |
cld | Cloud Landscape Directory |
ctms | Cloud Transport Management |
hanalm | HANA Cloud Lifecycle Management |
jira | Jira Integration |
monitoring | Monitoring |
welcome | Welcome/onboarding (credential inputs) |
Input references inject pre-configured credentials into the command at runtime. Format: <catalog>-<<<TENANT_ID>>>:<InputName>:<version>
| Input Reference | Domain | Used By |
|---|---|---|
capops-<<<TENANT_ID>>>:BtpCredentials:1 | BTP / Cloud Foundry | BTP Resource Discovery, App Logs & Metrics |
capops-<<<TENANT_ID>>>:AnsCredentials:1 | Alert Notification Service | ANS Event Producer, App Logs & Metrics |
capops-<<<TENANT_ID>>>:JiraDestination:1 | Jira | Incident Management |
welcome-<<<TENANT_ID>>>:CldDefaultValues:1 | Cloud Landscape Directory | Cloud Landscape Directory |
welcome-<<<TENANT_ID>>>:CtmsCredentials:1 | Cloud Transport Management | Cloud Transport Management |
welcome-<<<TENANT_ID>>>:ServiceManager:1 | SAP Service Manager | HANA Cloud Lifecycle Management |
[] when credentials are embedded directly in the command definition (e.g., custom commands with hardcoded config)AnsCredentials and BtpCredentials)The four hint booleans form a semantic contract. Follow these rules strictly:
readOnlyHinttrue for all list_*, get_*, fetch_* operations that only read datafalse for all write, create, update, delete, start, stop operationsreadOnlyHint: true then destructiveHint MUST be falsedestructiveHinttrue ONLY for operations that delete or permanently alter state:
delete_*, remove_*)revert_to_snapshot, upgrade_instance)false for everything else (reads, creates, updates, start/stop)destructiveHint: true then readOnlyHint MUST be falseidempotentHinttrue ONLY for write operations that are safe to repeat:
update_transport_node, update_transport_route)start_instance, stop_instance, restart_instance, enable_capabilities)false for read operations (idempotent by nature but not tagged)false for creates, deletes, and non-idempotent mutationsopenWorldHinttrue for operations that discover or act across a landscape/collection (most tools)false for operations on a specific, known resource (e.g., get/create/update/delete a single node by ID)| Operation Type | readOnly | destructive | idempotent | openWorld |
|---|---|---|---|---|
| List/discovery | true | false | false | true |
| Get single resource | true | false | false | true or false |
| Create | false | false | false | false |
| Update (idempotent) | false | false | true | false |
| Update (non-idempotent) | false | false | false | false or true |
| Delete | false | true | false | false |
| Start/Stop/Restart | false | false | true | true |
| Send/Trigger event | false | false | false | true |
| Prefix | Usage |
|---|---|
list_ | List/enumerate collections |
get_ | Retrieve a single resource |
fetch_ | Retrieve data from external sources |
create_ | Create a new resource |
update_ | Modify an existing resource |
delete_ | Remove a resource |
start_ | Start/activate a resource |
stop_ | Stop/deactivate a resource |
restart_ | Restart a resource |
enable_ | Enable a feature/capability |
send_ | Send a message/event |
add_ | Add to an existing resource |
import_ | Import resources |
forward_ | Forward/promote resources |
reset_ | Reset resource state |
remove_ | Remove from a collection |
revert_ | Revert to a previous state |
upgrade_ | Upgrade version |
list_orgs not list_cloud_foundry_organizations)title field should be the Title Case equivalent of the name (e.g., list_orgs → "List Orgs")The instructions field tells the AI when and how to use this server.
Use a single sentence:
"MCP server that can be used to send events through the Alert Notification service. Use it to notify your team about operational incidents."
Use markdown with bullet points organized by capability:
"MCP server for managing SAP HANA Cloud database instances through the SAP Service Manager API.\n\nUse this server to:\n- **Instance Lifecycle**: Start, stop, restart instances\n- **Snapshots**: Create, list, delete, and revert to storage snapshots\n- **Configuration**: Update instance size and enable capabilities\n- **Upgrades**: Upgrade instances to newer versions"
\n for newlines within the JSON string valueBefore finalizing a generated MCP server definition, verify:
name matches the intended filename (without .json)enabled is set appropriately (true for active, false for draft)instructions clearly describes when to use the servercommandId follows the <catalog>-<suffix>:<Name>:<version> formatname is snake_case and starts with an appropriate verbtitle is Title Case and matches the tool name semanticallyinputReferences uses the correct credential type for the domain (or [] if embedded)tags is {} (always empty)readOnlyHint: true → destructiveHint: falsedestructiveHint: true → readOnlyHint: falseidempotentHint: true only on safe-to-repeat write operations{
"name": "ans-event-producer",
"enabled": true,
"instructions": "Use this server to send Alert Notification Service events. Call send_event when you need to publish an alert.",
"mcpTools": [
{
"commandId": "ans-sapcp:SendEvent:1",
"name": "send_event",
"enabled": true,
"inputReferences": ["ans-<<<TENANT_ID>>>:AnsCredentials:1"],
"tags": {},
"title": "Send ANS Event",
"destructiveHint": false,
"idempotentHint": false,
"openWorldHint": true,
"readOnlyHint": false
}
]
}
{
"name": "btp-resource-discovery",
"enabled": true,
"instructions": "Use this server to discover BTP resources. All tools are read-only — they list or retrieve data and never modify state.",
"mcpTools": [
{
"commandId": "cf-sapcp:ListCfOrgs:1",
"name": "list_orgs",
"enabled": true,
"inputReferences": ["cf-<<<TENANT_ID>>>:CfCredentials:1"],
"tags": {},
"title": "List CF Organizations",
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": true,
"readOnlyHint": true
},
{
"commandId": "cf-sapcp:ListCfSpaces:1",
"name": "list_spaces",
"enabled": true,
"inputReferences": ["cf-<<<TENANT_ID>>>:CfCredentials:1"],
"tags": {},
"title": "List CF Spaces",
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": true,
"readOnlyHint": true
},
{
"commandId": "cf-sapcp:ListCfApps:1",
"name": "list_apps",
"enabled": true,
"inputReferences": ["cf-<<<TENANT_ID>>>:CfCredentials:1"],
"tags": {},
"title": "List CF Applications",
"destructiveHint": false,
"idempotentHint": true,
"openWorldHint": true,
"readOnlyHint": true
}
]
}
User: "Create an MCP server for listing Cloud Foundry resources"
name, enabled: true, and clear instructionslist_orgs, list_spaces, list_appsreadOnlyHint: true, destructiveHint: false for all toolscf-sapcp catalog prefix for built-in CF commandsBtpCredentials input reference for authenticationUser: "Build an MCP server for managing HANA Cloud instances"
list_instances, get_instance, start_instance, stop_instance, restart_instance, delete_instancereadOnlyHint: true for list/get, idempotentHint: true for start/stop/restart, destructiveHint: true for deletehanalm-<<<TENANT_ID>>> catalog prefixinstructions with markdown grouping capabilities by categoryUser: "Create a minimal MCP server with one tool to send ANS alerts"
send_eventreadOnlyHint: false, destructiveHint: false, openWorldHint: trueAnsCredentials input referenceinstructionsSet the following required environment variables:
export AUTOPI_HOSTNAME="emea.autopilot.cloud.sap"
export AUTOPI_USERNAME="your-username"
export AUTOPI_PASSWORD="your-password"
For the full list of supported hostnames (emea, aus, apac, amer, ksa), see ../automation-pilot-content-management-via-api/SKILL.md → Prerequisites.
Ensure curl is available in your environment.
The API requires the GenAI permission for all write operations.
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/mcp-servers"
MCP servers have a system-generated id field (e.g. T000414R2-0000001779286543418-1-1) returned in the create/list responses. This is distinct from the human-readable name field. Always use the id for GET, update, and delete operations.
# First, find the ID from the list response
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/mcp-servers" | jq '[.[] | {name, id}]'
# Then fetch by ID
MCP_SERVER_ID="T000414R2-0000001779286543418-1-1"
curl -s -u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" "https://$AUTOPI_HOSTNAME/api/v1/mcp-servers/$MCP_SERVER_ID"
MCP_SERVER_FILE="path/to/my-server.json"
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$MCP_SERVER_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers"
Use the system-generated id (not the name) in the URL.
MCP_SERVER_ID="<id-from-list-response>"
MCP_SERVER_FILE="path/to/updated-server.json"
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$MCP_SERVER_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers/$MCP_SERVER_ID"
MCP_SERVER_ID="<id-from-list-response>"
curl -s -X DELETE \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers/$MCP_SERVER_ID"
Check for existence first — create if new, update if already deployed. Use the system-generated id returned from a previous create or list call:
MCP_SERVER_FILE="my-server.json"
MCP_SERVER_ID="<id-from-prior-create-or-list>"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers/$MCP_SERVER_ID")
if [[ "$STATUS" == "200" ]]; then
echo "Updating existing MCP server..."
curl -s -X PUT \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$MCP_SERVER_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers/$MCP_SERVER_ID"
else
echo "Creating new MCP server..."
curl -s -X POST \
-u "$AUTOPI_USERNAME:$AUTOPI_PASSWORD" \
-H "Content-Type: application/json" \
-d @"$MCP_SERVER_FILE" \
"https://$AUTOPI_HOSTNAME/api/v1/mcp-servers"
fi
Error: Duplicate tool names in server
Cause: Two tools have the same name field value.
Solution: Each tool name must be unique within a server. Use verb prefixes (list_, get_, create_) to differentiate tools that operate on the same resource type.
Error: Conflicting hint booleans
Cause: readOnlyHint: true and destructiveHint: true set on the same tool.
Solution: These are mutually exclusive — a read-only tool cannot be destructive. Refer to the Quick Reference Matrix to determine the correct combination for the operation type.
Error: Invalid commandId format
Cause: Command ID doesn't follow the <catalog>-<suffix>:<Name>:<version> pattern — missing separator, wrong suffix, or missing version.
Solution: Verify catalog prefix, suffix (-sapcp for SAP-provided, -<<<TENANT_ID>>> for custom), PascalCase command name, and integer version number.
Error: MCP server tool not activating
Cause: Individual tool enabled field set to false.
Solution: Set enabled: true on each tool that should be active. Use false only for tools intentionally included but not yet ready for use.