LeanIX Automation Assistant
Comprehensive help for LeanIX Run Script automations: create, debug, design, optimize, and learn.
CRITICAL: API Access
All LeanIX API calls use MCP tools. No shell commands, no token exchange, no curl.
- Authentication is handled internally by the MCP server
- No bearer tokens need to be managed in the skill workflow
- No
.mcp.jsonparsing is required — MCP handles credentials automatically
Anti-Patterns (Don't Do This)
| Pattern | Why It Fails |
|---|---|
function main(data) or function main(context) |
main() takes zero parameters. data and context are injected globals at runtime. |
function main(){}; export { main } (re-export) or export default function main() |
The save-time validator requires direct export: export function main(), export async function main(), or export const main = .... |
async function main() with no await inside |
require-await lint rule blocks save. Drop async if no await. |
Top-level await outside main() |
Rejected by TS1308. await only works inside the async main() body. |
import statements |
The runner blocks module imports and remote code fetches; rejected by TS2307. Only fetch, data, context, and standard JS built-ins are available. |
TS-style type annotations in .js (e.g. function f(x: string)) |
Rejected by TS8010. |
Calling Deno, setTimeout, setInterval, eval, Function, WebAssembly |
These globals are deleted at runtime. ReferenceError at execute time. |
Relying on console.log for visible output |
Captured to stdout (returned in execution result), but NOT shown prominently in the Automations UI. For visible failure, throw new Error(...). |
relApplicationToITComponent without wrapper |
Must use ... on Application { relApplicationToITComponent { } } |
Mutations without currentRev tracking |
Revision conflict errors |
| No idempotency check | Infinite loop when automation re-triggers itself |
Using name for ITComponent matching |
Use displayName to include Provider prefix |
| Returning relations/subscriptions | Return object can't update these - use GraphQL |
| Using a technical/service identity as ownerId | Shows "null null" in UI — use set_owner_to_current_user=True (on update_automation only) or a real human user's ID |
Guessing API endpoints (e.g., /scripts/{id}, /technicalUsers) |
Use MCP tools — they handle endpoints internally |
Setting creatorId on template update |
Immutable; silently ignored (API returns 200 but no change) |
| Investigating UUIDs before comparing with working automations | Slow; comparison-first debugging is faster |
| Re-fetching templates without caching | Redundant MCP calls; cache list_automations() result in context for the session |
Building the secret key dynamically ("default_" + "auto...") |
Reference the secret key literally as "default_automations_secret" so it resolves. Dynamic construction means the secret is not attached — context.secrets ends up empty. |
LIFECYCLE_PHASE_CHANGE trigger combined with IGNORE_TECHNICAL_USERS condition |
Service rejects: technical-user condition is not allowed on job-type triggers. |
Multiple actions with startsAfter: null (or zero such actions) |
Exactly one first action is allowed per template. All others must chain via startsAfter. |
Inline script: "..." field on an action payload |
Service strips it. Always create scripts separately via POST /scripts and reference by scriptId. |
Using console.warn / console.info / console.debug for diagnostics |
Not captured by the runner; output is silently dropped. Only console.log and console.error are captured (to stdout / stderr). |
Mutating data or context in place |
The runner process is reused across executions and globals are restored between runs. Treat both as read-only. |
Quick Reference
Trigger limitations:
Relation is removed- The removed relation's other-end fact sheet id IS available atdata.metadata.triggerData.previousRelatedFactSheetId(id only; best-effort/undocumented path — not part of the vendor's author API, may change). GraphQL-fetch by that id to read the removed fact sheet's fields. Reconciliation (or triggering on the target fact sheet) is optional, not required merely to identify the removed relationLifecycle state reached- Nightly onlyCompletion score changed- Fires on almost any edit
Common patterns:
// Safe edge/node access
const items = (fs.rel?.edges || []).map(e => e?.node).filter(Boolean);
// Idempotency check
if (newValue === currentValue) return {};
// Revision tracking
currentRev = mutJson?.data?.updateFactSheet?.factSheet?.rev ?? currentRev;
Greeting
When this skill is invoked, display this welcome message:
SAP LeanIX Automation Assistant
Welcome! I help you build, debug, and optimize LeanIX automations—using built-in actions when possible, scripts only when needed.
Reference Files (Progressive Disclosure)
Load these files only when needed for specific workflow steps:
| File | Load When | Contains |
|---|---|---|
references/API-REFERENCE.md |
Deploying automations (Step 9) | API endpoints, DTOs, action types |
references/TEMPLATES.md |
Generating scripts (Step 7) | Ready-to-use script templates |
references/LEANIX-MODEL.md |
Understanding capabilities | Fact sheet types, relations, triggers |
references/NAMING-CONVENTION.md |
Analyzing/standardizing automations | Naming convention, categories |
references/WORKSPACE-ANALYSIS.md |
Analyzing workspace automations | Full audit workflow, report format |
references/MANAGE-AUTOMATIONS.md |
Managing existing automations | Transfer, enable/disable, troubleshoot, bulk update |
MCP-SETUP.md |
Setting up MCP connection | MCP server configuration |
Workflow
Step 0: Determine Intent
When invoked, use a two-tier AskUserQuestion flow to determine the user's intent:
IMPORTANT: AskUserQuestion supports 2-4 options only. Never pass more than 4 options. If more choices exist, group them or use a follow-up question.
Question 1: What type of help do you need?
| Option | Description |
|---|---|
| Create or build | New automation, convert Azure Function |
| Debug or fix | Diagnose failing script, refactor existing |
| Understand or learn | Learn what automations can do |
| Manage | Audit workspace, transfer ownership, enable/disable, bulk update, troubleshoot |
Question 2 (based on selection):
| If selected | Follow-up options |
|---|---|
| Create or build | "Create new automation" or "Convert Azure Function" |
| Debug or fix | "Debug failing script" or "Refactor/optimize script" |
| Understand or learn | Skip follow-up → go directly to [Understand Capabilities] Workflow |
| Manage | "Analyze workspace automations", "Transfer ownership", "Enable/disable/troubleshoot", "Bulk update" |
Branch to the appropriate workflow based on final response.
Step 0.1: Verify Automations Toolset
Run immediately after determining intent (before any other MCP call):
Call mcp__leanix__list_automations(). This is a lightweight check that confirms the automations toolset is active.
If the call succeeds: Continue to the next step silently (no message needed).
If mcp__leanix__list_automations is not directly available (PTD may be enabled): Before concluding the toolset is missing, try the PTD proxy path — call_tool(tool_name='list_automations', arguments={}), or search_mcp_tools('automations') then call_tool. If that succeeds, PTD is working; continue silently.
If both the direct call and the PTD proxy path fail:
The automations toolset is optional and hidden by default. Display this message to the user:
Automation tools are not available. Your MCP connection is missing the
automationstoolset.Fix: Add
?toolsets=inventory,automationsto your MCP server URL.Claude Code (OAuth):
claude mcp remove leanix claude mcp add --transport http leanix "https://mcp.leanix.net/services/mcp-server/v1/mcp?toolsets=inventory,automations"Claude Code (.mcp.json): Change the URL to:
https://{SUBDOMAIN}.leanix.net/services/mcp-server/v1/mcp?toolsets=inventory,automationsAfter updating, restart Claude Code and re-invoke
/automations-toolkit.
Then stop the workflow — do not continue without automation tools, as deployment will fail.
If MCP is not connected at all (no mcp__leanix__* tools available):
No LeanIX MCP connection detected. This skill requires the LeanIX MCP server.
Quickest setup:
claude mcp add --transport http leanix "https://mcp.leanix.net/services/mcp-server/v1/mcp?toolsets=inventory,automations"See MCP Setup for full instructions.
Step 0.5: Schema Reference Check (Optional)
After the user selects a workflow, optionally verify schema awareness:
Run: mcp__leanix__get_automation_schema()
This returns the live trigger, condition, and action type reference from the API. Compare against references/API-REFERENCE.md if discrepancies are suspected.
Key principle: Never block the user. Schema checking is informational only.
[Create New Automation] Workflow
Step 1: Workspace Context (Automatic)
MCP handles authentication and workspace connection automatically. No credential extraction or token exchange needed.
Verify connection:
- Call
mcp__leanix__get_overview()to confirm workspace access and get basic statistics (deprecated-but-working — still directly callable; may not appear in tool-discovery catalogs)
Get owner ID (when needed for deployment):
- Use
mcp__leanix__search_users(email=...)to find a specific user's UUID - Or use
set_owner_to_current_user=Trueonupdate_automation(this flag exists ONLY onupdate_automation) to assign the authenticated user as owner. Forcreate_automation, omitowner_id(it defaults to the authenticated user) or pass an explicitowner_idUUID
Display: Connected to LeanIX workspace - Ready for automatic deployment.
Step 2: Understand the Goal
Ask using AskUserQuestion:
"What do you want this automation to accomplish?"
| Option | Likely Approach |
|---|---|
| Add/remove tags | Built-in |
| Set field value | Built-in |
| Change quality state | Built-in |
| Add subscription | Built-in |
| Create to-do/approval | Built-in |
| Send notification | Built-in |
| Multiple simple actions | Built-in |
| Tag based on relations | Script |
| Sync subscriptions | Script |
| Update relation attributes | Script |
| Calculate/aggregate | Script |
| Field from relations | Script |
| Custom conditional logic | Script |
| Other | Varies |
Step 2.5: Evaluate Action Strategy
Decision Tree:
1. Does goal require reading data from OTHER fact sheets? → Script required
2. Does goal require modifying OTHER fact sheets? → Script required
3. Does goal require conditional logic based on related data? → Script required
4. Can goal be achieved with built-in actions? → Proceed to Step 3
5. Otherwise → Skip to Step 4 (Trigger Strategy for scripts)
Built-in Actions Available:
| Action | What It Does |
|---|---|
ADD_TAG / REMOVE_TAG |
Add/remove tag |
SET_FIELD |
Set single-select field |
SET_FIELD (fieldType: QUALITY_SEAL) |
Set quality seal |
ADD_SUBSCRIPTION / SET_SUBSCRIPTION |
Manage subscribers |
CREATE_ACTION_ITEM / CREATE_APPROVAL |
Create to-dos/approvals |
SEND_EMAIL_V2 / SEND_USER_WEBHOOK |
Send notifications |
If built-in actions suffice → Step 3 If script needed → Step 4
Step 3: Configure Actions (Built-in Only)
Use AskUserQuestion with multiSelect to select actions, then gather parameters for each.
Email placeholders use triple braces: {{{factsheet.displayName}}}, {{{link.factsheet}}}
Action ordering: Use startsAfter to chain actions. For approvals, use onResolution: "ACCEPTED" or "REJECTED".
See API Reference for detailed action configuration.
After configuring → Select trigger → Step 7.5 (Deploy)
Step 4: Recommend Trigger Strategy
Goal → Trigger Mapping (Common Patterns):
| Goal | # Automations | Triggers |
|---|---|---|
| Sync subscriptions | 4 | Sub added, Sub removed, Rel added [source], Rel removed [target] |
| Tag based on relations | 2 | Relation added, Relation removed |
| Update field from relations | 2-3 | Rel added, Rel removed, Field changed [optional] |
| Initialize new FS | 1 | Fact sheet is created |
| Validate and block | 1-2 | Field changed OR Completion score changed |
Key insight: For the "Relation is removed" trigger, the removed relation's other-end fact sheet id IS delivered at data.metadata.triggerData.previousRelatedFactSheetId (note data.trigger itself does not exist — the trigger context lives under data.metadata.triggerData). Only the id string is provided, not the removed fact sheet's fields, so GraphQL-fetch by that id to read them. This is a best-effort/undocumented path (discoverable by inspecting the injected data; it may change). Reconciliation (or triggering on the target fact sheet) is a robust option for reading full fields but is not required merely to identify the removed relation.
See LeanIX Model for complete trigger reference.
Step 5: Gather Remaining Details
Collect:
- Fact Sheet Type(s)
- Tag IDs, role IDs, field names (use MCP to discover)
- Business logic rules (tie-breaker, edge cases)
- Whether GraphQL API calls are needed
Step 6: Find Similar Examples
Search examples/INDEX.md for patterns:
tagging/- Tag manipulationsubscription-*/- Subscription syncrelation-*/- Relation-based logicinitiative-management/- Multi-relation aggregation
Step 7: Generate Script
Critical Rules Checklist:
-
mainis exported directly (not via re-export or default export) -
maintakes zero parameters (dataandcontextare injected globals) - If
mainisasync, it contains at least oneawait - No
importstatements — onlyfetch,data,context, JS built-ins - Wrap relations in type fragments:
... on Application { relApplicationTo... } - Track
currentRevafter each mutation - Include idempotency checks
- Use
throw new Error(...)for visible failures (notconsole.log— captured to stdout, not surfaced in UI)
Return Object Limitations: Can update description, name, tags, lifecycle, custom fields. Cannot update relations, subscriptions, or other fact sheets (use GraphQL).
See Templates for script templates.
Step 8: Provide Automation Setup
Document the configuration for manual setup if needed.
Step 7.5: Deploy Action-Only Automation
When no script needed, create automation directly via POST /templates. Requires name, description, factSheetType, ownerId, trigger, conditions, actions, and active: false.
See API Reference for action payload examples and complete DTO structure.
Step 8.5: Pre-Deployment Checklist
Before deploying, verify ALL items:
| # | Check | How to Verify |
|---|---|---|
| 1 | ownerId is a real human user | Use mcp__leanix__search_users(email=...) and verify display name |
| 2 | ownerId is NOT a technical/service identity | Verify it resolves to a human name (not "? ?" or "null null") |
| 3 | Script endpoint is correct | Create: mcp__leanix__create_automation_script. Read: mcp__leanix__get_automation_script |
| 4 | For updates, PATCH /templates/{id} only toggles active |
PATCH accepts { "active": boolean } only; use PUT with the full body for any other change (MCP update_automation handles this internally) |
| 5 | No creatorId in request body |
Immutable field, silently ignored — omit to avoid confusion |
| 6 | active: false for initial deploy |
Test before enabling |
| 7 | All required action fields present | id, actionType, startsAfter, onResolution |
| 8 | No template literals in script | Use string concat for API deployment (backticks fail via API) |
See API Reference — Ownership for details.
Step 9: Deploy Automation (Script Path)
Two MCP calls:
mcp__leanix__create_automation_script(name="Script Name", code=SCRIPT_CODE)— Create script, returns script IDmcp__leanix__create_automation(name="Automation Name", template_json=TEMPLATE_JSON)— Create automation referencing script ID
Before deploying: Call mcp__leanix__get_automation_schema() to validate payload against live schema
Success: Report the automation name and ID. The user can find it in LeanIX Admin → Automations.
See API Reference for deployment details and error patterns.
Step 9.5: Verify Deployment
After deployment, verify the automation works:
Check deployment succeeded
- Confirm automation appears in LeanIX Admin → Automations
- Verify trigger and conditions are configured correctly
Test if possible
- If safe to test: trigger the automation manually on a test fact sheet with
mcp__leanix__trigger_automation(template_id, entity_ids=[…]). Confirm first — it is side-effecting and NOT idempotent (re-running re-runs the actions), so state what will run on how many fact sheets and get an OK. Also confirm the automation isactive(an inactive one returns 202 but produces no runs). - The 202 means QUEUED, not executed — do NOT report success off it. Poll
mcp__leanix__list_automation_runs(automation_ids=[id], fact_sheet_ids=[submitted], run_after=[just before trigger])with backoff until the matched run count is stable across two consecutive polls, thenmcp__leanix__get_automation_run(run_id)for per-action detail. Report per fact sheet by NAME. - A submitted fact sheet may produce no run for two silent reasons: automation inactive, or condition not met (e.g.
WITH_TAGS). A manual run skips the trigger event entirely — you do NOT need to reproduce the triggering change — so it jumps straight to conditions + actions. Nothing stuck ever shows asIN_PROGRESS; zero rows in every state means it never ran, not that it's queued. - Verify expected outcome matches design
- If safe to test: trigger the automation manually on a test fact sheet with
Review for edge cases Before marking complete, consider:
- What happens if the automation triggers on itself? (idempotency)
- What if related fact sheets don't exist?
- What if values are already set correctly?
- Can this be simpler?
Ask: "Would you like me to review edge cases before we finalize?"
Step 10: Ask for Refinements
Offer:
- Add edge case handling
- Add companion automation (for remove triggers)
- Generate additional scripts for multi-trigger scenarios
[Debug Failing Script] Workflow
Step 1: Collect Information
Request: script code, trigger config, observed behavior, error messages.
If the automation is already deployed, pull its run history first — it's the fastest source of truth for "what actually happened":
mcp__leanix__list_automation_runs(automation_ids=[id], states=["FAILED"])— find failed runsmcp__leanix__get_automation_run(run_id)— per-action outcomes and the error message on the failing action
A deployed automation with an active: true status but zero runs on fact sheets that should have triggered it points at a trigger/condition mismatch rather than a script bug.
Step 2: Automated Diagnostic Checks
| Check | Issue | Fix |
|---|---|---|
| Export form | main not exported, re-export, default export |
Use export function main() or export async function main() |
Parameters on main |
function main(data) |
Drop the parameter — injected as globals |
async without await |
async main with no await inside |
Drop async, or add an await |
| Imports | Any import statement |
Remove — the runner blocks module imports and remote code fetches |
| Inline fragments | Relations without type wrapper | Wrap in ... on Application { } |
| Revision tracking | No currentRev updates |
Track after each mutation |
| Idempotency | No early return | Add if (newValue === currentValue) return {} |
| Error handling | No json?.errors check |
Add error checks |
| Bearer token | Wrong path | Use context?.secrets?.["default_automations_secret"]?.value?.bearerToken |
| Visible failure | Relying on console.log |
console.log is captured to stdout, not shown in UI. Use throw new Error(...) |
Step 3: Provide Diagnosis
Generate diagnostic report with issues found and recommended fixes.
Step 4: Offer Corrected Script
Ask if user wants fully corrected script generated.
[Understand Capabilities] Workflow
Can automate:
- Update fields, tags on triggering fact sheet
- Query/update other fact sheets via GraphQL
- Manage subscriptions, relations, relation attributes
- Send webhooks, emails, create to-dos
Cannot automate:
- Scheduled triggers (use external schedulers)
- Access to "before" state
- Bulk operations on unrelated fact sheets
- User archive events
See LeanIX Model for detailed capabilities.
[Refactor/Optimize Script] Workflow
- Collect current script and desired improvements
- Check for: unnecessary API calls, missing idempotency, inefficient loops, missing error handling
- Provide refactored version with explanations
[Convert Azure Function] Workflow
Key conversions:
| Python | LeanIX Run Script |
|---|---|
import requests/json/logging |
Remove — no import allowed |
logging.info/error |
Remove or replace with throw new Error(...) for visible failures (console.log is captured to stdout, not surfaced in UI) |
os.environ["LEANIX_API_TOKEN"] |
context?.secrets?.["default_automations_secret"]?.value?.bearerToken |
requests.post() |
await fetch() |
For scheduled functions: Convert to event-driven, or keep external scheduler.
External Documentation
references/CRITICAL-RULES.md- Critical rules, common errors, key patternsreferences/TEMPLATES.md- Script templates & GraphQL patternsreferences/LEANIX-MODEL.md- Triggers, actions, capabilitiesreferences/API-REFERENCE.md- API endpoints & deploymentexamples/INDEX.md- Production scripts
[Analyze Workspace Automations] Workflow
Audit existing automations, update descriptions, and standardize naming conventions.
Speed: Load references/WORKSPACE-ANALYSIS.md + references/NAMING-CONVENTION.md + call mcp__leanix__get_overview() (deprecated-but-working — still directly callable; may not appear in tool-discovery catalogs) + mcp__leanix__list_automations() all in one parallel message to avoid sequential round-trips.
See Workspace Analysis for the full workflow.
[Manage Existing Automations] Workflow
Transfer ownership, enable/disable, troubleshoot, or bulk update existing automations.
See Manage Automations for the full workflow.