@mcp-abap-adt/proxy 0.1.9 → 1.0.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.
@@ -27,6 +27,24 @@ export interface ProxyConfig {
27
27
  * - If --config is NOT provided, load ONLY from command line parameters and environment variables
28
28
  */
29
29
  export declare function loadConfig(configPath?: string): ProxyConfig;
30
+ /**
31
+ * Transport-related fields from config file (not part of ProxyConfig but needed by transportConfig)
32
+ */
33
+ export interface FileTransportFields {
34
+ transport?: string;
35
+ httpPort?: number;
36
+ httpHost?: string;
37
+ ssePort?: number;
38
+ sseHost?: string;
39
+ }
40
+ /**
41
+ * Extract transport-related fields from a raw config object
42
+ */
43
+ export declare function extractTransportFields(raw: Record<string, unknown>): FileTransportFields;
44
+ /**
45
+ * Load raw config from file for transport field extraction
46
+ */
47
+ export declare function loadRawConfigFile(filePath: string): Record<string, unknown> | null;
30
48
  /**
31
49
  * Get config file path from command line (--config or -c)
32
50
  */
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,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;IAGnB,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,CAqC3D;AAuHD;;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;AAMH,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;IAGnB,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,CAqC3D;AAED;;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;AAgBD;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,MAAM,GACf,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAYhC;AA0GD;;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"}
@@ -37,6 +37,8 @@ var __importStar = (this && this.__importStar) || (function () {
37
37
  })();
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.loadConfig = loadConfig;
40
+ exports.extractTransportFields = extractTransportFields;
41
+ exports.loadRawConfigFile = loadRawConfigFile;
40
42
  exports.getConfigPath = getConfigPath;
41
43
  exports.validateConfig = validateConfig;
42
44
  const fs = __importStar(require("node:fs"));
@@ -80,6 +82,18 @@ function loadConfig(configPath) {
80
82
  // No --config provided: load ONLY from command line parameters and environment variables
81
83
  return loadFromEnv();
82
84
  }
85
+ /**
86
+ * Extract transport-related fields from a raw config object
87
+ */
88
+ function extractTransportFields(raw) {
89
+ return {
90
+ transport: typeof raw.transport === 'string' ? raw.transport : undefined,
91
+ httpPort: typeof raw.httpPort === 'number' ? raw.httpPort : undefined,
92
+ httpHost: typeof raw.httpHost === 'string' ? raw.httpHost : undefined,
93
+ ssePort: typeof raw.ssePort === 'number' ? raw.ssePort : undefined,
94
+ sseHost: typeof raw.sseHost === 'string' ? raw.sseHost : undefined,
95
+ };
96
+ }
83
97
  /**
84
98
  * Load configuration from file (supports JSON and YAML)
85
99
  */
@@ -93,6 +107,24 @@ function loadConfigFile(filePath) {
93
107
  return JSON.parse(configContent);
94
108
  }
95
109
  }
110
+ /**
111
+ * Load raw config from file for transport field extraction
112
+ */
113
+ function loadRawConfigFile(filePath) {
114
+ try {
115
+ if (!fs.existsSync(filePath))
116
+ return null;
117
+ const content = fs.readFileSync(filePath, 'utf-8');
118
+ const ext = path.extname(filePath).toLowerCase();
119
+ if (ext === '.yaml' || ext === '.yml') {
120
+ return yaml.load(content) || {};
121
+ }
122
+ return JSON.parse(content);
123
+ }
124
+ catch {
125
+ return null;
126
+ }
127
+ }
96
128
  /**
97
129
  * Apply default values to partial config (used when loading from file)
98
130
  */
@@ -104,6 +136,7 @@ function applyDefaults(fileConfig) {
104
136
  sseHost: fileConfig.sseHost || '0.0.0.0',
105
137
  logLevel: fileConfig.logLevel || 'info',
106
138
  btpDestination: fileConfig.btpDestination,
139
+ targetUrl: fileConfig.targetUrl,
107
140
  unsafe: fileConfig.unsafe ?? false,
108
141
  maxRetries: fileConfig.maxRetries ?? 3,
109
142
  retryDelay: fileConfig.retryDelay ?? 1000,
@@ -184,7 +217,7 @@ function validateConfig(config) {
184
217
  // Check if BTP destination is provided
185
218
  const hasDestination = config.btpDestination;
186
219
  if (!hasDestination) {
187
- warnings.push('No BTP destination provided (--btp). Proxy will not work unless requests include x-btp-destination header.');
220
+ warnings.push('No BTP destination provided (--btp). Proxy will not work unless requests include x-sap-destination header.');
188
221
  }
189
222
  if (config.httpPort < 1 || config.httpPort > 65535) {
190
223
  errors.push('MCP_HTTP_PORT must be between 1 and 65535');
@@ -4,6 +4,20 @@
4
4
  * depending on the unsafe configuration parameter
5
5
  */
6
6
  import type { IServiceKeyStore, ISessionStore } from '@mcp-abap-adt/auth-broker';
7
+ /**
8
+ * Get platform-specific default paths for service keys and sessions
9
+ *
10
+ * Priority (matching mcp-abap-adt logic):
11
+ * 1. AUTH_BROKER_PATH environment variable
12
+ * 2. Platform-specific standard paths (only if no env paths were found):
13
+ * - Unix: ~/.config/mcp-abap-adt/{subfolder}
14
+ * - Windows: %USERPROFILE%\Documents\mcp-abap-adt\{subfolder}
15
+ * 3. Current working directory (process.cwd())
16
+ *
17
+ * @param subfolder Subfolder name ('service-keys' or 'sessions')
18
+ * @returns Array of resolved absolute paths
19
+ */
20
+ export declare function getPlatformPaths(subfolder?: 'service-keys' | 'sessions'): string[];
7
21
  /**
8
22
  * Get platform-specific stores
9
23
  * Returns XSUAA stores for BTP authentication:
@@ -1 +1 @@
1
- {"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../src/lib/stores.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,2BAA2B,CAAC;AAsFnC;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,GAAE,OAAe,GAAG,OAAO,CAAC;IACxE,eAAe,EAAE,gBAAgB,CAAC;IAClC,YAAY,EAAE,aAAa,CAAC;CAC7B,CAAC,CAqBD"}
1
+ {"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../src/lib/stores.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACd,MAAM,2BAA2B,CAAC;AAOnC;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,CAAC,EAAE,cAAc,GAAG,UAAU,GACtC,MAAM,EAAE,CAgEV;AAED;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,GAAE,OAAe,GAAG,OAAO,CAAC;IACxE,eAAe,EAAE,gBAAgB,CAAC;IAClC,YAAY,EAAE,aAAa,CAAC;CAC7B,CAAC,CAqBD"}
@@ -38,6 +38,7 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  };
39
39
  })();
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.getPlatformPaths = getPlatformPaths;
41
42
  exports.getPlatformStores = getPlatformStores;
42
43
  const os = __importStar(require("node:os"));
43
44
  const path = __importStar(require("node:path"));
@@ -12,7 +12,20 @@ export interface TransportConfig {
12
12
  enableDnsRebindingProtection?: boolean;
13
13
  }
14
14
  /**
15
- * Parse transport configuration from command line arguments and environment variables
15
+ * File-based config overrides for transport settings (from YAML/JSON config file)
16
16
  */
17
- export declare function parseTransportConfig(): TransportConfig;
17
+ export interface FileTransportOverrides {
18
+ transport?: string;
19
+ httpPort?: number;
20
+ httpHost?: string;
21
+ ssePort?: number;
22
+ sseHost?: string;
23
+ }
24
+ /**
25
+ * Parse transport configuration from command line arguments, environment variables,
26
+ * and optional file-based config overrides.
27
+ *
28
+ * Priority: CLI args > file config > env vars > auto-detect > defaults
29
+ */
30
+ export declare function parseTransportConfig(fileOverrides?: FileTransportOverrides): TransportConfig;
18
31
  //# sourceMappingURL=transportConfig.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transportConfig.d.ts","sourceRoot":"","sources":["../../src/lib/transportConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,iBAAiB,GAAG,KAAK,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,eAAe,CAkItD"}
1
+ {"version":3,"file":"transportConfig.d.ts","sourceRoot":"","sources":["../../src/lib/transportConfig.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,iBAAiB,GAAG,KAAK,CAAC;AAEhE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACxC;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,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;AAaD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,aAAa,CAAC,EAAE,sBAAsB,GACrC,eAAe,CAwHjB"}
@@ -5,33 +5,32 @@
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.parseTransportConfig = parseTransportConfig;
7
7
  /**
8
- * Parse transport configuration from command line arguments and environment variables
8
+ * Normalize transport value to TransportType
9
9
  */
10
- function parseTransportConfig() {
10
+ function normalizeTransport(value) {
11
+ const v = value.toLowerCase();
12
+ if (v === 'stdio' || v === 'streamable-http' || v === 'http' || v === 'sse') {
13
+ return v === 'http' ? 'streamable-http' : v;
14
+ }
15
+ return null;
16
+ }
17
+ /**
18
+ * Parse transport configuration from command line arguments, environment variables,
19
+ * and optional file-based config overrides.
20
+ *
21
+ * Priority: CLI args > file config > env vars > auto-detect > defaults
22
+ */
23
+ function parseTransportConfig(fileOverrides) {
11
24
  const args = process.argv;
12
- // Check for explicit transport type
25
+ // Check for explicit transport type from CLI
13
26
  let transportType = null;
14
27
  for (let i = 0; i < args.length; i++) {
15
28
  const arg = args[i];
16
29
  if (arg.startsWith('--transport=')) {
17
- const value = arg.split('=')[1].toLowerCase();
18
- if (value === 'stdio' ||
19
- value === 'streamable-http' ||
20
- value === 'http' ||
21
- value === 'sse') {
22
- transportType =
23
- value === 'http' ? 'streamable-http' : value;
24
- }
30
+ transportType = normalizeTransport(arg.split('=')[1]);
25
31
  }
26
32
  if (arg === '--transport' && i + 1 < args.length) {
27
- const value = args[i + 1].toLowerCase();
28
- if (value === 'stdio' ||
29
- value === 'streamable-http' ||
30
- value === 'http' ||
31
- value === 'sse') {
32
- transportType =
33
- value === 'http' ? 'streamable-http' : value;
34
- }
33
+ transportType = normalizeTransport(args[i + 1]);
35
34
  }
36
35
  if (arg === '--http')
37
36
  transportType = 'streamable-http';
@@ -40,18 +39,13 @@ function parseTransportConfig() {
40
39
  if (arg === '--sse')
41
40
  transportType = 'sse';
42
41
  }
42
+ // Check file config overrides
43
+ if (!transportType && fileOverrides?.transport) {
44
+ transportType = normalizeTransport(fileOverrides.transport);
45
+ }
43
46
  // Check environment variable
44
47
  if (!transportType && process.env.MCP_TRANSPORT) {
45
- const envTransport = process.env.MCP_TRANSPORT.toLowerCase();
46
- if (envTransport === 'stdio' ||
47
- envTransport === 'streamable-http' ||
48
- envTransport === 'http' ||
49
- envTransport === 'sse') {
50
- transportType =
51
- envTransport === 'http'
52
- ? 'streamable-http'
53
- : envTransport;
54
- }
48
+ transportType = normalizeTransport(process.env.MCP_TRANSPORT);
55
49
  }
56
50
  // Auto-detect stdio mode: if stdin is not a TTY, we're likely in stdio mode
57
51
  if (!transportType && !process.stdin.isTTY) {
@@ -63,8 +57,14 @@ function parseTransportConfig() {
63
57
  }
64
58
  // Parse SSE config
65
59
  if (transportType === 'sse') {
66
- const port = parseInt(getArgValue('--sse-port') || process.env.MCP_SSE_PORT || '3002', 10);
67
- const host = getArgValue('--sse-host') || process.env.MCP_SSE_HOST || '0.0.0.0';
60
+ const port = parseInt(getArgValue('--sse-port') ||
61
+ (fileOverrides?.ssePort != null ? String(fileOverrides.ssePort) : '') ||
62
+ process.env.MCP_SSE_PORT ||
63
+ '3002', 10);
64
+ const host = getArgValue('--sse-host') ||
65
+ fileOverrides?.sseHost ||
66
+ process.env.MCP_SSE_HOST ||
67
+ '0.0.0.0';
68
68
  return {
69
69
  type: 'sse',
70
70
  host,
@@ -76,8 +76,16 @@ function parseTransportConfig() {
76
76
  }
77
77
  // Parse HTTP/streamable-http config
78
78
  if (transportType === 'streamable-http') {
79
- const port = parseInt(getArgValue('--http-port') || process.env.MCP_HTTP_PORT || '3001', 10);
80
- const host = getArgValue('--http-host') || process.env.MCP_HTTP_HOST || '0.0.0.0';
79
+ const port = parseInt(getArgValue('--http-port') ||
80
+ (fileOverrides?.httpPort != null
81
+ ? String(fileOverrides.httpPort)
82
+ : '') ||
83
+ process.env.MCP_HTTP_PORT ||
84
+ '3001', 10);
85
+ const host = getArgValue('--http-host') ||
86
+ fileOverrides?.httpHost ||
87
+ process.env.MCP_HTTP_HOST ||
88
+ '0.0.0.0';
81
89
  return {
82
90
  type: 'streamable-http',
83
91
  host,
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * BTP Proxy - Proxies requests to MCP servers with JWT authentication
3
3
  *
4
- * For requests with x-btp-destination, proxies to target MCP server
4
+ * For requests with x-sap-destination, proxies to target MCP server
5
5
  * with JWT token from auth-broker (XSUAA/BTP)
6
6
  */
7
7
  import { AuthBroker } from '@mcp-abap-adt/auth-broker';
@@ -63,7 +63,12 @@ export declare class BtpProxy {
63
63
  * @param destination Destination name
64
64
  * @param forceRefresh Force token refresh
65
65
  */
66
- private getJwtToken;
66
+ getJwtToken(destination: string, forceRefresh?: boolean): Promise<string>;
67
+ /**
68
+ * Get the target service URL for a destination.
69
+ * Priority: config.targetUrl > service key's serviceUrl
70
+ */
71
+ getTargetUrl(destination: string): Promise<string>;
67
72
  /**
68
73
  * Helper function to extract string value from header (handles arrays)
69
74
  */
@@ -74,7 +79,7 @@ export declare class BtpProxy {
74
79
  * Process flow:
75
80
  *
76
81
  * 1. BTP Authentication (XSUAA):
77
- * 1.1 If x-btp-destination header exists:
82
+ * 1.1 If x-sap-destination header exists:
78
83
  * - Check map for broker with key = destination, get or create, save to map
79
84
  * - Get token from xsuaa broker
80
85
  * - Add/replace Authorization: Bearer <token> header
@@ -1 +1 @@
1
- {"version":3,"file":"btpProxy.d.ts","sourceRoot":"","sources":["../../src/proxy/btpProxy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAgB,MAAM,2BAA2B,CAAC;AAYrE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA4CnE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAU3C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAGD,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,UAAU,CACN;IACZ,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAU;gBAEZ,oBAAoB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;IAqF3E;;;OAGG;IACU,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB3D;;;;OAIG;YACW,wBAAwB;IAqFtC;;OAEG;YACW,uBAAuB;IAyFrC;;;;OAIG;YACW,WAAW;IAwHzB;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;;;;;;;;OAgBG;YACW,iBAAiB;IAmT/B;;OAEG;IACG,YAAY,CAChB,eAAe,EAAE,YAAY,EAC7B,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7D,OAAO,CAAC,aAAa,CAAC;IA8JzB;;OAEG;WACiB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;CAyB7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAEnB"}
1
+ {"version":3,"file":"btpProxy.d.ts","sourceRoot":"","sources":["../../src/proxy/btpProxy.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,UAAU,EAAgB,MAAM,2BAA2B,CAAC;AAYrE,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAGhE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AA4CnE;;;GAGG;AACH,wBAAgB,iBAAiB,IAAI,OAAO,CAU3C;AAED,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAGD,MAAM,MAAM,YAAY,GAAG,mBAAmB,CAAC;AAE/C,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC5B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH;AAED;;GAEG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,aAAa,CAAgB;IACrC,OAAO,CAAC,oBAAoB,CAAa;IACzC,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,UAAU,CACN;IACZ,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAkB;IAClD,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,MAAM,CAAU;gBAEZ,oBAAoB,EAAE,UAAU,EAAE,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC;IAqF3E;;;OAGG;IACU,UAAU,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAuB3D;;;;OAIG;YACW,wBAAwB;IAqFtC;;OAEG;YACW,uBAAuB;IAyFrC;;;;OAIG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,OAAe,GAC5B,OAAO,CAAC,MAAM,CAAC;IAqHlB;;;OAGG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAexD;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;;;;;;;;;;;;;;OAgBG;YACW,iBAAiB;IAmT/B;;OAEG;IACG,YAAY,CAChB,eAAe,EAAE,YAAY,EAC7B,eAAe,EAAE,eAAe,EAChC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GAC7D,OAAO,CAAC,aAAa,CAAC;IA8JzB;;OAEG;WACiB,MAAM,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAAG,OAAO,CAAC,QAAQ,CAAC;CAyB7E;AAED;;GAEG;AACH,wBAAsB,cAAc,CAClC,MAAM,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,GAC5B,OAAO,CAAC,QAAQ,CAAC,CAEnB"}
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * BTP Proxy - Proxies requests to MCP servers with JWT authentication
4
4
  *
5
- * For requests with x-btp-destination, proxies to target MCP server
5
+ * For requests with x-sap-destination, proxies to target MCP server
6
6
  * with JWT token from auth-broker (XSUAA/BTP)
7
7
  */
8
8
  var __importDefault = (this && this.__importDefault) || function (mod) {
@@ -404,6 +404,22 @@ class BtpProxy {
404
404
  throw new Error(errorMessage);
405
405
  }
406
406
  }
407
+ /**
408
+ * Get the target service URL for a destination.
409
+ * Priority: config.targetUrl > service key's serviceUrl
410
+ */
411
+ async getTargetUrl(destination) {
412
+ if (this.config.targetUrl) {
413
+ return this.config.targetUrl;
414
+ }
415
+ const broker = await this.getOrCreateBtpAuthBroker(destination);
416
+ const connConfig = await broker.getConnectionConfig(destination);
417
+ if (connConfig?.serviceUrl) {
418
+ return connConfig.serviceUrl;
419
+ }
420
+ throw new Error(`No target URL found for destination "${destination}". ` +
421
+ `Set targetUrl in config or ensure service key contains abap.url.`);
422
+ }
407
423
  /**
408
424
  * Helper function to extract string value from header (handles arrays)
409
425
  */
@@ -421,7 +437,7 @@ class BtpProxy {
421
437
  * Process flow:
422
438
  *
423
439
  * 1. BTP Authentication (XSUAA):
424
- * 1.1 If x-btp-destination header exists:
440
+ * 1.1 If x-sap-destination header exists:
425
441
  * - Check map for broker with key = destination, get or create, save to map
426
442
  * - Get token from xsuaa broker
427
443
  * - Add/replace Authorization: Bearer <token> header
@@ -448,21 +464,21 @@ class BtpProxy {
448
464
  // ============================================
449
465
  // BTP Authentication (XSUAA)
450
466
  // ============================================
451
- const btpDestinationHeader = this.getHeaderValue(originalHeaders[interfaces_1.HEADER_BTP_DESTINATION]);
467
+ const btpDestinationHeader = this.getHeaderValue(originalHeaders[interfaces_1.HEADER_SAP_DESTINATION]);
452
468
  const btpDestinationFromParam = routingDecision.btpDestination && !btpDestinationHeader
453
469
  ? routingDecision.btpDestination
454
470
  : undefined;
455
471
  let btpDestination;
456
472
  if (btpDestinationHeader) {
457
473
  btpDestination = btpDestinationHeader;
458
- logger_js_1.logger?.debug('Using x-btp-destination from header', {
474
+ logger_js_1.logger?.debug('Using x-sap-destination from header', {
459
475
  type: 'BTP_DESTINATION_FROM_HEADER',
460
476
  destination: btpDestination,
461
477
  });
462
478
  }
463
479
  else if (btpDestinationFromParam) {
464
480
  btpDestination = btpDestinationFromParam;
465
- logger_js_1.logger?.debug('Using x-btp-destination from parameter', {
481
+ logger_js_1.logger?.debug('Using x-sap-destination from parameter', {
466
482
  type: 'BTP_DESTINATION_FROM_PARAM',
467
483
  destination: btpDestination,
468
484
  });
@@ -0,0 +1,7 @@
1
+ import * as http from 'node:http';
2
+ /**
3
+ * Forward an HTTP request to a target URL with JWT injection.
4
+ * Streams both request and response using pipe().
5
+ */
6
+ export declare function forwardRequest(clientReq: http.IncomingMessage, clientRes: http.ServerResponse, targetBaseUrl: string, jwtToken: string): Promise<void>;
7
+ //# sourceMappingURL=reverseProxy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reverseProxy.d.ts","sourceRoot":"","sources":["../../src/proxy/reverseProxy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAoBlC;;;GAGG;AACH,wBAAsB,cAAc,CAClC,SAAS,EAAE,IAAI,CAAC,eAAe,EAC/B,SAAS,EAAE,IAAI,CAAC,cAAc,EAC9B,aAAa,EAAE,MAAM,EACrB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CA+Ef"}
@@ -0,0 +1,130 @@
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.forwardRequest = forwardRequest;
37
+ // src/proxy/reverseProxy.ts
38
+ const http = __importStar(require("node:http"));
39
+ const https = __importStar(require("node:https"));
40
+ const node_url_1 = require("node:url");
41
+ const logger_js_1 = require("../lib/logger.js");
42
+ /**
43
+ * Hop-by-hop headers that should not be forwarded
44
+ */
45
+ const HOP_BY_HOP_HEADERS = new Set([
46
+ 'connection',
47
+ 'keep-alive',
48
+ 'proxy-authenticate',
49
+ 'proxy-authorization',
50
+ 'te',
51
+ 'trailers',
52
+ 'transfer-encoding',
53
+ 'upgrade',
54
+ 'host',
55
+ ]);
56
+ /**
57
+ * Forward an HTTP request to a target URL with JWT injection.
58
+ * Streams both request and response using pipe().
59
+ */
60
+ async function forwardRequest(clientReq, clientRes, targetBaseUrl, jwtToken) {
61
+ const targetUrl = new node_url_1.URL(clientReq.url || '/', targetBaseUrl);
62
+ // Build forwarded headers (skip hop-by-hop and replace auth)
63
+ const forwardedHeaders = {};
64
+ for (const [key, value] of Object.entries(clientReq.headers)) {
65
+ if (HOP_BY_HOP_HEADERS.has(key.toLowerCase()))
66
+ continue;
67
+ if (key.toLowerCase() === 'authorization')
68
+ continue;
69
+ if (value !== undefined) {
70
+ forwardedHeaders[key] = value;
71
+ }
72
+ }
73
+ // Inject JWT
74
+ if (jwtToken) {
75
+ forwardedHeaders.authorization = `Bearer ${jwtToken}`;
76
+ }
77
+ // Set correct host for target
78
+ forwardedHeaders.host = targetUrl.host;
79
+ const isHttps = targetUrl.protocol === 'https:';
80
+ const transport = isHttps ? https : http;
81
+ const options = {
82
+ hostname: targetUrl.hostname,
83
+ port: targetUrl.port || (isHttps ? 443 : 80),
84
+ path: targetUrl.pathname + targetUrl.search,
85
+ method: clientReq.method || 'GET',
86
+ headers: forwardedHeaders,
87
+ };
88
+ logger_js_1.logger?.info('Forwarding request', {
89
+ type: 'REVERSE_PROXY_FORWARD',
90
+ method: options.method,
91
+ target: targetUrl.toString(),
92
+ });
93
+ return new Promise((resolve) => {
94
+ const proxyReq = transport.request(options, (proxyRes) => {
95
+ // Forward status code
96
+ const statusCode = proxyRes.statusCode || 502;
97
+ // Forward response headers (skip hop-by-hop)
98
+ const responseHeaders = {};
99
+ for (const [key, value] of Object.entries(proxyRes.headers)) {
100
+ if (HOP_BY_HOP_HEADERS.has(key.toLowerCase()))
101
+ continue;
102
+ if (value !== undefined) {
103
+ responseHeaders[key] = value;
104
+ }
105
+ }
106
+ logger_js_1.logger?.info('Backend responded', {
107
+ type: 'REVERSE_PROXY_RESPONSE',
108
+ statusCode,
109
+ contentType: proxyRes.headers['content-type'],
110
+ });
111
+ clientRes.writeHead(statusCode, responseHeaders);
112
+ proxyRes.pipe(clientRes);
113
+ proxyRes.on('end', resolve);
114
+ });
115
+ proxyReq.on('error', (err) => {
116
+ logger_js_1.logger?.error('Reverse proxy connection error', {
117
+ type: 'REVERSE_PROXY_ERROR',
118
+ error: err.message,
119
+ target: targetUrl.toString(),
120
+ });
121
+ if (!clientRes.headersSent) {
122
+ clientRes.writeHead(502, { 'Content-Type': 'application/json' });
123
+ clientRes.end(JSON.stringify({ error: `Proxy error: ${err.message}` }));
124
+ }
125
+ resolve();
126
+ });
127
+ // Pipe client request body to backend
128
+ clientReq.pipe(proxyReq);
129
+ });
130
+ }
@@ -18,17 +18,17 @@ export interface RoutingDecision {
18
18
  * Analyze headers and extract routing information
19
19
  *
20
20
  * Proxy validates only one header for authentication:
21
- * - x-btp-destination (or --btp): destination for BTP Cloud authorization token (Authorization: Bearer) and MCP server URL
21
+ * - x-sap-destination (or --btp): destination for BTP Cloud authorization token (Authorization: Bearer) and MCP server URL
22
22
  *
23
23
 
24
24
  *
25
25
  * Command-line overrides (take precedence over headers):
26
- * - --btp=<destination>: overrides x-btp-destination header
26
+ * - --btp=<destination>: overrides x-sap-destination header
27
27
  *
28
28
  * Other headers are passed directly to MCP server without validation.
29
29
  *
30
30
  * MPC server URL is obtained from:
31
- * - service key for x-btp-destination (via auth-broker)
31
+ * - service key for x-sap-destination (via auth-broker)
32
32
  *
33
33
  *
34
34
  * Note: Proxy does NOT use .env files for connection configuration. Only destinations via auth-broker (service key files) are used.
@@ -19,17 +19,17 @@ var RoutingStrategy;
19
19
  * Analyze headers and extract routing information
20
20
  *
21
21
  * Proxy validates only one header for authentication:
22
- * - x-btp-destination (or --btp): destination for BTP Cloud authorization token (Authorization: Bearer) and MCP server URL
22
+ * - x-sap-destination (or --btp): destination for BTP Cloud authorization token (Authorization: Bearer) and MCP server URL
23
23
  *
24
24
 
25
25
  *
26
26
  * Command-line overrides (take precedence over headers):
27
- * - --btp=<destination>: overrides x-btp-destination header
27
+ * - --btp=<destination>: overrides x-sap-destination header
28
28
  *
29
29
  * Other headers are passed directly to MCP server without validation.
30
30
  *
31
31
  * MPC server URL is obtained from:
32
- * - service key for x-btp-destination (via auth-broker)
32
+ * - service key for x-sap-destination (via auth-broker)
33
33
  *
34
34
  *
35
35
  * Note: Proxy does NOT use .env files for connection configuration. Only destinations via auth-broker (service key files) are used.
@@ -48,9 +48,9 @@ function analyzeHeaders(headers, configOverrides) {
48
48
  }
49
49
  return typeof headerValue === 'string' ? headerValue.trim() : undefined;
50
50
  };
51
- // Extract authorization destination for BTP Cloud (x-btp-destination)
51
+ // Extract authorization destination for BTP Cloud (x-sap-destination)
52
52
  // Command-line parameter --btp takes precedence over header
53
- const btpDestinationHeader = getHeaderValue(interfaces_1.HEADER_BTP_DESTINATION);
53
+ const btpDestinationHeader = getHeaderValue(interfaces_1.HEADER_SAP_DESTINATION);
54
54
  const extractedBtpDestination = configOverrides?.btpDestination
55
55
  ? configOverrides.btpDestination
56
56
  : btpDestinationHeader;
@@ -72,10 +72,10 @@ function analyzeHeaders(headers, configOverrides) {
72
72
  if (!extractedBtpDestination) {
73
73
  return {
74
74
  strategy: RoutingStrategy.UNKNOWN,
75
- reason: 'No BTP destination provided (missing x-btp-destination or --btp)',
75
+ reason: 'No BTP destination provided (missing x-sap-destination or --btp)',
76
76
  };
77
77
  }
78
- // If x-btp-destination is present, we can proxy with BTP authentication (URL will be obtained from service key)
78
+ // If x-sap-destination is present, we can proxy with BTP authentication (URL will be obtained from service key)
79
79
  // If x-mcp-url is provided, we can use it directly (for local testing)
80
80
  logger_js_1.logger?.debug('Routing decision: PROXY', {
81
81
  btpDestination: extractedBtpDestination,
@@ -18,6 +18,8 @@ export interface InterceptedRequest {
18
18
  export declare function interceptRequest(req: IncomingMessage, body?: unknown, configOverrides?: {
19
19
  btpDestination?: string;
20
20
  targetUrl?: string;
21
+ }, options?: {
22
+ skipHeaderValidation?: boolean;
21
23
  }): InterceptedRequest;
22
24
  /**
23
25
  * Sanitize headers for logging (remove sensitive data)
@@ -1 +1 @@
1
- {"version":3,"file":"requestInterceptor.d.ts","sourceRoot":"","sources":["../../src/router/requestInterceptor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAajD,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,IAAI,CAAC,EAAE,OAAO,EACd,eAAe,CAAC,EAAE;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,GACA,kBAAkB,CAqDpB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBxB"}
1
+ {"version":3,"file":"requestInterceptor.d.ts","sourceRoot":"","sources":["../../src/router/requestInterceptor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAajD,OAAO,EAAkB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAE3E,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,CAAC;IACvD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,eAAe,EAAE,eAAe,CAAC;IACjC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,eAAe,EACpB,IAAI,CAAC,EAAE,OAAO,EACd,eAAe,CAAC,EAAE;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,EACD,OAAO,CAAC,EAAE;IACR,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAChC,GACA,kBAAkB,CAuDpB;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC,GACrD,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAmBxB"}