@gavdi/cap-mcp 1.0.1 → 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.
@@ -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
- MCP_WRAP: "@mcp.wrap",
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.MCP_WRAP:
104
- // Wrapper container to expose resources as tools
105
- annotations.wrap = v;
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);
@@ -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
@@ -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 = config.wrap_entity_modes ?? ["query", "get"];
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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gavdi/cap-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "description": "MCP Pluging for CAP",
5
5
  "keywords": [
6
6
  "MCP",