@mcp-abap-adt/connection 1.5.1 → 1.5.3
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/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# @mcp-abap-adt/connection
|
|
2
2
|
|
|
3
|
+
[](https://stand-with-ukraine.pp.ua)
|
|
4
|
+
|
|
3
5
|
ABAP connection layer for MCP ABAP ADT server. Provides a unified interface for connecting to SAP ABAP systems via ADT (ABAP Development Tools) protocol, supporting both on-premise (Basic Auth) and cloud (JWT/OAuth2) authentication methods.
|
|
4
6
|
|
|
5
7
|
## Key Features
|
|
@@ -27,11 +27,13 @@ export declare class RfcAbapConnection implements AbapConnection {
|
|
|
27
27
|
private readonly sessionId;
|
|
28
28
|
private readonly baseUrl;
|
|
29
29
|
private readonly rfcParams;
|
|
30
|
+
private sessionType;
|
|
31
|
+
private sessionCookie;
|
|
30
32
|
constructor(config: SapConfig, logger?: ILogger | null);
|
|
31
33
|
connect(): Promise<void>;
|
|
32
34
|
getBaseUrl(): Promise<string>;
|
|
33
35
|
getSessionId(): string | null;
|
|
34
|
-
setSessionType(
|
|
36
|
+
setSessionType(type: 'stateful' | 'stateless'): void;
|
|
35
37
|
makeAdtRequest<T = any, D = any>(options: IAbapRequestOptions): Promise<IAdtResponse<T, D>>;
|
|
36
38
|
/**
|
|
37
39
|
* Reset the connection — for RFC this closes the session.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RfcAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/RfcAbapConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA0F1D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,cAAc;
|
|
1
|
+
{"version":3,"file":"RfcAbapConnection.d.ts","sourceRoot":"","sources":["../../src/connection/RfcAbapConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,mBAAmB,EACnB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AA0F1D;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,iBAAkB,YAAW,cAAc;IASpD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IATzB,OAAO,CAAC,SAAS,CAA2B;IAC5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAsB;IAChD,OAAO,CAAC,WAAW,CAAyC;IAC5D,OAAO,CAAC,aAAa,CAAuB;gBAGzB,MAAM,EAAE,SAAS,EACjB,MAAM,GAAE,OAAO,GAAG,IAAW;IAa1C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IA8BxB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAInC,YAAY,IAAI,MAAM,GAAG,IAAI;IAI7B,cAAc,CAAC,IAAI,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAU9C,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA8L9B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B,OAAO,CAAC,MAAM,CAAC,cAAc;CAqB9B"}
|
|
@@ -81,6 +81,8 @@ class RfcAbapConnection {
|
|
|
81
81
|
sessionId;
|
|
82
82
|
baseUrl;
|
|
83
83
|
rfcParams;
|
|
84
|
+
sessionType = 'stateless';
|
|
85
|
+
sessionCookie = null;
|
|
84
86
|
constructor(config, logger = null) {
|
|
85
87
|
this.config = config;
|
|
86
88
|
this.logger = logger;
|
|
@@ -123,8 +125,15 @@ class RfcAbapConnection {
|
|
|
123
125
|
getSessionId() {
|
|
124
126
|
return this.sessionId;
|
|
125
127
|
}
|
|
126
|
-
setSessionType(
|
|
127
|
-
|
|
128
|
+
setSessionType(type) {
|
|
129
|
+
this.sessionType = type;
|
|
130
|
+
if (type === 'stateless') {
|
|
131
|
+
this.sessionCookie = null;
|
|
132
|
+
this.logger?.debug('RFC session type: stateless (cookie cleared)');
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
this.logger?.debug('RFC session type: stateful (will capture cookies)');
|
|
136
|
+
}
|
|
128
137
|
}
|
|
129
138
|
async makeAdtRequest(options) {
|
|
130
139
|
if (!this.rfcClient?.alive) {
|
|
@@ -152,6 +161,17 @@ class RfcAbapConnection {
|
|
|
152
161
|
headerFields.push({ NAME: name, VALUE: value });
|
|
153
162
|
}
|
|
154
163
|
}
|
|
164
|
+
// Inject session type header so SAP creates/maintains an ICM session
|
|
165
|
+
if (this.sessionType === 'stateful') {
|
|
166
|
+
headerFields.push({ NAME: 'x-sap-adt-sessiontype', VALUE: 'stateful' });
|
|
167
|
+
}
|
|
168
|
+
// Replay session cookie for stateful operations (cookie captured from LOCK response)
|
|
169
|
+
if (this.sessionCookie) {
|
|
170
|
+
const existingCookie = headerFields.find((h) => h.NAME.toLowerCase() === 'cookie');
|
|
171
|
+
if (!existingCookie) {
|
|
172
|
+
headerFields.push({ NAME: 'Cookie', VALUE: this.sessionCookie });
|
|
173
|
+
}
|
|
174
|
+
}
|
|
155
175
|
// Ensure Content-Type for body
|
|
156
176
|
const body = options.data !== undefined && options.data !== null
|
|
157
177
|
? String(options.data)
|
|
@@ -164,6 +184,9 @@ class RfcAbapConnection {
|
|
|
164
184
|
});
|
|
165
185
|
}
|
|
166
186
|
this.logger?.debug(`RFC → ${method} ${uri}`);
|
|
187
|
+
this.logger?.debug(`RFC → headers: ${JSON.stringify(headerFields)}`);
|
|
188
|
+
if (body)
|
|
189
|
+
this.logger?.debug(`RFC → body: ${body}`);
|
|
167
190
|
try {
|
|
168
191
|
const result = await this.rfcClient.call('SADT_REST_RFC_ENDPOINT', {
|
|
169
192
|
REQUEST: {
|
|
@@ -201,6 +224,21 @@ class RfcAbapConnection {
|
|
|
201
224
|
respHeaders[field.NAME.toLowerCase()] = field.VALUE;
|
|
202
225
|
}
|
|
203
226
|
}
|
|
227
|
+
// Capture session cookie from LOCK/stateful responses for cookie replay
|
|
228
|
+
if (this.sessionType === 'stateful' && respHeaders['set-cookie']) {
|
|
229
|
+
const setCookieHeader = respHeaders['set-cookie'];
|
|
230
|
+
// Extract cookie name=value pairs (strip attributes like Path, Secure, etc.)
|
|
231
|
+
const cookies = Array.isArray(setCookieHeader)
|
|
232
|
+
? setCookieHeader
|
|
233
|
+
: [setCookieHeader];
|
|
234
|
+
const cookieValues = cookies
|
|
235
|
+
.map((c) => c.split(';')[0].trim())
|
|
236
|
+
.filter(Boolean);
|
|
237
|
+
if (cookieValues.length > 0) {
|
|
238
|
+
this.sessionCookie = cookieValues.join('; ');
|
|
239
|
+
this.logger?.debug(`RFC: captured session cookie: ${this.sessionCookie}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
204
242
|
// On some systems (e.g. BASIS < 7.50), SADT_REST_RFC_ENDPOINT does not
|
|
205
243
|
// populate STATUS_LINE, returning status 0. Detect errors from the
|
|
206
244
|
// response body: ADT exception XML indicates a failed request.
|
|
@@ -216,6 +254,9 @@ class RfcAbapConnection {
|
|
|
216
254
|
statusText = statusText || 'OK';
|
|
217
255
|
}
|
|
218
256
|
this.logger?.debug(`RFC ← ${statusCode} ${statusText} (${respBody.length} bytes)`);
|
|
257
|
+
this.logger?.debug(`RFC ← headers: ${JSON.stringify(respHeaders)}`);
|
|
258
|
+
if (respBody)
|
|
259
|
+
this.logger?.debug(`RFC ← body: ${respBody}`);
|
|
219
260
|
const response = {
|
|
220
261
|
data: respBody,
|
|
221
262
|
status: statusCode,
|