@mcp-abap-adt/proxy 2.0.0 → 4.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.
Files changed (67) hide show
  1. package/CHANGELOG.md +218 -0
  2. package/LICENSE +669 -17
  3. package/README.md +79 -8
  4. package/bin/mcp-abap-adt-proxy-mcp.js +113 -0
  5. package/dist/index.d.ts +10 -4
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +54 -97
  8. package/dist/lib/config.d.ts.map +1 -1
  9. package/dist/lib/config.js +9 -1
  10. package/dist/lib/envInterpolation.d.ts +14 -2
  11. package/dist/lib/envInterpolation.d.ts.map +1 -1
  12. package/dist/lib/envInterpolation.js +34 -7
  13. package/dist/lib/stores.d.ts +23 -2
  14. package/dist/lib/stores.d.ts.map +1 -1
  15. package/dist/lib/stores.js +56 -1
  16. package/dist/mcp/cli.d.ts +2 -0
  17. package/dist/mcp/cli.d.ts.map +1 -0
  18. package/dist/mcp/cli.js +21 -0
  19. package/dist/mcp/configs.d.ts +33 -0
  20. package/dist/mcp/configs.d.ts.map +1 -0
  21. package/dist/mcp/configs.js +142 -0
  22. package/dist/mcp/ports.d.ts +15 -0
  23. package/dist/mcp/ports.d.ts.map +1 -0
  24. package/dist/mcp/ports.js +35 -0
  25. package/dist/mcp/registry.d.ts +57 -0
  26. package/dist/mcp/registry.d.ts.map +1 -0
  27. package/dist/mcp/registry.js +145 -0
  28. package/dist/mcp/server.d.ts +34 -0
  29. package/dist/mcp/server.d.ts.map +1 -0
  30. package/dist/mcp/server.js +79 -0
  31. package/dist/mcp/shutdown.d.ts +37 -0
  32. package/dist/mcp/shutdown.d.ts.map +1 -0
  33. package/dist/mcp/shutdown.js +82 -0
  34. package/dist/mcp/supervisor.d.ts +125 -0
  35. package/dist/mcp/supervisor.d.ts.map +1 -0
  36. package/dist/mcp/supervisor.js +330 -0
  37. package/dist/mcp/tools.d.ts +28 -0
  38. package/dist/mcp/tools.d.ts.map +1 -0
  39. package/dist/mcp/tools.js +152 -0
  40. package/dist/proxy/btpProxy.d.ts +24 -73
  41. package/dist/proxy/btpProxy.d.ts.map +1 -1
  42. package/dist/proxy/btpProxy.js +65 -616
  43. package/dist/proxy/credentials.d.ts +45 -0
  44. package/dist/proxy/credentials.d.ts.map +1 -0
  45. package/dist/proxy/credentials.js +41 -0
  46. package/dist/proxy/requestHandler.d.ts +38 -0
  47. package/dist/proxy/requestHandler.d.ts.map +1 -0
  48. package/dist/proxy/requestHandler.js +73 -0
  49. package/dist/proxy/reverseProxy.d.ts +11 -2
  50. package/dist/proxy/reverseProxy.d.ts.map +1 -1
  51. package/dist/proxy/reverseProxy.js +52 -9
  52. package/dist/router/headerAnalyzer.js +2 -2
  53. package/dist/router/requestInterceptor.js +9 -9
  54. package/docs/API.md +172 -0
  55. package/docs/ARCHITECTURE.md +322 -0
  56. package/docs/CLIENT_SETUP.md +413 -0
  57. package/docs/CONFIGURATION.md +258 -0
  58. package/docs/MIGRATION-4.0.md +125 -0
  59. package/docs/ROUTING_LOGIC.md +126 -0
  60. package/docs/TROUBLESHOOTING.md +488 -0
  61. package/docs/USAGE.md +422 -0
  62. package/docs/YAML_CONFIG.md +290 -0
  63. package/docs/mcp-proxy-config.example.yaml +62 -0
  64. package/package.json +17 -10
  65. package/dist/proxy/cloudLlmHubProxy.d.ts +0 -2
  66. package/dist/proxy/cloudLlmHubProxy.d.ts.map +0 -1
  67. package/dist/proxy/cloudLlmHubProxy.js +0 -3
package/CHANGELOG.md CHANGED
@@ -7,6 +7,224 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.0.1] - 2026-09-24
11
+
12
+ **A variable from an env file could be silently lost, and the failure blamed the
13
+ variable instead of naming the file.** Both hit Windows hardest, with the same
14
+ config and the same `.env` that work on Linux.
15
+
16
+ ### Fixed
17
+
18
+ - **An empty environment variable no longer shadows the env file.** The lookup
19
+ was `process.env[key] ?? envFileMap[key]`, and `??` skips only `undefined` — so
20
+ a `SAP_LOGIN=` sitting in the environment beat the file the user had pointed at
21
+ with `--env-file`, and the header went out empty with nothing reported. Windows
22
+ carries variables nobody set deliberately, which is why the same files behaved
23
+ differently there. A real value in the environment still wins.
24
+
25
+ - **The interpolation failure now names where it looked.** It said only
26
+ `Config references undefined env variable: SAP_LOGIN`, so three different
27
+ causes produced one sentence and the cause had to be guessed:
28
+
29
+ ```
30
+ ... looked in process.env only — no env file was given (--env-file or envFile:)
31
+ ... looked in process.env, then /path/e19.env, which yielded NO variables
32
+ (0 bytes) — check its encoding, since a UTF-16 file reads as nothing here
33
+ ... looked in process.env, then /path/e19.env (1: SAP_PASSWORD)
34
+ ```
35
+
36
+ An env file that yields nothing is still not an error — an empty `.env` is
37
+ legitimate, and a config may take every value from the environment. It is
38
+ reported by whoever then needs a variable, where it can be said usefully.
39
+
40
+ ### Documentation
41
+
42
+ - `docs/YAML_CONFIG.md`: a Windows path in **double** quotes is a YAML escape
43
+ context. `envFile: "C:\Users\..."` fails to parse (`\U` is a Unicode escape)
44
+ and `"C:\temp\e19.env"` parses *silently wrong* (`\e` becomes 0x1B). Use
45
+ single quotes, no quotes, or forward slashes.
46
+
47
+ ## [4.0.0] - 2026-09-24
48
+
49
+ **The proxy stops re-implementing the broker, there is one forwarding path
50
+ instead of two, and a second command manages proxies for a client that cannot
51
+ start them itself.** No existing configuration key changes meaning and no flag is
52
+ added to `mcp-abap-adt-proxy`; what changes is what the proxy holds while it runs,
53
+ how an SSE response reaches the client, and which packages the contracts come
54
+ from.
55
+
56
+ ### Changed
57
+
58
+ - **BREAKING** — `@mcp-abap-adt/interfaces` is no longer a dependency.
59
+ The contracts now come from the packages they live in, and
60
+ `@mcp-abap-adt/connection` arrives for the credential:
61
+
62
+ ```
63
+ @mcp-abap-adt/interfaces ^7.0.0 → removed
64
+ @mcp-abap-adt/interfaces-network (new) → ^2.0.0 every HTTP header constant
65
+ @mcp-abap-adt/interfaces-auth (new) → ^1.2.0 ITokenRefresher
66
+ @mcp-abap-adt/interfaces-auth-sap (new) → ^1.0.0 IAuthorizationConfig
67
+ @mcp-abap-adt/connection (new) → ^9.2.0 TokenAuthProvider
68
+ @mcp-abap-adt/auth-broker ^1.0.8 → ^2.2.0 (a major)
69
+ @mcp-abap-adt/auth-providers ^2.0.0 → ^2.2.2
70
+ @mcp-abap-adt/auth-stores ^1.0.4 → ^1.2.0
71
+ @mcp-abap-adt/header-validator ^0.1.8 → ^0.3.0
72
+ @mcp-abap-adt/logger ^0.1.4 → ^0.4.0
73
+ ```
74
+
75
+ `interfaces-adt` was taken and then dropped: the contracts moved twice while
76
+ this release was being written, and after the second move nothing here imports
77
+ anything from it. The old umbrella name is 376 deprecated re-exports; a
78
+ consumer importing contract types through this package's tree now installs the
79
+ package it names.
80
+
81
+ **Copies of the deprecated umbrella left in the tree: 6 → 0.** The last two
82
+ were declared by `auth-broker` and `header-validator`; both have since moved
83
+ onto the split packages themselves, so taking their latest removed the final
84
+ two. Every contract package now resolves to exactly one copy, except
85
+ `interfaces-utils`, which appears six times at the same version — a consequence
86
+ of this repository's `install-strategy=nested`, not of version skew.
87
+
88
+ - **BREAKING** — `BtpProxy.getJwtToken()` is replaced by
89
+ `getAuthorizationHeader()`, which answers a complete header VALUE
90
+ (`Bearer <token>`) or `null` where a credential is not a header at all.
91
+ `proxyRequest()`, `buildProxyRequest()` and the `ProxyRequest` / `ProxyResponse`
92
+ types are gone with the axios path they served.
93
+
94
+ - `forwardRequest()` takes an `Authorization` header value rather than a bare
95
+ token, and optionally a body a caller has already read off the request.
96
+ - `forwardRequest()` destroys the upstream connection when the client goes away.
97
+ It did not, so an abandoned event stream left a live connection to the target
98
+ after the client socket was gone — a released port reported while a socket was
99
+ still held, accumulating across repeated start/stop.
100
+
101
+ ### Added
102
+
103
+ - **A second command, `mcp-abap-adt-proxy-mcp`.** It speaks MCP over stdio and
104
+ its tools start and stop proxies: `proxy_start`, `proxy_stop`,
105
+ `proxy_status`. A client that wants to BE proxied still uses
106
+ `mcp-abap-adt-proxy`; this one is for a client that wants to MANAGE proxies,
107
+ and the two are kept apart so every plain proxy does not carry a management
108
+ surface it never uses.
109
+
110
+ **It works from the proxy configs already on disk** —
111
+ `~/.config/mcp-abap-adt/proxy/<name>.yaml`, the same files
112
+ `mcp-abap-adt-proxy --config` takes. `proxy_configs` lists them and
113
+ `proxy_start` takes one by name, so starting a proxy is choosing a name
114
+ rather than assembling settings, and credentials stay in the config behind
115
+ `${VAR}` interpolation instead of travelling through a tool call.
116
+
117
+ The config NAME is the unit, not the destination: several configs commonly
118
+ name the same `btpDestination` and differ in target URL and headers, so a
119
+ destination cannot identify one.
120
+
121
+ Each proxy takes a **free port**, and the port written in the config is
122
+ deliberately ignored — four of the configs in a real directory say 3001, so
123
+ honouring it is precisely the collision this mode exists to end.
124
+ `proxy_start` returns the URL actually bound.
125
+ `proxy_stop` frees the port and releases the credential; it never touches a
126
+ proxy another session started, which `proxy_status` shows and marks as such.
127
+
128
+ Every proxy runs inside the management process, and closing the session — or
129
+ `SIGINT`, or `SIGTERM` — stops all of them. A spawned child would be orphaned
130
+ by any signal the parent did not forward and would go on holding its HTTP and
131
+ OAuth callback ports, which is the same reason `bin/mcp-abap-adt-proxy.js`
132
+ has always loaded the server in-process.
133
+
134
+ A proxy nobody has used for 30 minutes (`idleTimeoutMs`) stops itself. It is
135
+ a backstop for a client that finished and forgot, not a substitute for
136
+ `proxy_stop` — which the tool descriptions say, and say again beside the URL.
137
+ The countdown runs only while nothing is in flight, so an open SSE connection
138
+ is never called idle however quiet it is.
139
+
140
+ Records under `runtime/` carry the machine's boot time, because a pid is
141
+ unique only within a boot: after a restart the same number can belong to an
142
+ unrelated live process, and a record would then be reported as another
143
+ session's proxy forever. They are written to a temporary name and renamed, so
144
+ a concurrent reader sees a whole record or no file, never a truncated one.
145
+
146
+ - `src/lib/stores.ts` now holds the whole path convention: four folders under
147
+ one relocatable base — `service-keys/`, `sessions/`, `proxy/` and the new
148
+ `runtime/` — through `storeBaseDir()` and `storeDir()`. `proxy/` and
149
+ `runtime/` had each grown a private copy of the platform logic in the module
150
+ that used them, which is three places for one convention to drift.
151
+
152
+ `storeDir()` deliberately has no working-directory fallback, unlike the
153
+ search path `getPlatformPaths()` returns: a runtime record written beside
154
+ wherever a client happened to be launched from is a record the next session
155
+ will not find.
156
+
157
+ - **The SSE transport imposes no headers of its own.** The deleted axios path set
158
+ `Accept: application/json, application/x-ndjson, text/event-stream` and
159
+ `Content-Type: application/json` on every request and forwarded none of the
160
+ client's. The proxy is transparent and answers for the authorization header
161
+ only, so the client's headers now go through as sent. A target that needs a
162
+ particular `Accept` gets it from that proxy's `defaultHeaders`, where it is
163
+ visible in the config rather than hidden in the proxy.
164
+ - **The upstream path is the client's path.** The old SSE path had three
165
+ branches: an explicit `targetUrl` meant base + the client's path; a service-key
166
+ URL already containing `/mcp` was used as-is with the client's path DISCARDED;
167
+ anything else got `/mcp/stream/http` appended. Now it is always base + the
168
+ client's path. Configs that set `targetUrl` — which is the documented way and
169
+ what every config in practice does — are unaffected; a config relying on the
170
+ service key's own URL should set `targetUrl` explicitly.
171
+ - The SSE transport forwards through the same transparent pipe as every other
172
+ transport. It used to rebuild the request by hand, carry it over axios, buffer
173
+ the answer and rewrap it as a JSON-RPC envelope — so an SSE response now
174
+ streams rather than arriving whole. Error envelopes still echo the JSON-RPC
175
+ `id`, which is why the body is still read before forwarding; the bytes handed
176
+ on are the ones that arrived, so `content-length` stays true.
177
+
178
+ ### Removed
179
+
180
+ - **The circuit breaker.** It only ever guarded the buffered axios forward that
181
+ this release deletes, and the streaming path has nowhere to put one without
182
+ buffering the response again — which is the thing being fixed.
183
+ `circuitBreakerThreshold` and `circuitBreakerTimeout` are still accepted so
184
+ existing configs load unchanged, and are now documented as inert.
185
+ `MCP_PROXY_CIRCUIT_BREAKER_THRESHOLD` likewise.
186
+
187
+
188
+ - The token cache, its TTL, the JWT `exp` decoder, and the proactive-refresh
189
+ timer per destination. The broker caches and knows expiry, and
190
+ `authorizationHeader()` renews behind the call — the timer was a `setTimeout`
191
+ per destination, alive for the life of the process, topping up a cache that
192
+ duplicated the broker.
193
+ - `src/proxy/cloudLlmHubProxy.ts`, whose entire contents were `// DELETED`.
194
+ - `src/proxy/btpProxy.ts` goes from 1248 lines to 490.
195
+
196
+ ### Documentation
197
+
198
+ - `docs/MIGRATION-4.0.md` (new): what a consumer does about each breaking change,
199
+ and which of them do not apply to them. Per the repository's release rule, a
200
+ breaking release owes one.
201
+ - **`docs/` now ships.** It did not, so `README.md` linked to nine pages that
202
+ were absent from the installed package — including the migration note this
203
+ release owes. The internal design document under `docs/superpowers/` is
204
+ deliberately still excluded.
205
+
206
+ - `docs/API.md` described `CloudLlmHubProxy.proxyRequest()` and an import path
207
+ for a file containing one comment. It now documents the facade and the pipe.
208
+ - `docs/ARCHITECTURE.md` and `CLAUDE.md` described a proxy client that caches
209
+ tokens for 30 minutes, and pointed at `cloudLlmHubProxy.ts` for it.
210
+ - `CLAUDE.md` pointed test instructions at `cloudLlmHubProxy.test.ts`, which has
211
+ not existed for some time.
212
+
213
+ ## [3.0.0] - 2026-09-03
214
+
215
+ ### Licence
216
+
217
+ - **This tool is now `GPL-3.0-only`.** It was MIT up to and including 2.0.0, and
218
+ those versions stay MIT — a licence change is not retroactive.
219
+
220
+ A finished tool rather than a library to build on, so it takes the full GPL
221
+ rather than the LGPL its own dependencies carry: running it and using it on your
222
+ own data carries no conditions, while distributing it — or a modified version —
223
+ means passing on the same freedoms, source included.
224
+
225
+ Copyright © 2025–2026 Oleksii Kyslytsia.
226
+
227
+
10
228
  ## [2.0.0] - 2026-08-01
11
229
 
12
230
  ### Breaking Changes