From sap-gen-code
Validates ABAP CALL FUNCTION statements in ABAP source code against the actual function module parameter definitions retrieved via RFC. Checks parameter names (unknown, missing mandatory, wrong section) and data types (compatibility of the passed variable's type with the FM parameter type). For structure parameters, validates every component field by field. Uses RPY_FUNCTIONMODULE_READ_NEW to get FM definitions, DDIF_FIELDINFO_GET for structure/table type details, and DDIF_DTEL_GET for data element details. Prerequisites: SAP GUI installed (provides SAP.Functions 32-bit COM object).
How this skill is triggered — by the user, by Claude, or both
Slash command
/sap-gen-code:sap-check-fm <path-to-abap-source-file><path-to-abap-source-file>The summary Claude sees in its skill listing — used to decide when to auto-load this skill
You validate ABAP `CALL FUNCTION` statements in a source file against live SAP function module definitions via RFC. You check parameter names and data types, and report unknown parameters, missing mandatory parameters, wrong-section parameters, and type compatibility issues.
You validate ABAP CALL FUNCTION statements in a source file against live SAP function module definitions via RFC. You check parameter names and data types, and report unknown parameters, missing mandatory parameters, wrong-section parameters, and type compatibility issues.
Task: $ARGUMENTS
Settings reads/writes follow <SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md — merge settings.local.json over settings.json per-key on the .value field; writes always go to settings.local.json. Resolve cross-plugin paths: 3 levels up from <SKILL_DIR>, then into sap-dev-core\settings.json and (if present) sap-dev-core\settings.local.json. Read work_dir, custom_url.
| Setting | Default if blank |
|---|---|
work_dir | C:\sap_dev_work |
custom_url | {work_dir}\custom |
Set {WORK_TEMP} = {work_dir}\temp
Ensure the temp directory exists:
cmd /c if not exist "{WORK_TEMP}" mkdir "{WORK_TEMP}"
Start a structured log run. State file: {WORK_TEMP}\sap_check_fm_run.json. Best-effort.
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action start -StateFile "{WORK_TEMP}\sap_check_fm_run.json" -Skill sap-check-fm -ParamsJson "{\"abap_file\":\"<ABAP_FILE>\"}"
Extract from $ARGUMENTS:
Verify the source file exists:
powershell -Command "if (Test-Path 'THE_FILE_PATH') { 'EXISTS' } else { 'NOT FOUND' }"
If the file does not exist, tell the user and stop.
Resolve the object naming rules path (custom override → default):
powershell -Command "if (Test-Path '{custom_url}\sap_object_naming_rules.tsv') { 'CUSTOM' } else { 'DEFAULT' }"
Parse the ABAP source file:
^\s*FUNCTION\s+(\w+)\s*\. line → FUNCTION_MODULE candidate.FUNCTION-POOL\s+(\w+) line, if present → FUNCTION_GROUP candidate.
When only an FM include is provided (no FUNCTION-POOL header), ask the user
for the target function group name and validate that.For each candidate, call:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_check_object_name.ps1" -ObjectType <TYPE> -ObjectName <NAME> -CustomUrl "{custom_url}"
Exit code 1 → append a finding to {WORK_TEMP}\checkfm_result.txt with code
OBJECT_NAMING and the validator's stdout line. Exit 0 is silent. Exit 2
(no rule for that type / rules file missing) is logged once and skipped.
| File | Token | Purpose |
|---|---|---|
sap-dev-core/settings.json | (config) | SAP connection parameters |
sap-dev-core/shared/tables/sap_object_naming_rules.tsv | (read by helper) | FM / FG naming patterns. Custom override: {custom_url}\sap_object_naming_rules.tsv |
sap-dev-core/shared/scripts/sap_check_object_name.ps1 | (helper) | Shared name validator invoked in Step 1.5 |
Read SAP connection parameters from the merged sap-dev-core settings (per <SAP_DEV_CORE_SHARED_DIR>/rules/settings_lookup.md). The sap_password value typically comes from settings.local.json and is a dpapi:... blob — decrypt via sap_dpapi.ps1 before use.
Resolve path: go 3 levels up from <SKILL_DIR> (skill → skills/ → plugin dir → plugins root),
then into sap-dev-core\settings.json.
| Setting key | Maps to | Example |
|---|---|---|
sap_application_server | %%SAP_SERVER%% | 10.0.0.1 |
sap_system_number | %%SAP_SYSNR%% | 00 |
sap_client | %%SAP_CLIENT%% | 100 |
sap_user | %%SAP_USER%% | DEVELOPER |
sap_password | %%SAP_PASSWORD%% | (masked) |
sap_language | %%SAP_LANGUAGE%% | EN |
If settings are not configured, ask the user to provide the values and suggest they configure sap-dev-core settings.json for future use.
/sap-gen-abap (Step 1.5e) and /sap-docs-check-process (Step 3.5) both
populate _struct_signatures.txt in the work folder alongside the ABAP
source. When this file is present, the operator already has live DDIF
field lists for every BAPI structure type the FM signatures reference —
and the per-system disk cache at {work_dir}\cache\struct_signatures\
is warm.
Detect the cache and log it so the operator sees the chain at work:
powershell -Command "$s = Split-Path 'THE_ABAP_FILE'; $f = Join-Path $s '_struct_signatures.txt'; if (Test-Path $f) { 'STRUCT_CACHE: ' + $f + ' (' + ((Get-Content $f).Count) + ' rows)' } else { 'STRUCT_CACHE: not present (will fall back to direct DDIF_FIELDINFO_GET via ddic helper)' }"
The Step 3 ddic helper (sap_rfc_lookup_ddic.ps1) already RFCs live for
type resolution; the work-folder cache is informational here. A future
enhancement would feed _struct_signatures.txt directly into the VBS to
skip RFC roundtrips for cache hits — tracked but not yet implemented.
These two checks are complementary, not redundant:
| Reference shape in source | Caught by sap-check-fm | Caught by sap-check-abap Step 3.5 |
|---|---|---|
CALL FUNCTION 'BAPI_X' EXPORTING param = ls_x. (parameter name + struct type) | ✓ (deep field-by-field check inside Step 4) | partial (validates ls_x-field refs elsewhere in source) |
ls_x-field = ... (any struct field assignment / read in source) | ✗ (only inspects CALL FUNCTION contexts) | ✓ (consumes _struct_signatures.txt) |
AUTHORITY-CHECK OBJECT '<X>' ID '<F>' … | ✗ | ✓ (consumes _authz_signatures.txt) |
SELECT field FROM <table> | ✗ | ✓ (existing SQL field validation) |
Inline VALUE bapi_mara( field = … ) constructor with bad field | ✗ (constructor parsing is out of scope) | ✗ (regex-only scanner doesn't follow VALUE typed constructors) |
Recommended order: run /sap-check-fm <file> then /sap-check-abap <file>.
The FM check catches CALL FUNCTION mistakes early; the ABAP check catches
the broader field-reference / AUTHORITY-CHECK class. Both write to
different result files (.check_fm.tsv vs .check.tsv) so neither
overwrites the other.
The inline VALUE-constructor gap remains — neither checker walks typed constructors today. Generator rule §22 documents the trap; ATC would catch wrong-field constructors at deploy time. Worth flagging for a future check enhancement.
The VBScript template is at ./references/sap_check_fm.vbs (relative to this skill directory).
It delegates RFC calls to two PowerShell sidecar helpers (NCo 3.1 based), both
shared across multiple skills:
<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_rfc_lookup_fm.ps1 — fetches FM parameter signatures (with per-system disk cache)<SAP_DEV_CORE_SHARED_DIR>/scripts/sap_rfc_lookup_ddic.ps1 — resolves DDIC typesThe FM helper template is at <SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_fm.ps1.
Resolve cache directory + system ID (see "Step 0 — Resolve Work Directory"):
{FM_CACHE_DIR} = userConfig.fm_cache_dir, or if blank: {work_dir}\cache\fm_signatures{SYSTEM_ID} = {sap_application_server}_{sap_system_number}_{sap_client} (e.g. saphost.example.com_00_100)$h = Get-Content '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_fm.ps1' -Raw
$h = $h -replace '%%SAP_SERVER%%', 'THE_SERVER'
$h = $h -replace '%%SAP_SYSNR%%', 'THE_SYSNR'
$h = $h -replace '%%SAP_CLIENT%%', 'THE_CLIENT'
$h = $h -replace '%%SAP_USER%%', 'THE_USER'
$h = $h -replace '%%SAP_PASSWORD%%', 'THE_PASSWORD'
$h = $h -replace '%%SAP_LANGUAGE%%', 'THE_LANGUAGE'
$h = $h -replace '%%RFC_LIB_PS1%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1'
$h = $h -replace '%%REQUEST_FILE%%', '{WORK_TEMP}\sap_checkfm_fm_names.txt'
$h = $h -replace '%%RESULT_FILE%%', '{WORK_TEMP}\sap_checkfm_fm_result.tsv'
$h = $h -replace '%%CACHE_DIR%%', '{FM_CACHE_DIR}'
$h = $h -replace '%%SYSTEM_ID%%', '{SYSTEM_ID}'
$h = $h -replace '%%TTL_STD_DAYS%%', '30' # or userConfig.fm_cache_ttl_std_days
$h = $h -replace '%%TTL_Z_DAYS%%', '1' # or userConfig.fm_cache_ttl_z_days
$h = $h -replace '%%REFRESH_CACHE%%', 'false' # 'true' to bypass cache for this run
Set-Content '{WORK_TEMP}\sap_checkfm_fm_helper.ps1' $h -Encoding UTF8
The cache layer means: if a customer ran sap-gen-abap (which now also pre-fetches signatures) and sap-check-fm against the same SAP system, the second skill reuses the first skill's cached signatures — no redundant RFC roundtrips.
$d = Get-Content '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lookup_ddic.ps1' -Raw
$d = $d -replace '%%SAP_SERVER%%', 'THE_SERVER'
$d = $d -replace '%%SAP_SYSNR%%', 'THE_SYSNR'
$d = $d -replace '%%SAP_CLIENT%%', 'THE_CLIENT'
$d = $d -replace '%%SAP_USER%%', 'THE_USER'
$d = $d -replace '%%SAP_PASSWORD%%', 'THE_PASSWORD'
$d = $d -replace '%%SAP_LANGUAGE%%', 'THE_LANGUAGE'
$d = $d -replace '%%RFC_LIB_PS1%%', '<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_rfc_lib.ps1'
$d = $d -replace '%%REQUEST_FILE%%', '{WORK_TEMP}\sap_checkfm_ddic_request.txt'
$d = $d -replace '%%RESULT_FILE%%', '{WORK_TEMP}\sap_checkfm_ddic_result.tsv'
Set-Content '{WORK_TEMP}\sap_checkfm_ddic_helper.ps1' $d -Encoding UTF8
Write {WORK_TEMP}\sap_checkfm_run.ps1:
$content = Get-Content '<SKILL_DIR>\references\sap_check_fm.vbs' -Raw
$content = $content -replace '%%SAP_SERVER%%', 'THE_SERVER'
$content = $content -replace '%%SAP_SYSNR%%', 'THE_SYSNR'
$content = $content -replace '%%SAP_CLIENT%%', 'THE_CLIENT'
$content = $content -replace '%%SAP_USER%%', 'THE_USER'
$content = $content -replace '%%SAP_PASSWORD%%', 'THE_PASSWORD'
$content = $content -replace '%%SAP_LANGUAGE%%', 'THE_LANGUAGE'
$content = $content -replace '%%ABAP_FILE%%', 'THE_ABAP_FILE'
$content = $content -replace '%%RESULT_FILE%%', '{WORK_TEMP}\checkfm_result.txt'
$content = $content -replace '%%FM_HELPER_PS1%%', '{WORK_TEMP}\sap_checkfm_fm_helper.ps1'
$content = $content -replace '%%FM_NAMES_FILE%%', '{WORK_TEMP}\sap_checkfm_fm_names.txt'
$content = $content -replace '%%FM_RESULT_FILE%%', '{WORK_TEMP}\sap_checkfm_fm_result.tsv'
$content = $content -replace '%%DDIC_HELPER_PS1%%', '{WORK_TEMP}\sap_checkfm_ddic_helper.ps1'
$content = $content -replace '%%DDIC_REQUEST_FILE%%', '{WORK_TEMP}\sap_checkfm_ddic_request.txt'
$content = $content -replace '%%DDIC_RESULT_FILE%%', '{WORK_TEMP}\sap_checkfm_ddic_result.tsv'
Set-Content '{WORK_TEMP}\sap_checkfm_run.vbs' $content -Encoding Unicode
Write-Host 'Done'
Replace all THE_* placeholders and <SKILL_DIR> / <SAP_DEV_CORE_SHARED_DIR> / <FM_HELPER_TEMPLATE> with absolute paths.
Run:
powershell -ExecutionPolicy Bypass -File "{WORK_TEMP}\sap_checkfm_run.ps1"
Run via standard cscript (the helpers internally invoke 32-bit PowerShell):
cscript.exe //NoLogo {WORK_TEMP}\sap_checkfm_run.vbs
Show the full script output as it runs. Then read and display {WORK_TEMP}\checkfm_result.txt.
Delete the filled scripts (they contain plaintext credentials):
cmd /c del {WORK_TEMP}\sap_checkfm_run.vbs
cmd /c del {WORK_TEMP}\sap_checkfm_fm_helper.ps1
cmd /c del {WORK_TEMP}\sap_checkfm_ddic_helper.ps1
Read {WORK_TEMP}\checkfm_result.txt. The result file has a status line followed by one tab-delimited entry per finding.
| FM Name | Line | Issues |
|---|
Finding codes and their meaning:
| Code | Meaning |
|---|---|
PARAM_NAME_OK | Parameter name is valid and in the correct section |
UNKNOWN_PARAM | Parameter name does not exist in the FM definition |
WRONG_SECTION | Parameter exists in the FM but was placed under the wrong keyword |
MISSING_MANDATORY | A mandatory FM parameter was not passed in the CALL |
TYPE_MATCH | Passed variable type exactly matches the FM parameter type |
TYPE_COMPATIBLE | Different type names but compatible (e.g. same DATATYPE and length) |
TYPE_WARNING | Compatible but with risk — e.g. length mismatch may cause truncation |
TYPE_INCOMPATIBLE | Types are not compatible |
TYPE_UNKNOWN | Variable type could not be resolved from local declarations |
FM_NOT_FOUND | FM does not exist in SAP or RFC call failed |
OBJECT_NAMING | FM or function-group name does not match sap_object_naming_rules.tsv (Step 1.5) |
On STATUS: SUCCESS — all CALL FUNCTION statements are valid.
On STATUS: SUCCESS_WITH_ISSUES — show all issues grouped by CALL FUNCTION.
On STATUS: ERROR:
| Error message | Cause | Fix |
|---|---|---|
Cannot create SAP.Functions | SAP GUI not installed or OCX not registered | Install SAP GUI; verify wdtfuncs.ocx in SAP GUI install dir |
RFC connection failed | Wrong server/credentials | Verify server, SysNr, client, user, password |
ABAP file not found | Wrong path | Verify the file path |
No CALL FUNCTION statements found | Not an ABAP file or file is empty | Verify correct file was provided |
RPY_FUNCTIONMODULE_READ_NEW failed | FM not in SAP or missing S_RFC auth | Check FM name; check S_RFC authorization |
DDIF_FIELDINFO_GET failed | Type not in SAP or missing auth | Check type name; check S_RFC auth |
cmd /c del {WORK_TEMP}\sap_checkfm_run.ps1
Keep {WORK_TEMP}\checkfm_result.txt so the user can review it. To remove:
cmd /c del {WORK_TEMP}\checkfm_result.txt
Log the run-end record. Best-effort.
On success:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{WORK_TEMP}\sap_check_fm_run.json" -Status SUCCESS -ExitCode 0
On failure:
powershell -ExecutionPolicy Bypass -File "<SAP_DEV_CORE_SHARED_DIR>\scripts\sap_log_helper.ps1" -Action end -StateFile "{WORK_TEMP}\sap_check_fm_run.json" -Status FAILED -ExitCode 1 -ErrorClass <CLASS> -ErrorMsg "<short>"
Suggested <CLASS>: CHECK_FM_FAILED, RFC_LOGON_FAILED.
The generated .vbs file contains the SAP password in plain text and is deleted automatically after execution.
Connection parameters are stored in sap-dev-core settings.json. The password field is marked as
sensitive and masked in the Claude Code UI.
CALL FUNCTION fm_variable) — silently skipped; FM name must be a quoted literalLIKE clauses in DATA declarations — variable type marked TYPE_UNKNOWN; manual check requiredTYPE_UNKNOWNCALL FUNCTIONThis skill checks CALL FUNCTION parameter correctness, but not ABAP syntax. If the user deployed a function module via sap-se37 and the deployment reported syntax errors, those are different from this skill's findings.
When sap-se37 deploy fails with syntax errors, the output looks like:
ERROR: Syntax check found 1 error(s):
Line 5: Function Module ZHKFM_TEST004
-> The last statement is not complete (period missing).
The error grid has paired rows: the first row shows the line number and FM name, the second row shows the error description.
| Error | Cause | Fix |
|---|---|---|
Statement is not accessible | Function group has inactive version, OR source file missing FUNCTION <name>. / ENDFUNCTION. wrapper | The sap-se37 templates activate before syntax check to resolve inactive versions. If it persists, check the upload file includes the full FUNCTION include — see sap-se37 SKILL.md Step 2 |
The last statement is not complete (period missing) | Missing period at end of a statement | Add . at end of the offending line |
"X" is not defined | Undeclared variable or typo | Add DATA: declaration or fix the name |
"X" is not a type | Wrong TYPE in DATA declaration | Check SAP data element spelling |
Field "X" is unknown | Wrong structure field name | Check field name against SE11 definition |
npx claudepluginhub sapdev-ai/sap-dev --plugin sap-gen-codeSearches MemPalace before answering questions about past work, people, projects, or prior decisions. Returns verbatim stored content instead of guessing from model memory.
Fetches up-to-date documentation from Context7 for libraries and frameworks like React, Next.js, Prisma. Use for setup questions, API references, and code examples.
Guides Payload CMS config (payload.config.ts), collections, fields, hooks, access control, APIs. Debugs validation errors, security, relationships, queries, transactions, hook behavior.