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 +1 -1
- package/src/object-uris.js +15 -2
- package/src/tools/cds.js +22 -6
- package/src/tools/discovery.js +3 -2
- package/src/tools/source.js +20 -7
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sap-adt-mcp",
|
|
3
|
-
"version": "0.8.
|
|
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",
|
package/src/object-uris.js
CHANGED
|
@@ -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:
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
150
|
+
entity: entity.toUpperCase(),
|
|
135
151
|
uri,
|
|
136
152
|
dependencyCount: refs.length,
|
|
137
153
|
dependencies: refs,
|
package/src/tools/discovery.js
CHANGED
|
@@ -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
|
|
319
|
-
|
|
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').",
|
package/src/tools/source.js
CHANGED
|
@@ -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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
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
|