@gavdi/cap-mcp 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/annotations/constants.js +6 -2
- package/lib/annotations/parser.js +24 -10
- package/lib/annotations/utils.js +28 -2
- package/lib/mcp/factory.js +2 -1
- package/package.json +1 -1
|
@@ -25,8 +25,12 @@ exports.MCP_ANNOTATION_PROPS = {
|
|
|
25
25
|
MCP_TOOL: "@mcp.tool",
|
|
26
26
|
/** Prompt templates annotation for CAP services */
|
|
27
27
|
MCP_PROMPT: "@mcp.prompts",
|
|
28
|
-
/** Wrapper configuration for exposing entities as tools */
|
|
29
|
-
|
|
28
|
+
/** Wrapper configuration for exposing entities as tools - tools prop*/
|
|
29
|
+
MCP_WRAP_TOOLS: "@mcp.wrap.tools",
|
|
30
|
+
/** Wrapper configuration for exposing entities as tools - modes prop*/
|
|
31
|
+
MCP_WRAP_MODES: "@mcp.wrap.modes",
|
|
32
|
+
/** Wrapper configuration for exposing entities as tools - hint prop*/
|
|
33
|
+
MCP_WRAP_HINT: "@mcp.wrap.hint",
|
|
30
34
|
/** Elicited user input annotation for tools in CAP services */
|
|
31
35
|
MCP_ELICIT: "@mcp.elicit",
|
|
32
36
|
};
|
|
@@ -22,7 +22,7 @@ function parseDefinitions(model) {
|
|
|
22
22
|
const def = value;
|
|
23
23
|
const parsedAnnotations = parseAnnotations(def);
|
|
24
24
|
const { serviceName, target } = (0, utils_1.splitDefinitionName)(key);
|
|
25
|
-
parseBoundOperations(serviceName, target, def, result); // Mutates result map with bound operations
|
|
25
|
+
parseBoundOperations(model, serviceName, target, def, result); // Mutates result map with bound operations
|
|
26
26
|
if (!parsedAnnotations || !(0, utils_1.containsRequiredAnnotations)(parsedAnnotations)) {
|
|
27
27
|
continue; // This check must occur here, since we do want the bound operations even if the parent is not annotated
|
|
28
28
|
}
|
|
@@ -42,13 +42,13 @@ function parseDefinitions(model) {
|
|
|
42
42
|
result.set(resourceAnnotation.target, resourceAnnotation);
|
|
43
43
|
continue;
|
|
44
44
|
case "function":
|
|
45
|
-
const functionAnnotation = constructToolAnnotation(serviceName, target, verifiedAnnotations);
|
|
45
|
+
const functionAnnotation = constructToolAnnotation(model, serviceName, target, verifiedAnnotations);
|
|
46
46
|
if (!functionAnnotation)
|
|
47
47
|
continue;
|
|
48
48
|
result.set(functionAnnotation.target, functionAnnotation);
|
|
49
49
|
continue;
|
|
50
50
|
case "action":
|
|
51
|
-
const actionAnnotation = constructToolAnnotation(serviceName, target, verifiedAnnotations);
|
|
51
|
+
const actionAnnotation = constructToolAnnotation(model, serviceName, target, verifiedAnnotations);
|
|
52
52
|
if (!actionAnnotation)
|
|
53
53
|
continue;
|
|
54
54
|
result.set(actionAnnotation.target, actionAnnotation);
|
|
@@ -100,9 +100,23 @@ function parseAnnotations(definition) {
|
|
|
100
100
|
case constants_1.MCP_ANNOTATION_PROPS.MCP_PROMPT:
|
|
101
101
|
annotations.prompts = v;
|
|
102
102
|
continue;
|
|
103
|
-
case constants_1.MCP_ANNOTATION_PROPS.
|
|
104
|
-
|
|
105
|
-
|
|
103
|
+
case constants_1.MCP_ANNOTATION_PROPS.MCP_WRAP_TOOLS:
|
|
104
|
+
if (!annotations.wrap) {
|
|
105
|
+
annotations.wrap = {};
|
|
106
|
+
}
|
|
107
|
+
annotations.wrap.tools = v;
|
|
108
|
+
continue;
|
|
109
|
+
case constants_1.MCP_ANNOTATION_PROPS.MCP_WRAP_HINT:
|
|
110
|
+
if (!annotations.wrap) {
|
|
111
|
+
annotations.wrap = {};
|
|
112
|
+
}
|
|
113
|
+
annotations.wrap.hint = v;
|
|
114
|
+
continue;
|
|
115
|
+
case constants_1.MCP_ANNOTATION_PROPS.MCP_WRAP_MODES:
|
|
116
|
+
if (!annotations.wrap) {
|
|
117
|
+
annotations.wrap = {};
|
|
118
|
+
}
|
|
119
|
+
annotations.wrap.modes = v;
|
|
106
120
|
continue;
|
|
107
121
|
case constants_1.MCP_ANNOTATION_PROPS.MCP_ELICIT:
|
|
108
122
|
annotations.elicit = v;
|
|
@@ -144,10 +158,10 @@ function constructResourceAnnotation(serviceName, target, annotations, definitio
|
|
|
144
158
|
* @param keyParams - Optional key parameters for bound operations
|
|
145
159
|
* @returns Tool annotation or undefined if invalid
|
|
146
160
|
*/
|
|
147
|
-
function constructToolAnnotation(serviceName, target, annotations, entityKey, keyParams) {
|
|
161
|
+
function constructToolAnnotation(model, serviceName, target, annotations, entityKey, keyParams) {
|
|
148
162
|
if (!(0, utils_1.isValidToolAnnotation)(annotations))
|
|
149
163
|
return undefined;
|
|
150
|
-
const { parameters, operationKind } = (0, utils_1.parseOperationElements)(annotations);
|
|
164
|
+
const { parameters, operationKind } = (0, utils_1.parseOperationElements)(annotations, model);
|
|
151
165
|
const restrictions = (0, utils_1.parseCdsRestrictions)(annotations.restrict, annotations.requires);
|
|
152
166
|
return new structures_1.McpToolAnnotation(annotations.name, annotations.description, target, serviceName, parameters, entityKey, operationKind, keyParams, restrictions, annotations.elicit);
|
|
153
167
|
}
|
|
@@ -170,7 +184,7 @@ function constructPromptAnnotation(serviceName, annotations) {
|
|
|
170
184
|
* @param definition - CSN entity definition containing bound operations
|
|
171
185
|
* @param resultRef - Map to store parsed annotations (mutated by this function)
|
|
172
186
|
*/
|
|
173
|
-
function parseBoundOperations(serviceName, entityKey, definition, resultRef) {
|
|
187
|
+
function parseBoundOperations(model, serviceName, entityKey, definition, resultRef) {
|
|
174
188
|
if (definition.kind !== "entity")
|
|
175
189
|
return;
|
|
176
190
|
const boundOperations = definition
|
|
@@ -192,7 +206,7 @@ function parseBoundOperations(serviceName, entityKey, definition, resultRef) {
|
|
|
192
206
|
continue;
|
|
193
207
|
}
|
|
194
208
|
const verifiedAnnotations = parsedAnnotations;
|
|
195
|
-
const toolAnnotation = constructToolAnnotation(serviceName, k, verifiedAnnotations, entityKey, keyParams);
|
|
209
|
+
const toolAnnotation = constructToolAnnotation(model, serviceName, k, verifiedAnnotations, entityKey, keyParams);
|
|
196
210
|
if (!toolAnnotation)
|
|
197
211
|
continue;
|
|
198
212
|
resultRef.set(k, toolAnnotation);
|
package/lib/annotations/utils.js
CHANGED
|
@@ -156,7 +156,7 @@ function determineResourceOptions(annotations) {
|
|
|
156
156
|
function parseResourceElements(definition) {
|
|
157
157
|
const properties = new Map();
|
|
158
158
|
const resourceKeys = new Map();
|
|
159
|
-
for (const [key, value] of Object.entries(definition.elements)) {
|
|
159
|
+
for (const [key, value] of Object.entries(definition.elements || {})) {
|
|
160
160
|
if (!value.type)
|
|
161
161
|
continue;
|
|
162
162
|
const parsedType = value.type.replace("cds.", "");
|
|
@@ -175,12 +175,22 @@ function parseResourceElements(definition) {
|
|
|
175
175
|
* @param annotations - The annotation structure to parse
|
|
176
176
|
* @returns Object containing parameters and operation kind
|
|
177
177
|
*/
|
|
178
|
-
function parseOperationElements(annotations) {
|
|
178
|
+
function parseOperationElements(annotations, model) {
|
|
179
179
|
let parameters;
|
|
180
180
|
const params = annotations.definition["params"];
|
|
181
181
|
if (params && Object.entries(params).length > 0) {
|
|
182
182
|
parameters = new Map();
|
|
183
183
|
for (const [k, v] of Object.entries(params)) {
|
|
184
|
+
if (typeof v.type !== "string") {
|
|
185
|
+
// const references = v.type.ref;
|
|
186
|
+
// const typeReference =
|
|
187
|
+
// model.definitions?.[references[0]].elements[references[1]];
|
|
188
|
+
// parameters.set(k, typeReference?.type?.replace("cds.", "") as string);
|
|
189
|
+
const referencedType = parseTypedReference(v.type, model);
|
|
190
|
+
parameters.set(k, referencedType);
|
|
191
|
+
logger_1.LOGGER.debug("Typed reference found", referencedType);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
184
194
|
parameters.set(k, v.type.replace("cds.", ""));
|
|
185
195
|
}
|
|
186
196
|
}
|
|
@@ -189,6 +199,22 @@ function parseOperationElements(annotations) {
|
|
|
189
199
|
operationKind: annotations.definition.kind,
|
|
190
200
|
};
|
|
191
201
|
}
|
|
202
|
+
/**
|
|
203
|
+
* Recursively digs through the typed reference object of an operation parameter.
|
|
204
|
+
* @param param
|
|
205
|
+
* @param model
|
|
206
|
+
* @returns string|undefined
|
|
207
|
+
* @throws Error if nested type is not parseable
|
|
208
|
+
*/
|
|
209
|
+
function parseTypedReference(param, model) {
|
|
210
|
+
if (!param || !param.ref) {
|
|
211
|
+
throw new Error("Failed to parse nested type reference");
|
|
212
|
+
}
|
|
213
|
+
const referenceType = model.definitions?.[param.ref[0]].elements[param.ref[1]];
|
|
214
|
+
return typeof referenceType?.type === "string"
|
|
215
|
+
? referenceType.type?.replace("cds.", "")
|
|
216
|
+
: parseTypedReference(referenceType?.type, model);
|
|
217
|
+
}
|
|
192
218
|
/**
|
|
193
219
|
* Parses entity keys from a definition
|
|
194
220
|
* @param definition - The definition to parse keys from
|
package/lib/mcp/factory.js
CHANGED
|
@@ -51,7 +51,8 @@ function createMcpServer(config, annotations) {
|
|
|
51
51
|
const localWrap = entry.wrap?.tools;
|
|
52
52
|
const enabled = localWrap === true || (localWrap === undefined && globalWrap);
|
|
53
53
|
if (enabled) {
|
|
54
|
-
const modes =
|
|
54
|
+
const modes = entry.wrap?.modes ??
|
|
55
|
+
config.wrap_entity_modes ?? ["query", "get"];
|
|
55
56
|
(0, entity_tools_1.registerEntityWrappers)(entry, server, authEnabled, modes, accesses);
|
|
56
57
|
}
|
|
57
58
|
continue;
|