@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.
- package/CHANGELOG.md +40 -0
- package/README.md +45 -0
- package/dist/server/auth/buildBroker.d.ts +22 -0
- package/dist/server/auth/buildBroker.d.ts.map +1 -0
- package/dist/server/auth/buildBroker.js +55 -0
- package/dist/server/auth/buildBroker.js.map +1 -0
- package/dist/server/auth/legacyEnvShim.d.ts +10 -0
- package/dist/server/auth/legacyEnvShim.d.ts.map +1 -0
- package/dist/server/auth/legacyEnvShim.js +23 -0
- package/dist/server/auth/legacyEnvShim.js.map +1 -0
- package/dist/server/buildClient.d.ts +17 -5
- package/dist/server/buildClient.d.ts.map +1 -1
- package/dist/server/buildClient.js +22 -64
- package/dist/server/buildClient.js.map +1 -1
- package/dist/server/config.d.ts +3 -0
- package/dist/server/config.d.ts.map +1 -1
- package/dist/server/config.js +19 -3
- package/dist/server/config.js.map +1 -1
- package/dist/server/connection/AbstractCalmConnection.d.ts +43 -0
- package/dist/server/connection/AbstractCalmConnection.d.ts.map +1 -0
- package/dist/server/connection/AbstractCalmConnection.js +155 -0
- package/dist/server/connection/AbstractCalmConnection.js.map +1 -0
- package/dist/server/connection/OAuth2CalmConnection.d.ts +13 -0
- package/dist/server/connection/OAuth2CalmConnection.d.ts.map +1 -0
- package/dist/server/connection/OAuth2CalmConnection.js +27 -0
- package/dist/server/connection/OAuth2CalmConnection.js.map +1 -0
- package/dist/server/connection/SandboxCalmConnection.d.ts +10 -0
- package/dist/server/connection/SandboxCalmConnection.d.ts.map +1 -0
- package/dist/server/connection/SandboxCalmConnection.js +16 -0
- package/dist/server/connection/SandboxCalmConnection.js.map +1 -0
- package/dist/server/connection/XsuaaRefresher.d.ts +18 -0
- package/dist/server/connection/XsuaaRefresher.d.ts.map +1 -0
- package/dist/server/connection/XsuaaRefresher.js +51 -0
- package/dist/server/connection/XsuaaRefresher.js.map +1 -0
- package/dist/server/connection/createCalmConnection.d.ts +13 -0
- package/dist/server/connection/createCalmConnection.d.ts.map +1 -0
- package/dist/server/connection/createCalmConnection.js +28 -0
- package/dist/server/connection/createCalmConnection.js.map +1 -0
- package/dist/server/connection/index.d.ts +10 -0
- package/dist/server/connection/index.d.ts.map +1 -0
- package/dist/server/connection/index.js +16 -0
- package/dist/server/connection/index.js.map +1 -0
- package/dist/server/connection/serviceRoutes.d.ts +16 -0
- package/dist/server/connection/serviceRoutes.d.ts.map +1 -0
- package/dist/server/connection/serviceRoutes.js +27 -0
- package/dist/server/connection/serviceRoutes.js.map +1 -0
- package/dist/server/runStdio.js +1 -1
- package/dist/server/runStdio.js.map +1 -1
- 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`.
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
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,
|
|
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
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
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
|
-
|
|
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
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
tokenRefresher
|
|
63
|
-
|
|
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
|
-
|
|
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":";;
|
|
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"}
|
package/dist/server/config.d.ts
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/server/config.js
CHANGED
|
@@ -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:
|
|
49
|
-
uaaClientId:
|
|
50
|
-
uaaClientSecret:
|
|
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;
|
|
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":"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"}
|
|
@@ -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"}
|
package/dist/server/runStdio.js
CHANGED
|
@@ -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;
|
|
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
|
+
"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/
|
|
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.
|
|
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",
|