Build on CAP

How to build a CAP plugin

A CAP plugin is an npm package or a Maven artifact that CAP loads by itself and that composes with every other plugin in the project. What follows is how CAP documents that, and what the plugins already published do.

Guidance, not rules. Each project's own contributing guide wins. Links read 2026-09-29

01Work with an agent

The skills written for CAP teach an agent how to build with it, and the atlas is not part of what they read. The atlas's MCP server and its skill answer a different question, what exists, whether it is alive and where it stops, and carry no procedure for code, so the two sit side by side. It reads; it never opens an issue or a pull request. How the atlas serves agents

Skills indexed in CAP

READMEs on record for 9 of 9. None of them names the atlas.

Any MCP client: the endpoint

The atlas over streamable HTTP, stateless, answered from the bundled graph.

claude mcp add --transport http onedevatlas https://onedevatlas.dev/mcp

02How CAP is extended

CAP has a few deliberate seams. Work that uses them composes with other plugins and survives upgrades; work that patches around them tends not to.

Event handler classes with @Before / @On / @After

Java
EventHandler, @ServiceName, @Before, @On, @After

capire documents that CAP Java event handler methods are annotated with @Before, @On or @After to pick the phase, in classes implementing the EventHandler marker interface, with @ServiceName setting the default service; value "*" with type = ApplicationService.class registers on all application services. The Building Plugins guide states that because handlers are not tightly coupled to the request handling, a plugin can package handlers that react to common types, annotations or entity values in an external library.

javaAdapted from cap.cloud.sap ↗
@ServiceName(value = "*", type = ApplicationService.class)
public class SampleHandler implements EventHandler {

    @After
    public void handleSample(CdsReadEventContext context) {
      // any custom Java code using the event context and CQL APIs
    }
}

CAP Java plugin registration: ServiceLoader or Spring Boot auto-configuration

Java
CdsRuntimeConfiguration, META-INF/services

capire documents two ways for a plugin JAR to register its handlers: CAP Java's ServiceLoader loads every CdsRuntimeConfiguration named in META-INF/services/com.sap.cds.services.runtime.CdsRuntimeConfiguration, and a module that depends on other Spring components uses Spring Boot's AutoConfiguration instead. The guide ties the choice to dependency injection: a handler that needs another CAP service such as CqnService or PersistenceService uses the Spring approach, and an isolated one such as a validation can use the ServiceLoader.

Packages in CAP 40
javaAdapted from cap.cloud.sap ↗
public class SampleHandlerRuntimeConfiguration implements CdsRuntimeConfiguration {

  @Override
  public void eventHandlers(CdsRuntimeConfigurer configurer) {
    configurer.eventHandler(new SampleHandler());
  }
}
// listed in src/main/resources/META-INF/services/
//   com.sap.cds.services.runtime.CdsRuntimeConfiguration

CDS models shared as Maven artifacts

JavaCDS
src/main/resources/cds/<groupId>/<artifactId>, cds-maven-plugin resolve

capire documents that CDS models, CSV data and i18n files, together with Java code such as event handlers, can ship in one Maven dependency, placed under src/main/resources/cds/<groupId>/<artifactId>/ in a plain Maven Java project (not a CAP Java or Spring Boot project). The consuming project adds the dependency and the cds-maven-plugin resolve goal, then imports the models with using ... from 'groupId/artifactId'.

Packages in CAP 40
xmlAdapted from cap.cloud.sap ↗
<!-- consumer: srv/pom.xml -->
<dependency>
  <groupId>com.sap.capire</groupId>
  <artifactId>bookshop</artifactId>
  <version>1.0.0</version>
</dependency>
<!-- plus the cds-maven-plugin 'resolve' goal; then in CDS:
     using { CatalogService } from 'com.sap.capire/bookshop'; -->

Custom protocol adapters

Java
ServletAdapterFactory

capire documents that a CAP Java protocol adapter, the inbound mechanism that maps requests to CQL on an ApplicationService, comes as an adapter (usually an HttpServlet) plus a ServletAdapterFactory loaded with the same ServiceLoader approach as handlers. The guide says protocol adapters are generic and model-independent modules that should be packaged and distributed independently.

javaAdapted from cap.cloud.sap ↗
public class SampleAdapterFactory implements ServletAdapterFactory, CdsRuntimeAware {
  static final String PROTOCOL_KEY = "protocol-key";
  private CdsRuntime runtime;

  @Override
  public void setCdsRuntime(CdsRuntime runtime) { this.runtime = runtime; }

  @Override
  public Object create() { return new SampleAdapter(runtime); }
  // isEnabled(), getBasePath(), getMappings(), getServletPath()
}

Reuse aspects

CDS
aspect / entity Foo : aspectA, aspectB

capire's Aspect-Oriented Modeling guide marks separate reuse aspects as the preferred form over a central base class: each aspect (cuid, managed, temporal and the like) is defined on its own and consumers combine them with the ':' include syntax, which works as mixins rather than inheritance. A reuse package that adds fields or relationships to consumers' entities ships them as aspects, and @sap/cds/common already provides some of them.

cdsAdapted from cap.cloud.sap ↗
aspect managed {
  createdAt  : DateTime;
  createdBy  : User;
  modifiedAt : DateTime;
  modifiedBy : User;
}

// consumer
using { cuid, managed, temporal } from 'your-reuse-aspects';
entity Bar : cuid, managed, temporal {...}

Extending and annotating reused definitions

CDS
extend / annotate

capire documents that a consumer adapts definitions it obtained from a reuse package with extend (adding fields, adapting a field's length, adding associations and compositions, adding aspects) and with annotate, without editing the package. The same guide shows extend managed adding a composition that every entity using the aspect then receives, own or reused.

cdsAdapted from cap.cloud.sap ↗
using { CodeList, Currencies, Languages } from 'some-reuse-package';
extend CodeList with { descr: localized String }
extend Currencies with { symbol: String(2) }
extend Languages:locale with (length:15);

Annotations as a plugin's trigger

CDSNode.jsJava
annotate <Entity> with @<Annotation>

capire's plugin page describes the usual flow for a plugin as adding the package, adding annotations to the model, and binding the platform service, using annotate Customer with @PersonalData for Audit Logging on both runtimes. The CAP Java Building Plugins guide names handlers that react to model characteristics, common types or annotations, as the case for an event-handler plugin.

cdsAdapted from cap.cloud.sap ↗
// after: npm add @cap-js/audit-logging   (Node.js)
//   or: cds-feature-auditlog-v2 dependency  (Java)
annotate Customer with @PersonalData ...;

03Tests, checks, types and releases

Each as CAP's own documentation describes it, with the pages to read. Where a source recommends something, the recommendation is its own.

Testing (Java)

JUnitSpring Boot TestMockitoMockMvcRestTestClientH2

capire's CAP Java testing guide describes three layers: event handler unit tests with mocked dependencies (Mockito, included in spring-boot-starter-test), service layer tests that run CQN against an autowired service or emit an event context, and integration tests through the protocol adapter with Spring's MockMvc or RestTestClient. It recommends JUnit and the Spring test support, uses H2 for tests, and takes its examples from the CAP Java bookshop sample.

javaAdapted from cap.cloud.sap ↗
@SpringBootTest
public class CatalogServiceTest {
    @Autowired
    private CatalogService catalogService;
    @Test
    public void submitOrder() {
        SubmitOrderContext context = SubmitOrderContext.create();
        context.setBook("4a519e61-3c3a-4bd9-ab12-d7e0c5329933");
        context.setQuantity(2);
        catalogService.emit(context);
        assertEquals(22 - context.getQuantity(), context.getResult().getStock());
    }
}

Plugin testing across @sap/cds and CAP Java majors

cds-services-bomcds-services-apiMaven

For CAP Java plugins, capire recommends building against the current LTS version (last minor and patch of the maintenance release) of cds-services-bom so the plugin is consumable by most applications, and running integration tests with embedding applications configured for each major version (current and prior). It also recommends Java 17 bytecode for plugins and reserves the com.sap.cds groupId and package names for the CAP Java team.

Linting: CDS Lint

@sap/eslint-plugin-cdscds lintcds add lintESLint

capire documents CDS Lint as the ESLint plugin @sap/eslint-plugin-cds together with the cds lint command of @sap/cds-dk; cds add lint installs ESLint, the plugin and the configuration, including VS Code settings. capire recommends the cds lint CLI over the plain ESLint CLI because it comes preconfigured, and the rules are listed on a rules reference page.

shellAdapted from cap.cloud.sap ↗
cds add lint
cds lint

Reuse package structure and publishing

npm publishnpm packmvn packagecds export

capire's Service Composition guide says every CAP-based project can serve as a reuse package; as a provider it describes an index.cds entry point (and optional further ones), @impl with a fully qualified module path for Node.js handlers, a CdsRuntimeConfiguration or Spring auto-configuration for Java handlers, CDS/CSV/i18n files under src/main/resources/cds/<groupId>/<artifactId>/ for Maven, and a README naming what is provided and the stable entry points. It names publishing to registries such as npmjs.org, pkg.github.com or Maven Central as the preferred way to share, because consumers then get version management; npm pack or mvn package work as well.

Versioning against @sap/cds and CAP Java majors

@sap/cdscds-services-bompeerDependencies

capire's release schedule states that CAP Node.js majors follow the Node.js release schedule, with a new major when a Node.js LTS reaches end of life, that major upgrades may carry breaking changes to public APIs, and that public APIs are only those documented in capire; minor releases come monthly without breaking changes to public APIs. CAP Java majors have no fixed schedule, the previous major stays in maintenance for at most twelve months with critical fixes only, and capire recommends importing BOM poms such as cds-services-bom to keep versions consistent. capire does not state a peerDependencies rule for plugins; @cap-js/audit-logging, for example, declares "@sap/cds": ">=9" as a peer dependency in its package.json.

CI

cds add github-actionsGitHub ActionsSAP Continuous Integration and DeliveryProject Piper

capire's CI/CD guide documents cds add github-actions (shortcut cds add gha), which adds GitHub Actions workflows to test, deploy to staging on pushes to main and deploy to production on a GitHub release, as used in the capire samples. It also names SAP Continuous Integration and Delivery, with a ready-to-use pipeline for CAP Node.js, Java and MTA projects, and project Piper for more flexible pipelines.

shellAdapted from cap.cloud.sap ↗
cds add github-actions

04Using open source

Taking open source into a project, and publishing your own under an open licence, as CAP's projects and documentation describe it. Every artifact in the atlas carries the same readings, maintenance and licence among them, before it goes into a project.

Keep open-source dependencies of a plugin to a minimum

OpenSSF Scorecard

capire states that end-of-chain projects have to run vulnerability checks for every open-source package they use directly or inherit transitively from reuse packages, and that providers of reuse packages keep open-source usage to a reasonable minimum. It names keeping third-party integrations optional, as express.js does, as one approach. OpenSSF Scorecard offers automated checks for evaluating a project before depending on it, among them whether it is maintained, declares a licence, has a security policy and uses dependency-update tooling.

Software bill of materials for npm and Maven

npm sbomCycloneDX Maven plugincds-services-bom

npm documents npm sbom, which generates a Software Bill of Materials of a project's dependencies in SPDX or CycloneDX format. For Maven builds, the CycloneDX Maven plugin generates a CycloneDX SBOM aggregating all direct and transitive dependencies. On the Java side, capire describes the cds-services-bom, a bill-of-materials POM imported into dependencyManagement to keep the versions of all CAP modules in sync.

Declaring a licence: package.json, LICENSE and REUSE

SPDXREUSEreuse lint

npm documents the package.json license field as an SPDX identifier or SPDX expression, with "SEE LICENSE IN <filename>" for a custom licence. SAP's repository template, the base of every repository on github.com/SAP, ships a LICENSE (Apache 2.0 by default), a LICENSES directory and REUSE metadata. cap-js plugins follow it: @cap-js/audit-logging declares "license": "Apache-2.0" and a REUSE.toml annotating every file with its copyright text and SPDX-License-Identifier. The REUSE tutorial describes the three steps: provide licences in LICENSES/, add copyright and licence information per file, and confirm compliance with reuse lint.

tomlAdapted from github.com ↗
version = 1
SPDX-PackageName = "audit-logging"
SPDX-PackageDownloadLocation = "https://github.com/cap-js/audit-logging"

[[annotations]]
path = "**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2023 SAP SE or an SAP affiliate company and audit-logging contributors."
SPDX-License-Identifier = "Apache-2.0"

Contribution terms: Apache 2.0 and the DCO

DCO

SAP's organisation-wide CONTRIBUTING asks contributors to comply with the project licence and accept a Developer Certificate of Origin, requested automatically with the first pull request, using the Linux Foundation's standard DCO text; it also links a separate guideline for contributions made with generative AI. The CONTRIBUTING files of cap-js and cap-js-community plugins state the same rule: contributions are licensed under Apache 2.0 and contributors accept the DCO.

Community health files: code of conduct and security policy

GitHub uses default community health files from an organisation's public .github repository for any repository that has no file of that type. The cap-js, cap-java and cap-js-community organisations each keep a CODE_OF_CONDUCT.md and a SECURITY.md in their .github repository, and cap-js plugin CONTRIBUTING files point to the SAP Open Source Code of Conduct. SAP's repository template README tells maintainers to replace its placeholders and links the REUSE API for licensing details.

Where CAP plugins live and who supports them

capire's plugin page lists @cap-js-scoped plugins as created and maintained in shared ownership of CAP and other SAP development teams, and points to the cap-js-community organisation and bestofcapjs.org for community plugins. It states that plugins by external parties such as the CAP JS community are out of scope for SAP incidents. SAP's GitHub profile lists cap-js, cap-java, cap-js-community and capire as the CAP organisations.

05Security

What CAP's documentation and its projects state about security, with the pages to read. A project's SECURITY.md, where it has one, says how to report a vulnerability.

Secure by default, and what stays the application's job

capire's Product Security guide lists CAP's secure defaults: no credentials needed to protect communication, all endpoints authenticated when bound to XSUAA, isolated multitenancy, and INFO-level logging. Index page, mock users and the Java developer dashboard are development-only features that the production profile turns off, and capire warns against enabling them manually in production. It also states that authorizations and sizing need application-specific configuration, and advises checking security settings with automated integration tests.

Authorization is declared in CDS and enforced by the generic providers

capire states that CDS services have no access control by default, so authenticated users can reach all entities, and that applications must implement proper authorization. Restrictions are declared with @requires on services and @restrict privileges (grant, to, where) on entities and actions, enforced by CAP's generic service providers. A plugin-provided service that is only meant for in-process use can be annotated @protocol: 'none', so that no protocol adapter exposes it.

cdsAdapted from cap.cloud.sap ↗
entity Orders @(restrict: [
    { grant: 'READ', to: 'Auditor', where: (AuditBy = $user) }
  ]) {/*...*/}

Privileged users bypass authorization checks

In Node.js, cds.User.Privileged and the ready-made cds.User.privileged run a transaction that bypasses authorization checks when consuming a local service, and capire shows it for writing a request log from a before handler. In production, capire states that services without @restrict or @requires implicitly get @requires: 'authenticated-user', which can be switched off with cds.requires.auth.restrict_all_services: false. Java's mock users include a privileged flag whose user passes all authorization checks.

jsAdapted from cap.cloud.sap ↗
this.before('*', function (req) {
  const user = new cds.User.Privileged
  return this.tx({ user }, tx => tx.run(
    INSERT.into('RequestLog').entries({
      url: req._.req.url,
      user: req.user.id
    })
  )
})

Untrusted input: CQL, constraints, CSRF

capire states that CQL statements become prepared statements, so parameter values from user input cannot inject SQL, but injection is still possible when the query structure (target entity, columns) comes from user input, which then needs validation such as a positive list. Declarative constraints (@mandatory, @assert) express input validation in the model. For CSRF, capire states that the Application Router enforces x-csrf-token protection, so CAP services that keep no client session do not handle it themselves.

jsAdapted from cap.cloud.sap ↗
const entity = <from user input>
const column = <from user input>
validate(entity, column) // for example, by comparing with positive list
SELECT.from(entity).columns(column)

Personal data and audit logging

@cap-js/audit-logging

capire describes annotating entities and elements that hold personal data with @PersonalData (EntitySemantics, DataSubjectRole, FieldSemantics, IsPotentiallyPersonal, IsPotentiallySensitive), which drives audit logging, personal data management and retention. The @cap-js/audit-logging plugin logs changes to personal data and reads of sensitive data automatically, and brings its own default configuration as a CDS plugin package. Its AuditLogService can also be used for custom events such as security events.

cdsAdapted from cap.cloud.sap ↗
annotate my.Customers with @PersonalData : {
  DataSubjectRole : 'Customer',
  EntitySemantics : 'DataSubject'
} {
  ID           @PersonalData.FieldSemantics: 'DataSubjectID';
  email        @PersonalData.IsPotentiallyPersonal;
  creditCardNo @PersonalData.IsPotentiallySensitive;
};

CAP Java: Spring Security auto-configuration and overrides

capire states that CAP Java activates a Spring Security configuration enforcing authentication on all endpoints only when both the required Maven dependencies (via cds-starter-cloudfoundry or cds-starter-k8s) and an XSUAA or IAS binding are present. cds.security.authentication.mode (never, model-relaxed, model-strict, always) controls authentication of protocol-adapter endpoints and has no impact on authorization. An additional SecurityFilterChain with a lower @Order value can override CAP's configuration for specific endpoints, and capire warns to make sure only the intended endpoints are affected.

javaAdapted from cap.cloud.sap ↗
@Bean
@Order(1) // needs to have higher priority than CAP security config
public SecurityFilterChain appFilterChain(HttpSecurity http) throws Exception {
  return http
    .securityMatcher(AntPathRequestMatcher.antMatcher("/public/**"))
    .csrf(c -> c.disable())
    .authorizeHttpRequests(r -> r.anyRequest().permitAll())
    .build();
}

Reporting vulnerabilities in SAP-owned repositories

SAP's Open Source Security Policy, the SECURITY.md in the SAP, cap-js and cap-js-community .github repositories, asks that vulnerabilities not be reported through public GitHub issues but through the SAP Trust Center or secure@sap.com. It lists the information a report includes (repository, issue type, affected files and commit, reproduction steps, impact) and refers to SAP's Disclosure Guidelines.

06Documentation the atlas can read

The atlas records only what a project's own documentation states, and quotes the sentence it rests on. What it looks for:

The stack it serves
A package is listed as an artifact only when its own documentation names the SAP stack it is built for, such as CAP. A generic library is not, however often a project installs it.
Where it runs
Runtimes and flavors are recorded where a sentence states them, and that sentence is shown beside the value. Where the documentation says nothing, the page says it does not say.
What it needs and talks to
Services it requires and systems it connects to, with the protocol, are edges drawn only from what the documentation names.
Its properties
A capability compares its artifacts on a few declared properties. A value counts only with the sentence it comes from; a property the documentation does not mention stays open.
Releases and a licence
Readings are crawled from the repository and the registry, releases and the last push among them. A README is shown whole only under a licence that permits copying; otherwise its opening and a link.

07Tutorials

Step-by-step material from SAP and the ecosystem, linked where it lives.

08Share it

Where finished CAP work is published and found, and how it gets into the atlas.

Publish/Share with Consumers (capire)
capire names publishing to registries such as npmjs.org, pkg.github.com or Maven Central as the preferred way to share reuse packages, for version management; packages made with npm pack or mvn package work as well. cap.cloud.sap ↗
Publishing APIs (capire, Calesi)
The output of cds export is an npm or Maven package published with npm publish to any npm-compatible registry; the capire org publishes to GitHub Packages. cap.cloud.sap ↗
Share CDS Models via Maven Artifacts (capire)
How a CAP Java plugin packages models, data, i18n and handler code in one Maven dependency, and how a consumer resolves it. cap.cloud.sap ↗
CAP Plugins & Enhancements (capire)
capire's curated list of plugins that integrate SAP BTP services and SAP products, with the @cap-js-scoped ones maintained with the CAP teams; it points to the CAP JS community org and bestofcapjs.org for the broader collection. cap.cloud.sap ↗
bestofcapjs.org
Named by capire as 'a broader collection of plugins'; its package list is read from sources.json in the bestofcapjs-data repository (https://github.com/marianfoo/bestofcapjs-data/blob/main/sources.json). bestofcapjs.org ↗
CAP Community at GitHub (cap-js-community)
Community org whose default health files are mirrored from a central internal location; a template repository (cap-js-community/repository-template) provides LICENSE, REUSE and code-of-conduct files. No public guideline for proposing a new repository was found. github.com ↗
Add it to the atlas
Use Feedback, Add, or open a pull request against the atlas data. A repository URL is enough to start.
Credit
Maintainers and authors are linked to their work.
Corrections
Anyone can correct an entry, through Feedback or a pull request.