@mcp-abap-adt/core 4.6.0 → 4.7.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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [4.7.0] - 2026-04-01
6
+
7
+ ### Changed
8
+ - Replace archived `node-rfc` with `@mcp-abap-adt/sap-rfc-lite` — a lightweight maintained fork with the same API surface. This change only affects RFC connections to legacy SAP systems (BASIS < 7.50). HTTP connections are not affected. If you experience issues with legacy RFC connections, please open an issue and downgrade to v4.6.0.
9
+
5
10
  ## [4.6.0] - 2026-03-31
6
11
 
7
12
  ### Added
@@ -234,7 +234,7 @@ AUTHENTICATION:
234
234
  --mcp=<destination> Default MCP destination name (for auth-broker mode)
235
235
  Example: --mcp=TRIAL
236
236
  --connection-type=<type> SAP connection type: http (default) or rfc
237
- RFC requires SAP NW RFC SDK + node-rfc installed
237
+ RFC requires SAP NW RFC SDK + @mcp-abap-adt/sap-rfc-lite installed
238
238
  Alternative: SAP_CONNECTION_TYPE env var in .env
239
239
  --system-type=<type> SAP system type: cloud (default) | onprem | legacy
240
240
  Controls which tools are available
@@ -159,7 +159,7 @@ SAP CONNECTION (.env file):
159
159
  SAP_USERNAME SAP username
160
160
  SAP_PASSWORD SAP password
161
161
  SAP_CLIENT SAP client number
162
- Requires: SAP NW RFC SDK + node-rfc package installed
162
+ Requires: SAP NW RFC SDK + @mcp-abap-adt/sap-rfc-lite package installed
163
163
 
164
164
  System Context (on-premise):
165
165
  SAP_MASTER_SYSTEM SAP system ID (e.g., DEV, QAS). Required for on-prem
@@ -40,18 +40,18 @@ export PATH=$SAPNWRFC_HOME/lib:$PATH
40
40
  export LD_LIBRARY_PATH=$SAPNWRFC_HOME/lib:${LD_LIBRARY_PATH:-}
41
41
  ```
42
42
 
43
- ### 3. node-rfc Package
43
+ ### 3. @mcp-abap-adt/sap-rfc-lite Package
44
44
 
45
45
  ```bash
46
- npm install node-rfc
46
+ npm install @mcp-abap-adt/sap-rfc-lite
47
47
  ```
48
48
 
49
- `node-rfc` is loaded dynamically at runtime — it is not a declared dependency.
49
+ `@mcp-abap-adt/sap-rfc-lite` is a lightweight fork of the archived `node-rfc` package, containing only the API surface needed for ADT RFC connections. It is loaded dynamically at runtime — it is not a declared dependency.
50
50
 
51
51
  Verify installation:
52
52
 
53
53
  ```bash
54
- node -e "try { require('node-rfc'); console.log('OK'); } catch(e) { console.log(e.message); }"
54
+ node -e "try { require('@mcp-abap-adt/sap-rfc-lite'); console.log('OK'); } catch(e) { console.log(e.message); }"
55
55
  ```
56
56
 
57
57
  ### 4. SAP Authorization
@@ -83,13 +83,13 @@ SAP_CONNECTION_TYPE=rfc
83
83
 
84
84
  ## Troubleshooting
85
85
 
86
- ### "node-rfc is not available"
86
+ ### "@mcp-abap-adt/sap-rfc-lite is not available"
87
87
 
88
88
  SAP NW RFC SDK is not installed or not in PATH. Check:
89
89
 
90
90
  ```bash
91
91
  echo $SAPNWRFC_HOME
92
- node -e "require('node-rfc')"
92
+ node -e "require('@mcp-abap-adt/sap-rfc-lite')"
93
93
  ```
94
94
 
95
95
  ### "The specified module could not be found: sapnwrfc.node"
@@ -89,7 +89,7 @@ if (process.env.SAP_AUTH_TYPE === 'rfc') {
89
89
  ```
90
90
 
91
91
  `createAbapConnection()` from connection package already handles `authType: 'rfc'`.
92
- `node-rfc` is loaded dynamically — no error if not installed and not used.
92
+ `@mcp-abap-adt/sap-rfc-lite` is loaded dynamically — no error if not installed and not used.
93
93
 
94
94
  ### Step 5: Launcher CLI support [DONE]
95
95
 
@@ -155,7 +155,7 @@ The [abapfs_extensions](https://github.com/marcellourbani/abapfs_extensions) pro
155
155
 
156
156
  | Risk | Mitigation |
157
157
  |------|-----------|
158
- | node-rfc needs SAP NW RFC SDK native lib | Dynamic import; graceful error if not installed; only loaded for `authType: 'rfc'` |
158
+ | @mcp-abap-adt/sap-rfc-lite needs SAP NW RFC SDK native lib | Dynamic import; graceful error if not installed; only loaded for `connectionType: 'rfc'` |
159
159
  | Docker images need SDK | Document; provide Dockerfile with SDK layer |
160
160
  | Legacy endpoint differences between versions | Discovery-based detection, not hardcoded |
161
161
  | One extra HTTP call at startup (core/discovery) | Cached, happens once |
@@ -0,0 +1,848 @@
1
+ # SAP RFC Lite — Lightweight node-rfc Fork
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Create a minimal, maintainable Node.js native addon that wraps SAP NW RFC SDK — replacing the archived `node-rfc` package with only the API surface we actually use.
6
+
7
+ **Architecture:** Fork and strip `node-rfc` (Apache-2.0) down to a single `Client` class with `open()`, `call()`, `close()`, and `alive`. Remove Pool, Server, Throughput, and all unused client methods. Keep the full data type marshalling layer (nwrfcsdk.cc) since SAP FM parameter types are determined at runtime by the SDK.
8
+
9
+ **Tech Stack:** C++17, Node-Addon-API (N-API v8), node-gyp, TypeScript, SAP NW RFC SDK
10
+
11
+ ---
12
+
13
+ ## File Structure
14
+
15
+ ### New package: `sap-rfc-lite/`
16
+
17
+ Location: `/home/okyslytsia/prj/sap-rfc-lite/` (separate repo, publishable to npm)
18
+
19
+ ```
20
+ sap-rfc-lite/
21
+ ├── package.json
22
+ ├── tsconfig.json
23
+ ├── binding.gyp
24
+ ├── LICENSE # Apache-2.0 (from node-rfc)
25
+ ├── NOTICE # Attribution to original SAP authors
26
+ ├── src/
27
+ │ ├── cpp/
28
+ │ │ ├── addon.cc # Entry point (Client only, no Pool/Server/Throughput)
29
+ │ │ ├── Client.cc # Client: Open, Close, Invoke, AliveGetter, connectionCheck
30
+ │ │ ├── Client.h # Client class header (no Pool friend, no Pool*)
31
+ │ │ ├── nwrfcsdk.cc # Full data type marshalling (keep all types)
32
+ │ │ ├── nwrfcsdk.h # Types/structs (remove Pool/Server refs)
33
+ │ │ └── noderfc.h # Constants (remove Pool/Server constants)
34
+ │ └── ts/
35
+ │ ├── index.ts # Exports: Client, types
36
+ │ ├── client.ts # Client class (Promise-only, no callback API)
37
+ │ ├── binding.ts # Native addon loader
38
+ │ └── types.ts # Minimal type definitions
39
+ ├── test/
40
+ │ ├── client.test.ts # Unit tests (mocked native binding)
41
+ │ └── integration.test.ts # Integration test (requires SAP NW RFC SDK)
42
+ └── .github/
43
+ └── workflows/
44
+ └── build.yml # CI: lint + typecheck (no SDK on CI)
45
+ ```
46
+
47
+ ### Files removed vs node-rfc
48
+
49
+ | Removed file | Reason |
50
+ |---|---|
51
+ | `Pool.cc/h` (747+85 LOC) | Connection pooling not used |
52
+ | `Server.cc/h` (634+144 LOC) | RFC inbound server not used |
53
+ | `server_api.cc/h` (814+229 LOC) | Server API not used |
54
+ | `Throughput.cc/h` (196+53 LOC) | Performance monitoring not used |
55
+ | `Log.cc/h` (57+142 LOC) | Replace with minimal stderr logging |
56
+ | `ext/date.h` | Only used by Log.h for formatting |
57
+ | `sapnwrfc-pool.ts` | Pool class |
58
+ | `sapnwrfc-server.ts` | Server class |
59
+ | `sapnwrfc-throughput.ts` | Throughput class |
60
+
61
+ **Reduction:** ~2,902 lines of C++ removed (Pool+Server+server_api+Throughput+Log). ~600 lines C++ remain (Client+addon). nwrfcsdk.cc (1,158 lines) kept intact — types are determined at runtime, removing "unused" ones would cause crashes.
62
+
63
+ ### Dependencies
64
+
65
+ **Keep:**
66
+ - `node-addon-api` ^6.1.0 — N-API C++ wrapper
67
+ - `node-gyp-build` ^4.6.0 — native module loader
68
+
69
+ **Remove:**
70
+ - `bluebird` — unused (Promise-only API, no callback)
71
+ - `decimal.js` — unused (BCD returns string by default)
72
+
73
+ ---
74
+
75
+ ## Tasks
76
+
77
+ ### Task 1: Initialize package structure
78
+
79
+ **Files:**
80
+ - Create: `sap-rfc-lite/package.json`
81
+ - Create: `sap-rfc-lite/tsconfig.json`
82
+ - Create: `sap-rfc-lite/LICENSE`
83
+ - Create: `sap-rfc-lite/NOTICE`
84
+ - Create: `sap-rfc-lite/.gitignore`
85
+
86
+ - [ ] **Step 1: Create directory and init git**
87
+
88
+ ```bash
89
+ mkdir -p /home/okyslytsia/prj/sap-rfc-lite
90
+ cd /home/okyslytsia/prj/sap-rfc-lite
91
+ git init
92
+ ```
93
+
94
+ - [ ] **Step 2: Create package.json**
95
+
96
+ ```json
97
+ {
98
+ "name": "sap-rfc-lite",
99
+ "version": "0.1.0",
100
+ "description": "Lightweight Node.js bindings for SAP NW RFC SDK",
101
+ "license": "Apache-2.0",
102
+ "main": "./lib/index.js",
103
+ "types": "./lib/index.d.ts",
104
+ "files": ["binding.gyp", "src", "lib"],
105
+ "scripts": {
106
+ "install": "node-gyp-build",
107
+ "build:ts": "tsc",
108
+ "build:cpp": "node-gyp rebuild",
109
+ "build": "npm run build:ts && npm run build:cpp",
110
+ "test": "jest --testTimeout 60000"
111
+ },
112
+ "config": {
113
+ "napi_version": 8
114
+ },
115
+ "dependencies": {
116
+ "node-addon-api": "^6.1.0",
117
+ "node-gyp-build": "^4.6.0"
118
+ },
119
+ "devDependencies": {
120
+ "@types/node": "^20.0.0",
121
+ "typescript": "^5.0.0",
122
+ "jest": "^29.0.0",
123
+ "ts-jest": "^29.0.0",
124
+ "@types/jest": "^29.0.0"
125
+ },
126
+ "engines": {
127
+ "node": ">=18"
128
+ },
129
+ "gypfile": true
130
+ }
131
+ ```
132
+
133
+ - [ ] **Step 3: Create tsconfig.json**
134
+
135
+ ```json
136
+ {
137
+ "compilerOptions": {
138
+ "target": "ES2020",
139
+ "module": "commonjs",
140
+ "lib": ["ES2020"],
141
+ "outDir": "./lib",
142
+ "rootDir": "./src/ts",
143
+ "declaration": true,
144
+ "strict": true,
145
+ "esModuleInterop": true,
146
+ "skipLibCheck": true,
147
+ "forceConsistentCasingInFileNames": true
148
+ },
149
+ "include": ["src/ts/**/*"]
150
+ }
151
+ ```
152
+
153
+ - [ ] **Step 4: Create LICENSE (Apache-2.0) and NOTICE**
154
+
155
+ Copy Apache-2.0 license. NOTICE file:
156
+ ```
157
+ This project is based on node-rfc (https://github.com/SAP/node-rfc)
158
+ Copyright 2014 SAP SE, Srdjan Boskovic <srdjan.boskovic@sap.com>
159
+ Licensed under the Apache License, Version 2.0
160
+
161
+ Modifications: Removed Pool, Server, Throughput classes. Simplified Client API to Promise-only.
162
+ ```
163
+
164
+ - [ ] **Step 5: Create .gitignore**
165
+
166
+ ```
167
+ node_modules/
168
+ lib/
169
+ build/
170
+ prebuilds/
171
+ *.node
172
+ ```
173
+
174
+ - [ ] **Step 6: Commit**
175
+
176
+ ```bash
177
+ git add -A
178
+ git commit -m "chore: init sap-rfc-lite package structure"
179
+ ```
180
+
181
+ ---
182
+
183
+ ### Task 2: Port C++ source — noderfc.h (common header)
184
+
185
+ **Files:**
186
+ - Create: `sap-rfc-lite/src/cpp/noderfc.h`
187
+
188
+ - [ ] **Step 1: Create stripped noderfc.h**
189
+
190
+ Keep from original (`/tmp/node-rfc-source/src/cpp/noderfc.h`):
191
+ - All includes (`napi.h`, `sapnwrfc.h`)
192
+ - `ERRMSG_LENGTH`, `ERROR_PATH_NAME_LEN`
193
+ - `uint_t`, `pointer_t`, `UNUSED`
194
+ - `ENV_UNDEFINED`
195
+ - `CLIENT_OPTION_BCD*`, `CLIENT_OPTION_DATE/TIME/FILTER/STATELESS/TIMEOUT`
196
+ - `CALL_OPTION_KEY_*`
197
+
198
+ Remove:
199
+ - `SRV_OPTION_*` (server constants) — **including `SRV_OPTION_LOG_LEVEL`** which is referenced in `checkClientOptions()` in nwrfcsdk.cc; that branch must also be removed (see Task 3)
200
+ - `POOL_*` (pool constants)
201
+
202
+ Change `NODERFC_VERSION` to `"0.1.0"`.
203
+
204
+ Add minimal debug logging macro (replaces entire Log class):
205
+ ```cpp
206
+ // Minimal logging — no-op by default, stderr in debug builds
207
+ #ifdef SAP_RFC_LITE_DEBUG
208
+ #include <cstdio>
209
+ #define RFC_LOG(fmt, ...) fprintf(stderr, "[sap-rfc-lite] " fmt "\n", ##__VA_ARGS__)
210
+ #else
211
+ #define RFC_LOG(fmt, ...) ((void)0)
212
+ #endif
213
+ ```
214
+
215
+ - [ ] **Step 2: Commit**
216
+
217
+ ```bash
218
+ git add src/cpp/noderfc.h
219
+ git commit -m "feat: add noderfc.h common header (stripped)"
220
+ ```
221
+
222
+ ---
223
+
224
+ ### Task 3: Port C++ source — nwrfcsdk.h and nwrfcsdk.cc (data marshalling)
225
+
226
+ **Files:**
227
+ - Create: `sap-rfc-lite/src/cpp/nwrfcsdk.h`
228
+ - Create: `sap-rfc-lite/src/cpp/nwrfcsdk.cc`
229
+
230
+ **IMPORTANT:** All Log.h references must be removed NOW (not deferred). Every file must be compilable once all C++ files are in place.
231
+
232
+ - [ ] **Step 1: Create nwrfcsdk.h**
233
+
234
+ Copy from original, with these changes:
235
+ - **Remove** `#include "Log.h"` — replace with `#include "noderfc.h"` only (which now has the `RFC_LOG` macro)
236
+ - **Remove** `extern Log _log;` declaration
237
+ - Remove `Pool`-related forward declarations if any
238
+ - Keep all type definitions: `ConnectionParamsStruct`, `ClientOptionsStruct`, `RfmErrorPath`, `ValuePair`, `ErrorPair`
239
+ - Keep all function declarations: `setString`, `setRfmParameter`, `setStructure`, `setVariable`, `getStructure`, `getVariable`, `getRfmParameters`, `getConnectionAttributes`
240
+ - Keep error functions: `RfcLibError`, `AbapError`, `rfcSdkError`, `nodeRfcError`
241
+ - Keep parsers: `getConnectionParams`, `checkClientOptions`
242
+
243
+ - [ ] **Step 2: Create nwrfcsdk.cc**
244
+
245
+ Copy from original (1,158 lines). This is the most critical file — handles all ABAP type conversions. Apply these targeted changes:
246
+
247
+ 1. **Replace all `_log.*()` calls** (5-6 occurrences):
248
+ - Lines 34-44: `_log.fatal(logClass::nwrfc, ...)` → keep the `Napi::Error::Fatal(...)` that follows it, remove the `_log.fatal` call itself (it's just informational before abort)
249
+ - Lines 68-78: same pattern
250
+ - Line 419-430: `_log.record(logClass::nwrfc, logLevel::all, ...)` → remove entirely (verbose trace, not useful)
251
+ - Lines 684-688: `_log.warning(logClass::nwrfc, ...)` → `RFC_LOG("Buffer for BCD type too small")`
252
+ - Lines 735-738: same pattern for DECF16/34
253
+
254
+ 2. **Remove the `logLevel` client option branch in `checkClientOptions()`** (line 1103-1105):
255
+ ```cpp
256
+ // REMOVE this entire block — SRV_OPTION_LOG_LEVEL is removed and Log class is gone:
257
+ else if (key == SRV_OPTION_LOG_LEVEL) {
258
+ _log.set_log_level(logClass::client, opt);
259
+ }
260
+ ```
261
+ Let it fall through to the "unknown option" error handler. This is correct because our lite version does not support per-client log level configuration.
262
+
263
+ 3. **Remove** `extern Log _log;` if present at top of file.
264
+
265
+ - [ ] **Step 3: Commit**
266
+
267
+ ```bash
268
+ git add src/cpp/nwrfcsdk.h src/cpp/nwrfcsdk.cc
269
+ git commit -m "feat: add nwrfcsdk data marshalling layer (Log-free)"
270
+ ```
271
+
272
+ ---
273
+
274
+ ### Task 4: Port C++ source — Client.h and Client.cc
275
+
276
+ **Files:**
277
+ - Create: `sap-rfc-lite/src/cpp/Client.h`
278
+ - Create: `sap-rfc-lite/src/cpp/Client.cc`
279
+
280
+ - [ ] **Step 1: Create Client.h**
281
+
282
+ From original, remove:
283
+ - `#include "Log.h"` — replace with `#include "nwrfcsdk.h"` only
284
+ - `extern Log _log;` declaration
285
+ - `friend class Pool`, `friend class AcquireAsync`, `friend class ReleaseAsync`, `friend class CheckPoolAsync`
286
+ - `friend class ResetServerAsync`, `friend class PingAsync`
287
+ - `class Pool;` forward declaration
288
+ - `Pool* pool` member
289
+ - `Release()`, `Cancel()`, `ResetServerContext()`, `Ping()` method declarations
290
+ - `ConfigGetter()`, `ConnectionHandleGetter()`, `PoolIdGetter()`, `ConnectionInfo()` — not needed
291
+ - `log_id()` method (logging)
292
+
293
+ Keep:
294
+ - `friend class OpenAsync`, `friend class CloseAsync`, `friend class PrepareAsync`, `friend class InvokeAsync`
295
+ - `Open()`, `Close()`, `Invoke()`, `AliveGetter()`, `IdGetter()`
296
+ - `connectionCheck()`, `getOperationError()`, `connectionClosedError()`
297
+ - `connectionHandle`, `client_params`, `client_options`, `errorPath`
298
+ - `invocationMutex`, `LockMutex()`, `UnlockMutex()`
299
+ - Constructor/destructor
300
+
301
+ - [ ] **Step 2: Create Client.cc**
302
+
303
+ From original (780 lines), keep:
304
+ - `Client::Init()` — remove `cancel`, `release`, `resetServerContext`, `ping`, `connectionInfo` from `DefineClass`. Remove `_connectionHandle`, `_pool_id`, `_config` accessors.
305
+ - `Client::AliveGetter()` — as-is
306
+ - `Client::IdGetter()` — as-is
307
+ - Constructor — remove pool-related logic, remove `clientOptionsRef` handling (not needed for our use case, but can keep for compatibility)
308
+ - Destructor — remove `pool->releaseClient()` branch, keep direct close only
309
+ - `OpenAsync` class — as-is
310
+ - `CloseAsync` class — as-is
311
+ - `PrepareAsync` class — as-is
312
+ - `InvokeAsync` class — as-is
313
+ - `Client::Open()` — remove pool check (`if (pool != nullptr)`)
314
+ - `Client::Close()` — remove pool check
315
+ - `Client::Invoke()` — as-is
316
+ - `Client::connectionCheck()` — remove pool branch (`if (pool != nullptr)`), keep direct reconnect only
317
+ - `Client::getOperationError()` — as-is
318
+ - `Client::connectionClosedError()` — as-is
319
+ - `Client::LockMutex()`, `Client::UnlockMutex()` — as-is
320
+
321
+ Remove:
322
+ - `Client::Release()` (~20 lines)
323
+ - `Client::Cancel()` and `cancelConnection()` (~30 lines)
324
+ - `Client::ResetServerContext()` (~15 lines)
325
+ - `Client::Ping()` (~15 lines)
326
+ - `ResetServerAsync` class (~35 lines)
327
+ - `PingAsync` class (~33 lines)
328
+ - `Client::ConnectionInfo()` (~6 lines)
329
+ - `Client::ConfigGetter()` (~20 lines)
330
+ - `Client::ConnectionHandleGetter()` (3 lines)
331
+ - `Client::PoolIdGetter()` (5 lines)
332
+ - `Client::NewInstance()` (4 lines)
333
+ - `#include "Pool.h"`
334
+
335
+ Replace all `_log.*()` calls (~10 occurrences) with `RFC_LOG(...)` macro or remove entirely. Remove `extern Log _log;` at file top. Replace `#include "Pool.h"` with nothing (Client.h already includes nwrfcsdk.h).
336
+
337
+ In `connectionCheck()`: remove the `pool != nullptr` branches entirely. Keep only the direct client reconnect path:
338
+ ```cpp
339
+ // Direct client reconnect (pool branches removed)
340
+ this->connectionHandle = nullptr;
341
+ RFC_CONNECTION_HANDLE new_handle = RfcOpenConnection(
342
+ client_params.connectionParams, client_params.paramSize, &errorInfoOpen);
343
+ if (errorInfoOpen.code == RFC_OK) {
344
+ this->connectionHandle = new_handle;
345
+ }
346
+ ```
347
+
348
+ Also keep `connectionCheckErrorInit()` free function — it is used by `InvokeAsync`.
349
+
350
+ - [ ] **Step 3: Compile-check the header dependencies**
351
+
352
+ Verify Client.h includes are correct:
353
+ ```cpp
354
+ #include "nwrfcsdk.h" // includes noderfc.h → napi.h, sapnwrfc.h
355
+ ```
356
+
357
+ - [ ] **Step 4: Commit**
358
+
359
+ ```bash
360
+ git add src/cpp/Client.h src/cpp/Client.cc
361
+ git commit -m "feat: add stripped Client class (open/close/invoke/alive)"
362
+ ```
363
+
364
+ ---
365
+
366
+ ### Task 5: Port C++ source — addon.cc (entry point)
367
+
368
+ **Files:**
369
+ - Create: `sap-rfc-lite/src/cpp/addon.cc`
370
+
371
+ - [ ] **Step 1: Create minimal addon.cc**
372
+
373
+ ```cpp
374
+ // SPDX-FileCopyrightText: 2014 SAP SE Srdjan Boskovic <srdjan.boskovic@sap.com>
375
+ // SPDX-FileCopyrightText: 2026 sap-rfc-lite contributors
376
+ //
377
+ // SPDX-License-Identifier: Apache-2.0
378
+
379
+ #include "Client.h"
380
+
381
+ namespace node_rfc {
382
+
383
+ Napi::Env __env = nullptr;
384
+
385
+ Napi::Value BindingVersions(Napi::Env env) {
386
+ uint_t major, minor, patchLevel;
387
+ Napi::EscapableHandleScope scope(env);
388
+
389
+ RfcGetVersion(&major, &minor, &patchLevel);
390
+
391
+ Napi::Object nwrfcsdk = Napi::Object::New(env);
392
+ nwrfcsdk.Set("major", major);
393
+ nwrfcsdk.Set("minor", minor);
394
+ nwrfcsdk.Set("patchLevel", patchLevel);
395
+
396
+ Napi::Object version = Napi::Object::New(env);
397
+ version.Set("version", "0.1.0");
398
+ version.Set("nwrfcsdk", nwrfcsdk);
399
+
400
+ return scope.Escape(version);
401
+ }
402
+
403
+ Napi::Object RegisterModule(Napi::Env env, Napi::Object exports) {
404
+ if (node_rfc::__env == nullptr) {
405
+ node_rfc::__env = env;
406
+ }
407
+
408
+ exports.Set("bindingVersions", BindingVersions(env));
409
+ Client::Init(env, exports);
410
+
411
+ return exports;
412
+ }
413
+
414
+ NODE_API_MODULE(NODE_GYP_MODULE_NAME, RegisterModule)
415
+
416
+ } // namespace node_rfc
417
+ ```
418
+
419
+ - [ ] **Step 2: Commit**
420
+
421
+ ```bash
422
+ git add src/cpp/addon.cc
423
+ git commit -m "feat: add addon entry point (Client only)"
424
+ ```
425
+
426
+ ---
427
+
428
+ ### Task 6: Create binding.gyp
429
+
430
+ **Files:**
431
+ - Create: `sap-rfc-lite/binding.gyp`
432
+
433
+ - [ ] **Step 1: Create binding.gyp**
434
+
435
+ Copy from original, with these changes:
436
+ - Remove `Pool.cc`, `Server.cc`, `server_api.cc`, `Throughput.cc`, `Log.cc` from sources
437
+ - Keep all platform conditions (mac/linux/win) — identical logic
438
+ - Keep all defines (UNICODE, SAPwithTHREADS, NAPI_CPP_EXCEPTIONS, etc.)
439
+ - Sources list becomes:
440
+
441
+ ```python
442
+ 'sources': [
443
+ 'src/cpp/addon.cc',
444
+ 'src/cpp/nwrfcsdk.cc',
445
+ 'src/cpp/Client.cc',
446
+ ]
447
+ ```
448
+
449
+ - [ ] **Step 2: Compile-verify C++ (requires SAP NW RFC SDK)**
450
+
451
+ ```bash
452
+ cd /home/okyslytsia/prj/sap-rfc-lite
453
+ npm install
454
+ npm run build:cpp 2>&1
455
+ ```
456
+
457
+ All C++ files should compile cleanly at this point — no Log.h, no Pool.h, no Server.h dependencies. If SDK is not available locally, at least verify `tsc` passes for TS and review C++ for any remaining `_log` / `Log` / `logClass` / `logLevel` references:
458
+ ```bash
459
+ grep -rn '_log\.\|Log\|logClass\|logLevel' src/cpp/
460
+ ```
461
+
462
+ - [ ] **Step 3: Commit**
463
+
464
+ ```bash
465
+ git add binding.gyp
466
+ git commit -m "feat: add binding.gyp build configuration"
467
+ ```
468
+
469
+ ---
470
+
471
+ ### Task 7: Port TypeScript — types.ts
472
+
473
+ **Files:**
474
+ - Create: `sap-rfc-lite/src/ts/types.ts`
475
+
476
+ - [ ] **Step 1: Create minimal type definitions**
477
+
478
+ ```typescript
479
+ // Connection parameters — same keys as SAP NW RFC SDK
480
+ export type RfcConnectionParameters = Record<string, string>;
481
+
482
+ // RFC data types
483
+ export type RfcVariable = string | number | Buffer;
484
+ export type RfcStructure = { [key: string]: RfcVariable | RfcStructure | RfcTable };
485
+ export type RfcTable = Array<RfcVariable | RfcStructure>;
486
+ export type RfcParameterValue = RfcVariable | RfcStructure | RfcTable;
487
+ export type RfcObject = { [key: string]: RfcParameterValue };
488
+
489
+ // Binding versions
490
+ export interface RfcSdkVersions {
491
+ version: string;
492
+ nwrfcsdk: { major: number; minor: number; patchLevel: number };
493
+ }
494
+
495
+ // Error types
496
+ export interface RfcLibError {
497
+ name: 'RfcLibError';
498
+ group: number;
499
+ code: number;
500
+ codeString: string;
501
+ key: string;
502
+ message: string;
503
+ }
504
+
505
+ export interface AbapError {
506
+ name: 'ABAPError';
507
+ group: number;
508
+ code: number;
509
+ codeString: string;
510
+ key: string;
511
+ message: string;
512
+ abapMsgClass: string;
513
+ abapMsgType: string;
514
+ abapMsgNumber: string;
515
+ abapMsgV1: string;
516
+ abapMsgV2: string;
517
+ abapMsgV3: string;
518
+ abapMsgV4: string;
519
+ }
520
+
521
+ export interface NodeRfcError {
522
+ name: 'nodeRfcError';
523
+ message: string;
524
+ }
525
+
526
+ export type RfcError = RfcLibError | AbapError | NodeRfcError;
527
+ ```
528
+
529
+ - [ ] **Step 2: Commit**
530
+
531
+ ```bash
532
+ git add src/ts/types.ts
533
+ git commit -m "feat: add minimal type definitions"
534
+ ```
535
+
536
+ ---
537
+
538
+ ### Task 8: Port TypeScript — binding.ts (native addon loader)
539
+
540
+ **Files:**
541
+ - Create: `sap-rfc-lite/src/ts/binding.ts`
542
+
543
+ - [ ] **Step 1: Create binding loader**
544
+
545
+ ```typescript
546
+ import path from 'path';
547
+ import { RfcConnectionParameters, RfcObject, RfcSdkVersions } from './types';
548
+
549
+ export interface RfcClientBinding {
550
+ new (connectionParameters: RfcConnectionParameters): RfcClientBinding;
551
+ _id: number;
552
+ _alive: boolean;
553
+ open(callback: Function): void;
554
+ close(callback: Function): void;
555
+ invoke(
556
+ rfmName: string,
557
+ rfmParams: RfcObject,
558
+ callback: Function,
559
+ callOptions?: object
560
+ ): void;
561
+ }
562
+
563
+ export interface NativeBinding {
564
+ Client: RfcClientBinding;
565
+ bindingVersions: RfcSdkVersions;
566
+ }
567
+
568
+ let binding: NativeBinding;
569
+
570
+ try {
571
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
572
+ binding = require('node-gyp-build')(
573
+ path.resolve(__dirname, '..')
574
+ ) as NativeBinding;
575
+ } catch (ex) {
576
+ const err = ex as Error;
577
+ const env = {
578
+ SAPNWRFC_HOME: process.env.SAPNWRFC_HOME || '(not set)',
579
+ platform: process.platform,
580
+ arch: process.arch,
581
+ node: process.version,
582
+ };
583
+ err.message += `\nEnvironment: ${JSON.stringify(env, null, 2)}`;
584
+ throw err;
585
+ }
586
+
587
+ export { binding };
588
+ ```
589
+
590
+ - [ ] **Step 2: Commit**
591
+
592
+ ```bash
593
+ git add src/ts/binding.ts
594
+ git commit -m "feat: add native binding loader"
595
+ ```
596
+
597
+ ---
598
+
599
+ ### Task 9: Port TypeScript — client.ts (Client class, Promise-only)
600
+
601
+ **Files:**
602
+ - Create: `sap-rfc-lite/src/ts/client.ts`
603
+
604
+ - [ ] **Step 1: Create Client class**
605
+
606
+ Key difference from node-rfc: **Promise-only API**, no callback overloads, no pool support, no cancel/ping/resetServerContext.
607
+
608
+ ```typescript
609
+ import { binding, RfcClientBinding } from './binding';
610
+ import { RfcConnectionParameters, RfcObject } from './types';
611
+
612
+ export class Client {
613
+ private __client: RfcClientBinding;
614
+
615
+ constructor(connectionParameters: RfcConnectionParameters) {
616
+ if (!connectionParameters || typeof connectionParameters !== 'object') {
617
+ throw new TypeError('Client constructor requires connection parameters object');
618
+ }
619
+ this.__client = new binding.Client(connectionParameters);
620
+ }
621
+
622
+ get id(): number {
623
+ return this.__client._id;
624
+ }
625
+
626
+ get alive(): boolean {
627
+ return this.__client._alive;
628
+ }
629
+
630
+ open(): Promise<this> {
631
+ return new Promise((resolve, reject) => {
632
+ try {
633
+ this.__client.open((err: unknown) => {
634
+ if (err !== undefined) {
635
+ reject(err);
636
+ } else {
637
+ resolve(this);
638
+ }
639
+ });
640
+ } catch (ex) {
641
+ reject(ex);
642
+ }
643
+ });
644
+ }
645
+
646
+ close(): Promise<void> {
647
+ return new Promise((resolve, reject) => {
648
+ try {
649
+ this.__client.close((err: unknown) => {
650
+ if (err === undefined) {
651
+ resolve();
652
+ } else {
653
+ reject(err);
654
+ }
655
+ });
656
+ } catch (ex) {
657
+ reject(ex);
658
+ }
659
+ });
660
+ }
661
+
662
+ call(rfmName: string, rfmParams: RfcObject = {}): Promise<RfcObject> {
663
+ return new Promise((resolve, reject) => {
664
+ if (typeof rfmName !== 'string' || rfmName.length === 0) {
665
+ reject(new TypeError('Function module name must be a non-empty string'));
666
+ return;
667
+ }
668
+ try {
669
+ this.__client.invoke(
670
+ rfmName,
671
+ rfmParams,
672
+ (err: unknown, res: RfcObject) => {
673
+ if (err !== undefined && err !== null) {
674
+ reject(err);
675
+ } else {
676
+ resolve(res);
677
+ }
678
+ }
679
+ );
680
+ } catch (ex) {
681
+ reject(ex);
682
+ }
683
+ });
684
+ }
685
+ }
686
+ ```
687
+
688
+ - [ ] **Step 2: Commit**
689
+
690
+ ```bash
691
+ git add src/ts/client.ts
692
+ git commit -m "feat: add Client class (Promise-only API)"
693
+ ```
694
+
695
+ ---
696
+
697
+ ### Task 10: Port TypeScript — index.ts (entry point)
698
+
699
+ **Files:**
700
+ - Create: `sap-rfc-lite/src/ts/index.ts`
701
+
702
+ - [ ] **Step 1: Create index.ts**
703
+
704
+ ```typescript
705
+ export { Client } from './client';
706
+ export { binding } from './binding';
707
+ export * from './types';
708
+ ```
709
+
710
+ - [ ] **Step 2: Commit**
711
+
712
+ ```bash
713
+ git add src/ts/index.ts
714
+ git commit -m "feat: add package entry point"
715
+ ```
716
+
717
+ ---
718
+
719
+ ### Task 11: Write tests
720
+
721
+ **Files:**
722
+ - Create: `sap-rfc-lite/test/client.test.ts`
723
+ - Create: `sap-rfc-lite/jest.config.ts`
724
+
725
+ - [ ] **Step 1: Create jest.config.ts**
726
+
727
+ ```typescript
728
+ export default {
729
+ preset: 'ts-jest',
730
+ testEnvironment: 'node',
731
+ roots: ['<rootDir>/test'],
732
+ };
733
+ ```
734
+
735
+ - [ ] **Step 2: Write unit test for Client class (type/API checks)**
736
+
737
+ ```typescript
738
+ import { Client } from '../src/ts/client';
739
+
740
+ // These tests verify the TypeScript API surface without requiring SAP NW RFC SDK.
741
+ // Integration tests that actually call RFC need SDK + SAP system.
742
+
743
+ describe('Client API surface', () => {
744
+ it('constructor requires connection parameters', () => {
745
+ expect(() => new Client(undefined as any)).toThrow(TypeError);
746
+ expect(() => new Client(null as any)).toThrow(TypeError);
747
+ });
748
+
749
+ it('exports expected types', () => {
750
+ // Verify the module exports compile correctly
751
+ const mod = require('../src/ts/index');
752
+ expect(mod.Client).toBeDefined();
753
+ expect(typeof mod.Client).toBe('function');
754
+ });
755
+ });
756
+ ```
757
+
758
+ Note: Full integration tests will be written after first successful build with SDK.
759
+
760
+ - [ ] **Step 3: Commit**
761
+
762
+ ```bash
763
+ git add test/ jest.config.ts
764
+ git commit -m "test: add Client API surface tests"
765
+ ```
766
+
767
+ ---
768
+
769
+ ### Task 12: Integrate into mcp-abap-adt
770
+
771
+ **Files:**
772
+ - Modify: `mcp-abap-adt/package.json` — replace `node-rfc` with `sap-rfc-lite`
773
+ - Modify: `mcp-abap-adt-clients` repo — update `require('node-rfc')` to `require('sap-rfc-lite')` in RfcAbapConnection **source** (not compiled .js in node_modules)
774
+
775
+ - [ ] **Step 1: Update mcp-abap-adt package.json**
776
+
777
+ Replace in `optionalDependencies`:
778
+ ```json
779
+ "node-rfc": "^3.3.1"
780
+ ```
781
+ with:
782
+ ```json
783
+ "sap-rfc-lite": "file:../sap-rfc-lite"
784
+ ```
785
+ (or npm registry path once published)
786
+
787
+ - [ ] **Step 2: Update RfcAbapConnection import in mcp-abap-adt-clients repo**
788
+
789
+ In `/home/okyslytsia/prj/mcp-abap-adt-clients` — the **source** of `@mcp-abap-adt/connection` package — change:
790
+ ```javascript
791
+ const noderfc = require('node-rfc');
792
+ Client = noderfc.Client;
793
+ ```
794
+ to:
795
+ ```javascript
796
+ const { Client } = require('sap-rfc-lite');
797
+ ```
798
+
799
+ The API is identical: `Client` constructor takes connection params object, has `open()`, `call()`, `close()`, `alive`.
800
+
801
+ - [ ] **Step 3: Verify RfcAbapConnection still works**
802
+
803
+ The `call()` method in sap-rfc-lite wraps `invoke()` with a Promise — same as the original node-rfc `Client.call()`. Return structure is identical since nwrfcsdk.cc is unchanged.
804
+
805
+ - [ ] **Step 4: Commit both repos**
806
+
807
+ ```bash
808
+ # sap-rfc-lite
809
+ cd /home/okyslytsia/prj/sap-rfc-lite
810
+ git add -A
811
+ git commit -m "chore: ready for integration"
812
+
813
+ # mcp-abap-adt
814
+ cd /home/okyslytsia/prj/mcp-abap-adt
815
+ git add package.json
816
+ git commit -m "feat: replace node-rfc with sap-rfc-lite"
817
+ ```
818
+
819
+ ---
820
+
821
+ ## API Compatibility Matrix
822
+
823
+ | node-rfc API | sap-rfc-lite | Notes |
824
+ |---|---|---|
825
+ | `new Client(params)` | `new Client(params)` | Identical |
826
+ | `client.open()` → Promise | `client.open()` → Promise | Identical |
827
+ | `client.call(name, params)` → Promise | `client.call(name, params)` → Promise | Identical |
828
+ | `client.close()` → Promise | `client.close()` → Promise | Identical |
829
+ | `client.alive` | `client.alive` | Identical |
830
+ | `client.open(callback)` | ❌ removed | Promise-only |
831
+ | `client.ping()` | ❌ removed | Use `alive` property |
832
+ | `client.cancel()` | ❌ removed | Not used |
833
+ | `client.resetServerContext()` | ❌ removed | Not used |
834
+ | `client.release()` | ❌ removed | Pool-only, removed |
835
+ | `client.connectionInfo` | ❌ removed | Not used |
836
+ | `Pool` class | ❌ removed | Not used |
837
+ | `Server` class | ❌ removed | Not used |
838
+ | `Throughput` class | ❌ removed | Not used |
839
+
840
+ ## Risk Assessment
841
+
842
+ | Risk | Mitigation |
843
+ |---|---|
844
+ | nwrfcsdk.cc has subtle bugs we don't catch | Keep it verbatim — zero modifications to marshalling logic |
845
+ | SAP NW RFC SDK version incompatibility | Same N-API version (8), same SDK API surface as node-rfc 3.3.1 |
846
+ | Build fails on some platforms | Keep binding.gyp platform conditions identical to node-rfc |
847
+ | connectionCheck() auto-reconnect breaks without Pool | Remove pool branch, keep direct client reconnect — simpler and correct |
848
+ | SNC/TLS via RFC not supported | `loadCryptoLibrary()` and `setIniFileDirectory()` removed from addon.cc. Can be added back later if needed. Document as known limitation |
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/core",
3
3
  "mcpName": "io.github.fr0ster/mcp-abap-adt",
4
- "version": "4.6.0",
4
+ "version": "4.7.0",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "exports": {
@@ -154,7 +154,7 @@
154
154
  "zod": "^4.3.6"
155
155
  },
156
156
  "optionalDependencies": {
157
- "node-rfc": "^3.3.1"
157
+ "@mcp-abap-adt/sap-rfc-lite": "^0.1.0"
158
158
  },
159
159
  "engines": {
160
160
  "node": ">=20.0.0",