@mcp-abap-adt/proxy 0.1.4 → 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.
Files changed (39) hide show
  1. package/CHANGELOG.md +66 -1
  2. package/README.md +73 -63
  3. package/bin/mcp-abap-adt-proxy.js +15 -42
  4. package/dist/index.d.ts +3 -12
  5. package/dist/index.d.ts.map +1 -1
  6. package/dist/index.js +440 -273
  7. package/dist/lib/config.d.ts +13 -4
  8. package/dist/lib/config.d.ts.map +1 -1
  9. package/dist/lib/config.js +110 -92
  10. package/dist/lib/errorHandler.d.ts +3 -3
  11. package/dist/lib/errorHandler.d.ts.map +1 -1
  12. package/dist/lib/errorHandler.js +31 -28
  13. package/dist/lib/logger.d.ts.map +1 -1
  14. package/dist/lib/logger.js +15 -1
  15. package/dist/lib/stores.d.ts +7 -6
  16. package/dist/lib/stores.d.ts.map +1 -1
  17. package/dist/lib/stores.js +121 -8
  18. package/dist/lib/transportConfig.d.ts +1 -1
  19. package/dist/lib/transportConfig.d.ts.map +1 -1
  20. package/dist/lib/transportConfig.js +58 -38
  21. package/dist/proxy/btpProxy.d.ts +102 -0
  22. package/dist/proxy/btpProxy.d.ts.map +1 -0
  23. package/dist/proxy/btpProxy.js +833 -0
  24. package/dist/proxy/cloudLlmHubProxy.d.ts +1 -60
  25. package/dist/proxy/cloudLlmHubProxy.d.ts.map +1 -1
  26. package/dist/proxy/cloudLlmHubProxy.js +1 -378
  27. package/dist/router/headerAnalyzer.d.ts +14 -18
  28. package/dist/router/headerAnalyzer.d.ts.map +1 -1
  29. package/dist/router/headerAnalyzer.js +40 -67
  30. package/dist/router/requestInterceptor.d.ts +5 -11
  31. package/dist/router/requestInterceptor.d.ts.map +1 -1
  32. package/dist/router/requestInterceptor.js +33 -30
  33. package/package.json +33 -14
  34. package/dist/router/directCloudRouter.d.ts +0 -32
  35. package/dist/router/directCloudRouter.d.ts.map +0 -1
  36. package/dist/router/directCloudRouter.js +0 -179
  37. package/dist/router/localBasicRouter.d.ts +0 -23
  38. package/dist/router/localBasicRouter.d.ts.map +0 -1
  39. package/dist/router/localBasicRouter.js +0 -140
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Request Interceptor - Intercepts and analyzes MCP requests
3
3
  */
4
- import { IncomingMessage } from "http";
5
- import { RoutingDecision } from "./headerAnalyzer.js";
4
+ import type { IncomingMessage } from 'node:http';
5
+ import { type RoutingDecision } from './headerAnalyzer.js';
6
6
  export interface InterceptedRequest {
7
7
  method: string;
8
8
  url: string;
9
9
  headers: Record<string, string | string[] | undefined>;
10
- body?: any;
10
+ body?: unknown;
11
11
  routingDecision: RoutingDecision;
12
12
  sessionId?: string;
13
13
  clientId?: string;
@@ -15,16 +15,10 @@ export interface InterceptedRequest {
15
15
  /**
16
16
  * Intercept and analyze incoming HTTP request
17
17
  */
18
- export declare function interceptRequest(req: IncomingMessage, body?: any, configOverrides?: {
18
+ export declare function interceptRequest(req: IncomingMessage, body?: unknown, configOverrides?: {
19
19
  btpDestination?: string;
20
- mcpDestination?: string;
21
- mcpUrl?: string;
20
+ targetUrl?: string;
22
21
  }): InterceptedRequest;
23
- /**
24
- * Check if request requires SAP configuration
25
- * Only tools/call requires SAP config - all other methods don't
26
- */
27
- export declare function requiresSapConfig(body: any): boolean;
28
22
  /**
29
23
  * Sanitize headers for logging (remove sensitive data)
30
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"requestInterceptor.d.ts","sourceRoot":"","sources":["../../src/router/requestInterceptor.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,MAAM,CAAC;AACvC,OAAO,EAAkB,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtE,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,GAAG,CAAC;IACX,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,GAAG,EACV,eAAe,CAAC,EAAE;IAAE,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,GACtF,kBAAkB,CAuCpB;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,GAAG,OAAO,CAOpD;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,CAqBxB"}
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"}
@@ -4,10 +4,11 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.interceptRequest = interceptRequest;
7
- exports.requiresSapConfig = requiresSapConfig;
8
7
  exports.sanitizeHeadersForLogging = sanitizeHeadersForLogging;
9
- const headerAnalyzer_js_1 = require("./headerAnalyzer.js");
8
+ const header_validator_1 = require("@mcp-abap-adt/header-validator");
9
+ const interfaces_1 = require("@mcp-abap-adt/interfaces");
10
10
  const logger_js_1 = require("../lib/logger.js");
11
+ const headerAnalyzer_js_1 = require("./headerAnalyzer.js");
11
12
  /**
12
13
  * Intercept and analyze incoming HTTP request
13
14
  */
@@ -19,15 +20,30 @@ function interceptRequest(req, body, configOverrides) {
19
20
  }
20
21
  // Analyze headers for routing decision (with config overrides)
21
22
  const routingDecision = (0, headerAnalyzer_js_1.analyzeHeaders)(req.headers, configOverrides);
23
+ // Validate proxy headers
24
+ const validation = (0, header_validator_1.validateProxyHeaders)(req.headers);
25
+ if (!validation.isValid && validation.errors.length > 0) {
26
+ logger_js_1.logger?.warn('Proxy header validation failed', {
27
+ type: 'PROXY_HEADER_VALIDATION_ERROR',
28
+ errors: validation.errors,
29
+ warnings: validation.warnings,
30
+ });
31
+ }
32
+ else if (validation.warnings.length > 0) {
33
+ logger_js_1.logger?.warn('Proxy header validation warnings', {
34
+ type: 'PROXY_HEADER_VALIDATION_WARNINGS',
35
+ warnings: validation.warnings,
36
+ });
37
+ }
22
38
  // Extract session ID if present
23
- const sessionId = (req.headers["x-session-id"] ||
24
- req.headers["mcp-session-id"] ||
25
- req.headers["x-mcp-session-id"]);
39
+ const sessionId = (req.headers[interfaces_1.HEADER_SESSION_ID] ||
40
+ req.headers[interfaces_1.HEADER_MCP_SESSION_ID] ||
41
+ req.headers[interfaces_1.HEADER_X_MCP_SESSION_ID]);
26
42
  // Generate client ID
27
43
  const clientId = `${req.socket.remoteAddress}:${req.socket.remotePort}`;
28
44
  // Log intercepted request
29
- logger_js_1.logger.debug("Request intercepted", {
30
- type: "REQUEST_INTERCEPTED",
45
+ logger_js_1.logger?.debug('Request intercepted', {
46
+ type: 'REQUEST_INTERCEPTED',
31
47
  method: req.method,
32
48
  url: req.url,
33
49
  clientId,
@@ -36,8 +52,8 @@ function interceptRequest(req, body, configOverrides) {
36
52
  routingReason: routingDecision.reason,
37
53
  });
38
54
  return {
39
- method: req.method || "GET",
40
- url: req.url || "/",
55
+ method: req.method || 'GET',
56
+ url: req.url || '/',
41
57
  headers,
42
58
  body,
43
59
  routingDecision,
@@ -45,37 +61,24 @@ function interceptRequest(req, body, configOverrides) {
45
61
  clientId,
46
62
  };
47
63
  }
48
- /**
49
- * Check if request requires SAP configuration
50
- * Only tools/call requires SAP config - all other methods don't
51
- */
52
- function requiresSapConfig(body) {
53
- if (!body || typeof body !== "object") {
54
- return false;
55
- }
56
- const method = body.method;
57
- return method === "tools/call";
58
- }
59
64
  /**
60
65
  * Sanitize headers for logging (remove sensitive data)
61
66
  */
62
67
  function sanitizeHeadersForLogging(headers) {
63
68
  const sanitized = {};
64
69
  const sensitiveKeys = [
65
- "authorization",
66
- "x-sap-jwt-token",
67
- "x-sap-refresh-token",
68
- "x-sap-password",
69
- "x-sap-uaa-client-secret",
70
- ];
70
+ interfaces_1.HEADER_AUTHORIZATION,
71
+ interfaces_1.HEADER_SAP_JWT_TOKEN,
72
+ interfaces_1.HEADER_SAP_REFRESH_TOKEN,
73
+ interfaces_1.HEADER_SAP_PASSWORD,
74
+ interfaces_1.HEADER_SAP_UAA_CLIENT_SECRET,
75
+ ].map((k) => k.toLowerCase());
71
76
  for (const [key, value] of Object.entries(headers)) {
72
77
  if (sensitiveKeys.includes(key.toLowerCase())) {
73
- sanitized[key] = "[REDACTED]";
78
+ sanitized[key] = '[REDACTED]';
74
79
  }
75
80
  else {
76
- sanitized[key] = Array.isArray(value)
77
- ? value.join(", ")
78
- : (value || "");
81
+ sanitized[key] = Array.isArray(value) ? value.join(', ') : value || '';
79
82
  }
80
83
  }
81
84
  return sanitized;
package/package.json CHANGED
@@ -1,19 +1,26 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/proxy",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
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",
7
7
  "scripts": {
8
- "test": "jest --passWithNoTests",
9
- "build": "npx tsc -p tsconfig.json",
8
+ "chrono": "./tools/version-stats.sh",
9
+ "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --passWithNoTests",
10
+ "test:check": "npx tsc --noEmit --project tsconfig.test.json",
11
+ "lint": "npx biome check --write src",
12
+ "lint:check": "npx biome check src",
13
+ "format": "npx biome format --write src",
14
+ "build": "npm run --silent clean && npx biome check src --diagnostic-level=error && npx tsc -p tsconfig.json",
15
+ "build:fast": "npx tsc -p tsconfig.json",
10
16
  "start": "node ./bin/mcp-abap-adt-proxy.js",
11
17
  "start:http": "node ./bin/mcp-abap-adt-proxy.js --transport streamable-http",
12
18
  "start:sse": "node ./bin/mcp-abap-adt-proxy.js --transport sse",
13
19
  "dev": "npx @modelcontextprotocol/inspector node ./dist/index.js",
14
- "clean": "rm -rf dist tsconfig.tsbuildinfo",
20
+ "clean": "rimraf dist tsconfig.tsbuildinfo",
15
21
  "prepublishOnly": "npm run build",
16
- "test:servers": "node ./tools/start-servers.js"
22
+ "test:servers": "node ./tools/start-servers.js",
23
+ "test-destination": "npx tsx tools/test-destination.ts"
17
24
  },
18
25
  "keywords": [
19
26
  "abap",
@@ -37,23 +44,35 @@
37
44
  "access": "public"
38
45
  },
39
46
  "devDependencies": {
40
- "@modelcontextprotocol/inspector": "^0.17.2",
41
- "@types/jest": "^29.5.0",
47
+ "@biomejs/biome": "^2.3.10",
48
+ "@jest/globals": "^30.2.0",
49
+ "@modelcontextprotocol/inspector": "^0.18.0",
50
+ "@types/jest": "^30.0.0",
51
+ "@types/js-yaml": "^4.0.9",
42
52
  "@types/node": "^24.2.1",
43
53
  "commander": "^14.0.2",
44
54
  "cors": "^2.8.5",
45
- "dotenv": "^17.2.1",
55
+ "cross-env": "^10.1.0",
46
56
  "express": "^5.1.0",
47
- "jest": "^29.7.0",
48
- "ts-jest": "^29.2.0",
57
+ "jest": "^30.2.0",
58
+ "jest-util": "^30.2.0",
59
+ "pino": "^10.3.0",
60
+ "pino-pretty": "^13.1.3",
61
+ "rimraf": "^6.1.2",
62
+ "ts-jest": "^29.2.5",
63
+ "tsx": "^4.21.0",
49
64
  "typescript": "^5.9.2"
50
65
  },
51
66
  "dependencies": {
52
- "@mcp-abap-adt/auth-broker": "^0.1.5",
53
- "@mcp-abap-adt/connection": "^0.1.13",
54
- "@mcp-abap-adt/header-validator": "^0.1.3",
55
- "@modelcontextprotocol/sdk": "^1.23.0",
67
+ "@mcp-abap-adt/auth-broker": "^0.3.6",
68
+ "@mcp-abap-adt/auth-providers": "^0.2.10",
69
+ "@mcp-abap-adt/auth-stores": "^0.3.0",
70
+ "@mcp-abap-adt/header-validator": "^0.1.8",
71
+ "@mcp-abap-adt/interfaces": "^0.2.15",
72
+ "@mcp-abap-adt/logger": "^0.1.4",
73
+ "@modelcontextprotocol/sdk": "^1.25.1",
56
74
  "axios": "^1.11.0",
75
+ "js-yaml": "^4.1.1",
57
76
  "zod": "^3.25.76"
58
77
  },
59
78
  "engines": {
@@ -1,32 +0,0 @@
1
- /**
2
- * Direct Cloud Router - Routes requests directly to cloud ABAP systems
3
- *
4
- * For requests with x-sap-destination (not "sk"), routes directly to cloud ABAP
5
- * using the same approach as mcp-abap-adt
6
- */
7
- import { IncomingHttpHeaders } from "http";
8
- import { AuthBroker } from "@mcp-abap-adt/auth-broker";
9
- import { AbapConnection } from "@mcp-abap-adt/connection";
10
- import { RoutingDecision } from "./headerAnalyzer.js";
11
- export interface DirectCloudConfig {
12
- sapUrl: string;
13
- destination?: string;
14
- authType: "jwt" | "basic";
15
- jwtToken?: string;
16
- refreshToken?: string;
17
- username?: string;
18
- password?: string;
19
- sapClient?: string;
20
- uaaUrl?: string;
21
- uaaClientId?: string;
22
- uaaClientSecret?: string;
23
- }
24
- /**
25
- * Convert validated auth config to direct cloud config
26
- */
27
- export declare function createDirectCloudConfig(routingDecision: RoutingDecision, headers: IncomingHttpHeaders): DirectCloudConfig | null;
28
- /**
29
- * Get or create ABAP connection for direct cloud routing
30
- */
31
- export declare function getDirectCloudConnection(sessionId: string, config: DirectCloudConfig, authBroker?: AuthBroker): Promise<AbapConnection>;
32
- //# sourceMappingURL=directCloudRouter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"directCloudRouter.d.ts","sourceRoot":"","sources":["../../src/router/directCloudRouter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAwB,cAAc,EAAsB,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAEtD,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,KAAK,GAAG,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,mBAAmB,GAC3B,iBAAiB,GAAG,IAAI,CAgD1B;AA4CD;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,iBAAiB,EACzB,UAAU,CAAC,EAAE,UAAU,GACtB,OAAO,CAAC,cAAc,CAAC,CA+FzB"}
@@ -1,179 +0,0 @@
1
- "use strict";
2
- /**
3
- * Direct Cloud Router - Routes requests directly to cloud ABAP systems
4
- *
5
- * For requests with x-sap-destination (not "sk"), routes directly to cloud ABAP
6
- * using the same approach as mcp-abap-adt
7
- */
8
- Object.defineProperty(exports, "__esModule", { value: true });
9
- exports.createDirectCloudConfig = createDirectCloudConfig;
10
- exports.getDirectCloudConnection = getDirectCloudConnection;
11
- const connection_1 = require("@mcp-abap-adt/connection");
12
- const logger_js_1 = require("../lib/logger.js");
13
- /**
14
- * Convert validated auth config to direct cloud config
15
- */
16
- function createDirectCloudConfig(routingDecision, headers) {
17
- const validatedConfig = routingDecision.validationResult?.config;
18
- if (!validatedConfig) {
19
- logger_js_1.logger.warn("No validated config available for direct cloud routing", {
20
- type: "DIRECT_CLOUD_CONFIG_ERROR",
21
- strategy: routingDecision.strategy,
22
- });
23
- return null;
24
- }
25
- // Extract SAP URL
26
- let sapUrl = validatedConfig.sapUrl;
27
- const destination = routingDecision.btpDestination || routingDecision.mcpDestination;
28
- if (!sapUrl && destination) {
29
- // For destination-based auth, URL comes from destination service key
30
- // We'll need to load it from AuthBroker
31
- // For now, return null and let the caller handle it
32
- logger_js_1.logger.debug("SAP URL not in validated config, will load from destination", {
33
- destination: destination,
34
- });
35
- }
36
- // Convert xsuaa to jwt (they're the same for connection purposes)
37
- const authType = validatedConfig.authType === "xsuaa" ? "jwt" : validatedConfig.authType;
38
- const config = {
39
- sapUrl: sapUrl || "", // Will be loaded from destination if needed
40
- destination: validatedConfig.destination || destination,
41
- authType: authType,
42
- sapClient: validatedConfig.sapClient,
43
- };
44
- // Add JWT-specific fields
45
- if (validatedConfig.authType === "jwt" || validatedConfig.authType === "xsuaa") {
46
- config.jwtToken = validatedConfig.jwtToken;
47
- config.refreshToken = validatedConfig.refreshToken;
48
- config.uaaUrl = validatedConfig.uaaUrl;
49
- config.uaaClientId = validatedConfig.uaaClientId;
50
- config.uaaClientSecret = validatedConfig.uaaClientSecret;
51
- }
52
- // Add basic auth fields
53
- if (validatedConfig.authType === "basic") {
54
- config.username = validatedConfig.username;
55
- config.password = validatedConfig.password;
56
- }
57
- return config;
58
- }
59
- /**
60
- * Connection cache for direct cloud connections
61
- */
62
- const connectionCache = new Map();
63
- /**
64
- * Generate cache key for connection
65
- */
66
- function generateConnectionCacheKey(sessionId, config) {
67
- const crypto = require("crypto");
68
- const hash = crypto.createHash("sha256");
69
- hash.update(sessionId);
70
- hash.update(config.sapUrl);
71
- hash.update(config.destination || "");
72
- hash.update(config.authType);
73
- hash.update(config.sapClient || "");
74
- return hash.digest("hex");
75
- }
76
- /**
77
- * Clean up old connections from cache
78
- */
79
- function cleanupConnectionCache() {
80
- const now = new Date();
81
- const maxAge = 60 * 60 * 1000; // 1 hour
82
- for (const [key, entry] of connectionCache.entries()) {
83
- const age = now.getTime() - entry.lastUsed.getTime();
84
- if (age > maxAge) {
85
- logger_js_1.logger.debug("Cleaning up old connection cache entry", {
86
- type: "CONNECTION_CACHE_CLEANUP",
87
- key: key.substring(0, 16),
88
- });
89
- connectionCache.delete(key);
90
- }
91
- }
92
- }
93
- /**
94
- * Get or create ABAP connection for direct cloud routing
95
- */
96
- async function getDirectCloudConnection(sessionId, config, authBroker) {
97
- // Clean up old entries periodically
98
- if (connectionCache.size > 100) {
99
- cleanupConnectionCache();
100
- }
101
- const cacheKey = generateConnectionCacheKey(sessionId, config);
102
- let entry = connectionCache.get(cacheKey);
103
- // If we have a destination and no SAP URL, load it from AuthBroker
104
- if (config.destination && !config.sapUrl && authBroker) {
105
- try {
106
- // Get token from AuthBroker (this will also provide the SAP URL from destination)
107
- const token = await authBroker.getToken(config.destination);
108
- config.jwtToken = token;
109
- // Load destination config to get SAP URL from AuthBroker
110
- // AuthBroker stores session data, we can access it through the broker
111
- // For now, we'll rely on the token being valid and the URL being set from destination
112
- // The actual URL will be loaded when we create the connection
113
- }
114
- catch (error) {
115
- logger_js_1.logger.error("Failed to load destination config from AuthBroker", {
116
- type: "AUTH_BROKER_LOAD_ERROR",
117
- destination: config.destination,
118
- error: error instanceof Error ? error.message : String(error),
119
- });
120
- throw error;
121
- }
122
- }
123
- if (!entry || entry.configSignature !== cacheKey) {
124
- logger_js_1.logger.debug("Creating new direct cloud connection", {
125
- type: "DIRECT_CLOUD_CONNECTION_CREATE",
126
- sessionId: sessionId.substring(0, 8),
127
- destination: config.destination,
128
- sapUrl: config.sapUrl,
129
- });
130
- // Dispose old connection if exists
131
- if (entry) {
132
- // Connection disposal will be handled by connection package
133
- }
134
- // Create SAP config for connection
135
- const sapConfig = {
136
- url: config.sapUrl,
137
- authType: config.authType,
138
- jwtToken: config.jwtToken,
139
- refreshToken: config.refreshToken,
140
- username: config.username,
141
- password: config.password,
142
- sapClient: config.sapClient,
143
- uaaUrl: config.uaaUrl,
144
- uaaClientId: config.uaaClientId,
145
- uaaClientSecret: config.uaaClientSecret,
146
- };
147
- // Create connection
148
- const connectionSessionId = `mcp-proxy-direct-${sessionId}`;
149
- const sessionStorage = new connection_1.FileSessionStorage();
150
- const connection = (0, connection_1.createAbapConnection)(sapConfig, logger_js_1.logger, // Use our logger
151
- sessionStorage, connectionSessionId);
152
- // Connect
153
- try {
154
- await connection.connect();
155
- }
156
- catch (error) {
157
- logger_js_1.logger.error("Failed to connect to cloud ABAP", {
158
- type: "DIRECT_CLOUD_CONNECTION_ERROR",
159
- error: error instanceof Error ? error.message : String(error),
160
- destination: config.destination,
161
- });
162
- throw error;
163
- }
164
- entry = {
165
- connection,
166
- configSignature: cacheKey,
167
- lastUsed: new Date(),
168
- };
169
- connectionCache.set(cacheKey, entry);
170
- }
171
- else {
172
- entry.lastUsed = new Date();
173
- logger_js_1.logger.debug("Reusing cached direct cloud connection", {
174
- type: "DIRECT_CLOUD_CONNECTION_REUSE",
175
- sessionId: sessionId.substring(0, 8),
176
- });
177
- }
178
- return entry.connection;
179
- }
@@ -1,23 +0,0 @@
1
- /**
2
- * Local Basic Router - Handles basic authentication requests locally
3
- *
4
- * For requests with x-sap-auth-type: "basic", handles locally without proxying
5
- */
6
- import { IncomingHttpHeaders } from "http";
7
- import { AbapConnection } from "@mcp-abap-adt/connection";
8
- import { RoutingDecision } from "./headerAnalyzer.js";
9
- export interface LocalBasicConfig {
10
- sapUrl: string;
11
- username: string;
12
- password: string;
13
- sapClient?: string;
14
- }
15
- /**
16
- * Convert validated auth config to local basic config
17
- */
18
- export declare function createLocalBasicConfig(routingDecision: RoutingDecision, headers: IncomingHttpHeaders): LocalBasicConfig | null;
19
- /**
20
- * Get or create ABAP connection for local basic auth
21
- */
22
- export declare function getLocalBasicConnection(sessionId: string, config: LocalBasicConfig): Promise<AbapConnection>;
23
- //# sourceMappingURL=localBasicRouter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"localBasicRouter.d.ts","sourceRoot":"","sources":["../../src/router/localBasicRouter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,mBAAmB,EAAE,MAAM,MAAM,CAAC;AAC3C,OAAO,EAAwB,cAAc,EAAsB,MAAM,0BAA0B,CAAC;AAEpG,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAGtD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,eAAe,EAAE,eAAe,EAChC,OAAO,EAAE,mBAAmB,GAC3B,gBAAgB,GAAG,IAAI,CAkCzB;AA2CD;;GAEG;AACH,wBAAsB,uBAAuB,CAC3C,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,gBAAgB,GACvB,OAAO,CAAC,cAAc,CAAC,CAoEzB"}
@@ -1,140 +0,0 @@
1
- "use strict";
2
- /**
3
- * Local Basic Router - Handles basic authentication requests locally
4
- *
5
- * For requests with x-sap-auth-type: "basic", handles locally without proxying
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.createLocalBasicConfig = createLocalBasicConfig;
9
- exports.getLocalBasicConnection = getLocalBasicConnection;
10
- const connection_1 = require("@mcp-abap-adt/connection");
11
- const logger_js_1 = require("../lib/logger.js");
12
- /**
13
- * Convert validated auth config to local basic config
14
- */
15
- function createLocalBasicConfig(routingDecision, headers) {
16
- const validatedConfig = routingDecision.validationResult?.config;
17
- if (!validatedConfig) {
18
- logger_js_1.logger.warn("No validated config available for local basic routing", {
19
- type: "LOCAL_BASIC_CONFIG_ERROR",
20
- strategy: routingDecision.strategy,
21
- });
22
- return null;
23
- }
24
- if (validatedConfig.authType !== "basic") {
25
- logger_js_1.logger.warn("Config is not basic auth type", {
26
- type: "LOCAL_BASIC_CONFIG_TYPE_ERROR",
27
- authType: validatedConfig.authType,
28
- });
29
- return null;
30
- }
31
- if (!validatedConfig.sapUrl || !validatedConfig.username || !validatedConfig.password) {
32
- logger_js_1.logger.warn("Missing required fields for basic auth", {
33
- type: "LOCAL_BASIC_CONFIG_MISSING",
34
- hasUrl: !!validatedConfig.sapUrl,
35
- hasUsername: !!validatedConfig.username,
36
- hasPassword: !!validatedConfig.password,
37
- });
38
- return null;
39
- }
40
- return {
41
- sapUrl: validatedConfig.sapUrl,
42
- username: validatedConfig.username,
43
- password: validatedConfig.password,
44
- sapClient: validatedConfig.sapClient,
45
- };
46
- }
47
- /**
48
- * Connection cache for local basic connections
49
- */
50
- const connectionCache = new Map();
51
- /**
52
- * Generate cache key for connection
53
- */
54
- function generateConnectionCacheKey(sessionId, config) {
55
- const crypto = require("crypto");
56
- const hash = crypto.createHash("sha256");
57
- hash.update(sessionId);
58
- hash.update(config.sapUrl);
59
- hash.update(config.username);
60
- hash.update(config.sapClient || "");
61
- return hash.digest("hex");
62
- }
63
- /**
64
- * Clean up old connections from cache
65
- */
66
- function cleanupConnectionCache() {
67
- const now = new Date();
68
- const maxAge = 60 * 60 * 1000; // 1 hour
69
- for (const [key, entry] of connectionCache.entries()) {
70
- const age = now.getTime() - entry.lastUsed.getTime();
71
- if (age > maxAge) {
72
- logger_js_1.logger.debug("Cleaning up old local basic connection cache entry", {
73
- type: "LOCAL_BASIC_CACHE_CLEANUP",
74
- key: key.substring(0, 16),
75
- });
76
- connectionCache.delete(key);
77
- }
78
- }
79
- }
80
- /**
81
- * Get or create ABAP connection for local basic auth
82
- */
83
- async function getLocalBasicConnection(sessionId, config) {
84
- // Clean up old entries periodically
85
- if (connectionCache.size > 100) {
86
- cleanupConnectionCache();
87
- }
88
- const cacheKey = generateConnectionCacheKey(sessionId, config);
89
- let entry = connectionCache.get(cacheKey);
90
- if (!entry || entry.configSignature !== cacheKey) {
91
- logger_js_1.logger.debug("Creating new local basic connection", {
92
- type: "LOCAL_BASIC_CONNECTION_CREATE",
93
- sessionId: sessionId.substring(0, 8),
94
- sapUrl: config.sapUrl,
95
- });
96
- // Dispose old connection if exists
97
- if (entry) {
98
- // Connection disposal will be handled by connection package
99
- }
100
- // Create SAP config for connection
101
- const sapConfig = {
102
- url: config.sapUrl,
103
- authType: "basic",
104
- username: config.username,
105
- password: config.password,
106
- client: config.sapClient,
107
- };
108
- // Create connection
109
- const connectionSessionId = `mcp-proxy-basic-${sessionId}`;
110
- const sessionStorage = new connection_1.FileSessionStorage();
111
- const connection = (0, connection_1.createAbapConnection)(sapConfig, logger_js_1.logger, // Use our logger
112
- sessionStorage, connectionSessionId);
113
- // Connect
114
- try {
115
- await connection.connect();
116
- }
117
- catch (error) {
118
- logger_js_1.logger.error("Failed to connect to ABAP with basic auth", {
119
- type: "LOCAL_BASIC_CONNECTION_ERROR",
120
- error: error instanceof Error ? error.message : String(error),
121
- sapUrl: config.sapUrl,
122
- });
123
- throw error;
124
- }
125
- entry = {
126
- connection,
127
- configSignature: cacheKey,
128
- lastUsed: new Date(),
129
- };
130
- connectionCache.set(cacheKey, entry);
131
- }
132
- else {
133
- entry.lastUsed = new Date();
134
- logger_js_1.logger.debug("Reusing cached local basic connection", {
135
- type: "LOCAL_BASIC_CONNECTION_REUSE",
136
- sessionId: sessionId.substring(0, 8),
137
- });
138
- }
139
- return entry.connection;
140
- }