@mcp-abap-adt/calm-server 0.3.0 → 0.4.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/README.md +45 -0
  3. package/dist/server/auth/buildBroker.d.ts +22 -0
  4. package/dist/server/auth/buildBroker.d.ts.map +1 -0
  5. package/dist/server/auth/buildBroker.js +55 -0
  6. package/dist/server/auth/buildBroker.js.map +1 -0
  7. package/dist/server/auth/legacyEnvShim.d.ts +10 -0
  8. package/dist/server/auth/legacyEnvShim.d.ts.map +1 -0
  9. package/dist/server/auth/legacyEnvShim.js +23 -0
  10. package/dist/server/auth/legacyEnvShim.js.map +1 -0
  11. package/dist/server/buildClient.d.ts +17 -5
  12. package/dist/server/buildClient.d.ts.map +1 -1
  13. package/dist/server/buildClient.js +22 -64
  14. package/dist/server/buildClient.js.map +1 -1
  15. package/dist/server/config.d.ts +3 -0
  16. package/dist/server/config.d.ts.map +1 -1
  17. package/dist/server/config.js +19 -3
  18. package/dist/server/config.js.map +1 -1
  19. package/dist/server/connection/AbstractCalmConnection.d.ts +43 -0
  20. package/dist/server/connection/AbstractCalmConnection.d.ts.map +1 -0
  21. package/dist/server/connection/AbstractCalmConnection.js +155 -0
  22. package/dist/server/connection/AbstractCalmConnection.js.map +1 -0
  23. package/dist/server/connection/OAuth2CalmConnection.d.ts +13 -0
  24. package/dist/server/connection/OAuth2CalmConnection.d.ts.map +1 -0
  25. package/dist/server/connection/OAuth2CalmConnection.js +27 -0
  26. package/dist/server/connection/OAuth2CalmConnection.js.map +1 -0
  27. package/dist/server/connection/SandboxCalmConnection.d.ts +10 -0
  28. package/dist/server/connection/SandboxCalmConnection.d.ts.map +1 -0
  29. package/dist/server/connection/SandboxCalmConnection.js +16 -0
  30. package/dist/server/connection/SandboxCalmConnection.js.map +1 -0
  31. package/dist/server/connection/XsuaaRefresher.d.ts +18 -0
  32. package/dist/server/connection/XsuaaRefresher.d.ts.map +1 -0
  33. package/dist/server/connection/XsuaaRefresher.js +51 -0
  34. package/dist/server/connection/XsuaaRefresher.js.map +1 -0
  35. package/dist/server/connection/createCalmConnection.d.ts +13 -0
  36. package/dist/server/connection/createCalmConnection.d.ts.map +1 -0
  37. package/dist/server/connection/createCalmConnection.js +28 -0
  38. package/dist/server/connection/createCalmConnection.js.map +1 -0
  39. package/dist/server/connection/index.d.ts +10 -0
  40. package/dist/server/connection/index.d.ts.map +1 -0
  41. package/dist/server/connection/index.js +16 -0
  42. package/dist/server/connection/index.js.map +1 -0
  43. package/dist/server/connection/serviceRoutes.d.ts +16 -0
  44. package/dist/server/connection/serviceRoutes.d.ts.map +1 -0
  45. package/dist/server/connection/serviceRoutes.js +27 -0
  46. package/dist/server/connection/serviceRoutes.js.map +1 -0
  47. package/dist/server/runStdio.js +1 -1
  48. package/dist/server/runStdio.js.map +1 -1
  49. package/package.json +13 -3
package/CHANGELOG.md CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.4.0 — 2026-05-24
4
+
5
+ Follows `@mcp-abap-adt/calm-client@0.4.0` (connection moved out of the
6
+ client). Combines the auth-broker integration (M19) with a
7
+ server-owned connection layer.
8
+
9
+ ### Changed (BREAKING)
10
+
11
+ - **The server now owns the CALM connection.** New
12
+ `src/server/connection/` module on native `fetch`:
13
+ `AbstractCalmConnection`, `SandboxCalmConnection` (api-key),
14
+ `OAuth2CalmConnection` (Bearer via `ITokenRefresher`, one-shot
15
+ refresh+retry on 401/403), and the
16
+ `createCalmConnection(config, overrides?)` factory. Exposed via the
17
+ `./connection` subpath export. `@mcp-abap-adt/calm-client` no longer
18
+ ships `CalmConnection` (peer bumped to `^0.4.0`).
19
+ - **`CALM_BASE_URL` is consumed verbatim** — service routes are appended
20
+ by plain concatenation; no `/api` injection. Paste `endpoints.Api`
21
+ from the service-key as-is (it already includes `/api`). Fixes the
22
+ silent double-`/api` 404 against live tenants.
23
+
24
+ ### Added
25
+
26
+ - **Token acquisition via `@mcp-abap-adt/auth-broker`** (M19):
27
+ `buildCalmClient` is `async`, honours `CALM_AUTH_FLOW`
28
+ (`client_credentials` | `authorization_code`), and sources UAA creds
29
+ from inline `CALM_UAA_*` (legacy shim) or `./{destination}.env`
30
+ (produced by the `mcp-auth` CLI). The broker yields the
31
+ `ITokenRefresher` that is injected into `OAuth2CalmConnection` via the
32
+ factory's `tokenRefresher` override.
33
+ - **Request-lifecycle logging** in the connection via an optional
34
+ `ILogger` (debug on request/response/retry, warn on transport
35
+ failure), threaded from `runStdio` through the stderr-safe
36
+ `StderrLogger`.
37
+
38
+ ### Fixed
39
+
40
+ - `dotenv` moved from `devDependencies` to `dependencies` (imported at
41
+ runtime by `config.ts`).
42
+
3
43
  ## 0.3.0 — 2026-05-13
4
44
 
5
45
  Follows `@mcp-abap-adt/calm-client@0.3.0` (issue / PR #3 / #4 there).
package/README.md CHANGED
@@ -83,6 +83,51 @@ npm run build && node dist/bin/stdio.js
83
83
  The server speaks MCP over stdio. Misconfiguration is reported to
84
84
  `stderr` with a non-zero exit code.
85
85
 
86
+ ## Authentication setup
87
+
88
+ `calm-mcp` supports two OAuth2 flows for live tenants, selectable via
89
+ `CALM_AUTH_FLOW`:
90
+
91
+ | Flow | Use case | Browser? | Refresh token? |
92
+ |---|---|---|---|
93
+ | `client_credentials` (default) | technical service-binding (`sb-*` client) | no | no |
94
+ | `authorization_code` | end-user dev workflow, full user scope | once | yes |
95
+
96
+ ### Option A — quick CC setup (technical user)
97
+
98
+ Plain `.env` with inline `CALM_UAA_URL` / `CALM_UAA_CLIENT_ID` /
99
+ `CALM_UAA_CLIENT_SECRET` works as before — no extra steps. The broker uses
100
+ an in-memory session shim for these inline creds.
101
+
102
+ ### Option B — broker-backed setup (CC or AC)
103
+
104
+ Use the bundled [`mcp-auth`](https://www.npmjs.com/package/@mcp-abap-adt/auth-broker)
105
+ CLI to convert a BTP service key into a token-bearing `.env`:
106
+
107
+ ```bash
108
+ # CC (no browser)
109
+ npx mcp-auth --service-key ./sk.json --output ./DEFAULT.env \
110
+ --type xsuaa --credential
111
+
112
+ # AC (browser pops once; refresh_token persists)
113
+ npx mcp-auth --service-key ./sk.json --output ./DEFAULT.env \
114
+ --type xsuaa --browser auto
115
+ ```
116
+
117
+ Then in your `.env`:
118
+
119
+ ```
120
+ CALM_MODE=oauth2
121
+ CALM_BASE_URL=https://<tenant>.<region>.alm.cloud.sap
122
+ CALM_AUTH_FLOW=authorization_code # or client_credentials
123
+ CALM_DESTINATION=DEFAULT
124
+ ```
125
+
126
+ The server's runtime auth pipeline is `@mcp-abap-adt/auth-broker`.
127
+
128
+ > Note: `buildCalmClient` is async since v0.4.0 (was sync in v0.3.x). Library
129
+ > consumers must `await` it.
130
+
86
131
  ### 3. Wire into Claude Desktop
87
132
 
88
133
  Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`
@@ -0,0 +1,22 @@
1
+ import { AuthBroker } from '@mcp-abap-adt/auth-broker';
2
+ import type { ILogger } from '@mcp-abap-adt/interfaces';
3
+ import type { ICalmServerConfig } from '../config';
4
+ /**
5
+ * Assemble an `AuthBroker` from server config.
6
+ *
7
+ * - Chooses `ClientCredentialsProvider` or `AuthorizationCodeProvider`
8
+ * based on `config.authFlow`.
9
+ * - Chooses session store: legacy `SafeXsuaaSessionStore` shim when the
10
+ * `.env` still inlines `CALM_UAA_*`, otherwise file-based
11
+ * `XsuaaSessionStore` rooted at cwd (loads `./{destination}.env`).
12
+ * - `browser: 'none'` always — the runtime server never pops a browser;
13
+ * interactive AC login is the job of the `mcp-auth` CLI.
14
+ * - `allowBrowserAuth` is flow-aware: `true` for CC (broker has a hard
15
+ * gate that fires when a session lacks a refresh_token, regardless of
16
+ * provider type — CC providers never need a browser anyway, so we let
17
+ * the gate pass), `false` for AC (fail fast when refresh_token is
18
+ * missing rather than try to launch a browser the stdio process
19
+ * cannot show).
20
+ */
21
+ export declare function buildAuthBroker(config: ICalmServerConfig, logger?: ILogger): Promise<AuthBroker>;
22
+ //# sourceMappingURL=buildBroker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildBroker.d.ts","sourceRoot":"","sources":["../../../src/server/auth/buildBroker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAMvD,OAAO,KAAK,EACV,OAAO,EAGR,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAGnD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,EACzB,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,UAAU,CAAC,CA6CrB"}
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildAuthBroker = buildAuthBroker;
4
+ const auth_broker_1 = require("@mcp-abap-adt/auth-broker");
5
+ const auth_providers_1 = require("@mcp-abap-adt/auth-providers");
6
+ const auth_stores_1 = require("@mcp-abap-adt/auth-stores");
7
+ const legacyEnvShim_1 = require("./legacyEnvShim");
8
+ /**
9
+ * Assemble an `AuthBroker` from server config.
10
+ *
11
+ * - Chooses `ClientCredentialsProvider` or `AuthorizationCodeProvider`
12
+ * based on `config.authFlow`.
13
+ * - Chooses session store: legacy `SafeXsuaaSessionStore` shim when the
14
+ * `.env` still inlines `CALM_UAA_*`, otherwise file-based
15
+ * `XsuaaSessionStore` rooted at cwd (loads `./{destination}.env`).
16
+ * - `browser: 'none'` always — the runtime server never pops a browser;
17
+ * interactive AC login is the job of the `mcp-auth` CLI.
18
+ * - `allowBrowserAuth` is flow-aware: `true` for CC (broker has a hard
19
+ * gate that fires when a session lacks a refresh_token, regardless of
20
+ * provider type — CC providers never need a browser anyway, so we let
21
+ * the gate pass), `false` for AC (fail fast when refresh_token is
22
+ * missing rather than try to launch a browser the stdio process
23
+ * cannot show).
24
+ */
25
+ async function buildAuthBroker(config, logger) {
26
+ const shimStore = await (0, legacyEnvShim_1.buildLegacyShimStore)(config);
27
+ const sessionStore = shimStore ?? new auth_stores_1.XsuaaSessionStore(process.cwd(), config.baseUrl, logger);
28
+ // Resolve UAA credentials: inline config wins, otherwise read from
29
+ // session store (populated from `./{destination}.env` by `mcp-auth`).
30
+ const sessionAuth = await sessionStore.getAuthorizationConfig(config.destination);
31
+ const uaaUrl = config.uaaUrl || sessionAuth?.uaaUrl;
32
+ const uaaClientId = config.uaaClientId || sessionAuth?.uaaClientId;
33
+ const uaaClientSecret = config.uaaClientSecret || sessionAuth?.uaaClientSecret;
34
+ if (!uaaUrl || !uaaClientId || !uaaClientSecret) {
35
+ throw new Error(`[calm-mcp] UAA credentials missing for destination "${config.destination}". ` +
36
+ `Either inline CALM_UAA_URL/CALM_UAA_CLIENT_ID/CALM_UAA_CLIENT_SECRET in .env, ` +
37
+ `or run 'npx mcp-auth --service-key ./sk.json --output ./${config.destination}.env --type xsuaa' first.`);
38
+ }
39
+ const tokenProvider = config.authFlow === 'authorization_code'
40
+ ? new auth_providers_1.AuthorizationCodeProvider({
41
+ uaaUrl,
42
+ clientId: uaaClientId,
43
+ clientSecret: uaaClientSecret,
44
+ browser: 'none',
45
+ logger,
46
+ })
47
+ : new auth_providers_1.ClientCredentialsProvider({
48
+ uaaUrl,
49
+ clientId: uaaClientId,
50
+ clientSecret: uaaClientSecret,
51
+ });
52
+ const allowBrowserAuth = config.authFlow !== 'authorization_code';
53
+ return new auth_broker_1.AuthBroker({ sessionStore, tokenProvider, allowBrowserAuth }, 'none', logger);
54
+ }
55
+ //# sourceMappingURL=buildBroker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buildBroker.js","sourceRoot":"","sources":["../../../src/server/auth/buildBroker.ts"],"names":[],"mappings":";;AA+BA,0CAgDC;AA/ED,2DAAuD;AACvD,iEAGsC;AACtC,2DAA8D;AAO9D,mDAAuD;AAEvD;;;;;;;;;;;;;;;;GAgBG;AACI,KAAK,UAAU,eAAe,CACnC,MAAyB,EACzB,MAAgB;IAEhB,MAAM,SAAS,GAAG,MAAM,IAAA,oCAAoB,EAAC,MAAM,CAAC,CAAC;IACrD,MAAM,YAAY,GAChB,SAAS,IAAI,IAAI,+BAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE5E,mEAAmE;IACnE,sEAAsE;IACtE,MAAM,WAAW,GAAG,MAAM,YAAY,CAAC,sBAAsB,CAC3D,MAAM,CAAC,WAAW,CACnB,CAAC;IACF,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,WAAW,EAAE,MAAM,CAAC;IACpD,MAAM,WAAW,GAAG,MAAM,CAAC,WAAW,IAAI,WAAW,EAAE,WAAW,CAAC;IACnE,MAAM,eAAe,GACnB,MAAM,CAAC,eAAe,IAAI,WAAW,EAAE,eAAe,CAAC;IAEzD,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,IAAI,CAAC,eAAe,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CACb,uDAAuD,MAAM,CAAC,WAAW,KAAK;YAC5E,gFAAgF;YAChF,2DAA2D,MAAM,CAAC,WAAW,2BAA2B,CAC3G,CAAC;IACJ,CAAC;IAED,MAAM,aAAa,GACjB,MAAM,CAAC,QAAQ,KAAK,oBAAoB;QACtC,CAAC,CAAC,IAAI,0CAAyB,CAAC;YAC5B,MAAM;YACN,QAAQ,EAAE,WAAW;YACrB,YAAY,EAAE,eAAe;YAC7B,OAAO,EAAE,MAAM;YACf,MAAM;SACP,CAAC;QACJ,CAAC,CAAC,IAAI,0CAAyB,CAAC;YAC5B,MAAM;YACN,QAAQ,EAAE,WAAW;YACrB,YAAY,EAAE,eAAe;SAC9B,CAAC,CAAC;IAET,MAAM,gBAAgB,GAAG,MAAM,CAAC,QAAQ,KAAK,oBAAoB,CAAC;IAElE,OAAO,IAAI,wBAAU,CACnB,EAAE,YAAY,EAAE,aAAa,EAAE,gBAAgB,EAAE,EACjD,MAAM,EACN,MAAM,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ISessionStore } from '@mcp-abap-adt/interfaces';
2
+ import type { ICalmServerConfig } from '../config';
3
+ /**
4
+ * If the user's `.env` still inlines `CALM_UAA_URL`/`CALM_UAA_CLIENT_ID`/
5
+ * `CALM_UAA_CLIENT_SECRET` (pre-broker convention), build an in-memory
6
+ * SafeXsuaaSessionStore preloaded with those creds. Returns null if any
7
+ * of the three are missing — caller falls back to file-based store.
8
+ */
9
+ export declare function buildLegacyShimStore(config: ICalmServerConfig): Promise<ISessionStore | null>;
10
+ //# sourceMappingURL=legacyEnvShim.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyEnvShim.d.ts","sourceRoot":"","sources":["../../../src/server/auth/legacyEnvShim.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD;;;;;GAKG;AACH,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAW/B"}
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildLegacyShimStore = buildLegacyShimStore;
4
+ const auth_stores_1 = require("@mcp-abap-adt/auth-stores");
5
+ /**
6
+ * If the user's `.env` still inlines `CALM_UAA_URL`/`CALM_UAA_CLIENT_ID`/
7
+ * `CALM_UAA_CLIENT_SECRET` (pre-broker convention), build an in-memory
8
+ * SafeXsuaaSessionStore preloaded with those creds. Returns null if any
9
+ * of the three are missing — caller falls back to file-based store.
10
+ */
11
+ async function buildLegacyShimStore(config) {
12
+ const { uaaUrl, uaaClientId, uaaClientSecret, baseUrl, destination } = config;
13
+ if (!uaaUrl || !uaaClientId || !uaaClientSecret)
14
+ return null;
15
+ const store = new auth_stores_1.SafeXsuaaSessionStore(baseUrl);
16
+ await store.setAuthorizationConfig(destination, {
17
+ uaaUrl,
18
+ uaaClientId,
19
+ uaaClientSecret,
20
+ });
21
+ return store;
22
+ }
23
+ //# sourceMappingURL=legacyEnvShim.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legacyEnvShim.js","sourceRoot":"","sources":["../../../src/server/auth/legacyEnvShim.ts"],"names":[],"mappings":";;AAUA,oDAaC;AAvBD,2DAAkE;AAIlE;;;;;GAKG;AACI,KAAK,UAAU,oBAAoB,CACxC,MAAyB;IAEzB,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,MAAM,CAAC;IAC9E,IAAI,CAAC,MAAM,IAAI,CAAC,WAAW,IAAI,CAAC,eAAe;QAAE,OAAO,IAAI,CAAC;IAE7D,MAAM,KAAK,GAAG,IAAI,mCAAqB,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,KAAK,CAAC,sBAAsB,CAAC,WAAW,EAAE;QAC9C,MAAM;QACN,WAAW;QACX,eAAe;KAChB,CAAC,CAAC;IACH,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,10 +1,22 @@
1
1
  import { CalmClient } from '@mcp-abap-adt/calm-client';
2
+ import type { ILogger } from '@mcp-abap-adt/interfaces';
2
3
  import type { ICalmServerConfig } from './config';
4
+ export interface BuildCalmClientOptions {
5
+ logger?: ILogger;
6
+ }
3
7
  /**
4
- * Build a ready-to-use `CalmClient` from a `ICalmServerConfig`. Picks
5
- * OAuth2 or sandbox mode based on config.mode. The returned connection
6
- * is stateless from the server's perspective constructing a fresh
7
- * CalmClient per process is cheap.
8
+ * Build a ready-to-use `CalmClient` from a `ICalmServerConfig`.
9
+ *
10
+ * Connection construction (fetch transport, verbatim base URL, request
11
+ * logging) lives in `./connection`. Token acquisition is delegated:
12
+ *
13
+ * - `oauth2` mode: `@mcp-abap-adt/auth-broker` produces the
14
+ * `ITokenRefresher` (honouring `CALM_AUTH_FLOW` + session stores);
15
+ * it is injected into the connection via the factory's
16
+ * `tokenRefresher` override. Pass `options.logger` (e.g.
17
+ * `StderrLogger`) in stdio mode so broker + connection logs go to
18
+ * stderr, never the MCP stdout frame stream.
19
+ * - `sandbox` mode: direct API-key auth, no broker involved.
8
20
  */
9
- export declare function buildCalmClient(config: ICalmServerConfig): CalmClient;
21
+ export declare function buildCalmClient(config: ICalmServerConfig, options?: BuildCalmClientOptions): Promise<CalmClient>;
10
22
  //# sourceMappingURL=buildClient.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildClient.d.ts","sourceRoot":"","sources":["../../src/server/buildClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAkB,MAAM,2BAA2B,CAAC;AAEvE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAoDlD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,iBAAiB,GAAG,UAAU,CAoBrE"}
1
+ {"version":3,"file":"buildClient.d.ts","sourceRoot":"","sources":["../../src/server/buildClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAExD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAGlD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,MAAM,EAAE,iBAAiB,EACzB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,UAAU,CAAC,CAcrB"}
@@ -2,73 +2,31 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.buildCalmClient = buildCalmClient;
4
4
  const calm_client_1 = require("@mcp-abap-adt/calm-client");
5
+ const buildBroker_1 = require("./auth/buildBroker");
6
+ const createCalmConnection_1 = require("./connection/createCalmConnection");
5
7
  /**
6
- * Minimal XSUAA `client_credentials` refresher sufficient for
7
- * standalone-mode servers where no shared auth-broker/session store is
8
- * configured. Caches the token until explicitly refreshed. Production
9
- * consumers that already run `@mcp-abap-adt/auth-broker` elsewhere
10
- * should pass their own `ITokenRefresher` via `buildCalmClient.override`.
8
+ * Build a ready-to-use `CalmClient` from a `ICalmServerConfig`.
9
+ *
10
+ * Connection construction (fetch transport, verbatim base URL, request
11
+ * logging) lives in `./connection`. Token acquisition is delegated:
12
+ *
13
+ * - `oauth2` mode: `@mcp-abap-adt/auth-broker` produces the
14
+ * `ITokenRefresher` (honouring `CALM_AUTH_FLOW` + session stores);
15
+ * it is injected into the connection via the factory's
16
+ * `tokenRefresher` override. Pass `options.logger` (e.g.
17
+ * `StderrLogger`) in stdio mode so broker + connection logs go to
18
+ * stderr, never the MCP stdout frame stream.
19
+ * - `sandbox` mode: direct API-key auth, no broker involved.
11
20
  */
12
- class XsuaaRefresher {
13
- uaaUrl;
14
- clientId;
15
- clientSecret;
16
- cached;
17
- constructor(uaaUrl, clientId, clientSecret) {
18
- this.uaaUrl = uaaUrl;
19
- this.clientId = clientId;
20
- this.clientSecret = clientSecret;
21
- }
22
- async getToken() {
23
- if (!this.cached)
24
- return this.refreshToken();
25
- return this.cached;
26
- }
27
- async refreshToken() {
28
- const url = `${this.uaaUrl.replace(/\/$/, '')}/oauth/token`;
29
- const basic = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
30
- const response = await fetch(url, {
31
- method: 'POST',
32
- headers: {
33
- Authorization: `Basic ${basic}`,
34
- 'Content-Type': 'application/x-www-form-urlencoded',
35
- Accept: 'application/json',
36
- },
37
- body: 'grant_type=client_credentials',
38
- });
39
- if (!response.ok) {
40
- const body = await response.text().catch(() => '');
41
- throw new Error(`XSUAA token request failed: ${response.status} ${response.statusText} — ${body.slice(0, 200)}`);
42
- }
43
- const json = (await response.json());
44
- if (!json.access_token) {
45
- throw new Error('XSUAA token response missing access_token');
46
- }
47
- this.cached = json.access_token;
48
- return this.cached;
49
- }
50
- }
51
- /**
52
- * Build a ready-to-use `CalmClient` from a `ICalmServerConfig`. Picks
53
- * OAuth2 or sandbox mode based on config.mode. The returned connection
54
- * is stateless from the server's perspective — constructing a fresh
55
- * CalmClient per process is cheap.
56
- */
57
- function buildCalmClient(config) {
21
+ async function buildCalmClient(config, options = {}) {
58
22
  if (config.mode === 'oauth2') {
59
- const refresher = new XsuaaRefresher(config.uaaUrl, config.uaaClientId, config.uaaClientSecret);
60
- const connection = new calm_client_1.CalmConnection({
61
- baseUrl: config.baseUrl,
62
- tokenRefresher: refresher,
63
- defaultTimeout: config.timeoutMs,
64
- });
65
- return new calm_client_1.CalmClient(connection);
23
+ const broker = await (0, buildBroker_1.buildAuthBroker)(config, options.logger);
24
+ const tokenRefresher = broker.createTokenRefresher(config.destination);
25
+ return new calm_client_1.CalmClient((0, createCalmConnection_1.createCalmConnection)(config, {
26
+ tokenRefresher,
27
+ logger: options.logger,
28
+ }));
66
29
  }
67
- const connection = new calm_client_1.CalmConnection({
68
- baseUrl: config.baseUrl,
69
- apiKey: config.apiKey,
70
- defaultTimeout: config.timeoutMs,
71
- });
72
- return new calm_client_1.CalmClient(connection);
30
+ return new calm_client_1.CalmClient((0, createCalmConnection_1.createCalmConnection)(config, { logger: options.logger }));
73
31
  }
74
32
  //# sourceMappingURL=buildClient.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"buildClient.js","sourceRoot":"","sources":["../../src/server/buildClient.ts"],"names":[],"mappings":";;AA4DA,0CAoBC;AAhFD,2DAAuE;AAIvE;;;;;;GAMG;AACH,MAAM,cAAc;IAIC;IACA;IACA;IALX,MAAM,CAAU;IAExB,YACmB,MAAc,EACd,QAAgB,EAChB,YAAoB;QAFpB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;QAChB,iBAAY,GAAZ,YAAY,CAAQ;IACpC,CAAC;IAEJ,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,QAAQ,CACzE,QAAQ,CACT,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,SAAS,KAAK,EAAE;gBAC/B,cAAc,EAAE,mCAAmC;gBACnD,MAAM,EAAE,kBAAkB;aAC3B;YACD,IAAI,EAAE,+BAA+B;SACtC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,+BAA+B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAChG,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA8B,CAAC;QAClE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAED;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,MAAyB;IACvD,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,SAAS,GAAG,IAAI,cAAc,CAClC,MAAM,CAAC,MAAgB,EACvB,MAAM,CAAC,WAAqB,EAC5B,MAAM,CAAC,eAAyB,CACjC,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,4BAAc,CAAC;YACpC,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,cAAc,EAAE,SAAS;YACzB,cAAc,EAAE,MAAM,CAAC,SAAS;SACjC,CAAC,CAAC;QACH,OAAO,IAAI,wBAAU,CAAC,UAAU,CAAC,CAAC;IACpC,CAAC;IACD,MAAM,UAAU,GAAG,IAAI,4BAAc,CAAC;QACpC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,MAAM,EAAE,MAAM,CAAC,MAAgB;QAC/B,cAAc,EAAE,MAAM,CAAC,SAAS;KACjC,CAAC,CAAC;IACH,OAAO,IAAI,wBAAU,CAAC,UAAU,CAAC,CAAC;AACpC,CAAC"}
1
+ {"version":3,"file":"buildClient.js","sourceRoot":"","sources":["../../src/server/buildClient.ts"],"names":[],"mappings":";;AAwBA,0CAiBC;AAzCD,2DAAuD;AAEvD,oDAAqD;AAErD,4EAAyE;AAMzE;;;;;;;;;;;;;GAaG;AACI,KAAK,UAAU,eAAe,CACnC,MAAyB,EACzB,UAAkC,EAAE;IAEpC,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,MAAM,IAAA,6BAAe,EAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;QAC7D,MAAM,cAAc,GAAG,MAAM,CAAC,oBAAoB,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC;QACvE,OAAO,IAAI,wBAAU,CACnB,IAAA,2CAAoB,EAAC,MAAM,EAAE;YAC3B,cAAc;YACd,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CACH,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,wBAAU,CACnB,IAAA,2CAAoB,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CACzD,CAAC;AACJ,CAAC"}
@@ -5,6 +5,7 @@
5
5
  */
6
6
  export declare function loadEnv(): void;
7
7
  export type CalmServerMode = 'oauth2' | 'sandbox';
8
+ export type CalmAuthFlow = 'client_credentials' | 'authorization_code';
8
9
  export interface ICalmServerConfig {
9
10
  mode: CalmServerMode;
10
11
  baseUrl: string;
@@ -13,6 +14,8 @@ export interface ICalmServerConfig {
13
14
  uaaClientSecret?: string;
14
15
  apiKey?: string;
15
16
  timeoutMs: number;
17
+ authFlow: CalmAuthFlow;
18
+ destination: string;
16
19
  }
17
20
  export declare function readConfig(): ICalmServerConfig;
18
21
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAU9B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAElD,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAYD,wBAAgB,UAAU,IAAI,iBAAiB,CAkC9C"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,wBAAgB,OAAO,IAAI,IAAI,CAU9B;AAED,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,SAAS,CAAC;AAClD,MAAM,MAAM,YAAY,GAAG,oBAAoB,GAAG,oBAAoB,CAAC;AAEvE,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,YAAY,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AAYD,wBAAgB,UAAU,IAAI,iBAAiB,CAsD9C"}
@@ -38,6 +38,18 @@ function readConfig() {
38
38
  if (!mode) {
39
39
  throw new Error('[calm-mcp] CALM_MODE is required (oauth2 or sandbox). See .env.example.');
40
40
  }
41
+ const rawAuthFlow = process.env.CALM_AUTH_FLOW;
42
+ let authFlow = 'client_credentials';
43
+ if (rawAuthFlow) {
44
+ if (rawAuthFlow === 'client_credentials' ||
45
+ rawAuthFlow === 'authorization_code') {
46
+ authFlow = rawAuthFlow;
47
+ }
48
+ else {
49
+ throw new Error(`[calm-mcp] CALM_AUTH_FLOW must be 'client_credentials' or 'authorization_code', got "${rawAuthFlow}".`);
50
+ }
51
+ }
52
+ const destination = process.env.CALM_DESTINATION || 'DEFAULT';
41
53
  const timeoutMs = process.env.CALM_TIMEOUT
42
54
  ? Number(process.env.CALM_TIMEOUT)
43
55
  : 30_000;
@@ -45,10 +57,12 @@ function readConfig() {
45
57
  return {
46
58
  mode,
47
59
  baseUrl: required('CALM_BASE_URL'),
48
- uaaUrl: required('CALM_UAA_URL'),
49
- uaaClientId: required('CALM_UAA_CLIENT_ID'),
50
- uaaClientSecret: required('CALM_UAA_CLIENT_SECRET'),
60
+ uaaUrl: process.env.CALM_UAA_URL,
61
+ uaaClientId: process.env.CALM_UAA_CLIENT_ID,
62
+ uaaClientSecret: process.env.CALM_UAA_CLIENT_SECRET,
51
63
  timeoutMs,
64
+ authFlow,
65
+ destination,
52
66
  };
53
67
  }
54
68
  if (mode === 'sandbox') {
@@ -57,6 +71,8 @@ function readConfig() {
57
71
  baseUrl: process.env.CALM_BASE_URL || 'https://sandbox.api.sap.com/SAPCALM',
58
72
  apiKey: required('CALM_API_KEY'),
59
73
  timeoutMs,
74
+ authFlow,
75
+ destination,
60
76
  };
61
77
  }
62
78
  throw new Error(`[calm-mcp] unknown CALM_MODE "${mode}"`);
@@ -1 +1 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":";;AAWA,0BAUC;AAwBD,gCAkCC;AA/ED,qCAAqC;AACrC,yCAAoC;AACpC,mCAAgD;AAEhD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB;;;;GAIG;AACH,SAAgB,OAAO;IACrB,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,GAAG,IAAI,CAAC;IACd,qEAAqE;IACrE,mEAAmE;IACnE,kEAAkE;IAClE,2EAA2E;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO;IACvC,MAAM,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAA,oBAAU,EAAC,IAAI,CAAC;QAAE,IAAA,eAAY,EAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAcD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,6CAA6C,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,EAAE,CAAC;IACV,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAElC,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC;IAEX,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC;YAClC,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,WAAW,EAAE,QAAQ,CAAC,oBAAoB,CAAC;YAC3C,eAAe,EAAE,QAAQ,CAAC,wBAAwB,CAAC;YACnD,SAAS;SACV,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,IAAI;YACJ,OAAO,EACL,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qCAAqC;YACpE,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,SAAS;SACV,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,GAAG,CAAC,CAAC;AAC5D,CAAC"}
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/server/config.ts"],"names":[],"mappings":";;AAWA,0BAUC;AA2BD,gCAsDC;AAtGD,qCAAqC;AACrC,yCAAoC;AACpC,mCAAgD;AAEhD,IAAI,MAAM,GAAG,KAAK,CAAC;AAEnB;;;;GAIG;AACH,SAAgB,OAAO;IACrB,IAAI,MAAM;QAAE,OAAO;IACnB,MAAM,GAAG,IAAI,CAAC;IACd,qEAAqE;IACrE,mEAAmE;IACnE,kEAAkE;IAClE,2EAA2E;IAC3E,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;QAAE,OAAO;IACvC,MAAM,IAAI,GAAG,IAAA,mBAAO,EAAC,OAAO,CAAC,GAAG,EAAE,EAAE,MAAM,CAAC,CAAC;IAC5C,IAAI,IAAA,oBAAU,EAAC,IAAI,CAAC;QAAE,IAAA,eAAY,EAAC,EAAE,IAAI,EAAE,CAAC,CAAC;AAC/C,CAAC;AAiBD,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CACb,sBAAsB,IAAI,6CAA6C,CACxE,CAAC;IACJ,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAgB,UAAU;IACxB,OAAO,EAAE,CAAC;IACV,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,EAElC,CAAC;IACd,IAAI,CAAC,IAAI,EAAE,CAAC;QACV,MAAM,IAAI,KAAK,CACb,yEAAyE,CAC1E,CAAC;IACJ,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC;IAC/C,IAAI,QAAQ,GAAiB,oBAAoB,CAAC;IAClD,IAAI,WAAW,EAAE,CAAC;QAChB,IACE,WAAW,KAAK,oBAAoB;YACpC,WAAW,KAAK,oBAAoB,EACpC,CAAC;YACD,QAAQ,GAAG,WAAW,CAAC;QACzB,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,wFAAwF,WAAW,IAAI,CACxG,CAAC;QACJ,CAAC;IACH,CAAC;IACD,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,IAAI,SAAS,CAAC;IAE9D,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,YAAY;QACxC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC;QAClC,CAAC,CAAC,MAAM,CAAC;IAEX,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtB,OAAO;YACL,IAAI;YACJ,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC;YAClC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,YAAY;YAChC,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,kBAAkB;YAC3C,eAAe,EAAE,OAAO,CAAC,GAAG,CAAC,sBAAsB;YACnD,SAAS;YACT,QAAQ;YACR,WAAW;SACZ,CAAC;IACJ,CAAC;IACD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,OAAO;YACL,IAAI;YACJ,OAAO,EACL,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,qCAAqC;YACpE,MAAM,EAAE,QAAQ,CAAC,cAAc,CAAC;YAChC,SAAS;YACT,QAAQ;YACR,WAAW;SACZ,CAAC;IACJ,CAAC;IACD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,GAAG,CAAC,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { CalmService, ICalmConnection, ICalmRequestOptions, ICalmResponse, ILogger } from '@mcp-abap-adt/interfaces';
2
+ import { type CalmServiceRouteMap } from './serviceRoutes';
3
+ export interface IAbstractCalmConnectionOptions {
4
+ baseUrl: string;
5
+ defaultTimeout?: number;
6
+ serviceRoutes?: Partial<CalmServiceRouteMap>;
7
+ defaultHeaders?: Record<string, string>;
8
+ /**
9
+ * Optional logger. Request lifecycle is logged at `debug`; transport
10
+ * failures at `warn`. In a stdio MCP runtime this MUST be a
11
+ * stderr-only logger (see `StderrLogger`) — never one that writes to
12
+ * stdout.
13
+ */
14
+ logger?: ILogger;
15
+ }
16
+ /**
17
+ * Shared fetch-based transport for Cloud ALM. Subclasses provide
18
+ * `attachAuth()` and may override `onAuthFailure()`. `baseUrl` is used
19
+ * verbatim — NO prefix injection.
20
+ */
21
+ export declare abstract class AbstractCalmConnection implements ICalmConnection {
22
+ protected readonly baseUrl: string;
23
+ protected readonly defaultTimeout: number;
24
+ protected readonly defaultHeaders: Record<string, string>;
25
+ protected readonly serviceRoutes: CalmServiceRouteMap;
26
+ protected readonly logger?: ILogger;
27
+ constructor(options: IAbstractCalmConnectionOptions);
28
+ /** Subclass: return auth headers for a request. */
29
+ protected abstract attachAuth(): Promise<Record<string, string>>;
30
+ /**
31
+ * Subclass hook on 401/403. Return true to retry once. Default: no
32
+ * retry.
33
+ */
34
+ protected onAuthFailure(_status: number): Promise<boolean>;
35
+ connect(): Promise<void>;
36
+ getBaseUrl(): Promise<string>;
37
+ getServiceUrl(service: CalmService): Promise<string>;
38
+ makeRequest<T = unknown, D = unknown>(options: ICalmRequestOptions): Promise<ICalmResponse<T, D>>;
39
+ private logFail;
40
+ private normalizeError;
41
+ private execute;
42
+ }
43
+ //# sourceMappingURL=AbstractCalmConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractCalmConnection.d.ts","sourceRoot":"","sources":["../../../src/server/connection/AbstractCalmConnection.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,WAAW,EACX,eAAe,EACf,mBAAmB,EACnB,aAAa,EACb,OAAO,EACR,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,KAAK,mBAAmB,EAEzB,MAAM,iBAAiB,CAAC;AAEzB,MAAM,WAAW,8BAA8B;IAC7C,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC7C,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAuBD;;;;GAIG;AACH,8BAAsB,sBAAuB,YAAW,eAAe;IACrE,SAAS,CAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACnC,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAC1C,SAAS,CAAC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1D,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,mBAAmB,CAAC;IACtD,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;gBAExB,OAAO,EAAE,8BAA8B;IAcnD,mDAAmD;IACnD,SAAS,CAAC,QAAQ,CAAC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEhE;;;OAGG;cACa,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAI1D,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAExB,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;IAI7B,aAAa,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC;IAIpD,WAAW,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO,EACxC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IA+B/B,OAAO,CAAC,OAAO;IAkBf,OAAO,CAAC,cAAc;YAQR,OAAO;CA0CtB"}
@@ -0,0 +1,155 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AbstractCalmConnection = void 0;
4
+ const calm_client_1 = require("@mcp-abap-adt/calm-client");
5
+ const serviceRoutes_1 = require("./serviceRoutes");
6
+ function trimTrailingSlash(v) {
7
+ return v.endsWith('/') ? v.slice(0, -1) : v;
8
+ }
9
+ function trimLeadingSlash(v) {
10
+ return v.startsWith('/') ? v.slice(1) : v;
11
+ }
12
+ function joinUrl(base, path) {
13
+ if (!path)
14
+ return trimTrailingSlash(base);
15
+ return `${trimTrailingSlash(base)}/${trimLeadingSlash(path)}`;
16
+ }
17
+ function toQueryString(params) {
18
+ if (!params || typeof params !== 'object')
19
+ return '';
20
+ const usp = new URLSearchParams();
21
+ for (const [k, v] of Object.entries(params)) {
22
+ if (v === undefined || v === null)
23
+ continue;
24
+ usp.append(k, String(v));
25
+ }
26
+ const s = usp.toString();
27
+ return s ? `?${s}` : '';
28
+ }
29
+ /**
30
+ * Shared fetch-based transport for Cloud ALM. Subclasses provide
31
+ * `attachAuth()` and may override `onAuthFailure()`. `baseUrl` is used
32
+ * verbatim — NO prefix injection.
33
+ */
34
+ class AbstractCalmConnection {
35
+ baseUrl;
36
+ defaultTimeout;
37
+ defaultHeaders;
38
+ serviceRoutes;
39
+ logger;
40
+ constructor(options) {
41
+ this.baseUrl = trimTrailingSlash(options.baseUrl);
42
+ this.defaultTimeout = options.defaultTimeout ?? 30_000;
43
+ this.defaultHeaders = {
44
+ Accept: 'application/json',
45
+ ...options.defaultHeaders,
46
+ };
47
+ this.serviceRoutes = {
48
+ ...serviceRoutes_1.DEFAULT_CALM_SERVICE_ROUTES,
49
+ ...options.serviceRoutes,
50
+ };
51
+ this.logger = options.logger;
52
+ }
53
+ /**
54
+ * Subclass hook on 401/403. Return true to retry once. Default: no
55
+ * retry.
56
+ */
57
+ async onAuthFailure(_status) {
58
+ return false;
59
+ }
60
+ async connect() { }
61
+ async getBaseUrl() {
62
+ return this.baseUrl;
63
+ }
64
+ async getServiceUrl(service) {
65
+ return joinUrl(this.baseUrl, this.serviceRoutes[service]);
66
+ }
67
+ async makeRequest(options) {
68
+ const base = options.service
69
+ ? await this.getServiceUrl(options.service)
70
+ : this.baseUrl;
71
+ const url = joinUrl(base, options.url) + toQueryString(options.params);
72
+ this.logger?.debug(`[calm] ${options.method} ${url}`);
73
+ try {
74
+ return await this.execute(url, options);
75
+ }
76
+ catch (err) {
77
+ const status = err instanceof calm_client_1.CalmApiError ? err.status : undefined;
78
+ if (status !== undefined && (await this.onAuthFailure(status))) {
79
+ this.logger?.debug(`[calm] ${status} from ${options.method} ${url} — refreshed auth, retrying once`);
80
+ // Retry once. The retry's own errors (network, abort, HTTP) go
81
+ // through the same normalization — never escape as a raw error.
82
+ try {
83
+ return await this.execute(url, options);
84
+ }
85
+ catch (retryErr) {
86
+ throw this.logFail(options.method, url, this.normalizeError(retryErr));
87
+ }
88
+ }
89
+ throw this.logFail(options.method, url, this.normalizeError(err));
90
+ }
91
+ }
92
+ logFail(method, url, err) {
93
+ // HTTP statuses (403/404/4xx/5xx) are expected operational signals —
94
+ // log at debug. Transport/network failures (no status) are logged at
95
+ // warn since they usually indicate misconfiguration or outage.
96
+ if (err.status === undefined) {
97
+ this.logger?.warn(`[calm] ${method} ${url} failed: ${err.message}`);
98
+ }
99
+ else {
100
+ this.logger?.debug(`[calm] ${method} ${url} -> ${err.status} (${err.code})`);
101
+ }
102
+ return err;
103
+ }
104
+ normalizeError(err) {
105
+ if (err instanceof calm_client_1.CalmApiError)
106
+ return err;
107
+ return calm_client_1.CalmApiError.fromNetwork(err, err instanceof Error ? err.message : String(err));
108
+ }
109
+ async execute(url, options) {
110
+ const auth = await this.attachAuth();
111
+ const headers = { ...this.defaultHeaders, ...auth, ...options.headers };
112
+ const init = {
113
+ method: options.method,
114
+ headers,
115
+ signal: AbortSignal.timeout(options.timeout ?? this.defaultTimeout),
116
+ };
117
+ if (options.data !== undefined) {
118
+ init.body =
119
+ typeof options.data === 'string'
120
+ ? options.data
121
+ : JSON.stringify(options.data);
122
+ if (!('Content-Type' in headers) && !('content-type' in headers)) {
123
+ headers['Content-Type'] =
124
+ 'application/json';
125
+ }
126
+ }
127
+ const response = await fetch(url, init);
128
+ const raw = await response.text();
129
+ const parsed = raw ? safeJson(raw) : undefined;
130
+ if (!response.ok) {
131
+ throw (0, calm_client_1.calmErrorFromBody)(response.status, parsed ?? raw);
132
+ }
133
+ this.logger?.debug(`[calm] ${response.status} ${options.method} ${url}`);
134
+ const outHeaders = {};
135
+ response.headers.forEach((v, k) => {
136
+ outHeaders[k] = v;
137
+ });
138
+ return {
139
+ data: parsed,
140
+ status: response.status,
141
+ statusText: response.statusText,
142
+ headers: outHeaders,
143
+ };
144
+ }
145
+ }
146
+ exports.AbstractCalmConnection = AbstractCalmConnection;
147
+ function safeJson(text) {
148
+ try {
149
+ return JSON.parse(text);
150
+ }
151
+ catch {
152
+ return text;
153
+ }
154
+ }
155
+ //# sourceMappingURL=AbstractCalmConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AbstractCalmConnection.js","sourceRoot":"","sources":["../../../src/server/connection/AbstractCalmConnection.ts"],"names":[],"mappings":";;;AAAA,2DAA4E;AAQ5E,mDAGyB;AAgBzB,SAAS,iBAAiB,CAAC,CAAS;IAClC,OAAO,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AACD,SAAS,gBAAgB,CAAC,CAAS;IACjC,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5C,CAAC;AACD,SAAS,OAAO,CAAC,IAAY,EAAE,IAAY;IACzC,IAAI,CAAC,IAAI;QAAE,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;IAC1C,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,IAAI,CAAC,EAAE,CAAC;AAChE,CAAC;AACD,SAAS,aAAa,CAAC,MAAe;IACpC,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,EAAE,CAAC;IACrD,MAAM,GAAG,GAAG,IAAI,eAAe,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAiC,CAAC,EAAE,CAAC;QACvE,IAAI,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,IAAI;YAAE,SAAS;QAC5C,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,CAAC;IACD,MAAM,CAAC,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;IACzB,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC1B,CAAC;AAED;;;;GAIG;AACH,MAAsB,sBAAsB;IACvB,OAAO,CAAS;IAChB,cAAc,CAAS;IACvB,cAAc,CAAyB;IACvC,aAAa,CAAsB;IACnC,MAAM,CAAW;IAEpC,YAAY,OAAuC;QACjD,IAAI,CAAC,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,MAAM,CAAC;QACvD,IAAI,CAAC,cAAc,GAAG;YACpB,MAAM,EAAE,kBAAkB;YAC1B,GAAG,OAAO,CAAC,cAAc;SAC1B,CAAC;QACF,IAAI,CAAC,aAAa,GAAG;YACnB,GAAG,2CAA2B;YAC9B,GAAG,OAAO,CAAC,aAAa;SACzB,CAAC;QACF,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;IAKD;;;OAGG;IACO,KAAK,CAAC,aAAa,CAAC,OAAe;QAC3C,OAAO,KAAK,CAAC;IACf,CAAC;IAED,KAAK,CAAC,OAAO,KAAmB,CAAC;IAEjC,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,OAAoB;QACtC,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,CAAC,WAAW,CACf,OAA4B;QAE5B,MAAM,IAAI,GAAG,OAAO,CAAC,OAAO;YAC1B,CAAC,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC;YAC3C,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC;QACjB,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEvE,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;QACtD,IAAI,CAAC;YACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAO,GAAG,EAAE,OAAO,CAAC,CAAC;QAChD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,MAAM,GAAG,GAAG,YAAY,0BAAY,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC;YACpE,IAAI,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;gBAC/D,IAAI,CAAC,MAAM,EAAE,KAAK,CAChB,UAAU,MAAM,SAAS,OAAO,CAAC,MAAM,IAAI,GAAG,kCAAkC,CACjF,CAAC;gBACF,+DAA+D;gBAC/D,gEAAgE;gBAChE,IAAI,CAAC;oBACH,OAAO,MAAM,IAAI,CAAC,OAAO,CAAO,GAAG,EAAE,OAAO,CAAC,CAAC;gBAChD,CAAC;gBAAC,OAAO,QAAQ,EAAE,CAAC;oBAClB,MAAM,IAAI,CAAC,OAAO,CAChB,OAAO,CAAC,MAAM,EACd,GAAG,EACH,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAC9B,CAAC;gBACJ,CAAC;YACH,CAAC;YACD,MAAM,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;QACpE,CAAC;IACH,CAAC;IAEO,OAAO,CACb,MAAc,EACd,GAAW,EACX,GAAiB;QAEjB,qEAAqE;QACrE,qEAAqE;QACrE,+DAA+D;QAC/D,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,UAAU,MAAM,IAAI,GAAG,YAAY,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACtE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,EAAE,KAAK,CAChB,UAAU,MAAM,IAAI,GAAG,OAAO,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG,CACzD,CAAC;QACJ,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,cAAc,CAAC,GAAY;QACjC,IAAI,GAAG,YAAY,0BAAY;YAAE,OAAO,GAAG,CAAC;QAC5C,OAAO,0BAAY,CAAC,WAAW,CAC7B,GAAG,EACH,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CACjD,CAAC;IACJ,CAAC;IAEO,KAAK,CAAC,OAAO,CACnB,GAAW,EACX,OAA4B;QAE5B,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACrC,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC,cAAc,EAAE,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC,OAAO,EAAE,CAAC;QACxE,MAAM,IAAI,GAAgB;YACxB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,OAAO;YACP,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,IAAI,IAAI,CAAC,cAAc,CAAC;SACpE,CAAC;QACF,IAAI,OAAO,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI;gBACP,OAAO,OAAO,CAAC,IAAI,KAAK,QAAQ;oBAC9B,CAAC,CAAC,OAAO,CAAC,IAAI;oBACd,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YACnC,IAAI,CAAC,CAAC,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,cAAc,IAAI,OAAO,CAAC,EAAE,CAAC;gBAChE,OAAkC,CAAC,cAAc,CAAC;oBACjD,kBAAkB,CAAC;YACvB,CAAC;QACH,CAAC;QAED,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAE/C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAA,+BAAiB,EAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,IAAI,GAAG,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,QAAQ,CAAC,MAAM,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;QACzE,MAAM,UAAU,GAA2B,EAAE,CAAC;QAC9C,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YAChC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACpB,CAAC,CAAC,CAAC;QACH,OAAO;YACL,IAAI,EAAE,MAAW;YACjB,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,UAAU,EAAE,QAAQ,CAAC,UAAU;YAC/B,OAAO,EAAE,UAAU;SACG,CAAC;IAC3B,CAAC;CACF;AA/ID,wDA+IC;AAED,SAAS,QAAQ,CAAC,IAAY;IAC5B,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ITokenRefresher } from '@mcp-abap-adt/interfaces';
2
+ import { AbstractCalmConnection, type IAbstractCalmConnectionOptions } from './AbstractCalmConnection';
3
+ export interface IOAuth2CalmConnectionOptions extends IAbstractCalmConnectionOptions {
4
+ tokenRefresher: ITokenRefresher;
5
+ }
6
+ export declare class OAuth2CalmConnection extends AbstractCalmConnection {
7
+ private readonly tokenRefresher;
8
+ constructor(options: IOAuth2CalmConnectionOptions);
9
+ connect(): Promise<void>;
10
+ protected attachAuth(): Promise<Record<string, string>>;
11
+ protected onAuthFailure(status: number): Promise<boolean>;
12
+ }
13
+ //# sourceMappingURL=OAuth2CalmConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuth2CalmConnection.d.ts","sourceRoot":"","sources":["../../../src/server/connection/OAuth2CalmConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAChE,OAAO,EACL,sBAAsB,EACtB,KAAK,8BAA8B,EACpC,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,4BACf,SAAQ,8BAA8B;IACtC,cAAc,EAAE,eAAe,CAAC;CACjC;AAED,qBAAa,oBAAqB,SAAQ,sBAAsB;IAC9D,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAkB;gBACrC,OAAO,EAAE,4BAA4B;IAK3C,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;cAId,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;cAK7C,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAOhE"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OAuth2CalmConnection = void 0;
4
+ const AbstractCalmConnection_1 = require("./AbstractCalmConnection");
5
+ class OAuth2CalmConnection extends AbstractCalmConnection_1.AbstractCalmConnection {
6
+ tokenRefresher;
7
+ constructor(options) {
8
+ super(options);
9
+ this.tokenRefresher = options.tokenRefresher;
10
+ }
11
+ async connect() {
12
+ await this.tokenRefresher.getToken();
13
+ }
14
+ async attachAuth() {
15
+ const token = await this.tokenRefresher.getToken();
16
+ return { Authorization: `Bearer ${token}` };
17
+ }
18
+ async onAuthFailure(status) {
19
+ if (status === 401 || status === 403) {
20
+ await this.tokenRefresher.refreshToken();
21
+ return true;
22
+ }
23
+ return false;
24
+ }
25
+ }
26
+ exports.OAuth2CalmConnection = OAuth2CalmConnection;
27
+ //# sourceMappingURL=OAuth2CalmConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OAuth2CalmConnection.js","sourceRoot":"","sources":["../../../src/server/connection/OAuth2CalmConnection.ts"],"names":[],"mappings":";;;AACA,qEAGkC;AAOlC,MAAa,oBAAqB,SAAQ,+CAAsB;IAC7C,cAAc,CAAkB;IACjD,YAAY,OAAqC;QAC/C,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,OAAO;QACX,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;IACvC,CAAC;IAES,KAAK,CAAC,UAAU;QACxB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,EAAE,CAAC;QACnD,OAAO,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,CAAC;IAC9C,CAAC;IAES,KAAK,CAAC,aAAa,CAAC,MAAc;QAC1C,IAAI,MAAM,KAAK,GAAG,IAAI,MAAM,KAAK,GAAG,EAAE,CAAC;YACrC,MAAM,IAAI,CAAC,cAAc,CAAC,YAAY,EAAE,CAAC;YACzC,OAAO,IAAI,CAAC;QACd,CAAC;QACD,OAAO,KAAK,CAAC;IACf,CAAC;CACF;AAvBD,oDAuBC"}
@@ -0,0 +1,10 @@
1
+ import { AbstractCalmConnection, type IAbstractCalmConnectionOptions } from './AbstractCalmConnection';
2
+ export interface ISandboxCalmConnectionOptions extends IAbstractCalmConnectionOptions {
3
+ apiKey: string;
4
+ }
5
+ export declare class SandboxCalmConnection extends AbstractCalmConnection {
6
+ private readonly apiKey;
7
+ constructor(options: ISandboxCalmConnectionOptions);
8
+ protected attachAuth(): Promise<Record<string, string>>;
9
+ }
10
+ //# sourceMappingURL=SandboxCalmConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SandboxCalmConnection.d.ts","sourceRoot":"","sources":["../../../src/server/connection/SandboxCalmConnection.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,sBAAsB,EACtB,KAAK,8BAA8B,EACpC,MAAM,0BAA0B,CAAC;AAElC,MAAM,WAAW,6BACf,SAAQ,8BAA8B;IACtC,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,qBAAa,qBAAsB,SAAQ,sBAAsB;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBACpB,OAAO,EAAE,6BAA6B;cAIlC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAG9D"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SandboxCalmConnection = void 0;
4
+ const AbstractCalmConnection_1 = require("./AbstractCalmConnection");
5
+ class SandboxCalmConnection extends AbstractCalmConnection_1.AbstractCalmConnection {
6
+ apiKey;
7
+ constructor(options) {
8
+ super(options);
9
+ this.apiKey = options.apiKey;
10
+ }
11
+ async attachAuth() {
12
+ return { APIKey: this.apiKey };
13
+ }
14
+ }
15
+ exports.SandboxCalmConnection = SandboxCalmConnection;
16
+ //# sourceMappingURL=SandboxCalmConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SandboxCalmConnection.js","sourceRoot":"","sources":["../../../src/server/connection/SandboxCalmConnection.ts"],"names":[],"mappings":";;;AAAA,qEAGkC;AAOlC,MAAa,qBAAsB,SAAQ,+CAAsB;IAC9C,MAAM,CAAS;IAChC,YAAY,OAAsC;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAC/B,CAAC;IACS,KAAK,CAAC,UAAU;QACxB,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACjC,CAAC;CACF;AATD,sDASC"}
@@ -0,0 +1,18 @@
1
+ import type { ITokenRefresher } from '@mcp-abap-adt/interfaces';
2
+ /**
3
+ * Minimal XSUAA `client_credentials` refresher — sufficient for
4
+ * standalone-mode servers without a shared auth-broker. Caches the
5
+ * token until `refreshToken()` is called. Consumers running
6
+ * `@mcp-abap-adt/auth-broker` can pass their own ITokenRefresher to
7
+ * the connection factory instead.
8
+ */
9
+ export declare class XsuaaRefresher implements ITokenRefresher {
10
+ private readonly uaaUrl;
11
+ private readonly clientId;
12
+ private readonly clientSecret;
13
+ private cached?;
14
+ constructor(uaaUrl: string, clientId: string, clientSecret: string);
15
+ getToken(): Promise<string>;
16
+ refreshToken(): Promise<string>;
17
+ }
18
+ //# sourceMappingURL=XsuaaRefresher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XsuaaRefresher.d.ts","sourceRoot":"","sources":["../../../src/server/connection/XsuaaRefresher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAEhE;;;;;;GAMG;AACH,qBAAa,cAAe,YAAW,eAAe;IAIlD,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAL/B,OAAO,CAAC,MAAM,CAAC,CAAS;gBAGL,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM;IAGjC,QAAQ,IAAI,OAAO,CAAC,MAAM,CAAC;IAK3B,YAAY,IAAI,OAAO,CAAC,MAAM,CAAC;CA2BtC"}
@@ -0,0 +1,51 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.XsuaaRefresher = void 0;
4
+ /**
5
+ * Minimal XSUAA `client_credentials` refresher — sufficient for
6
+ * standalone-mode servers without a shared auth-broker. Caches the
7
+ * token until `refreshToken()` is called. Consumers running
8
+ * `@mcp-abap-adt/auth-broker` can pass their own ITokenRefresher to
9
+ * the connection factory instead.
10
+ */
11
+ class XsuaaRefresher {
12
+ uaaUrl;
13
+ clientId;
14
+ clientSecret;
15
+ cached;
16
+ constructor(uaaUrl, clientId, clientSecret) {
17
+ this.uaaUrl = uaaUrl;
18
+ this.clientId = clientId;
19
+ this.clientSecret = clientSecret;
20
+ }
21
+ async getToken() {
22
+ if (!this.cached)
23
+ return this.refreshToken();
24
+ return this.cached;
25
+ }
26
+ async refreshToken() {
27
+ const url = `${this.uaaUrl.replace(/\/$/, '')}/oauth/token`;
28
+ const basic = Buffer.from(`${this.clientId}:${this.clientSecret}`).toString('base64');
29
+ const response = await fetch(url, {
30
+ method: 'POST',
31
+ headers: {
32
+ Authorization: `Basic ${basic}`,
33
+ 'Content-Type': 'application/x-www-form-urlencoded',
34
+ Accept: 'application/json',
35
+ },
36
+ body: 'grant_type=client_credentials',
37
+ });
38
+ if (!response.ok) {
39
+ const body = await response.text().catch(() => '');
40
+ throw new Error(`XSUAA token request failed: ${response.status} ${response.statusText} — ${body.slice(0, 200)}`);
41
+ }
42
+ const json = (await response.json());
43
+ if (!json.access_token) {
44
+ throw new Error('XSUAA token response missing access_token');
45
+ }
46
+ this.cached = json.access_token;
47
+ return this.cached;
48
+ }
49
+ }
50
+ exports.XsuaaRefresher = XsuaaRefresher;
51
+ //# sourceMappingURL=XsuaaRefresher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"XsuaaRefresher.js","sourceRoot":"","sources":["../../../src/server/connection/XsuaaRefresher.ts"],"names":[],"mappings":";;;AAEA;;;;;;GAMG;AACH,MAAa,cAAc;IAIN;IACA;IACA;IALX,MAAM,CAAU;IAExB,YACmB,MAAc,EACd,QAAgB,EAChB,YAAoB;QAFpB,WAAM,GAAN,MAAM,CAAQ;QACd,aAAQ,GAAR,QAAQ,CAAQ;QAChB,iBAAY,GAAZ,YAAY,CAAQ;IACpC,CAAC;IAEJ,KAAK,CAAC,QAAQ;QACZ,IAAI,CAAC,IAAI,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;QAC7C,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;IAED,KAAK,CAAC,YAAY;QAChB,MAAM,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,cAAc,CAAC;QAC5D,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC,CAAC,QAAQ,CACzE,QAAQ,CACT,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;YAChC,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,aAAa,EAAE,SAAS,KAAK,EAAE;gBAC/B,cAAc,EAAE,mCAAmC;gBACnD,MAAM,EAAE,kBAAkB;aAC3B;YACD,IAAI,EAAE,+BAA+B;SACtC,CAAC,CAAC;QACH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;YACnD,MAAM,IAAI,KAAK,CACb,+BAA+B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,MAAM,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,CAChG,CAAC;QACJ,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAA8B,CAAC;QAClE,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YACvB,MAAM,IAAI,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAC/D,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC;QAChC,OAAO,IAAI,CAAC,MAAM,CAAC;IACrB,CAAC;CACF;AAzCD,wCAyCC"}
@@ -0,0 +1,13 @@
1
+ import type { ICalmConnection, ILogger, ITokenRefresher } from '@mcp-abap-adt/interfaces';
2
+ import type { ICalmServerConfig } from '../config';
3
+ export interface ICreateCalmConnectionOverrides {
4
+ /** Bring-your-own refresher; overrides the default XsuaaRefresher. */
5
+ tokenRefresher?: ITokenRefresher;
6
+ /**
7
+ * Logger passed through to the connection. In a stdio runtime pass a
8
+ * stderr-only logger (`StderrLogger`); never one that writes stdout.
9
+ */
10
+ logger?: ILogger;
11
+ }
12
+ export declare function createCalmConnection(config: ICalmServerConfig, overrides?: ICreateCalmConnectionOverrides): ICalmConnection;
13
+ //# sourceMappingURL=createCalmConnection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createCalmConnection.d.ts","sourceRoot":"","sources":["../../../src/server/connection/createCalmConnection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,OAAO,EACP,eAAe,EAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAKnD,MAAM,WAAW,8BAA8B;IAC7C,sEAAsE;IACtE,cAAc,CAAC,EAAE,eAAe,CAAC;IACjC;;;OAGG;IACH,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,iBAAiB,EACzB,SAAS,GAAE,8BAAmC,GAC7C,eAAe,CA2BjB"}
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createCalmConnection = createCalmConnection;
4
+ const OAuth2CalmConnection_1 = require("./OAuth2CalmConnection");
5
+ const SandboxCalmConnection_1 = require("./SandboxCalmConnection");
6
+ const XsuaaRefresher_1 = require("./XsuaaRefresher");
7
+ function createCalmConnection(config, overrides = {}) {
8
+ if (config.mode === 'oauth2') {
9
+ const tokenRefresher = overrides.tokenRefresher ??
10
+ new XsuaaRefresher_1.XsuaaRefresher(config.uaaUrl, config.uaaClientId, config.uaaClientSecret);
11
+ return new OAuth2CalmConnection_1.OAuth2CalmConnection({
12
+ baseUrl: config.baseUrl,
13
+ tokenRefresher,
14
+ defaultTimeout: config.timeoutMs,
15
+ logger: overrides.logger,
16
+ });
17
+ }
18
+ if (config.mode === 'sandbox') {
19
+ return new SandboxCalmConnection_1.SandboxCalmConnection({
20
+ baseUrl: config.baseUrl,
21
+ apiKey: config.apiKey,
22
+ defaultTimeout: config.timeoutMs,
23
+ logger: overrides.logger,
24
+ });
25
+ }
26
+ throw new Error(`Unsupported CALM mode: ${config.mode}`);
27
+ }
28
+ //# sourceMappingURL=createCalmConnection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createCalmConnection.js","sourceRoot":"","sources":["../../../src/server/connection/createCalmConnection.ts"],"names":[],"mappings":";;AAoBA,oDA8BC;AA5CD,iEAA8D;AAC9D,mEAAgE;AAChE,qDAAkD;AAYlD,SAAgB,oBAAoB,CAClC,MAAyB,EACzB,YAA4C,EAAE;IAE9C,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,MAAM,cAAc,GAClB,SAAS,CAAC,cAAc;YACxB,IAAI,+BAAc,CAChB,MAAM,CAAC,MAAgB,EACvB,MAAM,CAAC,WAAqB,EAC5B,MAAM,CAAC,eAAyB,CACjC,CAAC;QACJ,OAAO,IAAI,2CAAoB,CAAC;YAC9B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,cAAc;YACd,cAAc,EAAE,MAAM,CAAC,SAAS;YAChC,MAAM,EAAE,SAAS,CAAC,MAAM;SACzB,CAAC,CAAC;IACL,CAAC;IACD,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;QAC9B,OAAO,IAAI,6CAAqB,CAAC;YAC/B,OAAO,EAAE,MAAM,CAAC,OAAO;YACvB,MAAM,EAAE,MAAM,CAAC,MAAgB;YAC/B,cAAc,EAAE,MAAM,CAAC,SAAS;YAChC,MAAM,EAAE,SAAS,CAAC,MAAM;SACzB,CAAC,CAAC;IACL,CAAC;IACD,MAAM,IAAI,KAAK,CACb,0BAA2B,MAA2B,CAAC,IAAI,EAAE,CAC9D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,10 @@
1
+ export type { IAbstractCalmConnectionOptions } from './AbstractCalmConnection';
2
+ export { AbstractCalmConnection } from './AbstractCalmConnection';
3
+ export { createCalmConnection, type ICreateCalmConnectionOverrides, } from './createCalmConnection';
4
+ export type { IOAuth2CalmConnectionOptions } from './OAuth2CalmConnection';
5
+ export { OAuth2CalmConnection } from './OAuth2CalmConnection';
6
+ export type { ISandboxCalmConnectionOptions } from './SandboxCalmConnection';
7
+ export { SandboxCalmConnection } from './SandboxCalmConnection';
8
+ export { type CalmServiceRouteMap, DEFAULT_CALM_SERVICE_ROUTES, } from './serviceRoutes';
9
+ export { XsuaaRefresher } from './XsuaaRefresher';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/server/connection/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,8BAA8B,EAAE,MAAM,0BAA0B,CAAC;AAC/E,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EACL,oBAAoB,EACpB,KAAK,8BAA8B,GACpC,MAAM,wBAAwB,CAAC;AAChC,YAAY,EAAE,4BAA4B,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAC9D,YAAY,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AAC7E,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,OAAO,EACL,KAAK,mBAAmB,EACxB,2BAA2B,GAC5B,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.XsuaaRefresher = exports.DEFAULT_CALM_SERVICE_ROUTES = exports.SandboxCalmConnection = exports.OAuth2CalmConnection = exports.createCalmConnection = exports.AbstractCalmConnection = void 0;
4
+ var AbstractCalmConnection_1 = require("./AbstractCalmConnection");
5
+ Object.defineProperty(exports, "AbstractCalmConnection", { enumerable: true, get: function () { return AbstractCalmConnection_1.AbstractCalmConnection; } });
6
+ var createCalmConnection_1 = require("./createCalmConnection");
7
+ Object.defineProperty(exports, "createCalmConnection", { enumerable: true, get: function () { return createCalmConnection_1.createCalmConnection; } });
8
+ var OAuth2CalmConnection_1 = require("./OAuth2CalmConnection");
9
+ Object.defineProperty(exports, "OAuth2CalmConnection", { enumerable: true, get: function () { return OAuth2CalmConnection_1.OAuth2CalmConnection; } });
10
+ var SandboxCalmConnection_1 = require("./SandboxCalmConnection");
11
+ Object.defineProperty(exports, "SandboxCalmConnection", { enumerable: true, get: function () { return SandboxCalmConnection_1.SandboxCalmConnection; } });
12
+ var serviceRoutes_1 = require("./serviceRoutes");
13
+ Object.defineProperty(exports, "DEFAULT_CALM_SERVICE_ROUTES", { enumerable: true, get: function () { return serviceRoutes_1.DEFAULT_CALM_SERVICE_ROUTES; } });
14
+ var XsuaaRefresher_1 = require("./XsuaaRefresher");
15
+ Object.defineProperty(exports, "XsuaaRefresher", { enumerable: true, get: function () { return XsuaaRefresher_1.XsuaaRefresher; } });
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/server/connection/index.ts"],"names":[],"mappings":";;;AACA,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,+DAGgC;AAF9B,4HAAA,oBAAoB,OAAA;AAItB,+DAA8D;AAArD,4HAAA,oBAAoB,OAAA;AAE7B,iEAAgE;AAAvD,8HAAA,qBAAqB,OAAA;AAC9B,iDAGyB;AADvB,4HAAA,2BAA2B,OAAA;AAE7B,mDAAkD;AAAzC,gHAAA,cAAc,OAAA"}
@@ -0,0 +1,16 @@
1
+ import type { CalmService } from '@mcp-abap-adt/interfaces';
2
+ export type CalmServiceRouteMap = Record<CalmService, string>;
3
+ /**
4
+ * Cloud ALM service route suffixes appended to `baseUrl` verbatim.
5
+ *
6
+ * Full URL = `joinUrl(baseUrl, serviceRoute)`. `baseUrl` already
7
+ * includes any tenant mount prefix (for OAuth2 tenants that is the
8
+ * `/api` that SAP puts in `endpoints.Api`; for sandbox it is the
9
+ * `/SAPCALM` suffix). The connection layer does NOT inject a prefix.
10
+ *
11
+ * Seeded from `sap-cloud-alm-odata-mcp/src/config.rs`; verify against
12
+ * a live tenant. Override per-connection via the `serviceRoutes` ctor
13
+ * option.
14
+ */
15
+ export declare const DEFAULT_CALM_SERVICE_ROUTES: CalmServiceRouteMap;
16
+ //# sourceMappingURL=serviceRoutes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceRoutes.d.ts","sourceRoot":"","sources":["../../../src/server/connection/serviceRoutes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAE5D,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AAE9D;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,2BAA2B,EAAE,mBAUzC,CAAC"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULT_CALM_SERVICE_ROUTES = void 0;
4
+ /**
5
+ * Cloud ALM service route suffixes appended to `baseUrl` verbatim.
6
+ *
7
+ * Full URL = `joinUrl(baseUrl, serviceRoute)`. `baseUrl` already
8
+ * includes any tenant mount prefix (for OAuth2 tenants that is the
9
+ * `/api` that SAP puts in `endpoints.Api`; for sandbox it is the
10
+ * `/SAPCALM` suffix). The connection layer does NOT inject a prefix.
11
+ *
12
+ * Seeded from `sap-cloud-alm-odata-mcp/src/config.rs`; verify against
13
+ * a live tenant. Override per-connection via the `serviceRoutes` ctor
14
+ * option.
15
+ */
16
+ exports.DEFAULT_CALM_SERVICE_ROUTES = {
17
+ features: '/calm-features/v1',
18
+ documents: '/calm-documents/v1',
19
+ tasks: '/calm-tasks/v1',
20
+ projects: '/calm-projects/v1',
21
+ testManagement: '/calm-testmanagement/v1',
22
+ hierarchy: '/calm-processhierarchy/v1',
23
+ analytics: '/calm-analytics/v1/odata/v4/analytics',
24
+ processMonitoring: '/calm-processmonitoring/v1',
25
+ logs: '/calm-logs/v1',
26
+ };
27
+ //# sourceMappingURL=serviceRoutes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"serviceRoutes.js","sourceRoot":"","sources":["../../../src/server/connection/serviceRoutes.ts"],"names":[],"mappings":";;;AAIA;;;;;;;;;;;GAWG;AACU,QAAA,2BAA2B,GAAwB;IAC9D,QAAQ,EAAE,mBAAmB;IAC7B,SAAS,EAAE,oBAAoB;IAC/B,KAAK,EAAE,gBAAgB;IACvB,QAAQ,EAAE,mBAAmB;IAC7B,cAAc,EAAE,yBAAyB;IACzC,SAAS,EAAE,2BAA2B;IACtC,SAAS,EAAE,uCAAuC;IAClD,iBAAiB,EAAE,4BAA4B;IAC/C,IAAI,EAAE,eAAe;CACtB,CAAC"}
@@ -20,7 +20,7 @@ const PACKAGE_VERSION = '0.3.0';
20
20
  async function runStdio() {
21
21
  const logger = new stderrLogger_1.StderrLogger();
22
22
  const config = (0, config_1.readConfig)();
23
- const calm = (0, buildClient_1.buildCalmClient)(config);
23
+ const calm = await (0, buildClient_1.buildCalmClient)(config, { logger });
24
24
  const server = new BaseCalmMcpServer_1.BaseCalmMcpServer({
25
25
  name: PACKAGE_NAME,
26
26
  version: PACKAGE_VERSION,
@@ -1 +1 @@
1
- {"version":3,"file":"runStdio.js","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":";;AAkBA,4BAwCC;AA1DD,wEAAiF;AACjF,oCAAsC;AACtC,2DAAwD;AACxD,+CAAgD;AAChD,qCAAsC;AACtC,iDAA8C;AAE9C,MAAM,YAAY,GAAG,2BAA2B,CAAC;AACjD,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC;;;;;;;GAOG;AACI,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,IAAI,2BAAY,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,IAAA,6BAAe,EAAC,MAAM,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,qCAAiB,CAAC;QACnC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,eAAe;QACxB,IAAI;QACJ,MAAM;QACN,MAAM,EAAE,CAAC,GAAG,kBAAU,CAAC;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC/B,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,eAAe;QACxB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,mBAAmB,EAAE,CAAC,MAAM;KAC3C,CAAC,CAAC;IAEH,mEAAmE;IACnE,0DAA0D;IAC1D,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;IAErC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAEhD,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
1
+ {"version":3,"file":"runStdio.js","sourceRoot":"","sources":["../../src/server/runStdio.ts"],"names":[],"mappings":";;AAkBA,4BAwCC;AA1DD,wEAAiF;AACjF,oCAAsC;AACtC,2DAAwD;AACxD,+CAAgD;AAChD,qCAAsC;AACtC,iDAA8C;AAE9C,MAAM,YAAY,GAAG,2BAA2B,CAAC;AACjD,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC;;;;;;;GAOG;AACI,KAAK,UAAU,QAAQ;IAC5B,MAAM,MAAM,GAAG,IAAI,2BAAY,EAAE,CAAC;IAClC,MAAM,MAAM,GAAG,IAAA,mBAAU,GAAE,CAAC;IAC5B,MAAM,IAAI,GAAG,MAAM,IAAA,6BAAe,EAAC,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACvD,MAAM,MAAM,GAAG,IAAI,qCAAiB,CAAC;QACnC,IAAI,EAAE,YAAY;QAClB,OAAO,EAAE,eAAe;QACxB,IAAI;QACJ,MAAM;QACN,MAAM,EAAE,CAAC,GAAG,kBAAU,CAAC;KACxB,CAAC,CAAC;IAEH,MAAM,CAAC,IAAI,CAAC,mBAAmB,EAAE;QAC/B,OAAO,EAAE,YAAY;QACrB,OAAO,EAAE,eAAe;QACxB,IAAI,EAAE,MAAM,CAAC,IAAI;QACjB,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,KAAK,EAAE,MAAM,CAAC,mBAAmB,EAAE,CAAC,MAAM;KAC3C,CAAC,CAAC;IAEH,mEAAmE;IACnE,0DAA0D;IAC1D,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC,OAAO,EAAE,CAAC;IAErC,MAAM,SAAS,GAAG,IAAI,+BAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,MAAM,CAAC,IAAI,CAAC,kCAAkC,CAAC,CAAC;IAEhD,mEAAmE;IACnE,4DAA4D;IAC5D,MAAM,QAAQ,GAAG,KAAK,IAAmB,EAAE;QACzC,MAAM,CAAC,IAAI,CAAC,mCAAmC,CAAC,CAAC;QACjD,IAAI,CAAC;YACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACvB,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC;IACF,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAC/B,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAClC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/calm-server",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "MCP server + reusable tool primitives for SAP Cloud ALM, backed by @mcp-abap-adt/calm-client.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -19,6 +19,10 @@
19
19
  "./registry": {
20
20
  "types": "./dist/registry/index.d.ts",
21
21
  "default": "./dist/registry/index.js"
22
+ },
23
+ "./connection": {
24
+ "types": "./dist/server/connection/index.d.ts",
25
+ "default": "./dist/server/connection/index.js"
22
26
  }
23
27
  },
24
28
  "files": [
@@ -63,16 +67,22 @@
63
67
  "tools"
64
68
  ],
65
69
  "peerDependencies": {
66
- "@mcp-abap-adt/calm-client": "^0.3.0",
70
+ "@mcp-abap-adt/auth-broker": "^1.0.5",
71
+ "@mcp-abap-adt/auth-providers": "^1.0.5",
72
+ "@mcp-abap-adt/auth-stores": "^1.0.4",
73
+ "@mcp-abap-adt/calm-client": "^0.4.0",
67
74
  "@mcp-abap-adt/interfaces": "^7.1.0",
68
75
  "@modelcontextprotocol/sdk": "^1.0.0"
69
76
  },
77
+ "dependencies": {
78
+ "dotenv": "^17.3.1"
79
+ },
70
80
  "devDependencies": {
71
81
  "@biomejs/biome": "^2.4.4",
72
82
  "@mcp-abap-adt/auth-broker": "^1.0.5",
73
83
  "@mcp-abap-adt/auth-providers": "^1.0.5",
74
84
  "@mcp-abap-adt/auth-stores": "^1.0.4",
75
- "@mcp-abap-adt/calm-client": "^0.3.0",
85
+ "@mcp-abap-adt/calm-client": "^0.4.0",
76
86
  "@mcp-abap-adt/interfaces": "^7.1.0",
77
87
  "@mcp-abap-adt/logger": "^0.1.4",
78
88
  "@modelcontextprotocol/sdk": "^1.0.0",