Model Context Protocol Adapter
The @cap-js/mcp plugin allows to easily expose given CAP services via the Model Context Protocol (MCP). Simply annotate a CAP service with @mcp to do so. With that it becomes accessible to AI agents and LLM-powered tools without additional implementation work.
SAP API Policy Applies!
The CAP MCP adapter is intended only for exposing custom CAP application services. It is NOT an SAP-endorsed architecture or pathway for exposing, proxying, or providing agentic access to SAP Application APIs as referred to in the SAP API Policy, section 2.2.2. -> Read section SAP API Policy below!
Note
This guide is about the MCP Adapter – for example, as provided through the @cap-js/mcp plugin – which powers domain-specific application use cases, for example, to respond to questions for CAP-based applications like "List all overstocked books".
In parallel, there's also the MCP Server plugin (@cap-js/mcp-server), which serves a different purpose, though, that is: AI-assisted development of CAP projects.
Add the MCP Plugin
In CAP Node.js Projects
Within your project root run this to add the @cap-js/mcp plugin:
npm add @cap-js/mcpIn CAP Java Projects
Add the cds-adapter-mcp dependency to your srv/pom.xml:
<dependency>
<groupId>com.sap.cds</groupId>
<artifactId>cds-adapter-mcp</artifactId>
<scope>runtime</scope>
</dependency>Declare @mcp Services
Using @mcp Annotation
Simply add the @mcp annotation to a service definition to expose it via MCP. For example, add the following to srv/cat-service.cds:
// Serve via OData, HCQL and REST
annotate CatalogService with @odata @hcql @rest;
annotate CatalogService with @mcp; Optionally specify an alternative endpoint path ...
As usual with CAP protocol annotations, you can also choose a custom path under which the MCP endpoint should be served, instead of using the default path /mcp/<service>:
annotate CatalogService with @mcp:'books'Just Another Protocol
From the perspective of a developer in a CAP-based project, @mcp is just another protocol for your services, similar to @odata, @graphql, @rest, or @hcql. The adapter takes care of the rest, with all the standard CAP features you know working out of the box also with MCP, including annotations like @cds.query.limit, etc.
Add Comments for LLMs
LLMs rely heavily on context information to produce high-quality output. Use standard CDS /**...*/ doc comments to do so, focusing on relevant hints and information about the service, entities, elements, actions, and parameters to the LLM, which aren't clear already from the given names and types in the model. In addition, information can be provided through annotations @title and @description.
For example:
/**
* This is the author entity.
* It contains information about book authors.
*/
annotate BookshopService.Authors with {
ID /** The ID of the author. */;
name /** The name of the author. */;
books /** All the books written by the author. */;
}Only for Node.js ...
Doc comments are currently supported for Node.js only. With the Java version of the MCP Adapter, only @title and @description annotations are supported.
This information is included in the output of the describe tool and can be used by agents to better understand the data model and available actions/functions.
Custom @mcp.instructions
The MCP Adapter automatically sends MCP instructions to MCP clients to help models understand how to interact with your services effectively. While these generic instructions are mostly fully sufficient, there may be cases where you want to provide additional guidance specific to your service through custom instructions.
Use the @mcp.instructions annotation on service level, entity level, or action level to specify such custom instructions. For example:
annotate CatalogService with @mcp.instructions: 'Specific hints about your servcie in general.';
annotate CatalogService.Books with @mcp.instructions: 'Specific hints about books.';The annotation also supports i18n references, e.g. {i18n>key}
Use Case-Specific Services
Frequently, you might want to create services that are tailored specifically for MCP usage. Instead of annotating an existing service with @mcp and exposing all its entities and actions, simply create a dedicated service specifically for MCP. For example, you could create a BooksService that only exposes a subset of the entities of the AdminService like that:
using { CatalogService } from './cat-service';
@agent service BookshopService {
@readonly entity Authors as projection on AdminService.Authors excluding {
createdBy, modifiedBy,
}
@readonly entity Books as projection on AdminService.Books {
ID, title, stock, price,
author,
genre.name as genre,
currency.name as currency,
}
@agent.hitl
action submitOrder ( book: Books:ID, quantity: Integer );
}=> See also: Use Case-Oriented Services in the getting started guide.
Test-drive Locally
As usual, and following the Calesi principles of "convention over configuration", you can run your CAP server locally and interact with it using the MCP protocol from common clients like OpenCode or Claude Code.
Run with cds watch
Run your CAP server locally as usual using cds watch, and note that the @mcp-annotated service gets served at an additional endpoint for the MCP protocol:
cds watchmvn cds:watch[cds] - serving CatalogService {
at: [ ..., '/mcp/browse' ],
...
}Using OpenCode, or alike
Assumed you have Claude Code or Opencode installed, start either one in a secondary terminal, for example:
opencodeInstalling Claude Code or OpenCode ...
Install OpenCode, for example via npm:
npm install -global opencode-aiInstall Claude Code, for example via Homebrew:
brew install claude-codeInstall Claude Code for VSCode:
code --install-extension anthropic.claude-code
Go ahead and interact with your CAP services using natural language and conversational style via OpenCode, entering prompts like these:
list booksorder wuthering heightsAnd answer the questions that OpenCode asks you back.

You can also run opencode web to open the OpenCode web interface, which provides a more user-friendly way to interact with your MCP servers, including features like tool inspection and query building. Here's a screenshot of a simple session:

Autowired MCP Clients
When we initially started OpenCode above, it indicated in the bottom line of the interface that there's (at least) one MCP server connected.

Enter /status in the OpenCode interface to see details, which should display the status of the connected MCP servers like this:

Autowired during development
Whenever you start your CAP application with cds watch, all served MCP endpoints are automatically registered with local MCP clients like Claude Code and OpenCode, so you can just go ahead and run queries from them without any additional configuration. This makes it super easy to test and interact with your services via MCP during development.
Click to expand the client-specific configuration files
{
"mcpServers": {
"cds:AdminService": {
"type": "http",
"url": "http://localhost:4004/mcp/admin",
"headers": {
"Authorization": "Basic YWxpY2U6"
}
},
"cds:CatalogService": {
"type": "http",
"url": "http://localhost:4004/mcp/browse",
"headers": {
"Authorization": "Basic YWxpY2U6"
}
}
},
}{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"cds:AdminService": {
"type": "remote",
"url": "http://localhost:4004/mcp/admin",
"headers": {
"Authorization": "Basic YWxpY2U6"
},
"enabled": true
},
"cds:CatalogService": {
"type": "remote",
"url": "http://localhost:4004/mcp/browse",
"headers": {
"Authorization": "Basic YWxpY2U6"
},
"enabled": true
}
}
}You can opt out of autowiring with cds.mcp.autowire: false.
Inspect Log Output
When you run queries, you can inspect the log output of your CAP server to see the incoming MCP requests and how they are processed. This can be helpful for debugging and understanding the interaction between the MCP client and your CAP services.
For example, for a list books prompt, you should see log output similar to this:
[mcp] - CatalogService describe { entities: [ 'Books' ] }
[mcp] - CatalogService query {
cql: 'SELECT ID, title, author, genre, stock, price, currency_code FROM Books'
}INFO com.sap.cds.adapter.mcp.McpServlet : Received MCP query request for entity 'Books' with select fields [ID, title, author.name, genre.name, stock, price] and limit 20Served out of the box
The adapter creates an MCP server per CAP service, hence each CAP application can expose multiple MCP servers. By default, the adapter creates three generic tools, describe, query, and call, for each MCP server, which can be used by LLMs and AI agents to interact with the service.
the following tools for each MCP server, which can be used by LLMs and AI agents to interact with the service.
Warning
Tools are meant to be used by LLMs and AI agents and do not constitute a stable API. They may change in the future based on the needs of LLMs and AI agents. For stable APIs, please use the existing CAP protocols like OData, REST, GraphQL, etc.
• describe service
This tool returns information about the entities and their elements exposed by the service. It also returns information about unbound actions and functions. If you do not provide a parameter, the tool describes all exposed entities, actions and functions. The optional parameter entity restricts the output to a single entity, the optional parameter action restricts the output to a single action/function. The tool provides an enum that lists all available entities, actions and functions.
• query entity
This tool is used to read data from the service. It expects a single parameter cql, which contains the query in CQL syntax to be executed.
CQL = SQL++ => well understood by LLMs
As common LLMs, like Claude Sonnet, are trained for SQL very well, they are quick to understand and generate CQL queries for interacting with the service.
For example, given the BookshopService as declared above that exposes Authors with its to-many association to Books , we can ask OpenCode running Opus something like this:
list authors with their written books and genresWhich it would nicely translate into the following CQL query using nested postfix projection to expand the books association, as shown in the screenshot below:
SELECT from Authors {
ID, name, books {
title, genre
}
}
• call action
This tool is used to call unbound actions or functions. The required parameter action is an enum that lists all unbound actions and functions exposed by the service. The parameters of the action or function to call can be provided via the optional parameter parameters, that must contain all required parameters of the action or function. The tool takes these parameters and calls the action or function on the service.
Generate Server Card
You can use cds compile to generate an Server Card for your MCP server:
cds compile srv/cat-service.cds --to mcpNote
A Server Card is not required for your MCP server to function, but it may be used in development and provides a standardized way to describe the server's capabilities and tools.
Inspect the Tools
You can use the MCP Inspector to inspect the tools served from CAP services:
npx @modelcontextprotocol/inspectorThe inspector should automatically open in your browser.

Register your @mcp-enabled CAP services to the inspector using the Add Servers > + Add manually menu option, and in the dialog that appears, follow these steps:
- Enter a name for your server, e.g. bookshop.
- Select streamable-http as transport type.
- Enter MCP endpoint into URL - for example,
http://localhost:4004/mcp/browse.
A screenshot is shown below:

Then connect, and switch to the Tools tab to the top of the inspector's window to inspect and try out the listed tools.
Configuration
cds.mcp.autowire
Opting Out of Autowiring
You can opt out of autowired MCP clients in development by setting the cds.mcp.autowire: false, like so in your package.json:
cds:
mcp:
autowire: false{
"cds": {
"mcp": {
"autowire": false
}
}
}Mock Authentication
Autowired MCP clients automatically add Authorization headers for the mock user alice (Node.js) or privileged (Java). If your service requires something different, you can customize the credentials via the cds.mcp.autowire configuration:
cds:
mcp:
autowire:
user: admin
password: admin{
"cds": {
"mcp": {
"autowire": {
"user": "admin",
"password": "admin"
}
}
}
}cds.mcp.prefix
Some MCP clients or harnesses might require unique tool names across all MCP servers. You can use the option cds.mcp.prefix to specify a prefix for the tool names. For example:
cds:
mcp:
prefix: {service.name}-{
"cds": {
"mcp": {
"prefix": "{service.name}-"
}
}
}With this, the tool names generated by the MCP clients will be prefixed with the specified value, with the placeholder {service.name} being replaced by the actual service's fully qualified name – e.g., CatalogService-describe.
cds.mcp.format
By default, the query tool expects queries in CQL syntax, which is a SQL-like language for querying CAP services, and best suited for most LLMs. You can change this to cds.mcp.format: cqn if to prefer to use structured CQN input instead of CQL.
cds.mcp.per_action_tool
By default, multiple actions may share the same generic call tool, which has advantages in regarding context windows by reducing the number of tools within the MCP ecosystem. Set cds.mcp.per_action_tool: true to configure that each action gets its own dedicated tool.
Current Limitations
Authorization with XSUAA
Important!
We are currently working on providing out-of-the-box support for authorization with XSUAA, with PKCE, but it is not fully implemented yet.
Query and Actions Only
The MCP tools created by the adapter are currently focused on reading data and calling unbound actions and functions only. This means that you can use MCP to query data from your CAP services, while any data changes need to be implemented via unbound actions for now.
For example, action submitOrder in the CatalogService ultimately creates an Order:
service CatalogService {
...
@requires: 'authenticated-user'
action submitOrder ( book: Books:ID, quantity: Integer );
}Future versions of the adapter may add support for data changes using CREATE, UPDATE, and DELETE operations.
Prompt Injection Attacks
Caution!
The MCP adapter does not perform any input validation or output validation to prevent prompt injection attacks. Agents can potentially be manipulated by data returned from the service to execute unintended actions. For any deployment ensure you use infrastructure and practices that mitigate prompt injection risks and connect only to trusted MCP agents (e.g., Joule).
SAP API Policy
Caution!
The adapter itself does not provide any built-in governance features: there is no automatic rate limiting, no specific audit logging of agent actions, no approval workflows for sensitive operations, and no policy enforcement layer. Before using MCP in a productive environment, put appropriate controls for example by using MCP Gateway of SAP Integration Suite or integrate with SAP Agent Gateway (not GA yet).
Caution!
The CAP MCP adapter must not be used as a gateway or proxy for SAP Application APIs. The adapter is not an SAP-endorsed architecture, data service, or service-specific pathway under section 2.2.2 of the SAP API Policy and is not an endorsed mechanism for exposing, proxying, or providing agentic access to SAP Application APIs. Any use of SAP Application APIs must be in accordance with the SAP API Policy. For SAP-endorsed patterns on agentic access to SAP Application APIs, consult the SAP Architecture Center reference architectures.