@mcp-abap-adt/proxy 0.1.5 → 0.1.8
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 +66 -1
- package/README.md +73 -65
- package/bin/mcp-abap-adt-proxy.js +15 -42
- package/dist/index.d.ts +3 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +414 -277
- package/dist/lib/config.d.ts +13 -4
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +110 -93
- package/dist/lib/errorHandler.d.ts +3 -3
- package/dist/lib/errorHandler.d.ts.map +1 -1
- package/dist/lib/errorHandler.js +31 -28
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +12 -9
- package/dist/lib/stores.d.ts +7 -7
- package/dist/lib/stores.d.ts.map +1 -1
- package/dist/lib/stores.js +46 -80
- package/dist/lib/transportConfig.d.ts +1 -1
- package/dist/lib/transportConfig.d.ts.map +1 -1
- package/dist/lib/transportConfig.js +58 -38
- package/dist/proxy/btpProxy.d.ts +102 -0
- package/dist/proxy/btpProxy.d.ts.map +1 -0
- package/dist/proxy/btpProxy.js +833 -0
- package/dist/proxy/cloudLlmHubProxy.d.ts +1 -65
- package/dist/proxy/cloudLlmHubProxy.d.ts.map +1 -1
- package/dist/proxy/cloudLlmHubProxy.js +1 -441
- package/dist/router/headerAnalyzer.d.ts +9 -18
- package/dist/router/headerAnalyzer.d.ts.map +1 -1
- package/dist/router/headerAnalyzer.js +35 -63
- package/dist/router/requestInterceptor.d.ts +5 -11
- package/dist/router/requestInterceptor.d.ts.map +1 -1
- package/dist/router/requestInterceptor.js +33 -30
- package/package.json +33 -15
- package/dist/router/directCloudRouter.d.ts +0 -32
- package/dist/router/directCloudRouter.d.ts.map +0 -1
- package/dist/router/directCloudRouter.js +0 -146
- package/dist/router/localBasicRouter.d.ts +0 -23
- package/dist/router/localBasicRouter.d.ts.map +0 -1
- package/dist/router/localBasicRouter.js +0 -118
package/dist/lib/config.d.ts
CHANGED
|
@@ -2,26 +2,35 @@
|
|
|
2
2
|
* Configuration management for MCP ABAP ADT Proxy
|
|
3
3
|
*/
|
|
4
4
|
export interface ProxyConfig {
|
|
5
|
-
cloudLlmHubUrl: string;
|
|
6
5
|
httpPort: number;
|
|
7
6
|
ssePort: number;
|
|
8
7
|
httpHost: string;
|
|
9
8
|
sseHost: string;
|
|
10
9
|
logLevel: string;
|
|
11
10
|
btpDestination?: string;
|
|
12
|
-
|
|
13
|
-
mcpUrl?: string;
|
|
11
|
+
targetUrl?: string;
|
|
14
12
|
unsafe?: boolean;
|
|
15
13
|
maxRetries?: number;
|
|
16
14
|
retryDelay?: number;
|
|
17
15
|
requestTimeout?: number;
|
|
18
16
|
circuitBreakerThreshold?: number;
|
|
19
17
|
circuitBreakerTimeout?: number;
|
|
18
|
+
browser?: 'system' | 'headless' | 'chrome' | 'edge' | 'firefox' | 'none';
|
|
19
|
+
browserAuthPort?: number;
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
|
-
* Load configuration from file if
|
|
22
|
+
* Load configuration from file if --config is provided, otherwise from environment variables and command line
|
|
23
|
+
* Supports both JSON and YAML formats
|
|
24
|
+
*
|
|
25
|
+
* IMPORTANT: YAML config and command line parameters are mutually exclusive:
|
|
26
|
+
* - If --config is provided, load ONLY from that file
|
|
27
|
+
* - If --config is NOT provided, load ONLY from command line parameters and environment variables
|
|
23
28
|
*/
|
|
24
29
|
export declare function loadConfig(configPath?: string): ProxyConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Get config file path from command line (--config or -c)
|
|
32
|
+
*/
|
|
33
|
+
export declare function getConfigPath(): string | undefined;
|
|
25
34
|
/**
|
|
26
35
|
* Validate configuration
|
|
27
36
|
*/
|
package/dist/lib/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
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"}
|
package/dist/lib/config.js
CHANGED
|
@@ -37,73 +37,113 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
})();
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.loadConfig = loadConfig;
|
|
40
|
+
exports.getConfigPath = getConfigPath;
|
|
40
41
|
exports.validateConfig = validateConfig;
|
|
41
|
-
const fs = __importStar(require("fs"));
|
|
42
|
-
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("node:fs"));
|
|
43
|
+
const path = __importStar(require("node:path"));
|
|
44
|
+
const yaml = __importStar(require("js-yaml"));
|
|
43
45
|
/**
|
|
44
|
-
* Load configuration from file if
|
|
46
|
+
* Load configuration from file if --config is provided, otherwise from environment variables and command line
|
|
47
|
+
* Supports both JSON and YAML formats
|
|
48
|
+
*
|
|
49
|
+
* IMPORTANT: YAML config and command line parameters are mutually exclusive:
|
|
50
|
+
* - If --config is provided, load ONLY from that file
|
|
51
|
+
* - If --config is NOT provided, load ONLY from command line parameters and environment variables
|
|
45
52
|
*/
|
|
46
53
|
function loadConfig(configPath) {
|
|
47
|
-
//
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
console.warn(`Failed to load config from file ${filePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
54
|
+
// Get config path from parameter or command line (--config/-c)
|
|
55
|
+
// Do NOT use environment variable MCP_PROXY_CONFIG - only explicit --config parameter
|
|
56
|
+
const finalConfigPath = configPath || getConfigPath();
|
|
57
|
+
// If --config is provided, load ONLY from that file (no merge with command line params)
|
|
58
|
+
if (finalConfigPath) {
|
|
59
|
+
// Warn if other CLI parameters are also provided (they will be ignored)
|
|
60
|
+
const conflictingParams = ['--btp', '--unsafe'].filter((param) => hasArg(param));
|
|
61
|
+
if (conflictingParams.length > 0) {
|
|
62
|
+
console.warn(`Warning: --config is specified, but the following CLI parameters will be ignored: ${conflictingParams.join(', ')}. ` +
|
|
63
|
+
`Configuration will be loaded ONLY from ${finalConfigPath}. ` +
|
|
64
|
+
`To use CLI parameters, do not specify --config.`);
|
|
59
65
|
}
|
|
60
|
-
}
|
|
61
|
-
// Try default config file locations
|
|
62
|
-
const defaultPaths = [
|
|
63
|
-
path.join(process.cwd(), "mcp-proxy-config.json"),
|
|
64
|
-
path.join(process.cwd(), ".mcp-proxy-config.json"),
|
|
65
|
-
path.join(process.env.HOME || process.env.USERPROFILE || "", ".mcp-proxy-config.json"),
|
|
66
|
-
];
|
|
67
|
-
for (const configPath of defaultPaths) {
|
|
68
66
|
try {
|
|
69
|
-
if (fs.existsSync(
|
|
70
|
-
const
|
|
71
|
-
|
|
72
|
-
return
|
|
67
|
+
if (fs.existsSync(finalConfigPath)) {
|
|
68
|
+
const fileConfig = loadConfigFile(finalConfigPath);
|
|
69
|
+
// Apply defaults for missing values in file config
|
|
70
|
+
return applyDefaults(fileConfig);
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
throw new Error(`Config file not found: ${finalConfigPath}`);
|
|
73
74
|
}
|
|
74
75
|
}
|
|
75
76
|
catch (error) {
|
|
76
|
-
|
|
77
|
+
throw new Error(`Failed to load config from file ${finalConfigPath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
77
78
|
}
|
|
78
79
|
}
|
|
79
|
-
//
|
|
80
|
+
// No --config provided: load ONLY from command line parameters and environment variables
|
|
80
81
|
return loadFromEnv();
|
|
81
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* Load configuration from file (supports JSON and YAML)
|
|
85
|
+
*/
|
|
86
|
+
function loadConfigFile(filePath) {
|
|
87
|
+
const configContent = fs.readFileSync(filePath, 'utf-8');
|
|
88
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
89
|
+
if (ext === '.yaml' || ext === '.yml') {
|
|
90
|
+
return yaml.load(configContent);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
return JSON.parse(configContent);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Apply default values to partial config (used when loading from file)
|
|
98
|
+
*/
|
|
99
|
+
function applyDefaults(fileConfig) {
|
|
100
|
+
const result = {
|
|
101
|
+
httpPort: fileConfig.httpPort ?? 3001,
|
|
102
|
+
ssePort: fileConfig.ssePort ?? 3002,
|
|
103
|
+
httpHost: fileConfig.httpHost || '0.0.0.0',
|
|
104
|
+
sseHost: fileConfig.sseHost || '0.0.0.0',
|
|
105
|
+
logLevel: fileConfig.logLevel || 'info',
|
|
106
|
+
btpDestination: fileConfig.btpDestination,
|
|
107
|
+
unsafe: fileConfig.unsafe ?? false,
|
|
108
|
+
maxRetries: fileConfig.maxRetries ?? 3,
|
|
109
|
+
retryDelay: fileConfig.retryDelay ?? 1000,
|
|
110
|
+
requestTimeout: fileConfig.requestTimeout ?? 60000,
|
|
111
|
+
circuitBreakerThreshold: fileConfig.circuitBreakerThreshold ?? 5,
|
|
112
|
+
circuitBreakerTimeout: fileConfig.circuitBreakerTimeout ?? 60000,
|
|
113
|
+
browser: fileConfig.browser || 'system',
|
|
114
|
+
browserAuthPort: fileConfig.browserAuthPort,
|
|
115
|
+
};
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
82
118
|
/**
|
|
83
119
|
* Load configuration from environment variables and command line
|
|
84
120
|
*/
|
|
85
121
|
function loadFromEnv() {
|
|
86
|
-
// Parse command line arguments for --btp, --mcp
|
|
87
|
-
const btpDestination = getArgValue(
|
|
88
|
-
const
|
|
89
|
-
const
|
|
90
|
-
const
|
|
122
|
+
// Parse command line arguments for --btp, --mcp-url, and --unsafe
|
|
123
|
+
const btpDestination = getArgValue('--btp');
|
|
124
|
+
const targetUrl = getArgValue('--target-url') || getArgValue('--url');
|
|
125
|
+
const unsafe = hasArg('--unsafe') || process.env.MCP_PROXY_UNSAFE === 'true';
|
|
126
|
+
const browser = getArgValue('--browser') || 'system';
|
|
127
|
+
const browserAuthPortStr = getArgValue('--browser-auth-port');
|
|
128
|
+
const browserAuthPort = browserAuthPortStr
|
|
129
|
+
? parseInt(browserAuthPortStr, 10)
|
|
130
|
+
: undefined;
|
|
91
131
|
return {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
logLevel: process.env.LOG_LEVEL || "info",
|
|
132
|
+
httpPort: parseInt(process.env.MCP_HTTP_PORT || '3001', 10),
|
|
133
|
+
ssePort: parseInt(process.env.MCP_SSE_PORT || '3002', 10),
|
|
134
|
+
httpHost: process.env.MCP_HTTP_HOST || '0.0.0.0',
|
|
135
|
+
sseHost: process.env.MCP_SSE_HOST || '0.0.0.0',
|
|
136
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
98
137
|
btpDestination,
|
|
99
|
-
|
|
100
|
-
mcpUrl: mcpUrl || process.env.MCP_URL,
|
|
138
|
+
targetUrl,
|
|
101
139
|
unsafe,
|
|
102
|
-
maxRetries: parseInt(process.env.MCP_PROXY_MAX_RETRIES ||
|
|
103
|
-
retryDelay: parseInt(process.env.MCP_PROXY_RETRY_DELAY ||
|
|
104
|
-
requestTimeout: parseInt(process.env.MCP_PROXY_REQUEST_TIMEOUT ||
|
|
105
|
-
circuitBreakerThreshold: parseInt(process.env.MCP_PROXY_CIRCUIT_BREAKER_THRESHOLD ||
|
|
106
|
-
circuitBreakerTimeout: parseInt(process.env.MCP_PROXY_CIRCUIT_BREAKER_TIMEOUT ||
|
|
140
|
+
maxRetries: parseInt(process.env.MCP_PROXY_MAX_RETRIES || '3', 10),
|
|
141
|
+
retryDelay: parseInt(process.env.MCP_PROXY_RETRY_DELAY || '1000', 10),
|
|
142
|
+
requestTimeout: parseInt(process.env.MCP_PROXY_REQUEST_TIMEOUT || '60000', 10),
|
|
143
|
+
circuitBreakerThreshold: parseInt(process.env.MCP_PROXY_CIRCUIT_BREAKER_THRESHOLD || '5', 10),
|
|
144
|
+
circuitBreakerTimeout: parseInt(process.env.MCP_PROXY_CIRCUIT_BREAKER_TIMEOUT || '60000', 10),
|
|
145
|
+
browser,
|
|
146
|
+
browserAuthPort,
|
|
107
147
|
};
|
|
108
148
|
}
|
|
109
149
|
/**
|
|
@@ -116,40 +156,23 @@ function getArgValue(argName) {
|
|
|
116
156
|
return args[i + 1];
|
|
117
157
|
}
|
|
118
158
|
if (args[i].startsWith(`${argName}=`)) {
|
|
119
|
-
return args[i].split(
|
|
159
|
+
return args[i].split('=')[1];
|
|
120
160
|
}
|
|
121
161
|
}
|
|
122
162
|
return undefined;
|
|
123
163
|
}
|
|
124
164
|
/**
|
|
125
|
-
*
|
|
165
|
+
* Get config file path from command line (--config or -c)
|
|
126
166
|
*/
|
|
127
|
-
function
|
|
128
|
-
|
|
129
|
-
return args.some(arg => arg === argName || arg.startsWith(`${argName}=`));
|
|
167
|
+
function getConfigPath() {
|
|
168
|
+
return getArgValue('--config') || getArgValue('-c');
|
|
130
169
|
}
|
|
131
170
|
/**
|
|
132
|
-
*
|
|
171
|
+
* Check if argument exists in command line
|
|
133
172
|
*/
|
|
134
|
-
function
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
httpPort: envConfig.httpPort || fileConfig.httpPort || 3001,
|
|
138
|
-
ssePort: envConfig.ssePort || fileConfig.ssePort || 3002,
|
|
139
|
-
httpHost: envConfig.httpHost || fileConfig.httpHost || "0.0.0.0",
|
|
140
|
-
sseHost: envConfig.sseHost || fileConfig.sseHost || "0.0.0.0",
|
|
141
|
-
logLevel: envConfig.logLevel || fileConfig.logLevel || "info",
|
|
142
|
-
// Command line overrides take precedence
|
|
143
|
-
btpDestination: envConfig.btpDestination ?? fileConfig.btpDestination,
|
|
144
|
-
mcpDestination: envConfig.mcpDestination ?? fileConfig.mcpDestination,
|
|
145
|
-
mcpUrl: envConfig.mcpUrl ?? fileConfig.mcpUrl,
|
|
146
|
-
unsafe: envConfig.unsafe ?? fileConfig.unsafe ?? false,
|
|
147
|
-
maxRetries: envConfig.maxRetries ?? fileConfig.maxRetries ?? 3,
|
|
148
|
-
retryDelay: envConfig.retryDelay ?? fileConfig.retryDelay ?? 1000,
|
|
149
|
-
requestTimeout: envConfig.requestTimeout ?? fileConfig.requestTimeout ?? 60000,
|
|
150
|
-
circuitBreakerThreshold: envConfig.circuitBreakerThreshold ?? fileConfig.circuitBreakerThreshold ?? 5,
|
|
151
|
-
circuitBreakerTimeout: envConfig.circuitBreakerTimeout ?? fileConfig.circuitBreakerTimeout ?? 60000,
|
|
152
|
-
};
|
|
173
|
+
function hasArg(argName) {
|
|
174
|
+
const args = process.argv;
|
|
175
|
+
return args.some((arg) => arg === argName || arg.startsWith(`${argName}=`));
|
|
153
176
|
}
|
|
154
177
|
/**
|
|
155
178
|
* Validate configuration
|
|
@@ -157,37 +180,31 @@ function mergeConfig(fileConfig, envConfig) {
|
|
|
157
180
|
function validateConfig(config) {
|
|
158
181
|
const errors = [];
|
|
159
182
|
const warnings = [];
|
|
160
|
-
//
|
|
161
|
-
//
|
|
162
|
-
const hasDestination = config.btpDestination
|
|
163
|
-
if (!
|
|
164
|
-
warnings.push(
|
|
165
|
-
}
|
|
166
|
-
else if (config.cloudLlmHubUrl) {
|
|
167
|
-
try {
|
|
168
|
-
new URL(config.cloudLlmHubUrl);
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
errors.push("CLOUD_LLM_HUB_URL must be a valid URL");
|
|
172
|
-
}
|
|
183
|
+
// mcpUrl is required (either from yaml config via --config or --mcp-url parameter)
|
|
184
|
+
// Check if BTP destination is provided
|
|
185
|
+
const hasDestination = config.btpDestination;
|
|
186
|
+
if (!hasDestination) {
|
|
187
|
+
warnings.push('No BTP destination provided (--btp). Proxy will not work unless requests include x-btp-destination header.');
|
|
173
188
|
}
|
|
174
189
|
if (config.httpPort < 1 || config.httpPort > 65535) {
|
|
175
|
-
errors.push(
|
|
190
|
+
errors.push('MCP_HTTP_PORT must be between 1 and 65535');
|
|
176
191
|
}
|
|
177
192
|
if (config.ssePort < 1 || config.ssePort > 65535) {
|
|
178
|
-
errors.push(
|
|
193
|
+
errors.push('MCP_SSE_PORT must be between 1 and 65535');
|
|
179
194
|
}
|
|
180
195
|
if (config.httpPort === config.ssePort) {
|
|
181
|
-
errors.push(
|
|
196
|
+
errors.push('MCP_HTTP_PORT and MCP_SSE_PORT must be different');
|
|
182
197
|
}
|
|
183
198
|
if (config.maxRetries && (config.maxRetries < 0 || config.maxRetries > 10)) {
|
|
184
|
-
warnings.push(
|
|
199
|
+
warnings.push('MCP_PROXY_MAX_RETRIES should be between 0 and 10');
|
|
185
200
|
}
|
|
186
|
-
if (config.retryDelay &&
|
|
187
|
-
|
|
201
|
+
if (config.retryDelay &&
|
|
202
|
+
(config.retryDelay < 0 || config.retryDelay > 60000)) {
|
|
203
|
+
warnings.push('MCP_PROXY_RETRY_DELAY should be between 0 and 60000ms');
|
|
188
204
|
}
|
|
189
|
-
if (config.requestTimeout &&
|
|
190
|
-
|
|
205
|
+
if (config.requestTimeout &&
|
|
206
|
+
(config.requestTimeout < 1000 || config.requestTimeout > 300000)) {
|
|
207
|
+
warnings.push('MCP_PROXY_REQUEST_TIMEOUT should be between 1000 and 300000ms');
|
|
191
208
|
}
|
|
192
209
|
return {
|
|
193
210
|
valid: errors.length === 0,
|
|
@@ -44,7 +44,7 @@ export declare class CircuitBreaker {
|
|
|
44
44
|
/**
|
|
45
45
|
* Get current state
|
|
46
46
|
*/
|
|
47
|
-
getState():
|
|
47
|
+
getState(): 'closed' | 'open' | 'half-open';
|
|
48
48
|
/**
|
|
49
49
|
* Reset circuit breaker
|
|
50
50
|
*/
|
|
@@ -57,13 +57,13 @@ export declare function isTokenExpirationError(error: unknown): boolean;
|
|
|
57
57
|
/**
|
|
58
58
|
* Create error response in MCP format
|
|
59
59
|
*/
|
|
60
|
-
export declare function createErrorResponse(id: string | number | null, code: number, message: string, data?:
|
|
60
|
+
export declare function createErrorResponse(id: string | number | null, code: number, message: string, data?: unknown): {
|
|
61
61
|
jsonrpc: string;
|
|
62
62
|
id: string | number | null;
|
|
63
63
|
error: {
|
|
64
64
|
code: number;
|
|
65
65
|
message: string;
|
|
66
|
-
data?:
|
|
66
|
+
data?: unknown;
|
|
67
67
|
};
|
|
68
68
|
};
|
|
69
69
|
//# sourceMappingURL=errorHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/lib/errorHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;
|
|
1
|
+
{"version":3,"file":"errorHandler.d.ts","sourceRoot":"","sources":["../../src/lib/errorHandler.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,OAAO,EACd,oBAAoB,GAAE,MAAM,EAAyB,GACpD,OAAO,CAyBT;AAED;;GAEG;AACH,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CAAC,CAAC,EACtC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,CAAC,CAAC,CAoDZ;AAED;;GAEG;AACH,qBAAa,cAAc;IAMvB,OAAO,CAAC,SAAS;IACjB,OAAO,CAAC,OAAO;IANjB,OAAO,CAAC,QAAQ,CAAa;IAC7B,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,KAAK,CAA6C;gBAGhD,SAAS,GAAE,MAAU,EACrB,OAAO,GAAE,MAAc;IAGjC;;OAEG;IACH,UAAU,IAAI,OAAO;IAkBrB;;OAEG;IACH,aAAa,IAAI,IAAI;IAYrB;;OAEG;IACH,aAAa,IAAI,IAAI;IAcrB;;OAEG;IACH,QAAQ,IAAI,QAAQ,GAAG,MAAM,GAAG,WAAW;IAI3C;;OAEG;IACH,KAAK,IAAI,IAAI;CAQd;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAmB9D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,EAC1B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,OAAO,GACb;IACD,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE;QACL,IAAI,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,OAAO,CAAC;KAChB,CAAC;CACH,CAUA"}
|
package/dist/lib/errorHandler.js
CHANGED
|
@@ -30,7 +30,8 @@ function isRetryableError(error, retryableStatusCodes = [500, 502, 503, 504]) {
|
|
|
30
30
|
// Token expiration errors are retryable (will be handled separately)
|
|
31
31
|
if (error instanceof Error) {
|
|
32
32
|
const message = error.message.toLowerCase();
|
|
33
|
-
if (message.includes(
|
|
33
|
+
if (message.includes('token') &&
|
|
34
|
+
(message.includes('expired') || message.includes('invalid'))) {
|
|
34
35
|
return true;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
@@ -46,7 +47,7 @@ function sleep(ms) {
|
|
|
46
47
|
* Retry function with exponential backoff
|
|
47
48
|
*/
|
|
48
49
|
async function retryWithBackoff(fn, options) {
|
|
49
|
-
const { maxRetries, retryDelay, retryableStatusCodes = [500, 502, 503, 504] } = options;
|
|
50
|
+
const { maxRetries, retryDelay, retryableStatusCodes = [500, 502, 503, 504], } = options;
|
|
50
51
|
let lastError;
|
|
51
52
|
for (let attempt = 0; attempt <= maxRetries; attempt++) {
|
|
52
53
|
try {
|
|
@@ -60,17 +61,17 @@ async function retryWithBackoff(fn, options) {
|
|
|
60
61
|
}
|
|
61
62
|
// Check if error is retryable
|
|
62
63
|
if (!isRetryableError(error, retryableStatusCodes)) {
|
|
63
|
-
logger_js_1.logger
|
|
64
|
-
type:
|
|
64
|
+
logger_js_1.logger?.debug('Error is not retryable, stopping retries', {
|
|
65
|
+
type: 'RETRY_STOPPED',
|
|
65
66
|
attempt,
|
|
66
67
|
error: error instanceof Error ? error.message : String(error),
|
|
67
68
|
});
|
|
68
69
|
throw error;
|
|
69
70
|
}
|
|
70
71
|
// Calculate backoff delay (exponential backoff)
|
|
71
|
-
const delay = retryDelay *
|
|
72
|
-
logger_js_1.logger
|
|
73
|
-
type:
|
|
72
|
+
const delay = retryDelay * 2 ** attempt;
|
|
73
|
+
logger_js_1.logger?.warn('Retrying after error', {
|
|
74
|
+
type: 'RETRY_ATTEMPT',
|
|
74
75
|
attempt: attempt + 1,
|
|
75
76
|
maxRetries,
|
|
76
77
|
delay,
|
|
@@ -80,8 +81,8 @@ async function retryWithBackoff(fn, options) {
|
|
|
80
81
|
}
|
|
81
82
|
}
|
|
82
83
|
// All retries exhausted
|
|
83
|
-
logger_js_1.logger
|
|
84
|
-
type:
|
|
84
|
+
logger_js_1.logger?.error('All retry attempts exhausted', {
|
|
85
|
+
type: 'RETRY_EXHAUSTED',
|
|
85
86
|
maxRetries,
|
|
86
87
|
error: lastError instanceof Error ? lastError.message : String(lastError),
|
|
87
88
|
});
|
|
@@ -95,7 +96,7 @@ class CircuitBreaker {
|
|
|
95
96
|
timeout;
|
|
96
97
|
failures = 0;
|
|
97
98
|
lastFailureTime = 0;
|
|
98
|
-
state =
|
|
99
|
+
state = 'closed';
|
|
99
100
|
constructor(threshold = 5, timeout = 60000) {
|
|
100
101
|
this.threshold = threshold;
|
|
101
102
|
this.timeout = timeout;
|
|
@@ -105,12 +106,12 @@ class CircuitBreaker {
|
|
|
105
106
|
*/
|
|
106
107
|
canProceed() {
|
|
107
108
|
const now = Date.now();
|
|
108
|
-
if (this.state ===
|
|
109
|
+
if (this.state === 'open') {
|
|
109
110
|
// Check if timeout has passed
|
|
110
111
|
if (now - this.lastFailureTime >= this.timeout) {
|
|
111
|
-
this.state =
|
|
112
|
-
logger_js_1.logger
|
|
113
|
-
type:
|
|
112
|
+
this.state = 'half-open';
|
|
113
|
+
logger_js_1.logger?.info('Circuit breaker entering half-open state', {
|
|
114
|
+
type: 'CIRCUIT_BREAKER_HALF_OPEN',
|
|
114
115
|
});
|
|
115
116
|
return true;
|
|
116
117
|
}
|
|
@@ -122,14 +123,14 @@ class CircuitBreaker {
|
|
|
122
123
|
* Record success
|
|
123
124
|
*/
|
|
124
125
|
recordSuccess() {
|
|
125
|
-
if (this.state ===
|
|
126
|
-
this.state =
|
|
126
|
+
if (this.state === 'half-open') {
|
|
127
|
+
this.state = 'closed';
|
|
127
128
|
this.failures = 0;
|
|
128
|
-
logger_js_1.logger
|
|
129
|
-
type:
|
|
129
|
+
logger_js_1.logger?.info('Circuit breaker closed after successful request', {
|
|
130
|
+
type: 'CIRCUIT_BREAKER_CLOSED',
|
|
130
131
|
});
|
|
131
132
|
}
|
|
132
|
-
else if (this.state ===
|
|
133
|
+
else if (this.state === 'closed') {
|
|
133
134
|
this.failures = 0;
|
|
134
135
|
}
|
|
135
136
|
}
|
|
@@ -140,9 +141,9 @@ class CircuitBreaker {
|
|
|
140
141
|
this.failures++;
|
|
141
142
|
this.lastFailureTime = Date.now();
|
|
142
143
|
if (this.failures >= this.threshold) {
|
|
143
|
-
this.state =
|
|
144
|
-
logger_js_1.logger
|
|
145
|
-
type:
|
|
144
|
+
this.state = 'open';
|
|
145
|
+
logger_js_1.logger?.error('Circuit breaker opened due to failures', {
|
|
146
|
+
type: 'CIRCUIT_BREAKER_OPENED',
|
|
146
147
|
failures: this.failures,
|
|
147
148
|
threshold: this.threshold,
|
|
148
149
|
});
|
|
@@ -160,9 +161,9 @@ class CircuitBreaker {
|
|
|
160
161
|
reset() {
|
|
161
162
|
this.failures = 0;
|
|
162
163
|
this.lastFailureTime = 0;
|
|
163
|
-
this.state =
|
|
164
|
-
logger_js_1.logger
|
|
165
|
-
type:
|
|
164
|
+
this.state = 'closed';
|
|
165
|
+
logger_js_1.logger?.info('Circuit breaker reset', {
|
|
166
|
+
type: 'CIRCUIT_BREAKER_RESET',
|
|
166
167
|
});
|
|
167
168
|
}
|
|
168
169
|
}
|
|
@@ -179,8 +180,10 @@ function isTokenExpirationError(error) {
|
|
|
179
180
|
}
|
|
180
181
|
if (error instanceof Error) {
|
|
181
182
|
const message = error.message.toLowerCase();
|
|
182
|
-
return (message.includes(
|
|
183
|
-
(message.includes(
|
|
183
|
+
return (message.includes('token') &&
|
|
184
|
+
(message.includes('expired') ||
|
|
185
|
+
message.includes('invalid') ||
|
|
186
|
+
message.includes('unauthorized')));
|
|
184
187
|
}
|
|
185
188
|
return false;
|
|
186
189
|
}
|
|
@@ -189,7 +192,7 @@ function isTokenExpirationError(error) {
|
|
|
189
192
|
*/
|
|
190
193
|
function createErrorResponse(id, code, message, data) {
|
|
191
194
|
return {
|
|
192
|
-
jsonrpc:
|
|
195
|
+
jsonrpc: '2.0',
|
|
193
196
|
id,
|
|
194
197
|
error: {
|
|
195
198
|
code,
|
package/dist/lib/logger.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9D;
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/lib/logger.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,oBAAY,QAAQ;IAClB,KAAK,IAAI;IACT,IAAI,IAAI;IACR,IAAI,IAAI;IACR,KAAK,IAAI;CACV;AAED,MAAM,WAAW,MAAM;IACrB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;IAC5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC9D;AAwDD,eAAO,MAAM,MAAM,EAAE,MAKpB,CAAC"}
|
package/dist/lib/logger.js
CHANGED
|
@@ -16,18 +16,20 @@ class SimpleLogger {
|
|
|
16
16
|
verbose;
|
|
17
17
|
constructor(level = LogLevel.INFO) {
|
|
18
18
|
this.level = level;
|
|
19
|
-
//
|
|
20
|
-
//
|
|
21
|
-
this.verbose =
|
|
22
|
-
process.env.
|
|
23
|
-
|
|
19
|
+
// DEBUG logs are only shown if MCP_PROXY_VERBOSE or DEBUG environment variable is set
|
|
20
|
+
// INFO, WARN, and ERROR logs are always shown (even without verbose mode)
|
|
21
|
+
this.verbose =
|
|
22
|
+
process.env.MCP_PROXY_VERBOSE === 'true' ||
|
|
23
|
+
process.env.DEBUG === 'true' ||
|
|
24
|
+
process.env.DEBUG?.includes('mcp-proxy') === true;
|
|
24
25
|
}
|
|
25
26
|
log(level, prefix, message, meta) {
|
|
26
|
-
//
|
|
27
|
-
|
|
27
|
+
// Always output INFO, WARN, and ERROR logs (even without verbose mode)
|
|
28
|
+
// Only suppress DEBUG logs in non-verbose mode
|
|
29
|
+
if (level === LogLevel.DEBUG && !this.verbose) {
|
|
28
30
|
return;
|
|
29
31
|
}
|
|
30
|
-
//
|
|
32
|
+
// Respect the log level
|
|
31
33
|
if (level < this.level) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
@@ -50,5 +52,6 @@ class SimpleLogger {
|
|
|
50
52
|
}
|
|
51
53
|
// Default logger instance
|
|
52
54
|
exports.logger = new SimpleLogger(process.env.LOG_LEVEL?.toUpperCase()
|
|
53
|
-
? (LogLevel[process.env.LOG_LEVEL.toUpperCase()] ??
|
|
55
|
+
? (LogLevel[process.env.LOG_LEVEL.toUpperCase()] ??
|
|
56
|
+
LogLevel.INFO)
|
|
54
57
|
: LogLevel.INFO);
|
package/dist/lib/stores.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Platform-specific stores for proxy
|
|
3
|
-
* Uses
|
|
3
|
+
* Uses XsuaaServiceKeyStore and either XsuaaSessionStore or SafeXsuaaSessionStore
|
|
4
4
|
* depending on the unsafe configuration parameter
|
|
5
5
|
*/
|
|
6
|
-
import type { IServiceKeyStore, ISessionStore } from
|
|
6
|
+
import type { IServiceKeyStore, ISessionStore } from '@mcp-abap-adt/auth-broker';
|
|
7
7
|
/**
|
|
8
8
|
* Get platform-specific stores
|
|
9
|
-
* Returns stores
|
|
10
|
-
* - If unsafe=true: uses
|
|
11
|
-
* - If unsafe=false: uses
|
|
12
|
-
* - Service key store:
|
|
13
|
-
* @param unsafe If true, use
|
|
9
|
+
* Returns XSUAA stores for BTP authentication:
|
|
10
|
+
* - If unsafe=true: uses XsuaaSessionStore (persists to disk)
|
|
11
|
+
* - If unsafe=false: uses SafeXsuaaSessionStore (in-memory, secure)
|
|
12
|
+
* - Service key store: XsuaaServiceKeyStore
|
|
13
|
+
* @param unsafe If true, use XsuaaSessionStore. If false, use SafeXsuaaSessionStore (default).
|
|
14
14
|
*/
|
|
15
15
|
export declare function getPlatformStores(unsafe?: boolean): Promise<{
|
|
16
16
|
serviceKeyStore: IServiceKeyStore;
|
package/dist/lib/stores.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stores.d.ts","sourceRoot":"","sources":["../../src/lib/stores.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
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;AA4EnC;;;;;;;GAOG;AACH,wBAAsB,iBAAiB,CAAC,MAAM,GAAE,OAAe,GAAG,OAAO,CAAC;IACxE,eAAe,EAAE,gBAAgB,CAAC;IAClC,YAAY,EAAE,aAAa,CAAC;CAC7B,CAAC,CAqBD"}
|