@mcp-abap-adt/connection 1.5.2 → 1.6.0

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
+ [![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](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
@@ -5,7 +5,7 @@ import type { AbapConnection } from './AbapConnection.js';
5
5
  /**
6
6
  * RFC-based connection for on-premise SAP systems.
7
7
  *
8
- * Uses node-rfc to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
8
+ * Uses @mcp-abap-adt/sap-rfc-lite to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
9
9
  * that Eclipse ADT uses for all on-premise ADT operations via JCo.
10
10
  *
11
11
  * RFC connections are inherently stateful: one ABAP session persists for the
@@ -18,7 +18,7 @@ import type { AbapConnection } from './AbapConnection.js';
18
18
  *
19
19
  * Prerequisites:
20
20
  * - SAP NW RFC SDK installed on the machine
21
- * - node-rfc package installed: npm install node-rfc
21
+ * - @mcp-abap-adt/sap-rfc-lite package installed: npm install @mcp-abap-adt/sap-rfc-lite
22
22
  */
23
23
  export declare class RfcAbapConnection implements AbapConnection {
24
24
  private readonly config;
@@ -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(_type: 'stateful' | 'stateless'): void;
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;IAOpD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IAPzB,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;gBAG7B,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,KAAK,EAAE,UAAU,GAAG,WAAW,GAAG,IAAI;IAI/C,cAAc,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,GAAG,GAAG,EACnC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA6J9B;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAc5B,OAAO,CAAC,MAAM,CAAC,cAAc;CAqB9B"}
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"}
@@ -59,7 +59,7 @@ function detectExceptionStatus(body) {
59
59
  /**
60
60
  * RFC-based connection for on-premise SAP systems.
61
61
  *
62
- * Uses node-rfc to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
62
+ * Uses @mcp-abap-adt/sap-rfc-lite to call SADT_REST_RFC_ENDPOINT — the same standard SAP FM
63
63
  * that Eclipse ADT uses for all on-premise ADT operations via JCo.
64
64
  *
65
65
  * RFC connections are inherently stateful: one ABAP session persists for the
@@ -72,7 +72,7 @@ function detectExceptionStatus(body) {
72
72
  *
73
73
  * Prerequisites:
74
74
  * - SAP NW RFC SDK installed on the machine
75
- * - node-rfc package installed: npm install node-rfc
75
+ * - @mcp-abap-adt/sap-rfc-lite package installed: npm install @mcp-abap-adt/sap-rfc-lite
76
76
  */
77
77
  class RfcAbapConnection {
78
78
  config;
@@ -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;
@@ -93,16 +95,16 @@ class RfcAbapConnection {
93
95
  async connect() {
94
96
  let Client;
95
97
  try {
96
- // Dynamic require — node-rfc is NOT a declared dependency.
98
+ // Dynamic require — @mcp-abap-adt/sap-rfc-lite is NOT a declared dependency.
97
99
  // Users who need RFC connections must install it manually:
98
- // npm install node-rfc (+ SAP NW RFC SDK on the machine)
100
+ // npm install @mcp-abap-adt/sap-rfc-lite (+ SAP NW RFC SDK on the machine)
99
101
  // eslint-disable-next-line @typescript-eslint/no-require-imports
100
- const noderfc = require('node-rfc');
102
+ const noderfc = require('@mcp-abap-adt/sap-rfc-lite');
101
103
  Client = noderfc.Client;
102
104
  }
103
105
  catch (e) {
104
- throw new Error('node-rfc is not available. To use RFC connections, install SAP NW RFC SDK ' +
105
- 'and run: npm install node-rfc. ' +
106
+ throw new Error('@mcp-abap-adt/sap-rfc-lite is not available. To use RFC connections, install SAP NW RFC SDK ' +
107
+ 'and run: npm install @mcp-abap-adt/sap-rfc-lite. ' +
106
108
  `Details: ${e instanceof Error ? e.message : String(e)}`);
107
109
  }
108
110
  this.rfcClient = new Client(this.rfcParams);
@@ -123,8 +125,15 @@ class RfcAbapConnection {
123
125
  getSessionId() {
124
126
  return this.sessionId;
125
127
  }
126
- setSessionType(_type) {
127
- // No-op — RFC connections are always stateful
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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/connection",
3
- "version": "1.5.2",
3
+ "version": "1.6.0",
4
4
  "description": "ABAP connection layer for MCP ABAP ADT server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -46,7 +46,8 @@
46
46
  "node": ">=18.0.0"
47
47
  },
48
48
  "dependencies": {
49
- "@mcp-abap-adt/interfaces": "^3.0.0",
49
+ "@mcp-abap-adt/interfaces": "^6.1.0",
50
+ "@mcp-abap-adt/sap-rfc-lite": "^0.1.0",
50
51
  "axios": "^1.13.5",
51
52
  "commander": "^14.0.3",
52
53
  "express": "^5.1.0",
@@ -56,6 +57,7 @@
56
57
  "@biomejs/biome": "^2.3.14",
57
58
  "@types/jest": "^30.0.0",
58
59
  "@types/node": "^25.2.3",
60
+ "dotenv": "^17.3.1",
59
61
  "jest": "^30.2.0",
60
62
  "jest-util": "^30.2.0",
61
63
  "ts-jest": "^29.2.5",