@mcp-abap-adt/proxy 4.0.1 → 4.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [4.1.0] - 2026-09-24
11
+
12
+ **The management mode can now say which credentials are available, and start a
13
+ proxy with one.** A config says which service to reach; an environment says what
14
+ to authenticate with. Both are chosen by name, so a client picks them without
15
+ being told any paths.
16
+
17
+ ### Added
18
+
19
+ - **`proxy_environments`** — lists the environments in `sessions/`, one SAP
20
+ system's credentials each, by the name `proxy_start` takes. Variable **names**
21
+ only, never values: this is read to help a client choose, and a value here
22
+ would be a credential in a tool result. `*.env.template` and anything that is
23
+ not a `.env` are left out.
24
+
25
+ - **`environment` on `proxy_start`** — optional, and the reason it exists: a
26
+ config that references `${SAP_LOGIN}` without naming an `envFile:` of its own
27
+ had nowhere to get it from, so `proxy_start` failed on exactly the configs a
28
+ user runs with `--env-file` on the command line. A name, not a path, for the
29
+ same reason `config` is a name: `sessions/` sits beside `proxy/` and
30
+ `service-keys/`.
31
+
32
+ One `--env-file` on the management process would not have served: it overrides
33
+ every config's own `envFile:`, so one system's secrets would reach every other
34
+ system started in that session. Per call, it cannot.
35
+
36
+ - `loadConfig(configPath, envFileOverride)` carries it. Precedence, most
37
+ specific first: this argument, then `--env-file`, then the config's own
38
+ `envFile:`.
39
+
40
+ ### Unchanged, and deliberately
41
+
42
+ The management mode speaks **stdio only** — no `--transport`, and it listens on
43
+ no port of its own. That is the whole point: the client runs it as a child
44
+ process, and when the client goes the stdio closes and every proxy it started
45
+ goes with it. Over HTTP it would outlive its client and leave ports and tokens
46
+ held, which would be worse than starting proxies by hand.
47
+
10
48
  ## [4.0.1] - 2026-09-24
11
49
 
12
50
  **A variable from an env file could be silently lost, and the failure blamed the
package/README.md CHANGED
@@ -76,7 +76,8 @@ than travelling through a tool call.
76
76
  | Tool | What it does |
77
77
  |---|---|
78
78
  | `proxy_configs` | Lists the configs available, by the name `proxy_start` takes. Call it first — the names cannot be guessed. |
79
- | `proxy_start` | Starts a proxy from one of those configs and returns the URL it bound. The **port is not taken from the config**: a free one is bound instead. |
79
+ | `proxy_environments` | Lists the environments available — one SAP system's credentials each, by the name `proxy_start` takes. Variable NAMES only, never values. |
80
+ | `proxy_start` | Starts a proxy from a config and, when the config needs one, an environment. Returns the URL it bound. The **port is not taken from the config**: a free one is bound instead. |
80
81
  | `proxy_stop` | Stops a proxy this session started, freeing its port and releasing its credential. Proxies started by other sessions are never touched. |
81
82
  | `proxy_status` | Lists this session's proxies and any others on this machine. Records whose process has died are pruned when read, so it cannot report a ghost. |
82
83
 
@@ -37,7 +37,11 @@ Tools offered to the client:
37
37
  proxy_configs List the proxy configs on this machine, by the name
38
38
  proxy_start takes. Call this first — the names cannot be
39
39
  guessed.
40
- proxy_start Start a proxy from one of those configs. The config supplies
40
+ proxy_environments
41
+ List the environments — one SAP system's credentials each,
42
+ from sessions/. Variable NAMES only, never values.
43
+ proxy_start Start a proxy from a config plus, when the config needs one,
44
+ an environment. The config supplies
41
45
  the destination, target URL, default headers and timeouts;
42
46
  the PORT does not come from it — a free one is bound, so
43
47
  several proxies can run at once, and the URL returned is the
@@ -27,7 +27,18 @@ export interface ProxyConfig {
27
27
  * - If --config is provided, load ONLY from that file
28
28
  * - If --config is NOT provided, load ONLY from command line parameters and environment variables
29
29
  */
30
- export declare function loadConfig(configPath?: string): ProxyConfig;
30
+ export declare function loadConfig(configPath?: string,
31
+ /**
32
+ * The env file for THIS load, ahead of `--env-file` and the config's own
33
+ * `envFile:`.
34
+ *
35
+ * The MCP mode starts a proxy from a config plus an ENVIRONMENT chosen by
36
+ * name, and has no command line of its own to put `--env-file` on. One flag on
37
+ * the management process would not serve either: it overrides every config's
38
+ * own `envFile:`, so one system's secrets would reach every other system
39
+ * started in that session.
40
+ */
41
+ envFileOverride?: string): ProxyConfig;
31
42
  /**
32
43
  * Transport-related fields from config file (not part of ProxyConfig but needed by transportConfig)
33
44
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGxC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,CA8C3D;AAwED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,mBAAmB,CAQrB;AAkCD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAYhC;AAmJD;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAUD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG;IACnD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAoDA"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAYH,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IAEjB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAGxC,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAG/B,OAAO,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;IACzE,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;;;;;;GAOG;AACH,wBAAgB,UAAU,CACxB,UAAU,CAAC,EAAE,MAAM;AACnB;;;;;;;;;GASG;AACH,eAAe,CAAC,EAAE,MAAM,GACvB,WAAW,CAkDb;AAwED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC3B,mBAAmB,CAQrB;AAsCD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAYhC;AAmJD;;GAEG;AACH,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAElD;AAUD;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,WAAW,GAAG;IACnD,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAoDA"}
@@ -53,7 +53,18 @@ const envInterpolation_js_1 = require("./envInterpolation.js");
53
53
  * - If --config is provided, load ONLY from that file
54
54
  * - If --config is NOT provided, load ONLY from command line parameters and environment variables
55
55
  */
56
- function loadConfig(configPath) {
56
+ function loadConfig(configPath,
57
+ /**
58
+ * The env file for THIS load, ahead of `--env-file` and the config's own
59
+ * `envFile:`.
60
+ *
61
+ * The MCP mode starts a proxy from a config plus an ENVIRONMENT chosen by
62
+ * name, and has no command line of its own to put `--env-file` on. One flag on
63
+ * the management process would not serve either: it overrides every config's
64
+ * own `envFile:`, so one system's secrets would reach every other system
65
+ * started in that session.
66
+ */
67
+ envFileOverride) {
57
68
  // Get config path from parameter or command line (--config/-c)
58
69
  // Do NOT use environment variable MCP_PROXY_CONFIG - only explicit --config parameter
59
70
  const finalConfigPath = configPath || getConfigPath();
@@ -65,7 +76,7 @@ function loadConfig(configPath) {
65
76
  throw new Error(`Config file not found: ${finalConfigPath}`);
66
77
  }
67
78
  const fileConfig = loadConfigFile(finalConfigPath);
68
- const envFilePath = resolveEnvFilePath(fileConfig, finalConfigPath);
79
+ const envFilePath = resolveEnvFilePath(fileConfig, finalConfigPath, envFileOverride);
69
80
  const envFileMap = envFilePath ? (0, envInterpolation_js_1.loadEnvFile)(envFilePath) : {};
70
81
  const lookup = (0, envInterpolation_js_1.buildLookup)(envFileMap);
71
82
  const keys = Object.keys(envFileMap);
@@ -174,7 +185,11 @@ function loadConfigFile(filePath) {
174
185
  * cwd); otherwise the YAML `envFile` field, resolved relative to the config
175
186
  * file's directory. Returns undefined when neither is set.
176
187
  */
177
- function resolveEnvFilePath(rawConfig, configPath) {
188
+ function resolveEnvFilePath(rawConfig, configPath, override) {
189
+ // Most specific first: this call, then the command line, then what the config
190
+ // says about itself.
191
+ if (override !== undefined)
192
+ return path.resolve(override);
178
193
  const cliEnvFile = getArgValue('--env-file');
179
194
  if (cliEnvFile !== undefined)
180
195
  return path.resolve(cliEnvFile);
@@ -0,0 +1,36 @@
1
+ /**
2
+ * One environment: a `.env` beside the configs, holding one SAP system's
3
+ * credentials.
4
+ *
5
+ * A config names the service to reach; an environment names the credentials to
6
+ * reach it with. They are separate because several configs commonly point at one
7
+ * system — four of the configs in practice all want `e19` — and one config can
8
+ * be run against different systems.
9
+ */
10
+ export interface EnvironmentEntry {
11
+ /** The file name without its extension — what a client asks for. */
12
+ name: string;
13
+ file: string;
14
+ /**
15
+ * Which variables it defines. Names only, never values: this is read to help
16
+ * a client choose, and a value here would be a credential in a tool result.
17
+ */
18
+ variables: string[];
19
+ }
20
+ /** Where the environments live, beside `proxy/` and `service-keys/`. */
21
+ export declare function environmentDir(): string;
22
+ /**
23
+ * The environments on disk.
24
+ *
25
+ * One that cannot be read is still listed, with no variables beside it — hiding
26
+ * it would make "no such environment" the answer for a file sitting right there.
27
+ */
28
+ export declare function listEnvironments(dir?: string): EnvironmentEntry[];
29
+ /**
30
+ * The file an environment name refers to.
31
+ *
32
+ * A name, never a path: `sessions/` sits beside `proxy/` and `service-keys/`,
33
+ * and the value comes from a language model.
34
+ */
35
+ export declare function resolveEnvironment(name: string, dir?: string): string;
36
+ //# sourceMappingURL=environments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"environments.d.ts","sourceRoot":"","sources":["../../src/mcp/environments.ts"],"names":[],"mappings":"AAKA;;;;;;;;GAQG;AACH,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,wEAAwE;AACxE,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,GAAE,MAAyB,GAC7B,gBAAgB,EAAE,CAgCpB;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,GAAG,GAAE,MAAyB,GAC7B,MAAM,CAmBR"}
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.environmentDir = environmentDir;
37
+ exports.listEnvironments = listEnvironments;
38
+ exports.resolveEnvironment = resolveEnvironment;
39
+ // src/mcp/environments.ts
40
+ const node_fs_1 = require("node:fs");
41
+ const path = __importStar(require("node:path"));
42
+ const stores_js_1 = require("../lib/stores.js");
43
+ /** Where the environments live, beside `proxy/` and `service-keys/`. */
44
+ function environmentDir() {
45
+ return (0, stores_js_1.storeDir)('sessions');
46
+ }
47
+ /**
48
+ * The environments on disk.
49
+ *
50
+ * One that cannot be read is still listed, with no variables beside it — hiding
51
+ * it would make "no such environment" the answer for a file sitting right there.
52
+ */
53
+ function listEnvironments(dir = environmentDir()) {
54
+ if (!(0, node_fs_1.existsSync)(dir))
55
+ return [];
56
+ const entries = [];
57
+ for (const name of (0, node_fs_1.readdirSync)(dir).sort()) {
58
+ // `.env` exactly: this leaves out `e19.env.template`, whose extension is
59
+ // `.template`, along with notes and anything else kept in the folder.
60
+ if (path.extname(name).toLowerCase() !== '.env')
61
+ continue;
62
+ const file = path.join(dir, name);
63
+ try {
64
+ if (!(0, node_fs_1.statSync)(file).isFile())
65
+ continue;
66
+ }
67
+ catch {
68
+ continue;
69
+ }
70
+ const entry = {
71
+ name: path.basename(name, '.env'),
72
+ file,
73
+ variables: [],
74
+ };
75
+ try {
76
+ for (const line of (0, node_fs_1.readFileSync)(file, 'utf-8').split(/\r?\n/)) {
77
+ const match = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=/);
78
+ if (match)
79
+ entry.variables.push(match[1]);
80
+ }
81
+ }
82
+ catch {
83
+ // Listed with no variables. Whoever starts a proxy with it gets the real
84
+ // failure, against a name the caller chose.
85
+ }
86
+ entries.push(entry);
87
+ }
88
+ return entries;
89
+ }
90
+ /**
91
+ * The file an environment name refers to.
92
+ *
93
+ * A name, never a path: `sessions/` sits beside `proxy/` and `service-keys/`,
94
+ * and the value comes from a language model.
95
+ */
96
+ function resolveEnvironment(name, dir = environmentDir()) {
97
+ if (name.includes('/') || name.includes('\\') || name.includes('..')) {
98
+ throw new Error(`Invalid environment name "${name}": it must be a name from ${dir}, not a path.`);
99
+ }
100
+ const available = listEnvironments(dir);
101
+ const found = available.find((entry) => entry.name === name || path.basename(entry.file) === name);
102
+ if (found)
103
+ return found.file;
104
+ const known = available.map((entry) => entry.name).join(', ');
105
+ throw new Error(known
106
+ ? `No environment named "${name}" in ${dir}. Available: ${known}`
107
+ : `No environments found in ${dir}.`);
108
+ }
@@ -24,5 +24,5 @@ export interface ProxyTool {
24
24
  inputSchema: z.ZodRawShape;
25
25
  handler: (args: Record<string, unknown>) => Promise<ToolResult>;
26
26
  }
27
- export declare function createProxyTools(supervisor: ProxySupervisor, configDir?: string): ProxyTool[];
27
+ export declare function createProxyTools(supervisor: ProxySupervisor, configDir?: string, envDir?: string): ProxyTool[];
28
28
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAQxB,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAGc,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACjE;AAMD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,eAAe,EAC3B,SAAS,GAAE,MAAyB,GACnC,SAAS,EAAE,CA2Jb"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../src/mcp/tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEhF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,QAGc,CAAC;AAE7C,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CAC3C;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,CAAC,CAAC,WAAW,CAAC;IAC3B,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;CACjE;AAMD,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,eAAe,EAC3B,SAAS,GAAE,MAAyB,EACpC,MAAM,GAAE,MAAyB,GAChC,SAAS,EAAE,CAmMb"}
package/dist/mcp/tools.js CHANGED
@@ -6,6 +6,7 @@ exports.createProxyTools = createProxyTools;
6
6
  const zod_1 = require("zod");
7
7
  const config_js_1 = require("../lib/config.js");
8
8
  const configs_js_1 = require("./configs.js");
9
+ const environments_js_1 = require("./environments.js");
9
10
  const supervisor_js_1 = require("./supervisor.js");
10
11
  /**
11
12
  * The thing the client has to keep being told.
@@ -23,8 +24,30 @@ exports.SHUTDOWN_REMINDER = 'When you are finished with this proxy, call proxy_s
23
24
  const text = (body) => ({
24
25
  content: [{ type: 'text', text: body }],
25
26
  });
26
- function createProxyTools(supervisor, configDir = (0, configs_js_1.proxyConfigDir)()) {
27
+ function createProxyTools(supervisor, configDir = (0, configs_js_1.proxyConfigDir)(), envDir = (0, environments_js_1.environmentDir)()) {
27
28
  return [
29
+ {
30
+ name: 'proxy_environments',
31
+ title: 'List the environments available',
32
+ description: 'List the environments on this machine, by the name proxy_start takes. ' +
33
+ 'An environment is one SAP system\u2019s credentials; a config says which ' +
34
+ 'service to reach, an environment says what to authenticate with. ' +
35
+ 'Several configs commonly want the same environment. Variable NAMES are ' +
36
+ 'shown, never their values.',
37
+ inputSchema: {},
38
+ handler: async () => {
39
+ const environments = (0, environments_js_1.listEnvironments)(envDir);
40
+ if (environments.length === 0) {
41
+ return text(`No environments found in ${envDir}.`);
42
+ }
43
+ return text([
44
+ `Environments in ${envDir}:`,
45
+ ...environments.map((e) => ` ${e.name}${e.variables.length ? ` — ${e.variables.join(', ')}` : ' — (none readable)'}`),
46
+ '',
47
+ 'Pass one to proxy_start as "environment" when the config uses ${VAR}.',
48
+ ].join('\n'));
49
+ },
50
+ },
28
51
  {
29
52
  name: 'proxy_configs',
30
53
  title: 'List the proxy configs available',
@@ -56,7 +79,7 @@ function createProxyTools(supervisor, configDir = (0, configs_js_1.proxyConfigDi
56
79
  {
57
80
  name: 'proxy_start',
58
81
  title: 'Start an authenticating proxy',
59
- description: 'Start a local proxy from one of the configs proxy_configs lists. The ' +
82
+ description: 'Start a local proxy from a config and, when the config needs one, an environment. The ' +
60
83
  'config supplies the destination, target URL, default headers and ' +
61
84
  'timeouts — including any credentials, which stay in the config and ' +
62
85
  'are never passed through here. The port is NOT taken from the config: ' +
@@ -66,6 +89,10 @@ function createProxyTools(supervisor, configDir = (0, configs_js_1.proxyConfigDi
66
89
  config: zod_1.z
67
90
  .string()
68
91
  .describe('Name of a proxy config, as proxy_configs lists it (for example "nvcr_d24").'),
92
+ environment: zod_1.z
93
+ .string()
94
+ .optional()
95
+ .describe('Name of an environment, as proxy_environments lists it (for example "e19"). Supplies the ${VAR} values the config references. Needed when the config names no envFile of its own — proxy_configs marks those.'),
69
96
  idleTimeoutMs: zod_1.z
70
97
  .number()
71
98
  .optional()
@@ -74,14 +101,19 @@ function createProxyTools(supervisor, configDir = (0, configs_js_1.proxyConfigDi
74
101
  handler: async (args) => {
75
102
  const name = String(args.config);
76
103
  const file = (0, configs_js_1.resolveProxyConfig)(name, configDir);
104
+ const environment = args.environment;
105
+ const envFile = environment
106
+ ? (0, environments_js_1.resolveEnvironment)(environment, envDir)
107
+ : undefined;
77
108
  const started = await supervisor.start({
78
109
  name,
79
- config: (0, config_js_1.loadConfig)(file),
110
+ config: (0, config_js_1.loadConfig)(file, envFile),
80
111
  idleTimeoutMs: args.idleTimeoutMs,
81
112
  });
82
113
  return text([
83
114
  `Proxy running at ${started.url}`,
84
115
  ` config: ${started.name}`,
116
+ ` environment: ${environment ?? "(the config's own, or the environment)"}`,
85
117
  ` instanceId: ${started.instanceId}`,
86
118
  ` destination: ${started.destination}`,
87
119
  '',
@@ -243,7 +243,8 @@ It works from the configs in `~/.config/mcp-abap-adt/proxy/` — the same files
243
243
  | Tool | |
244
244
  |---|---|
245
245
  | `proxy_configs` | the names available; call it first, they cannot be guessed |
246
- | `proxy_start` | starts one on a **free port** and returns the URL bound |
246
+ | `proxy_environments` | the environments available — one SAP system's credentials each. Variable names only, never values |
247
+ | `proxy_start` | starts one on a **free port** and returns the URL bound. Takes `environment` when the config uses `${VAR}` and names no `envFile` |
247
248
  | `proxy_stop` | frees the port and releases the credential; never touches another session's proxy |
248
249
  | `proxy_status` | this session's proxies and everyone else's, dead records pruned on read |
249
250
 
package/docs/USAGE.md CHANGED
@@ -155,7 +155,8 @@ It works from the configs in `~/.config/mcp-abap-adt/proxy/` — the same files
155
155
  | Tool | |
156
156
  |---|---|
157
157
  | `proxy_configs` | the names available; call it first, they cannot be guessed |
158
- | `proxy_start` | starts one on a **free port** and returns the URL bound |
158
+ | `proxy_environments` | the environments available — one SAP system's credentials each. Variable names only, never values |
159
+ | `proxy_start` | starts one on a **free port** and returns the URL bound. Takes `environment` when the config uses `${VAR}` and names no `envFile` |
159
160
  | `proxy_stop` | frees the port and releases the credential; never touches another session's proxy |
160
161
  | `proxy_status` | this session's proxies and everyone else's, dead records pruned on read |
161
162
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/proxy",
3
- "version": "4.0.1",
3
+ "version": "4.1.0",
4
4
  "description": "MCP proxy server for SAP ABAP ADT - proxies local requests to cloud-llm-hub with JWT authentication",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",