该实现基于Anthropic提出的模型上下文协议(MCP)。 关于MCP的更多信息,请参阅他们的官方文档。
这是一个CAP(云应用编程)插件,它使用简单的注解从您的CAP服务自动生成模型上下文协议(MCP)服务器。 将您的CAP OData服务转换为具有最小配置的AI可访问资源、工具和提示。
模型上下文协议弥合了企业数据与AI代理之间的差距。 通过将MCP集成到您的CAP应用程序中,您可以解锁以下功能:
npm install @gavdi/cap-mcp
该插件遵循CAP的标准插件架构,并在安装后自动集成到您的CAP应用程序中。
在您的package.json中添加MCP配置:
{
"cds": {
"mcp": {
"name": "my-bookshop-mcp",
"auth": "inherit",
"wrap_entities_to_actions": false,
"wrap_entity_modes": ["query", "get"],
"instructions": "MCP服务器代理指令"
}
}
}
用@mcp注解标注您的CAP服务:
// srv/catalog-service.cds
service CatalogService {
@mcp: {
name: 'books',
description: '带有搜索和过滤功能的图书目录',
resource: ['filter', 'orderby', 'select', 'top', 'skip']
}
entity Books as projection on my.Books;
// 可选地将Books作为LLMs的工具暴露(默认配置下查询/获取已启用)
annotate CatalogService.Books with @mcp.wrap: {
tools: true,
modes: ['query','get'],
hint: '用于只读查找书籍'
};
@mcp: {
name: 'get-book-recommendations',
description: '获取个性化图书推荐',
tool: true
}
function getRecommendations(genre: String, limit: Integer) returns array of String;
}
注意:
@mcp.wrap.hint注解提供操作级别的指导,而单独元素上的@mcp.hint注解提供字段级别的描述。两者结合为AI代理提供了全面的上下文。
cds serve
MCP服务器将在以下地址可用:
http://localhost:4004/mcphttp://localhost:4004/mcp/healthnpx @modelcontextprotocol/inspector
连接到http://localhost:4004/mcp以探索生成的MCP资源、工具和提示。
此插件将您的注解CAP服务转换为一个完全功能的MCP服务器,可以被任何兼容MCP的AI客户端消费。
query、get,以及可选的create、update)公开给LLM工具使用,同时保持资源完整npm testnpm run mocknpx @modelcontextprotocol/inspectorbruno/文件夹包含针对MCP端点的HTTP请求(方便使用Bruno或其他HTTP客户端进行本地手动测试)。您可以添加tools/list和tools/call调用来练习新的包装工具。
将CAP实体转换为AI可查询的资源:
service CatalogService {
@readonly
@mcp: {
name : 'books',
description: '图书数据列表',
resource : [
'filter',
'orderby',
'select',
'skip',
'top'
]
}
entity Books as projection on my.Books;
// 启用所有OData查询选项
@mcp: {
name : 'authors',
description: '作者数据列表',
resource : true
}
entity Authors as projection on my.Authors;
// 或者您可能只想将其作为一个静态的前100条数据列表?
@mcp: {
name : 'genres',
description: '图书类型列表',
resource : []
}
entity Genres as projection on my.Genres;
}
生成的MCP资源能力:
$filter、$orderby、$top、$skip、$select当启用wrap_entities_to_actions(全局或通过@mcp.wrap.tools: true)时,您会看到如下命名的工具:
CatalogService_Books_queryCatalogService_Books_getCatalogService_Books_create(如果启用)CatalogService_Books_update(如果启用)每个工具都包括一个带有字段和OData注释的描述,以引导模型。您可以在每个实体上添加@mcp.wrap.hint以丰富LLM的描述。
示例:
// 将Books实体作为查询/获取/创建/更新工具包装(演示)
annotate CatalogService.Books with @mcp.wrap: {
tools: true,
modes: [
'query',
'get',
'create',
'update'
],
hint : '用于读写演示操作'
};
对于这些工具中的字段级别描述,请参见元素提示与@mcphint。
使用@mcp.omit注解排除特定字段,以保护敏感数据:
namespace my.bookshop;
entity Books {
key ID : Integer;
title : String;
stock : Integer;
author : Association to Authors;
secretMessage : String @mcp.omit; // 在所有MCP响应中隐藏
}
entity Users {
key ID : Integer;
username : String;
email : String;
darkestSecret : String @mcp.omit; // 永不暴露给MCP客户端
ssn : String @mcp.omit; // 保护敏感数据
lastLogin : DateTime;
}
工作原理:
@mcp.omit的字段将自动从所有MCP响应中过滤掉常见用例:
重要注意事项:
@Core.Computed一起工作,以实现全面的字段控制多个注解示例:
entity Products {
key ID : Integer;
name : String;
price : Decimal;
costPrice : Decimal @mcp.omit; // 隐藏内部定价
createdAt : DateTime @Core.Computed; // 自动生成,不可写入
updatedAt : DateTime @Core.Computed; // 自动生成,不可写入
secretNote : String @mcp.omit; // 隐藏MCP
}
将CAP函数和动作转换为可执行的AI工具:
// 服务级函数
@mcp: {
name : 'get-author',
description: '获取所需的作者',
tool : true
}
function getAuthor(input: String) returns String;
// 实体级动作
extend projection Books with actions {
@mcp: {
name : 'get-stock',
description: '从给定的书中检索库存',
tool : true
}
function getStock() returns Integer;
}
使用elicit属性在工具执行前请求用户确认或输入:
// 执行前请求用户确认
@mcp: {
name : 'book-recommendation',
description: '获取随机图书推荐',
tool : true,
elicit : ['confirm']
}
function getBookRecommendation() returns String;
// 请求用户输入参数
@mcp: {
name : 'get-author',
description: '获取所需的作者',
tool : true,
elicit : ['input']
}
function getAuthor(id: String) returns String;
// 请求输入和确认
@mcp: {
name : 'books-by-author',
description: '获取由作者制作的图书列表',
tool : true,
elicit : ['input', 'confirm']
}
function getBooksByAuthor(authorName: String) returns array of String;
注意:目前只有直接工具支持引发。包装实体不在其覆盖范围内。
引发类型:
confirm:在执行工具前请求用户确认,以是/否的形式input:提示用户提供工具参数的值['input', 'confirm']先收集参数,然后询问确认用户体验:
使用@mcp.hint注解为各个属性和参数提供上下文描述。这些提示有助于AI代理更好地理解特定字段的目的、约束和预期值。
资源实体属性
entity Books {
key ID : Integer @mcp.hint: '必须是一个系统中尚未存在的唯一数字';
title : String;
stock : Integer @mcp.hint: '当前在货架上的书籍数量';
}
数组元素
entity Authors {
key ID : Integer;
name : String @mcp.hint: '作者全名';
nominations : array of String @mcp.hint: '作者被提名的奖项';
}
函数/动作参数
@mcp: {
name : 'books-by-author',
description: '获取由作者制作的图书列表',
tool : true
}
function getBooksByAuthor(
authorName : String @mcp.hint: '您想要获取其图书的作者全名'
) returns array of String;
复杂类型字段
type TValidQuantities {
positiveOnly : Integer @mcp.hint: '仅接受正数,即不允许负值,例如-1';
};
提示会自动整合到:
没有@mcp.hint:
{
"tool": "CatalogService_Books_create",
"parameters": {
"ID": { "type": "integer" },
"stock": { "type": "integer" }
}
}
有@mcp.hint:
{
"tool": "CatalogService_Books_create",
"parameters": {
"ID": {
"type": "integer",
"description": "必须是一个系统中尚未存在的唯一数字"
},
"stock": {
"type": "integer",
"description": "当前在货架上的书籍数量"
}
}
}
具体明确:提供具体的示例和约束
@mcp.hint: '作者姓名'@mcp.hint: '作者全名(例如:“Ernest Hemingway”)'包含约束:记录验证规则和业务逻辑
@mcp.hint: '必须在0到999之间,表示库存数量'澄清外键:帮助AI代理理解关联
@mcp.hint: '对外键Authors.ID的引用'解释业务背景:添加领域特定信息
@mcp.hint: 'ISBN-13格式,用于唯一识别书籍'避免冗余:不要重复字段名和类型显而易见的内容
stock: Integer @m_