@mcp-abap-adt/connection 1.5.2 → 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)
|
|
@@ -204,6 +224,21 @@ class RfcAbapConnection {
|
|
|
204
224
|
respHeaders[field.NAME.toLowerCase()] = field.VALUE;
|
|
205
225
|
}
|
|
206
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
|
+
}
|
|
207
242
|
// On some systems (e.g. BASIS < 7.50), SADT_REST_RFC_ENDPOINT does not
|
|
208
243
|
// populate STATUS_LINE, returning status 0. Detect errors from the
|
|
209
244
|
// response body: ADT exception XML indicates a failed request.
|