Back to Blog
SAP

SAP CAP September 2026 Release: AI Agents, GA MCP Adapter and What It Means for Your BTP Projects

October 3, 202612 min read

The September 2026 release of the SAP Cloud Application Programming Model (CAP) is one of the most significant in recent memory. AI is no longer a side topic: CAP now treats agents, MCP and embeddings as first-class runtime features. Alongside that come useful improvements to the CDS language, both runtimes, the tooling and several widely used plugins.

This release ships with @sap/cds 10.1.1+, @sap/cds-dk 10.1.2+, @sap/cds-compiler 7.1.0+, @sap/cds-mtxs 4.1.0+ and CAP Java 5.1.0+. Below is our practical summary of what's new and what it means for teams building on SAP BTP.

CAP Native AI: Agents Become a CAP Concept

CAP-level agents (Gamma)

The headline feature: you can now annotate a CAP service with @agent to turn it into an AI agent that runs a ReAct (Reasoning and Acting) loop over your service's own entities and actions. Agents are exposed through the Agent-to-Agent (A2A) protocol, so other agents can discover and call them, and they can be described with AGENTS.md and SKILL.md files. A built-in, experimental chat preview lets you talk to your agent during local development.

Both runtimes are supported, but CAP Java is still at alpha with a narrower feature set than Node.js. Teams that want to experiment today should start on Node.js.

Here's what that looks like for a procurement assistant. Suppliers are read-only for the agent, while approving a purchase requisition is marked @agent.hitl (human in the loop), so the agent has to get a user's confirmation before it runs:

cds
// srv/procurement-agent/service.cds
using { procurement as db } from '../../db/schema';

@agent
service ProcurementAgentService {

  @description: 'Approved suppliers with rating and payment terms'
  @readonly entity Suppliers as projection on db.Suppliers;

  @description: 'Open purchase requisitions waiting for a decision'
  @readonly entity Requisitions as projection on db.Requisitions
    where status = 'OPEN';

  @description: 'Approve a requisition and release it to purchasing'
  @agent.hitl
  action approveRequisition(ID : UUID, comment : String) returns Requisitions;
}

The agent needs a language model. On SAP BTP this comes from SAP AI Core through the new llm service binding. In local development, kind: auto picks up credentials from a locally installed Claude or OpenCode client:

json
{
  "cds": {
    "requires": {
      "llm": {
        "kind": "aicore",
        "model": "anthropic--claude-4.6-sonnet"
      }
    }
  }
}
sh
npm add @cap-js/agents
cds watch
# Chat preview: http://localhost:4004/a2a/procurement-agent/preview/

The @description texts matter: the agent reads them to decide which entity or action to use. Treat them like API documentation written for a colleague, not like code comments.

Our take: this changes the cost of adding AI to SAP extensions. Instead of building a separate agent stack and wiring it to OData APIs, your CDS model, authorisations and business logic become the agent's tools directly.

MCP adapter is now generally available

The Model Context Protocol (MCP) adapter, in beta since June 2026, is now GA for Node.js and Java. It lets AI assistants and agent frameworks call your CAP services as MCP tools. Changes since the beta:

  • The describe tool output is leaner, which saves tokens
  • The query tool now takes CQL instead of CQN, which language models produce far more reliably
  • The action tool has been renamed from call_action to call. Update any prompts or clients that reference the old name
  • Node.js no longer exposes the generated .drafts and .texts entities, composition targets are served correctly, and CodeList targets are skipped
  • In Java, enable it by adding the cds-adapter-mcp dependency to srv/pom.xml

Exposing an existing service takes one annotation. In this example the application's regular ProcurementService is published as an MCP server under a shorter path, and each tool name gets the service name as a prefix, so tools from several CAP apps don't collide in the same assistant:

cds
// srv/mcp.cds
using { ProcurementService } from './procurement-service';

annotate ProcurementService with @mcp: 'procurement';   // served at /mcp/procurement
yaml
# .cdsrc.yaml
cds:
  mcp:
    prefix: "{service.name}-"

Because the query tool now takes CQL, an assistant asking *"Which Indian suppliers have open requisitions over ₹5 lakh?"* produces a readable query that you can log, review and test:

cql
SELECT from Suppliers {
  name, rating,
  requisitions { number, amount, status }
} where country.code = 'IN'
  and exists requisitions[status = 'OPEN' and amount > 500000]

One caveat from SAP's own guide: the MCP adapter has no built-in rate limiting, audit logging or protection against prompt injection. Put it behind the same authorisation as the rest of your service (@requires, @restrict), and don't use it to proxy SAP application APIs, because SAP's API policy doesn't allow that.

A new XTravels sample shows several MCP services and CAP-level agents working together in a travel-booking scenario. It's a good reference architecture to study before you design your own.

Native vector embeddings for Node.js (Gamma)

CAP Java has had embeddings since April. Node.js now catches up through the @cap-js/ai plugin, with support for the built-in Vector type on both SAP HANA and SQLite. For local development, SQLite generates embeddings with ONNX models, and the default model downloads automatically on first run, so no configuration is needed:

sh
npm add -D @cap-js/ai @cap-js/sqlite @huggingface/hub @huggingface/tokenizers onnxruntime-node

A typical use is an IT or customer support desk: when a new ticket comes in, show the agent how similar tickets were resolved before. The embedding is declared once in the model as a stored calculated element, so it's recalculated whenever the subject or description changes:

cds
// db/support.cds
using { support } from './schema';

extend support.Tickets with {
  embedding : Vector = vector_embedding(
    'Subject: ' || subject || '. Description: ' || description,
    'DOCUMENT', 'SAP_GXY.20250407'
  ) stored;
}
cds
// srv/support-service.cds
using { support } from '../db/schema';

service SupportService {
  entity Tickets as projection on support.Tickets excluding { embedding };
  function similarTickets(text : String) returns many Tickets;
}

The search text is embedded with the 'QUERY' type, and only tickets above a similarity threshold are returned:

js
// srv/support-service.js
const cds = require('@sap/cds')

module.exports = class SupportService extends cds.ApplicationService {
  init() {
    // Query the db entity: the service projection hides the embedding column
    const { Tickets } = cds.entities('support')

    this.on('similarTickets', async (req) => {
      const { text } = req.data
      return SELECT.from(Tickets)
        .columns('ID', 'subject', 'status', 'resolution')
        .where`cosine_similarity(embedding,
          vector_embedding(${text}, 'QUERY', 'SAP_GXY.20250407')) > 0.75`
        .limit(5)
    })

    return super.init()
  }
}

Two practical points. First, keep the vector out of your OData projections (excluding { embedding }): it's large and useless to a UI. Second, use the same model name for the DOCUMENT and QUERY embeddings, otherwise the similarity scores are meaningless. This makes semantic search and retrieval-augmented generation (RAG) over your business data possible inside a normal CAP project, with the same model locally and on HANA Cloud in production.

AI-friendly documentation

Capire, the CAP documentation, now has a dedicated Native AI section. Every page is also available as Markdown (append .md to the URL or send Accept: text/markdown), with a Markdown sitemap at /sitemap.md. That's handy if your developers use AI coding assistants.

CDS Language Improvements

Three smaller changes that remove everyday friction. All three examples below use the same order-management model.

Boolean expressions in view columns. You can now write a condition directly as a column. The compiler wraps it in CASE WHEN for you, which SAP HANA requires:

cds
entity OrderOverview as select from Orders {
  key ID,
  customer.name as customerName,
  netAmount,
  netAmount > 100000    as isHighValue,
  status = 'BLOCKED'    as isBlocked
};

Before, each flag needed a hand-written case when … then true else false end, which made views like this noisy to read.

Extending views that already filter. You can now add conditions to a view that already has a where clause. This is the key piece for SaaS and partner extensibility: a customer-specific extension can narrow a delivered view without copying it:

cds
// Delivered with the application
entity OpenOrders as select from Orders where status = 'OPEN';

// Added in a tenant extension. Effective filter:
//   (status = 'OPEN') and (salesOrg = 'IN01')
extend OpenOrders with where salesOrg = 'IN01';

Keys inside nested projections. When you flatten an association with the assoc.{ … } shorthand, you can now mark a key inside it, which is handy for views that need a composite key:

cds
entity OrderItemsWithProduct as projection on OrderItems {
  key ID,
  product.{
    key ID as productID,
    name,
    category
  },
  quantity
};

Runtime Highlights

Ranked fuzzy search on SAP HANA

In both Node.js and Java, fuzzy search results on SAP HANA are now sorted by relevance score automatically. If the client sends its own $orderby, that still takes precedence. No code changes are needed, so search-heavy Fiori apps simply get better results after upgrading.

CAP Node.js: destinations without the Cloud SDK (Beta)

CAP Node.js can now resolve SAP BTP destinations itself, without @sap-cloud-sdk/http-client, for proxy type Internet with NoAuthentication, BasicAuthentication or the newly supported OAuth2ClientCredentials. On-premise destinations and other authentication types still need the Cloud SDK. For simple outbound integrations this reduces dependency weight.

CAP Java: JSON batch, smarter expands and faster builds

  • JSON batch requests (Beta): initial support for the OData V4.01 JSON batch format alongside multipart/mixed. It's easier to inspect with OWASP ModSecurity and works better with ABAP-based clients. Inter-request dependsOn isn't supported yet
  • Expands with excludes: the query builder can now exclude specific association elements, for example expand().excluding(a -> a.age())
  • Hierarchy sorting respects @cds.default.order and falls back to the projection's order by
  • Tooling: cds-maven-plugin build goals are now thread-safe for parallel builds (-T, Maven Daemon mvnd), watch mode has a fast mode (-Dfast), and static implementations of event context interfaces are now generated

Excludes are most useful for keeping a sensitive or heavy field out of an expand without listing every column you *do* want. For example, a customer-facing order history that must never return the customer's credit limit:

java
CqnSelect orderHistory = Select.from(ORDERS)
    .columns(
        o -> o.orderNumber(),
        o -> o.orderDate(),
        o -> o.netAmount(),
        o -> o.customer().expand().excluding(c -> c.creditLimit()))
    .where(o -> o.customer_ID().eq(customerId))
    .orderBy(o -> o.orderDate().desc());

New fields added to Customers later are included automatically, while the excluded field stays excluded. That's safer than a hand-maintained allow-list that someone forgets to update.

For faster local builds on multi-module projects:

sh
mvn -T 1C clean install          # parallel build, now safe with cds-maven-plugin
mvn cds:watch -Dfast             # faster watch mode

Tooling

  • Faster shell completions: cds completions are now pre-generated at install time. Set them up with cds add completion (use --shell for bash, zsh, fish, gitbash or ps)
  • IntelliJ plugin v3 adds CDS 10 language support and four new formatter options: annotationInNewLine, asProjectionInNewLine, conditionInNewLine and maxAlignmentWhitespace

Plugin Updates You Should Plan For

Attachments: major versions with breaking changes

Both runtimes now support a single attachment defined directly as a field of type Attachment, flattened onto the parent entity. Until now, attachments were always a composition of many. That fits "documents for this order", but it's awkward for "exactly one file per record". Supplier onboarding is a good example, where each supplier needs one tax certificate and one signed contract:

cds
using { cuid, managed } from '@sap/cds/common';
using { Attachment, Attachments } from '@cap-js/attachments';

entity Suppliers : cuid, managed {
  name           : String(120);
  gstin          : String(15);
  taxCertificate : Attachment;    // exactly one file, stored on the supplier
  signedContract : Attachment;
  otherDocuments : Composition of many Attachments;  // the classic list
}

In CAP Java the type comes from com.sap.cds/cds-feature-attachments instead; the model is otherwise the same.

@cap-js/attachments v4 (Node.js) is a major release:

  • Cloud storage SDKs are now optional peer dependencies, so install only the provider you use
  • The config key attachments.outbox is renamed to outboxed
  • Content-Disposition: attachment is now the default, and inline display is opt-in
  • axios has been replaced with the native Fetch API, and a race condition in concurrent rescans is fixed

cds-feature-attachments (Java) now requires Java 21. It also defaults to Content-Disposition: attachment, adds a standalone MalwareScannerService, and enables AES256 server-side encryption on AWS S3 by default.

Notifications: declarative and multi-runtime

@cap-js/notifications v1 (Node.js) lets you define notifications with @notification annotations in CDS instead of JSON configuration. It adds Mustache e-mail templates, a batch API, i18n support via {i18n>key} and dynamic priorities. A new cds-feature-notifications (alpha) brings the same model to CAP Java, sending to SAP Alert Notification Service through the outbox and supporting Fiori Launchpad deep links.

Continuing the procurement example: when a requisition is approved, the requester gets a Fiori Launchpad notification and an e-mail. Large amounts are flagged as high priority, and the notification opens the requisition in Fiori:

cds
// srv/procurement-notifications.cds
using { ProcurementService } from './procurement-service';

extend service ProcurementService with {

  @description: 'Sent to the requester when a requisition is approved'
  @notification: {
    title        : 'Requisition {{number}} approved',
    publicTitle  : 'Requisition approved',
    subtitle     : '{{approver}} approved {{amount}} {{currency}}',
    groupedTitle : 'Procurement',
    email: {
      subject : 'Approved: requisition {{number}}',
      html    : './templates/requisition-approved.html'
    }
  }
  @notification.priority: (amount > 500000 ? #HIGH : #LOW)
  @Common.SemanticObject       : 'PurchaseRequisition'
  @Common.SemanticObjectAction : 'display'
  event RequisitionApproved {
    number    : String;
    amount    : Decimal;
    currency  : String;
    approver  : String;
  }
}

Sending it is just an event emit from the action handler. The plugin turns it into a notification and (by default) delivers it through the outbox, so a failure in the notification service doesn't roll back the approval:

js
// srv/procurement-service.js
this.on('approveRequisition', async (req) => {
  const { ID, comment } = req.data
  const { Requisitions } = this.entities

  await UPDATE(Requisitions, ID).with({ status: 'APPROVED', approverComment: comment })
  const r = await SELECT.one.from(Requisitions, ID)

  await this.emit('RequisitionApproved', {
    number: r.number,
    amount: r.amount,
    currency: r.currency_code,
    approver: req.user.id,
    recipients: [r.createdBy]
  })
  return r
})

ORD and n8n

The ORD plugin (@cap-js/ord@1.5.0) now covers OData, REST, GraphQL, MCP and INA, and generates resources in parallel during cds build. The Java ORD plugin is now open source as cds-feature-ord. A new alpha n8n plugin (@cap-js/n8n / cds-feature-n8n) can trigger n8n workflows directly from CAP applications.

Upgrade Checklist

  1. Update core packages to the versions listed above and run your test suite
  2. Rename MCP tool references from call_action to call in prompts and clients
  3. Attachments (Node.js): install your storage provider SDK explicitly and rename attachments.outbox to outboxed
  4. Attachments (Java): confirm your build and runtime are on Java 21
  5. Check file downloads: with Content-Disposition: attachment as the new default, any UI that previews files inline needs the opt-in
  6. Review search UIs: fuzzy search results on HANA are now relevance-ranked, so check any tests that assert result order

How Intergratex Can Help

The September 2026 release makes CAP a strong foundation for AI-enabled SAP extensions: agents that work on your real business objects, MCP access for AI assistants, and semantic search on HANA Cloud. Intergratex helps organisations upgrade existing CAP applications safely, design agent and MCP architectures with proper security, and build new BTP extensions using these capabilities. Talk to our team to plan your CAP roadmap.

Source: CAP September 2026 release notes