@mcp-abap-adt/connection 8.0.0 → 8.0.1

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
@@ -7,6 +7,27 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [8.0.1] - 2026-09-08
11
+
12
+ **Documentation only — 7.0.0 and 8.0.0 shipped without their migration note.**
13
+
14
+ Both majors are already on npm, and `docs/` travels in the tarball, so the
15
+ installed package described the pre-7.0.0 connection: no request id, no
16
+ profiling, no `flushGoodbye`, and no word on what a consumer on 6.x must
17
+ change. This release carries the documentation those two releases owed and
18
+ changes no code.
19
+
20
+ ### Documentation
21
+
22
+ - `docs/MIGRATION-8.0.md` (new): what a consumer on 6.x does about the
23
+ `IAdtWireResponse` return of `makeAdtRequest`, the capability atoms the
24
+ class now declares, and the two headers every request now carries.
25
+ - `docs/STATEFUL_SESSION_GUIDE.md`: the session type is the connection's —
26
+ `x-sap-adt-sessiontype: stateful` is added by the transport, not by a
27
+ caller's headers — and `flushGoodbye` is how a caller waits for the
28
+ goodbye.
29
+ - `README.md` and `docs/INDEX.md` point at both.
30
+
10
31
  ## [8.0.0] - 2026-09-08
11
32
 
12
33
  **A consumer holding the contract can now do what the connection could always do.**
@@ -1513,7 +1534,8 @@ const connection = createAbapConnection(config, logger);
1513
1534
  - JWT token refresh now properly handles connection errors (401/403 during initial connect)
1514
1535
  - Permission errors (403 with "ExceptionResourceNoAccess") no longer trigger JWT refresh loops
1515
1536
  - Proper separation: base class handles HTTP/session, concrete classes handle auth-specific errors
1516
- [Unreleased]: https://github.com/fr0ster/mcp-abap-connection/compare/v8.0.0...HEAD
1537
+ [Unreleased]: https://github.com/fr0ster/mcp-abap-connection/compare/v8.0.1...HEAD
1538
+ [8.0.1]: https://github.com/fr0ster/mcp-abap-connection/compare/v8.0.0...v8.0.1
1517
1539
  [8.0.0]: https://github.com/fr0ster/mcp-abap-connection/compare/v7.0.0...v8.0.0
1518
1540
  [7.0.0]: https://github.com/fr0ster/mcp-abap-connection/compare/v6.1.0...v7.0.0
1519
1541
  [6.0.1]: https://github.com/fr0ster/mcp-abap-connection/compare/v6.0.0...v6.0.1
package/README.md CHANGED
@@ -34,7 +34,9 @@ The package uses a clean separation of concerns:
34
34
 
35
35
  - **`AbstractAbapConnection`** (abstract, internal only):
36
36
  - Common HTTP request logic
37
- - Session lifecycle: `connect()` / `disconnect()`, admission, lock windows, teardown draining
37
+ - Session lifecycle: `connect()` / `disconnect()` / `flushGoodbye()`, admission, lock windows, teardown draining
38
+ - Capability atoms a consumer narrows to, rather than casting to a connector class:
39
+ `ISessionLifecycleAware`, `ICriticalSection`, `IRequestProfiling`
38
40
  - Session management (cookies, CSRF tokens)
39
41
  - CSRF token fetching with retry
40
42
  - Auth-agnostic - knows nothing about Basic or JWT
package/docs/INDEX.md CHANGED
@@ -15,6 +15,7 @@ mcp-abap-connection/
15
15
  │ ├── INSTALLATION.md # Setup and installation guide
16
16
  │ ├── USAGE.md # API documentation and examples
17
17
  │ ├── MIGRATION-2.0.md # Moving to the explicit session lifecycle
18
+ │ ├── MIGRATION-8.0.md # request headers leave the stateful branch; onto interfaces 39; flushGoodbye
18
19
  │ ├── MIGRATION-6.0.md # the factory and the per-credential classes go; RFC is a transport
19
20
  │ ├── MIGRATION-4.0.md # JWT error classification: 401 refreshes, 403 propagates
20
21
  │ ├── SCOPE.md # What this package does and does not own
@@ -44,6 +45,7 @@ mcp-abap-connection/
44
45
  - 🔑 [JWT Auth Tools](./JWT_AUTH_TOOLS.md) - CLI tool for browser-based authentication
45
46
 
46
47
  ### Upgrading
48
+ - 🚚 [Migrating to 7.0.0 and 8.0.0](./MIGRATION-8.0.md) - `sap-adt-request-id` and `X-sap-adt-profiling` on every request, `x-sap-security-session: use` on cloud, the contracts floor at 39, and `flushGoodbye()`
47
49
  - 🚚 [Migrating to 6.0.0](./MIGRATION-6.0.md) - the factory and the per-credential classes are removed; the RFC wire is a transport you hand to the on-prem connector
48
50
  - 🧱 [Migrating to 4.0.0](./MIGRATION-4.0.md) - JWT error classification: a 401 refreshes, a 403 propagates with the server's message
49
51
  - 🧱 [Migrating to 2.0.0](./MIGRATION-2.0.md) - The explicit session lifecycle: `connect()` is required
@@ -0,0 +1,171 @@
1
+ # Migration to 7.0 and 8.0
2
+
3
+ Two majors, and **no code of yours has to change for either**. One alters what
4
+ goes on the wire; the other moves the contracts floor and makes methods you
5
+ already had reachable through the types you already hold.
6
+
7
+ If you build against `@mcp-abap-adt/interfaces` and never cast to a connector
8
+ class, there is nothing to do but install.
9
+
10
+ ## 7.0 — headers that belong to the request stop belonging to the session
11
+
12
+ Three headers used to be written together, inside the stateful branch:
13
+
14
+ ```ts
15
+ if (this.sessionMode === 'stateful') {
16
+ requestHeaders['x-sap-adt-sessiontype'] = 'stateful';
17
+ requestHeaders['sap-adt-request-id'] = randomUUID().replace(/-/g, '');
18
+ requestHeaders['X-sap-adt-profiling'] = 'server-time';
19
+ }
20
+ ```
21
+
22
+ Only the first belongs there. A request id identifies the **request**, and
23
+ asking the server to report its own processing time is not a property of the
24
+ session either. Eclipse sends both on everything — measured on ADT 3.60.0, a
25
+ stateless source `PUT` carries them and no session type at all.
26
+
27
+ It stayed invisible while every write ran inside a lock window. Once
28
+ `@mcp-abap-adt/adt-clients` narrowed stateful to the `LOCK` and the `UNLOCK`,
29
+ the writes silently lost two headers: of 792 requests in a full run, 99 carried
30
+ a request id and **693 carried neither**.
31
+
32
+ ### What changed for you
33
+
34
+ | | |
35
+ |---|---|
36
+ | `sap-adt-request-id` | now on every request, fresh each time |
37
+ | `X-sap-adt-profiling` | now on every request, from a settable default |
38
+ | `x-sap-security-session: use` | **cloud only**, on every request once a session exists |
39
+ | `x-sap-adt-sessiontype` | unchanged — still the only one that varies with the mode |
40
+
41
+ Nothing in the type surface moved. The major is for the wire: every request
42
+ looks different in an SAP trace, and a system that reacts badly to either should
43
+ be findable by version rather than by reading a dump.
44
+
45
+ ### Your own headers win
46
+
47
+ Both new headers are **defaults**. A caller who names either in
48
+ `options.headers` keeps their value, matched without case:
49
+
50
+ ```ts
51
+ await conn.makeAdtRequest({
52
+ url, method: 'GET', timeout: 30_000,
53
+ headers: { 'sap-adt-request-id': myCorrelationId }, // kept, not replaced
54
+ });
55
+ ```
56
+
57
+ That is not hypothetical: `adt-clients` passes its own id to
58
+ `getDiscovery({ requestId })` so the id it logs is the id on the wire.
59
+
60
+ ### Turning the profiling off
61
+
62
+ ```ts
63
+ conn.setProfilingRequest(null); // ask for nothing
64
+ conn.setProfilingRequest('server-time'); // the default, and what Eclipse asks for
65
+ ```
66
+
67
+ Nothing in this package reads the `server-time=…` that comes back. An
68
+ investigation does, though — the unit is **microseconds**, which a trial's
69
+ unpublish job settled by answering `server-time=132512547` on a request that
70
+ takes about 133 seconds.
71
+
72
+ ## 8.0 — onto interfaces 39.0.0, and the atoms are declared
73
+
74
+ ### The floor
75
+
76
+ `@mcp-abap-adt/interfaces` moves from `^21.0.0` to `^39.0.0`. Install it
77
+ alongside; a consumer pinned below 39 cannot have both.
78
+
79
+ Seventeen majors, and the whole migration inside this package was eight compiler
80
+ errors in one file — `makeAdtRequest` returning `IAdtWireResponse<T, D>` rather
81
+ than `IAdtResponse<T, D>`, and `isNetworkError` coming home because interfaces
82
+ stopped emitting code in 29.0.0.
83
+
84
+ **If you deduplicate nothing else, deduplicate this.** Two copies of the
85
+ contracts in one graph are structurally identical and do not compare equal, so
86
+ they produce errors that read as impossible.
87
+
88
+ ```
89
+ npm ls @mcp-abap-adt/interfaces # should print one version, deduped
90
+ ```
91
+
92
+ ### `flushGoodbye()` — the half of `disconnect()` that was missing
93
+
94
+ `disconnect()` dispatches the logoff and does not await it, on purpose: a
95
+ goodbye carries no request timeout, and a server that never answers must not
96
+ hold a teardown open.
97
+
98
+ That is right for a teardown and wrong for a **reconnect**:
99
+
100
+ ```ts
101
+ await conn.disconnect();
102
+ await conn.flushGoodbye(); // give the goodbye its budget to finish first
103
+ await conn.connect();
104
+ ```
105
+
106
+ Without the middle line, the next session opens while the previous one's goodbye
107
+ is still being assembled and the server keeps both. Measured on E19 through a
108
+ test harness that recycled the session after each test: **a new ABAP session
109
+ every one to two seconds for a whole run, none released**, each living to its
110
+ own thirty-minute idle timeout.
111
+
112
+ **The budget bounds the waiting, not the overlap.** If the goodbye finishes in
113
+ time there is no overlap; if it does not, you proceed and it stays outstanding
114
+ for as long as it takes. A return is not a confirmation and not even of
115
+ dispatch.
116
+
117
+ Calling `disconnect()` twice is **not** a substitute — a repeat call does not
118
+ wait either.
119
+
120
+ ### Reaching the controls through the contract
121
+
122
+ `beginCriticalSection()`, `setProfilingRequest()` and `flushGoodbye()` all
123
+ existed before. What changed is that a consumer can reach them without a cast:
124
+ `AbapConnection` is `IAbapConnection`, and these now live on capability atoms
125
+ that this connection declares.
126
+
127
+ ```ts
128
+ import type {
129
+ IAbapConnection,
130
+ ICriticalSection,
131
+ IRequestProfiling,
132
+ ISessionLifecycleAware,
133
+ } from '@mcp-abap-adt/interfaces';
134
+
135
+ function protectTheWindow(conn: IAbapConnection & ICriticalSection) {
136
+ conn.beginCriticalSection();
137
+ try {
138
+ // the ordinary per-request deadline does not apply in here
139
+ } finally {
140
+ conn.endCriticalSection();
141
+ }
142
+ }
143
+ ```
144
+
145
+ `flushGoodbye` is a member of `ISessionLifecycleAware`, not an atom of its own:
146
+ waiting for the goodbye is not a separate capability from sending it.
147
+
148
+ ### One thing to know about `implements`
149
+
150
+ TypeScript is structural. Removing an atom from a class's `implements` list
151
+ changes nothing for a consumer — the class still has the methods and narrowing
152
+ still succeeds. What the clause buys is the compiler checking the class *against*
153
+ the contract in the other direction: remove `beginCriticalSection` itself and it
154
+ is `TS2420`.
155
+
156
+ Worth knowing before writing a guard that tests the clause rather than the
157
+ method.
158
+
159
+ ### `IRenewableCredential` is an atom now
160
+
161
+ Not this package's change, but it lands with the floor. Renewing is something a
162
+ credential can also do, not a kind of credential, so a guard that narrowed to
163
+ `IRenewableCredential` alone now hands the caller something that renews and
164
+ cannot authenticate:
165
+
166
+ ```ts
167
+ // before
168
+ function isRenewable(c: IAuthProvider): c is IRenewableCredential
169
+ // after
170
+ function isRenewable(c: IAuthProvider): c is IAuthProvider & IRenewableCredential
171
+ ```
@@ -120,6 +120,22 @@ authentication answer. A 403 never did this: it is an authorization answer, not
120
120
  If you decide the refusal meant a stale token, `renew()` and reconnect are yours to call — and a
121
121
  reconnect is a NEW session, so do it outside a lock window rather than inside one.
122
122
 
123
+ **Wait for the goodbye before opening the next one.** `disconnect()` dispatches the logoff and does
124
+ not await it, so a reconnect otherwise opens the next session while the previous one's goodbye is
125
+ still being assembled, and the server keeps both:
126
+
127
+ ```typescript
128
+ await conn.disconnect();
129
+ await conn.flushGoodbye(); // give the goodbye its budget to finish first
130
+ await conn.connect();
131
+ ```
132
+
133
+ Measured on E19 through a harness that recycled the session after each test: a new ABAP session
134
+ every one to two seconds for a whole run, none released, each living to its own thirty-minute idle
135
+ timeout. The budget bounds the waiting, not the overlap — if the goodbye finishes in time there is
136
+ none, and if it does not you proceed while it stays outstanding. Calling `disconnect()` again is not
137
+ a substitute: a repeat call does not wait either.
138
+
123
139
  ---
124
140
 
125
141
  ## Interaction With ADT Clients
@@ -189,6 +205,13 @@ lock is still very much held. That — not any teardown — is why
189
205
  `beginCriticalSection()` raises the effective timeout to a large ceiling for the
190
206
  duration of a `lock → modify → unlock` chain.
191
207
 
208
+ Since 8.0.0 you reach it through the contract rather than the class: it is
209
+ `ICriticalSection` in `@mcp-abap-adt/interfaces`, which this connection declares.
210
+ What it promises is narrow and worth stating exactly — inside a section the
211
+ *ordinary* per-request deadline does not apply. Not that no request can be cut
212
+ short: the ceiling is `SAP_TIMEOUT_CRITICAL`, ten minutes by default, and a
213
+ socket ends a request whatever a contract says.
214
+
192
215
  ### Two sessions, and they are not the same thing
193
216
 
194
217
  There are two sessions here, and they are not the same thing:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/connection",
3
- "version": "8.0.0",
3
+ "version": "8.0.1",
4
4
  "description": "ABAP connection layer for MCP ABAP ADT server",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",