generate-cds-analytical-query — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited generate-cds-analytical-query (Agent Skill) and scored it 96/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 1 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 1 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Generate an analytical CDS query — a transient projection view with provider contract analytical_query — on top of an existing analytical cube (@Analytics.dataCategory: #CUBE).
This skill replicates SAP Joule's "CDS Analytical Query Generation" capability by combining ARC-1 (SAP system access) with mcp-sap-docs (documentation & best practices). The query view is what end users consume in analytical clients (SAP Analytics Cloud, Analysis for Office, embedded analytics) — it selects measures and dimensions from a cube and arranges them on rows/columns/free axes.
provider contract analytical_query form is unavailable on 7.56 and earlier. Note that SAPManage(action="probe") rap.available is necessary but not sufficient — RAP shipped in 7.54, so a true does not confirm the 7.57 floor. To gate reliably, check the release directly, e.g. SAPQuery(sql="SELECT release FROM cvers WHERE component = 'SAP_BASIS'") and require >= 757; otherwise be ready for the provider-contract syntax error to surface at activation (Step 5) and stop there.generate-analytics-star-schema skill first to build the cube + dimensions, then come back here.| Setting | Default | Rationale |
|---|---|---|
| Object type | DDLS | Analytical queries are CDS data definitions |
| Package | $TMP | Fast prototyping; ask before a transportable package |
| Authorization | #NOT_ALLOWED | Mandatory on analytical queries — any other value fails activation |
| Axis layout | Dimensions → #ROWS, measures → #COLUMNS | Sensible default grid; user can rearrange |
| ATC | No | Only run if user asks about code quality |
The user provides a cube name (e.g., ZI_Sales_Cube) or a business description ("revenue by region and month"). Only the cube is strictly required; if the user gives a description, find the cube via Step 1.
Optionally:
ZC_SalesQuery; must be ≤ 28 characters — the analytical engine prepends 2C to the runtime name)$TMP)SAPSearch(query="<keyword>*", searchType="object", objectType="DDLS")Pick the candidate whose name/description matches an analytical cube. If multiple match, list them and ask.
SAPRead(type="DDLS", name="<cube>")Verify the source contains @Analytics.dataCategory: #CUBE (or @Analytics: { dataCategory: #CUBE ... }). If it does NOT, stop: an analytical query can only project on a cube or a dimension. Suggest generate-analytics-star-schema to build a cube first.
SAPRead(type="DDLS", name="<cube>", include="elements")This returns every field with key markers and types. Use it to classify each element:
@Aggregation.default (e.g. #SUM) in the cube source@Semantics.amount.currencyCode / @Semantics.quantity.unitOfMeasureGround the generation in current docs — do NOT rely on memory for annotation names:
search("CDS analytical query provider contract analytical_query transient view")
search("AnalyticsDetails.query.axis ROWS COLUMNS FREE")Cite the doc IDs you used in your final summary. Key facts the docs confirm (7.58):
define transient view entity <name> provider contract analytical_query as projection on <cube>@AccessControl.authorizationCheck: #NOT_ALLOWED is mandatory (queries can't be read via ABAP SQL → no DCL allowed; the cube's access control applies)transient keyword is mandatory (no HANA SQL view is generated)KEY in a query view@Aggregation.default: #FORMULAPick the template that matches the request.
@EndUserText.label: 'Sales Analytical Query'
@AccessControl.authorizationCheck: #NOT_ALLOWED
define transient view entity ZC_SalesQuery
provider contract analytical_query
as projection on ZI_Sales_Cube
{
@AnalyticsDetails.query.axis: #ROWS
RegionId,
@AnalyticsDetails.query.axis: #FREE
CalendarYear,
@AnalyticsDetails.query.axis: #COLUMNS
@Semantics.amount.currencyCode: 'CurrencyCode'
SalesAmount,
CurrencyCode
}@EndUserText.label: 'Sales KPI Query'
@AccessControl.authorizationCheck: #NOT_ALLOWED
define transient view entity ZC_SalesKpiQuery
provider contract analytical_query
as projection on ZI_Sales_Cube
{
@AnalyticsDetails.query.axis: #ROWS
RegionId,
// restricted measure: selection-related CASE — WHEN must reference a DIMENSION
@Semantics.amount.currencyCode: 'CurrencyCode'
case when CalendarYear = '2025' then SalesAmount else null end as SalesAmount2025,
// calculated measure: formula CASE — needs @Aggregation.default: #FORMULA
@Aggregation.default: #FORMULA
case when SalesAmount is initial then abap.int8'0' else abap.int8'1' end as HasSales,
CurrencyCode
}@EndUserText.label: 'Sales Query (parameterized)'
@AccessControl.authorizationCheck: #NOT_ALLOWED
define transient view entity ZC_SalesParamQuery
provider contract analytical_query
with parameters
p_year : abap.numc(4)
as projection on ZI_Sales_Cube
{
@AnalyticsDetails.query.axis: #ROWS
RegionId,
@Semantics.amount.currencyCode: 'CurrencyCode'
SalesAmount,
CurrencyCode
}
where CalendarYear = $parameters.p_yearComposition rules (enforce before writing):
... as SalesAmount2025) that need not exist in the cube.@Semantics.amount.currencyCode: '<field>' and that currency field must also be projected.@Semantics.quantity.unitOfMeasure: '<field>' and that unit field must be projected.Show the user the composed DDL and the axis layout (which fields are rows / columns / free) before writing. On confirmation:
SAPWrite(action="create", type="DDLS", name="<query>", source="<ddl>", package="$TMP")For a transportable package, pass package="<pkg>" and transport="<TR>" (the user must supply the TR; create one with SAPTransport if needed).
SAPActivate(name="<query>", type="DDLS")If activation fails, surface the diagnostics verbatim and let the user re-prompt. Do NOT silently retry. Common failures:
@AccessControl.authorizationCheck not #NOT_ALLOWED → fix the annotation@Semantics.* annotation + project the currency/unit fieldSAPRead(type="DDLS", name="<query>", include="elements")Confirm the query is live and the projected elements appear. Report success with the runtime name (2C<query>).
| Error | Cause | Fix |
|---|---|---|
| Projection target is not a cube | Tried to project on a plain view | Build a cube first via generate-analytics-star-schema |
provider contract syntax error | SAP_BASIS < 7.57 | Stop — analytical_query unsupported on this release |
| Authorization check error on activate | Used a value other than #NOT_ALLOWED | Set @AccessControl.authorizationCheck: #NOT_ALLOWED |
| Element does not exist | Element name typo vs cube | Re-read the cube elements (Step 1c) |
| Name too long | Query name > 28 chars | Shorten it |
generate-analytics-star-schema first — the two skills chain: star-schema produces the cube, this skill projects on it.generate-rap-service instead.search() first.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.