@mcp-abap-adt/proxy 1.3.0 → 1.5.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
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.5.0] - 2026-06-02
11
+
12
+ ### Added
13
+ - **Exit on incomplete authentication.** When authentication is requested but not completed — at startup (eager auth) or on a request that needs a token — the proxy now logs a categorised reason (timeout / invalid credentials / service key / network) and exits with code 1, so it can be restarted to retry (mirroring the one-shot `mcp-auth` command) instead of running in a permanently unauthenticated state. A downstream/forward error (after auth succeeds) is still returned to the client without exiting.
14
+
15
+ ### Changed
16
+ - Bump `@mcp-abap-adt/auth-broker` `^1.0.5` → `^1.0.6`: the broker no longer starts a redundant second browser login after an interactive session-login failure, so the real cause (e.g. login timeout) is reported instead of a misleading `Port <n> is already in use`.
17
+
18
+ ## [1.4.0] - 2026-06-02
19
+
20
+ ### Added
21
+ - **Manual paste login for `--browser none` / `headless`** (via `@mcp-abap-adt/auth-providers@1.1.0`). When the browser runs on a different machine than the proxy, login can be completed by opening the paste form at `http://<host>:<browser-auth-port>/` and pasting the `code` (or the whole redirected URL), or by pasting the code in the terminal — in addition to the existing automatic localhost callback.
22
+
23
+ ### Fixed
24
+ - **`--browser none` now prints the authorization URL.** The per-destination `AuthorizationCodeProvider` was created without a logger, so the "Open this URL" prompt from `browserAuth` was silently dropped and login could never be completed in headless mode. The proxy now passes its logger to the provider; `auth-providers@1.1.0` additionally falls back to `stderr` when no logger is supplied.
25
+ - **Platform-correct service-key path in `--help` and errors.** The "service key not found" hint and `--help` hard-coded the Unix `~/.config/...` path, which is wrong on Windows (`%USERPROFILE%\Documents\mcp-abap-adt\service-keys`). Both now show the real resolved directory and mention the `AUTH_BROKER_PATH` override.
26
+
27
+ ### Changed
28
+ - Bump `@mcp-abap-adt/auth-providers` `^1.0.5` → `^1.1.0`.
29
+
10
30
  ## [1.3.0] - 2026-05-28
11
31
 
12
32
  ### Added
package/dist/index.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare class McpAbapAdtProxyServer {
17
17
  private config;
18
18
  private httpServer?;
19
19
  private btpProxy?;
20
+ private authExiting;
20
21
  constructor(transportConfig?: TransportConfig, configPath?: string);
21
22
  /**
22
23
  * Setup MCP handlers
@@ -38,6 +39,13 @@ export declare class McpAbapAdtProxyServer {
38
39
  * Start SSE server
39
40
  */
40
41
  private startSseServer;
42
+ /**
43
+ * Authentication was requested but could not be completed. Report the reason
44
+ * clearly, release resources, and exit(1) so the proxy is restarted (and the
45
+ * user gets a fresh login prompt) instead of running unauthenticated. Mirrors
46
+ * the one-shot `mcp-auth` command, which exits on a failed/incomplete login.
47
+ */
48
+ private fatalAuthFailure;
41
49
  /**
42
50
  * Shutdown server
43
51
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAuBH,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAqBlC;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAW;gBAEhB,eAAe,CAAC,EAAE,eAAe,EAAE,UAAU,CAAC,EAAE,MAAM;IAoDlE;;OAEG;IACH,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAuE1B;;OAEG;YACW,eAAe;IAgG7B;;OAEG;YACW,kBAAkB;IA8ChC;;OAEG;YACW,cAAc;IA6T5B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAoBhC;AAGD,eAAe,qBAAqB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH,OAAO,EAEL,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAgElC;;GAEG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,MAAM,CAAY;IAC1B,OAAO,CAAC,eAAe,CAAkB;IACzC,OAAO,CAAC,MAAM,CAAgC;IAC9C,OAAO,CAAC,UAAU,CAAC,CAAa;IAChC,OAAO,CAAC,QAAQ,CAAC,CAAW;IAC5B,OAAO,CAAC,WAAW,CAAS;gBAEhB,eAAe,CAAC,EAAE,eAAe,EAAE,UAAU,CAAC,EAAE,MAAM;IAoDlE;;OAEG;IACH,OAAO,CAAC,aAAa;IAMrB;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IA2E1B;;OAEG;YACW,eAAe;IAgG7B;;OAEG;YACW,kBAAkB;IAgEhC;;OAEG;YACW,cAAc;IA6T5B;;;;;OAKG;YACW,gBAAgB;IAiC9B;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAoBhC;AAGD,eAAe,qBAAqB,CAAC"}
package/dist/index.js CHANGED
@@ -18,6 +18,7 @@ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
18
18
  const reverseProxy_js_1 = require("./proxy/reverseProxy.js");
19
19
  const config_js_1 = require("./lib/config.js");
20
20
  const logger_js_1 = require("./lib/logger.js");
21
+ const stores_js_1 = require("./lib/stores.js");
21
22
  const transportConfig_js_1 = require("./lib/transportConfig.js");
22
23
  const btpProxy_js_1 = require("./proxy/btpProxy.js");
23
24
  const headerAnalyzer_js_1 = require("./router/headerAnalyzer.js");
@@ -31,6 +32,38 @@ function getBodyId(body) {
31
32
  }
32
33
  return null;
33
34
  }
35
+ /**
36
+ * Classify an authentication failure into a human-readable reason so the proxy
37
+ * can report *why* it is exiting (timeout vs bad credentials vs network ...).
38
+ */
39
+ function classifyAuthFailure(error) {
40
+ const msg = error instanceof Error ? error.message : String(error);
41
+ if (/timeout/i.test(msg)) {
42
+ return {
43
+ category: 'timeout',
44
+ reason: 'the login URL was not completed in time',
45
+ };
46
+ }
47
+ if (/service key|missing required fields/i.test(msg)) {
48
+ return {
49
+ category: 'service-key',
50
+ reason: 'service key missing or incomplete',
51
+ };
52
+ }
53
+ if (/invalid_client|unauthorized|\b401\b|invalid credentials/i.test(msg)) {
54
+ return {
55
+ category: 'credentials',
56
+ reason: 'invalid credentials — UAA rejected the client',
57
+ };
58
+ }
59
+ if (/ENOTFOUND|ECONNREFUSED|ETIMEDOUT|network|ENETUNREACH/i.test(msg)) {
60
+ return {
61
+ category: 'network',
62
+ reason: 'network error reaching the authentication server',
63
+ };
64
+ }
65
+ return { category: 'other', reason: msg };
66
+ }
34
67
  /**
35
68
  * MCP ABAP ADT Proxy Server
36
69
  */
@@ -40,6 +73,7 @@ class McpAbapAdtProxyServer {
40
73
  config;
41
74
  httpServer;
42
75
  btpProxy;
76
+ authExiting = false;
43
77
  constructor(transportConfig, configPath) {
44
78
  // Load config first to extract transport overrides from YAML/JSON file
45
79
  const resolvedConfigPath = configPath || (0, config_js_1.getConfigPath)();
@@ -125,20 +159,24 @@ Authorization source: BTP destination "${this.config.btpDestination}"`;
125
159
  });
126
160
  return;
127
161
  }
128
- // Eager initialization of BTP authentication
162
+ // Eager initialization of BTP authentication. Kept in the background so the
163
+ // server starts listening immediately (and, for --browser none, the auth URL
164
+ // is printed right away). If auth is requested but never completed, exit(1)
165
+ // so the proxy is restarted and the user gets a fresh login prompt.
129
166
  if (this.config.btpDestination) {
167
+ const dest = this.config.btpDestination;
130
168
  try {
131
169
  if (!this.btpProxy) {
132
170
  this.btpProxy = await (0, btpProxy_js_1.createBtpProxy)(this.config);
133
171
  }
134
- // Trigger initialization (background) - this will open the browser
135
- this.btpProxy.initialize(this.config.btpDestination);
136
172
  }
137
173
  catch (error) {
138
- logger_js_1.logger?.error('Failed to initialize BTP proxy for eager auth', {
139
- error: error instanceof Error ? error.message : String(error),
140
- });
174
+ await this.fatalAuthFailure(error, dest, 'startup');
175
+ return;
141
176
  }
177
+ this.btpProxy
178
+ .initialize(dest)
179
+ .catch((error) => this.fatalAuthFailure(error, dest, 'startup'));
142
180
  }
143
181
  if (this.transportConfig.type === 'streamable-http') {
144
182
  await this.startHttpServer();
@@ -246,9 +284,28 @@ Authorization source: ${authSourceObj}`;
246
284
  if (!this.btpProxy) {
247
285
  this.btpProxy = await (0, btpProxy_js_1.createBtpProxy)(this.config);
248
286
  }
287
+ // Authentication is separated from forwarding: an auth failure is fatal
288
+ // (the proxy exits so it can be restarted), while a downstream/forward error
289
+ // is returned to the client without killing the proxy.
290
+ let jwtToken;
249
291
  try {
250
292
  // Get JWT token (cached, auto-refresh)
251
- const jwtToken = await this.btpProxy.getJwtToken(destination);
293
+ jwtToken = await this.btpProxy.getJwtToken(destination);
294
+ }
295
+ catch (authError) {
296
+ logger_js_1.logger?.error('Proxy request failed: authentication error', {
297
+ type: 'PROXY_REQUEST_AUTH_ERROR',
298
+ destination,
299
+ error: authError instanceof Error ? authError.message : String(authError),
300
+ });
301
+ if (!res.headersSent) {
302
+ res.writeHead(502, { 'Content-Type': 'application/json' });
303
+ res.end(JSON.stringify({ error: 'Authentication failed' }));
304
+ }
305
+ await this.fatalAuthFailure(authError, destination, 'request');
306
+ return;
307
+ }
308
+ try {
252
309
  // Get target URL
253
310
  const targetUrl = intercepted.routingDecision.targetUrl ||
254
311
  (await this.btpProxy.getTargetUrl(destination));
@@ -540,6 +597,37 @@ Authorization source: ${authSourceObj}`;
540
597
  });
541
598
  });
542
599
  }
600
+ /**
601
+ * Authentication was requested but could not be completed. Report the reason
602
+ * clearly, release resources, and exit(1) so the proxy is restarted (and the
603
+ * user gets a fresh login prompt) instead of running unauthenticated. Mirrors
604
+ * the one-shot `mcp-auth` command, which exits on a failed/incomplete login.
605
+ */
606
+ async fatalAuthFailure(error, destination, phase) {
607
+ if (this.authExiting)
608
+ return;
609
+ this.authExiting = true;
610
+ const { category, reason } = classifyAuthFailure(error);
611
+ const detail = error instanceof Error ? error.message : String(error);
612
+ logger_js_1.logger?.error('Authentication not completed — exiting', {
613
+ type: 'AUTH_FATAL_EXIT',
614
+ destination,
615
+ phase,
616
+ category,
617
+ reason,
618
+ error: detail,
619
+ });
620
+ console.error(`[MCP Proxy] ✗ Authentication failed for destination "${destination}" (${phase}): ${reason}.\n` +
621
+ `[MCP Proxy] ${detail}\n` +
622
+ `[MCP Proxy] Exiting (code 1) so the proxy can be restarted to retry authentication.`);
623
+ try {
624
+ await this.shutdown();
625
+ }
626
+ catch {
627
+ // best-effort cleanup; exit regardless
628
+ }
629
+ process.exit(1);
630
+ }
543
631
  /**
544
632
  * Shutdown server
545
633
  */
@@ -571,6 +659,9 @@ exports.default = McpAbapAdtProxyServer;
571
659
  * Print CLI usage and exit.
572
660
  */
573
661
  function printHelp() {
662
+ // Show the real, platform-specific directory where service keys are read from
663
+ // (Windows: %USERPROFILE%\Documents\mcp-abap-adt\service-keys; Unix: ~/.config/...)
664
+ const serviceKeyDir = (0, stores_js_1.getPlatformPaths)('service-keys')[0];
574
665
  const help = `Usage: mcp-abap-adt-proxy [options]
575
666
 
576
667
  Authorization proxy for MCP clients (Cline, Claude Code, etc.) that cannot
@@ -582,7 +673,8 @@ CONFIGURATION
582
673
 
583
674
  AUTHENTICATION (BTP / XSUAA)
584
675
  --btp <destination> BTP destination name. Service key is read from
585
- ~/.config/mcp-abap-adt/service-keys/<dest>.json
676
+ ${serviceKeyDir}${require('node:path').sep}<dest>.json
677
+ (override the base dir with AUTH_BROKER_PATH)
586
678
  --target-url <url> Override target service URL (default: from service key)
587
679
  --url <url> Alias for --target-url
588
680
  --unsafe Persist session/tokens to disk (default: in-memory)
@@ -595,12 +687,18 @@ BROWSER AUTHORIZATION
595
687
  chrome - Google Chrome
596
688
  edge - Microsoft Edge
597
689
  firefox - Firefox
598
- none - Print URL to console and wait for
599
- callback (for SSH/headless sessions)
690
+ none - Print URL to console; complete login
691
+ via callback OR by pasting the code
692
+ (for SSH/headless/remote sessions)
600
693
  headless - Alias for 'none'
601
694
  --browser-auth-port <port> Local OAuth callback port. Must match the
602
695
  redirect_uri registered on XSUAA (default: 3333).
603
696
 
697
+ Run without auto-opening a browser (prints the URL, then completes via the
698
+ localhost callback, the paste form at http://<host>:<port>/, or by pasting
699
+ the code in the terminal):
700
+ mcp-abap-adt-proxy --btp <dest> --browser none --browser-auth-port 3333
701
+
604
702
  TRANSPORT
605
703
  --transport <type> stdio | streamable-http | http | sse
606
704
  (auto-detected from stdin TTY if not set)
@@ -1 +1 @@
1
- {"version":3,"file":"btpProxy.d.ts","sourceRoot":"","sources":["../../src/proxy/btpProxy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAgB,MAAM,2BAA2B,CAAC;AAYrE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA4CnE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAU3C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAGD,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,UAAU,CACN;IACZ,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAU;gBAEZ,oBAAoB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;IAqF3E;;;OAGG;IACU,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB3D;;;;OAIG;YACW,wBAAwB;IAqFtC;;OAEG;YACW,uBAAuB;IAyFrC;;;;OAIG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,MAAM,CAAC;IAiHlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;;OAGG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;YACW,gBAAgB;IAU9B;;OAEG;IACI,OAAO,IAAI,IAAI;IAStB;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAexD;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;;;;;;;;OAgBG;YACW,iBAAiB;IAmT/B;;OAEG;IACG,YAAY,CAChB,eAAe,EAAE,YAAY,EAC7B,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7D,OAAO,CAAC,aAAa,CAAC;IAiLzB;;OAEG;WACiB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;CAyB7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAEnB"}
1
+ {"version":3,"file":"btpProxy.d.ts","sourceRoot":"","sources":["../../src/proxy/btpProxy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAgB,MAAM,2BAA2B,CAAC;AAYrE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA4CnE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAU3C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAGD,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,UAAU,CACN;IACZ,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiB;IACjD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAU;gBAEZ,oBAAoB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;IAqF3E;;;OAGG;IACU,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAwB3D;;;;OAIG;YACW,wBAAwB;IA0FtC;;OAEG;YACW,uBAAuB;IAyFrC;;;;OAIG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,MAAM,CAAC;IAiHlB;;OAEG;IACH,OAAO,CAAC,YAAY;IAapB;;;OAGG;IACH,OAAO,CAAC,UAAU;IASlB;;OAEG;IACH,OAAO,CAAC,wBAAwB;IA6BhC;;OAEG;YACW,gBAAgB;IAU9B;;OAEG;IACI,OAAO,IAAI,IAAI;IAStB;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAexD;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;;;;;;;;OAgBG;YACW,iBAAiB;IAmT/B;;OAEG;IACG,YAAY,CAChB,eAAe,EAAE,YAAY,EAC7B,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7D,OAAO,CAAC,aAAa,CAAC;IAiLzB;;OAEG;WACiB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;CA0B7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAEnB"}
@@ -155,8 +155,9 @@ class BtpProxy {
155
155
  destination,
156
156
  error: error instanceof Error ? error.message : String(error),
157
157
  });
158
- // We don't throw here to avoid crashing the server on startup,
159
- // but the user will see the error in logs/stderr (via getJwtToken)
158
+ // Re-throw so the server can decide to exit (auth requested but not
159
+ // completed) instead of running in a permanently unauthenticated state.
160
+ throw error;
160
161
  }
161
162
  }
162
163
  /**
@@ -199,9 +200,10 @@ class BtpProxy {
199
200
  });
200
201
  }
201
202
  if (!authConfig) {
203
+ const serviceKeyDir = (0, stores_js_1.getPlatformPaths)('service-keys')[0];
202
204
  logger_js_1.logger?.error('Service key not found for destination', {
203
205
  destination,
204
- hint: 'Ensure service key file exists in ~/.config/mcp-abap-adt/service-keys/',
206
+ hint: `Ensure service key file "${destination}.json" exists in ${serviceKeyDir} (override the base dir with AUTH_BROKER_PATH)`,
205
207
  });
206
208
  // We cannot proceed without config, but we'll let it fail with a clear message
207
209
  // Or we could throw here.
@@ -213,6 +215,10 @@ class BtpProxy {
213
215
  const providerConfig = {
214
216
  browser: this.config.browser,
215
217
  redirectPort: this.config.browserAuthPort || 3333,
218
+ // Pass a logger so browserAuth can surface the "Open this URL" prompt in
219
+ // 'none'/'headless' mode. Without it, startBrowserAuth gets a null logger
220
+ // and silently drops the authorization URL.
221
+ logger: loggerAdapter,
216
222
  ...(authConfig
217
223
  ? {
218
224
  uaaUrl: authConfig.uaaUrl,
@@ -924,6 +930,7 @@ class BtpProxy {
924
930
  clientSecret: 'placeholder',
925
931
  browser: loadedConfig.browser,
926
932
  redirectPort: loadedConfig.browserAuthPort,
933
+ logger: loggerAdapter,
927
934
  }),
928
935
  }, 'none', loggerAdapter);
929
936
  return new BtpProxy(defaultBtpAuthBroker, loadedConfig);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/proxy",
3
- "version": "1.3.0",
3
+ "version": "1.5.0",
4
4
  "description": "MCP proxy server for SAP ABAP ADT - proxies local requests to cloud-llm-hub with JWT authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -63,8 +63,8 @@
63
63
  "typescript": "^6.0.3"
64
64
  },
65
65
  "dependencies": {
66
- "@mcp-abap-adt/auth-broker": "^1.0.5",
67
- "@mcp-abap-adt/auth-providers": "^1.0.5",
66
+ "@mcp-abap-adt/auth-broker": "^1.0.6",
67
+ "@mcp-abap-adt/auth-providers": "^1.1.0",
68
68
  "@mcp-abap-adt/auth-stores": "^1.0.4",
69
69
  "@mcp-abap-adt/header-validator": "^0.1.8",
70
70
  "@mcp-abap-adt/interfaces": "^7.0.0",