sap-adt-mcp 0.8.43 → 0.8.44

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sap-adt-mcp",
3
- "version": "0.8.43",
3
+ "version": "0.8.44",
4
4
  "mcpName": "io.github.yzonur/sap-adt-mcp",
5
5
  "description": "MCP server giving Claude live access to SAP systems via ADT (ABAP Development Tools) REST. Read source, search, run syntax checks, and edit ABAP objects from any MCP-compatible client.",
6
6
  "type": "module",
@@ -73,8 +73,21 @@ export function normalizeType(input) {
73
73
  return aliased ?? upper;
74
74
  }
75
75
 
76
+ // ADT types arrive either as a bare key (CLAS, SRVD) or in TADIR-style
77
+ // TYPE/SUBTYPE form (SRVD/SRV, CLAS/OC, DDLS/DF). The subtype only changes the
78
+ // endpoint for function-group members (FUGR/FF module, FUGR/I include); for
79
+ // every other kind it is decoration, so collapse X/Y to its base type X. Without
80
+ // this, a TYPE/SUBTYPE input misses the dispatch table and throws "Unsupported
81
+ // object type".
82
+ function collapseSubtype(t) {
83
+ if (t.includes("/") && t !== "FUGR/FF" && t !== "FUGR/I") {
84
+ return t.split("/")[0];
85
+ }
86
+ return t;
87
+ }
88
+
76
89
  export function objectUri({ type, name, group }) {
77
- const t = normalizeType(type);
90
+ const t = collapseSubtype(normalizeType(type));
78
91
  if (typeof name !== "string" || name.length === 0) {
79
92
  throw new Error("Object name is required");
80
93
  }
@@ -145,7 +158,7 @@ export const METADATA_XML_ACCEPT = {
145
158
  };
146
159
 
147
160
  export function sourceUri({ type, name, group, include }) {
148
- const t = normalizeType(type);
161
+ const t = collapseSubtype(normalizeType(type));
149
162
  const base = objectUri({ type: t, name, group });
150
163
 
151
164
  if (t === "CLAS") {
package/src/tools/cds.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { objectUri } from "../object-uris.js";
2
2
  import { parseDataPreview } from "../data-preview.js";
3
3
  import { parseObjectReferences } from "../object-references.js";
4
- import { errorResult, jsonResult } from "../result.js";
4
+ import { errorResult, jsonResult, textResult } from "../result.js";
5
5
  import { SYSTEM_HINT } from "./_shared.js";
6
6
 
7
7
  const CDS_PREVIEW_PATH = "/sap/bc/adt/datapreview/cds";
@@ -86,12 +86,21 @@ export const tools = [
86
86
  export function register({ getClient }) {
87
87
  return {
88
88
  adt_cds_data_preview: async (args) => {
89
+ // `entity` is the documented field, but callers reach for `name`/`cdsName`;
90
+ // accept those too and fail cleanly rather than throwing on `.toUpperCase`.
91
+ const entity = args.entity ?? args.name ?? args.cdsName;
92
+ if (typeof entity !== "string" || entity.length === 0) {
93
+ return textResult(
94
+ "adt_cds_data_preview: `entity` is required (the CDS view/entity name, e.g. 'I_CompanyCode').",
95
+ true
96
+ );
97
+ }
89
98
  const { client, name: sys } = getClient(args.system);
90
99
  const max = Math.min(args.maxRows ?? DEFAULT_MAX_ROWS, HARD_CAP_ROWS);
91
100
  const res = await client.request({
92
101
  method: "POST",
93
102
  path: CDS_PREVIEW_PATH,
94
- query: { ddlSourceName: args.entity.toUpperCase(), rowNumber: String(max) },
103
+ query: { ddlSourceName: entity.toUpperCase(), rowNumber: String(max) },
95
104
  accept: "application/xml",
96
105
  });
97
106
  const text = await res.text();
@@ -100,12 +109,12 @@ export function register({ getClient }) {
100
109
  try {
101
110
  parsed = parseDataPreview(text);
102
111
  } catch (err) {
103
- return jsonResult({ system: sys, entity: args.entity, parseError: err.message, raw: text.slice(0, 8000) });
112
+ return jsonResult({ system: sys, entity, parseError: err.message, raw: text.slice(0, 8000) });
104
113
  }
105
114
  const rowCount = parsed.rows.length;
106
115
  return jsonResult({
107
116
  system: sys,
108
- entity: args.entity.toUpperCase(),
117
+ entity: entity.toUpperCase(),
109
118
  rowCount,
110
119
  truncated: rowCount >= max,
111
120
  totalRows: parsed.totalRows,
@@ -116,8 +125,15 @@ export function register({ getClient }) {
116
125
  },
117
126
 
118
127
  adt_cds_dependencies: async (args) => {
128
+ const entity = args.entity ?? args.name ?? args.cdsName;
129
+ if (typeof entity !== "string" || entity.length === 0) {
130
+ return textResult(
131
+ "adt_cds_dependencies: `entity` is required (the CDS view/entity name, e.g. 'I_CompanyCode').",
132
+ true
133
+ );
134
+ }
119
135
  const { client, name: sys } = getClient(args.system);
120
- const uri = objectUri({ type: "ddls", name: args.entity });
136
+ const uri = objectUri({ type: "ddls", name: entity });
121
137
  const res = await client.request({ path: GRAPHDATA_PATH, query: { uri }, accept: "application/xml" });
122
138
  const text = await res.text();
123
139
  if (!res.ok) return errorResult(sys, res.status, text, res.headers.get("content-type"), { stage: "graphdata" });
@@ -131,7 +147,7 @@ export function register({ getClient }) {
131
147
  }
132
148
  return jsonResult({
133
149
  system: sys,
134
- entity: args.entity.toUpperCase(),
150
+ entity: entity.toUpperCase(),
135
151
  uri,
136
152
  dependencyCount: refs.length,
137
153
  dependencies: refs,
@@ -315,8 +315,9 @@ export function register({ getClient }) {
315
315
 
316
316
  adt_list_packages: async (args) => {
317
317
  // `package` is the field name on the sibling adt_browse_package tool, so
318
- // callers reach for it here too — accept it as an alias for `root`.
319
- const rootArg = args.root ?? args.package;
318
+ // callers reach for it here too — accept it (and a bare `name`) as an
319
+ // alias for `root`.
320
+ const rootArg = args.root ?? args.package ?? args.name;
320
321
  if (typeof rootArg !== "string" || rootArg.length === 0) {
321
322
  return textResult(
322
323
  "adt_list_packages: `root` is required (the root package to walk from, e.g. 'ZFLEET').",
@@ -286,13 +286,26 @@ export function register({ getClient }) {
286
286
  return textResult("adt_get_source: `type` is required (e.g. 'class', 'program', 'dataelement').", true);
287
287
  }
288
288
  const { client, name: sys } = getClient(args.system);
289
- const t = normalizeType(args.type);
290
- const path = sourceUri({
291
- type: args.type,
292
- name: args.object,
293
- group: args.group,
294
- include: args.include,
295
- });
289
+ let t;
290
+ let path;
291
+ try {
292
+ t = normalizeType(args.type);
293
+ path = sourceUri({
294
+ type: args.type,
295
+ name: args.object,
296
+ group: args.group,
297
+ include: args.include,
298
+ });
299
+ } catch (err) {
300
+ // Unknown/unsupported object types (e.g. WAPA) reach the dispatch
301
+ // tables and throw. Return that as a clean tool error with an escape
302
+ // hatch hint instead of letting it surface as a crash.
303
+ return textResult(
304
+ `adt_get_source: ${err.message}. If this type has no high-level mapping yet, ` +
305
+ `fetch it with adt_request against its ADT source URI.`,
306
+ true
307
+ );
308
+ }
296
309
 
297
310
  // DDIC primitives (data element / domain / message class) have no
298
311
  // plain-text source — fetch their XML metadata with the right media type