@mcp-abap-adt/header-validator 0.1.2 → 0.1.4

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
@@ -5,6 +5,52 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.4] - 2024-12-04
9
+
10
+ ### Added
11
+ - **Interfaces Package Integration**: Migrated to use `@mcp-abap-adt/interfaces` package for all interface definitions
12
+ - All interfaces now imported from shared package
13
+ - Backward compatibility maintained with type aliases
14
+ - Dependency on `@mcp-abap-adt/interfaces@^0.1.0` added
15
+
16
+ ### Changed
17
+ - **Interface Renaming**: Interfaces renamed to follow `I` prefix convention:
18
+ - `ValidatedAuthConfig` → `IValidatedAuthConfig` (type alias for backward compatibility)
19
+ - `HeaderValidationResult` → `IHeaderValidationResult` (type alias for backward compatibility)
20
+ - `AuthType` and `AuthMethodPriority` now imported from interfaces package
21
+ - Old names still work via type aliases for backward compatibility
22
+
23
+ ## [0.1.3] - 2025-12-01
24
+
25
+ ### Fixed
26
+ - **x-mcp-destination validation** – fixed issue where `x-mcp-destination` header incorrectly required `x-sap-url`:
27
+ - `x-mcp-destination` now works identically to `x-sap-destination` - URL is automatically derived from service key
28
+ - `x-sap-url` is now optional for `x-mcp-destination` (and will be ignored with a warning if provided)
29
+ - Fixed validation logic to check `x-mcp-destination` immediately after `x-sap-destination`, regardless of `x-sap-url` presence
30
+ - This fixes issues on Windows where `x-mcp-destination` was being ignored
31
+
32
+ ### Changed
33
+ - **Header case handling** – improved header value extraction to check both lowercase and original case:
34
+ - `getHeaderValue()` now checks lowercase first (Node.js normalizes headers), then original case for compatibility
35
+ - Both `validateSapDestinationAuth()` and `validateMcpDestinationAuth()` check headers in both cases
36
+ - Better cross-platform compatibility, especially for Windows
37
+
38
+ - **Validation order** – improved validation priority logic:
39
+ - `x-mcp-destination` is now checked immediately after `x-sap-destination`, before checking for `x-sap-url`
40
+ - This ensures destination-based authentication is prioritized correctly
41
+ - URL validation only happens for non-destination authentication methods
42
+
43
+ - **Documentation updates** – updated architecture documentation:
44
+ - Updated `ARCHITECTURE.md` to reflect that `x-sap-url` is not required for `x-mcp-destination`
45
+ - Updated `PRIORITY_DIAGRAM.md` with correct header requirements
46
+ - Updated examples to show `x-mcp-destination` without `x-sap-url`
47
+
48
+ ### Added
49
+ - **Test coverage** – added tests for new validation behavior:
50
+ - Test for `x-mcp-destination` without `x-sap-url` (should work)
51
+ - Test for warning when `x-sap-url` is provided with `x-mcp-destination`
52
+ - Updated existing tests to reflect new validation logic
53
+
8
54
  ## [0.1.2] - 2025-11-30
9
55
 
10
56
  ### Changed
package/README.md CHANGED
@@ -9,6 +9,56 @@ Header validator for MCP ABAP ADT - validates and prioritizes authentication hea
9
9
  - ✅ **Error Reporting**: Detailed error messages and warnings
10
10
  - ✅ **Type Safety**: Full TypeScript support with type definitions
11
11
 
12
+ ## Responsibilities and Design Principles
13
+
14
+ ### Core Development Principle
15
+
16
+ **Interface-Only Communication**: This package follows a fundamental development principle: **all interactions with external dependencies happen ONLY through interfaces**. The code knows **NOTHING beyond what is defined in the interfaces**.
17
+
18
+ This means:
19
+ - Does not know about concrete implementation classes from other packages
20
+ - Does not know about internal data structures or methods not defined in interfaces
21
+ - Does not make assumptions about implementation behavior beyond interface contracts
22
+ - Does not access properties or methods not explicitly defined in interfaces
23
+
24
+ This principle ensures:
25
+ - **Loose coupling**: Validator is decoupled from concrete implementations in other packages
26
+ - **Flexibility**: New implementations can be added without modifying validator
27
+ - **Testability**: Easy to mock dependencies for testing
28
+ - **Maintainability**: Changes to implementations don't affect validator
29
+
30
+ ### Package Responsibilities
31
+
32
+ This package is responsible for:
33
+
34
+ 1. **Header validation**: Validates authentication headers from HTTP requests
35
+ 2. **Priority resolution**: Determines which authentication method to use based on header presence and priority rules
36
+ 3. **Configuration extraction**: Extracts authentication configuration from headers
37
+ 4. **Error reporting**: Provides detailed validation errors and warnings
38
+
39
+ #### What This Package Does
40
+
41
+ - **Validates headers**: Checks authentication headers for validity and completeness
42
+ - **Prioritizes methods**: Determines authentication method priority (SAP destination > MCP destination > JWT token > Basic auth)
43
+ - **Extracts config**: Extracts `SapConfig` from validated headers
44
+ - **Reports errors**: Provides detailed error messages and warnings for invalid configurations
45
+ - **Type safety**: Returns typed validation results with configuration objects
46
+
47
+ #### What This Package Does NOT Do
48
+
49
+ - **Does NOT handle authentication**: Authentication is handled by `@mcp-abap-adt/connection` and `@mcp-abap-adt/auth-broker`
50
+ - **Does NOT manage tokens**: Token management is handled by `@mcp-abap-adt/auth-broker`
51
+ - **Does NOT make HTTP requests**: HTTP requests are handled by `@mcp-abap-adt/connection`
52
+ - **Does NOT store configuration**: Configuration storage is handled by consumers
53
+ - **Does NOT know about destinations**: Destination resolution is handled by `@mcp-abap-adt/auth-broker`
54
+
55
+ ### External Dependencies
56
+
57
+ This package interacts with external packages **ONLY through interfaces**:
58
+
59
+ - **`@mcp-abap-adt/connection`**: Uses `SapConfig` type for configuration - does not know about concrete connection implementation
60
+ - **No direct dependencies on other packages**: All interactions happen through well-defined types and interfaces
61
+
12
62
  ## Installation
13
63
 
14
64
  ```bash
@@ -7,7 +7,7 @@
7
7
  * 3. Basic auth (x-sap-login + x-sap-password) - lowest priority
8
8
  */
9
9
  import { IncomingHttpHeaders } from 'http';
10
- import { HeaderValidationResult } from './types';
10
+ import type { HeaderValidationResult } from './types';
11
11
  /**
12
12
  * Validate and prioritize authentication headers
13
13
  *
@@ -9,12 +9,18 @@
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
11
  exports.validateAuthHeaders = validateAuthHeaders;
12
- const types_1 = require("./types");
12
+ const interfaces_1 = require("@mcp-abap-adt/interfaces");
13
13
  /**
14
14
  * Extract header value (handles array values)
15
+ * Node.js normalizes headers to lowercase, but check both cases for safety
15
16
  */
16
17
  function getHeaderValue(headers, name) {
17
- const value = headers[name];
18
+ // Try lowercase first (Node.js normalizes to lowercase)
19
+ let value = headers[name.toLowerCase()];
20
+ // If not found, try original case (for compatibility)
21
+ if (!value) {
22
+ value = headers[name];
23
+ }
18
24
  if (!value) {
19
25
  return undefined;
20
26
  }
@@ -41,7 +47,8 @@ function isValidUrl(url) {
41
47
  * URL is taken from destination (service key or .env), not from x-sap-url header
42
48
  */
43
49
  function validateSapDestinationAuth(headers) {
44
- const destinationRaw = headers['x-sap-destination'];
50
+ // Check both lowercase and original case (Node.js normalizes to lowercase, but check both for safety)
51
+ const destinationRaw = headers['x-sap-destination'] || headers['X-SAP-Destination'];
45
52
  if (!destinationRaw) {
46
53
  return null;
47
54
  }
@@ -52,7 +59,7 @@ function validateSapDestinationAuth(headers) {
52
59
  if (!destination || destination.length === 0) {
53
60
  errors.push('x-sap-destination header is empty');
54
61
  return {
55
- priority: types_1.AuthMethodPriority.NONE,
62
+ priority: interfaces_1.AuthMethodPriority.NONE,
56
63
  authType: 'jwt', // SAP destination always uses JWT
57
64
  sapUrl: '', // URL will be loaded from destination
58
65
  errors,
@@ -80,7 +87,7 @@ function validateSapDestinationAuth(headers) {
80
87
  warnings.push('x-sap-auth-type is ignored when x-sap-destination is present (always uses JWT)');
81
88
  }
82
89
  return {
83
- priority: types_1.AuthMethodPriority.SAP_DESTINATION,
90
+ priority: interfaces_1.AuthMethodPriority.SAP_DESTINATION,
84
91
  authType: 'jwt', // Always JWT for x-sap-destination
85
92
  sapUrl: '', // URL will be loaded from destination (service key or .env)
86
93
  sapClient,
@@ -94,10 +101,12 @@ function validateSapDestinationAuth(headers) {
94
101
  /**
95
102
  * Validate MCP destination-based authentication (medium-high priority)
96
103
  * x-mcp-destination - uses AuthBroker, always JWT (no x-sap-auth-type needed)
97
- * x-sap-url is required for MCP destination (unlike x-sap-destination)
104
+ * URL is taken from destination (service key or .env), not from x-sap-url header
105
+ * x-sap-url is optional - if provided, it will be ignored (warning issued)
98
106
  */
99
107
  function validateMcpDestinationAuth(headers, sapUrl) {
100
- const destinationRaw = headers['x-mcp-destination'];
108
+ // Check both lowercase and original case (Node.js normalizes to lowercase, but check both for safety)
109
+ const destinationRaw = headers['x-mcp-destination'] || headers['X-MCP-Destination'];
101
110
  if (!destinationRaw) {
102
111
  return null;
103
112
  }
@@ -108,23 +117,16 @@ function validateMcpDestinationAuth(headers, sapUrl) {
108
117
  if (!destination || destination.length === 0) {
109
118
  errors.push('x-mcp-destination header is empty');
110
119
  return {
111
- priority: types_1.AuthMethodPriority.NONE,
120
+ priority: interfaces_1.AuthMethodPriority.NONE,
112
121
  authType: 'jwt', // MCP destination always uses JWT
113
- sapUrl: sapUrl || '',
122
+ sapUrl: '', // URL will be loaded from destination
114
123
  errors,
115
124
  warnings,
116
125
  };
117
126
  }
118
- // x-sap-url is required for MCP destination
119
- if (!sapUrl) {
120
- errors.push('x-sap-url header is required when x-mcp-destination is present');
121
- return {
122
- priority: types_1.AuthMethodPriority.NONE,
123
- authType: 'jwt',
124
- sapUrl: '',
125
- errors,
126
- warnings,
127
- };
127
+ // Warning if x-sap-url is provided (URL comes from destination, not header)
128
+ if (sapUrl) {
129
+ warnings.push('x-sap-url is ignored when x-mcp-destination is present (URL is loaded from destination service key or .env file)');
128
130
  }
129
131
  // Warning if x-sap-auth-type is provided (not needed for x-mcp-destination)
130
132
  const authType = getHeaderValue(headers, 'x-sap-auth-type');
@@ -139,9 +141,9 @@ function validateMcpDestinationAuth(headers, sapUrl) {
139
141
  warnings.push('x-sap-jwt-token is ignored when x-mcp-destination is present (destination-based auth takes priority)');
140
142
  }
141
143
  return {
142
- priority: types_1.AuthMethodPriority.MCP_DESTINATION,
144
+ priority: interfaces_1.AuthMethodPriority.MCP_DESTINATION,
143
145
  authType: 'jwt', // Always JWT for x-mcp-destination
144
- sapUrl,
146
+ sapUrl: '', // URL will be loaded from destination (service key or .env)
145
147
  sapClient,
146
148
  destination,
147
149
  errors,
@@ -173,7 +175,7 @@ function validateDirectJwtAuth(headers, sapUrl, authType) {
173
175
  // Extract optional SAP client
174
176
  const sapClient = getHeaderValue(headers, 'x-sap-client');
175
177
  return {
176
- priority: types_1.AuthMethodPriority.DIRECT_JWT,
178
+ priority: interfaces_1.AuthMethodPriority.DIRECT_JWT,
177
179
  authType,
178
180
  sapUrl,
179
181
  sapClient,
@@ -212,7 +214,7 @@ function validateBasicAuth(headers, sapUrl, authType) {
212
214
  // Return config with errors if validation failed
213
215
  if (errors.length > 0) {
214
216
  return {
215
- priority: types_1.AuthMethodPriority.NONE,
217
+ priority: interfaces_1.AuthMethodPriority.NONE,
216
218
  authType,
217
219
  sapUrl,
218
220
  errors,
@@ -220,7 +222,7 @@ function validateBasicAuth(headers, sapUrl, authType) {
220
222
  };
221
223
  }
222
224
  return {
223
- priority: types_1.AuthMethodPriority.BASIC,
225
+ priority: interfaces_1.AuthMethodPriority.BASIC,
224
226
  authType,
225
227
  sapUrl,
226
228
  username,
@@ -267,53 +269,48 @@ function validateAuthHeaders(headers) {
267
269
  };
268
270
  }
269
271
  }
270
- // For other auth methods, x-sap-url is required (but MCP destination can work without it if URL is optional)
272
+ // Check for MCP destination (doesn't require x-sap-url, URL comes from destination)
271
273
  const sapUrl = getHeaderValue(headers, 'x-sap-url');
272
- // Check for MCP destination (requires x-sap-url header)
273
- if (sapUrl) {
274
- // Validate URL format first
275
- if (!isValidUrl(sapUrl)) {
276
- errors.push(`x-sap-url is not a valid URL: ${sapUrl}`);
274
+ const mcpDestinationConfig = validateMcpDestinationAuth(headers, sapUrl);
275
+ if (mcpDestinationConfig) {
276
+ // MCP destination found - URL comes from destination, not header
277
+ if (mcpDestinationConfig.errors.length === 0) {
277
278
  return {
278
- isValid: false,
279
- errors,
280
- warnings,
279
+ isValid: true,
280
+ config: mcpDestinationConfig,
281
+ errors: [],
282
+ warnings: mcpDestinationConfig.warnings,
281
283
  };
282
284
  }
283
- const mcpDestinationConfig = validateMcpDestinationAuth(headers, sapUrl);
284
- if (mcpDestinationConfig) {
285
- if (mcpDestinationConfig.errors.length === 0) {
286
- return {
287
- isValid: true,
288
- config: mcpDestinationConfig,
289
- errors: [],
290
- warnings: mcpDestinationConfig.warnings,
291
- };
292
- }
293
- else {
294
- return {
295
- isValid: false,
296
- errors: mcpDestinationConfig.errors,
297
- warnings: mcpDestinationConfig.warnings,
298
- };
299
- }
285
+ else {
286
+ // Has errors, continue to check other methods or return error
287
+ return {
288
+ isValid: false,
289
+ errors: mcpDestinationConfig.errors,
290
+ warnings: mcpDestinationConfig.warnings,
291
+ };
300
292
  }
301
293
  }
302
- // If no auth headers at all, return empty result (not an error - may use .env file)
294
+ // For other auth methods, x-sap-url is required
303
295
  if (!sapUrl) {
296
+ // If no auth headers at all, return empty result (not an error - may use .env file)
304
297
  return {
305
298
  isValid: false,
306
299
  errors: [],
307
300
  warnings: [],
308
301
  };
309
302
  }
303
+ // Validate URL format
304
+ if (!isValidUrl(sapUrl)) {
305
+ errors.push(`x-sap-url is not a valid URL: ${sapUrl}`);
306
+ return {
307
+ isValid: false,
308
+ errors,
309
+ warnings,
310
+ };
311
+ }
310
312
  // Try to validate authentication methods in priority order
311
313
  const configs = [];
312
- // 2. MCP destination-based auth (medium-high priority) - doesn't require x-sap-auth-type
313
- const mcpDestinationConfig = validateMcpDestinationAuth(headers, sapUrl);
314
- if (mcpDestinationConfig) {
315
- configs.push(mcpDestinationConfig);
316
- }
317
314
  // 3. Other auth methods require x-sap-auth-type
318
315
  const sapAuthType = getHeaderValue(headers, 'x-sap-auth-type');
319
316
  if (sapAuthType) {
@@ -357,9 +354,12 @@ function validateAuthHeaders(headers) {
357
354
  errors.push('Basic authentication requires x-sap-login and x-sap-password headers');
358
355
  }
359
356
  }
360
- else if (mcpDestinationConfig && mcpDestinationConfig.errors.length > 0) {
361
- // MCP destination was found but has errors
362
- errors.push(...mcpDestinationConfig.errors);
357
+ else {
358
+ // Check if MCP destination was found but has errors
359
+ const mcpConfig = validateMcpDestinationAuth(headers, sapUrl);
360
+ if (mcpConfig && mcpConfig.errors.length > 0) {
361
+ errors.push(...mcpConfig.errors);
362
+ }
363
363
  }
364
364
  return {
365
365
  isValid: false,
@@ -372,7 +372,7 @@ function validateAuthHeaders(headers) {
372
372
  // Check for conflicts (multiple auth methods with same priority shouldn't happen, but check anyway)
373
373
  const samePriorityConfigs = configs.filter(c => c.priority === selectedConfig.priority);
374
374
  if (samePriorityConfigs.length > 1) {
375
- warnings.push(`Multiple authentication methods with same priority detected, using: ${types_1.AuthMethodPriority[selectedConfig.priority]}`);
375
+ warnings.push(`Multiple authentication methods with same priority detected, using: ${interfaces_1.AuthMethodPriority[selectedConfig.priority]}`);
376
376
  }
377
377
  // Merge errors and warnings
378
378
  const allErrors = [...errors, ...selectedConfig.errors];
package/dist/types.d.ts CHANGED
@@ -1,64 +1,9 @@
1
1
  /**
2
2
  * Types for header validator
3
3
  */
4
- /**
5
- * Authentication type
6
- */
7
- export type AuthType = 'jwt' | 'xsuaa' | 'basic';
8
- /**
9
- * Authentication method priority
10
- * Higher number = higher priority
11
- */
12
- export declare enum AuthMethodPriority {
13
- SAP_DESTINATION = 4,// x-sap-destination (uses AuthBroker, JWT only)
14
- MCP_DESTINATION = 3,// x-mcp-destination + x-sap-auth-type=jwt (uses AuthBroker)
15
- DIRECT_JWT = 2,// x-sap-jwt-token + x-sap-auth-type=jwt
16
- BASIC = 1,// x-sap-login + x-sap-password + x-sap-auth-type=basic
17
- NONE = 0
18
- }
19
- /**
20
- * Validated authentication configuration
21
- */
22
- export interface ValidatedAuthConfig {
23
- /** Authentication method priority */
24
- priority: AuthMethodPriority;
25
- /** Authentication type */
26
- authType: AuthType;
27
- /** SAP URL */
28
- sapUrl: string;
29
- /** SAP Client (optional) */
30
- sapClient?: string;
31
- /** Destination name (for destination-based auth: x-sap-destination or x-mcp-destination) */
32
- destination?: string;
33
- /** JWT token (for direct JWT auth) */
34
- jwtToken?: string;
35
- /** Refresh token (optional, for JWT auth) */
36
- refreshToken?: string;
37
- /** UAA URL (optional, for JWT auth) */
38
- uaaUrl?: string;
39
- /** UAA Client ID (optional, for JWT auth) */
40
- uaaClientId?: string;
41
- /** UAA Client Secret (optional, for JWT auth) */
42
- uaaClientSecret?: string;
43
- /** Username (for basic auth) */
44
- username?: string;
45
- /** Password (for basic auth) */
46
- password?: string;
47
- /** Validation errors (if any) */
48
- errors: string[];
49
- /** Warnings (if any) */
50
- warnings: string[];
51
- }
52
- /**
53
- * Header validation result
54
- */
55
- export interface HeaderValidationResult {
56
- /** Is configuration valid? */
57
- isValid: boolean;
58
- /** Validated authentication configuration */
59
- config?: ValidatedAuthConfig;
60
- /** Validation errors */
61
- errors: string[];
62
- /** Warnings */
63
- warnings: string[];
64
- }
4
+ import type { AuthType } from '@mcp-abap-adt/interfaces';
5
+ import { AuthMethodPriority, type IValidatedAuthConfig, type IHeaderValidationResult } from '@mcp-abap-adt/interfaces';
6
+ export type { AuthType };
7
+ export { AuthMethodPriority };
8
+ export type ValidatedAuthConfig = IValidatedAuthConfig;
9
+ export type HeaderValidationResult = IHeaderValidationResult;
package/dist/types.js CHANGED
@@ -4,15 +4,5 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.AuthMethodPriority = void 0;
7
- /**
8
- * Authentication method priority
9
- * Higher number = higher priority
10
- */
11
- var AuthMethodPriority;
12
- (function (AuthMethodPriority) {
13
- AuthMethodPriority[AuthMethodPriority["SAP_DESTINATION"] = 4] = "SAP_DESTINATION";
14
- AuthMethodPriority[AuthMethodPriority["MCP_DESTINATION"] = 3] = "MCP_DESTINATION";
15
- AuthMethodPriority[AuthMethodPriority["DIRECT_JWT"] = 2] = "DIRECT_JWT";
16
- AuthMethodPriority[AuthMethodPriority["BASIC"] = 1] = "BASIC";
17
- AuthMethodPriority[AuthMethodPriority["NONE"] = 0] = "NONE"; // No valid authentication
18
- })(AuthMethodPriority || (exports.AuthMethodPriority = AuthMethodPriority = {}));
7
+ const interfaces_1 = require("@mcp-abap-adt/interfaces");
8
+ Object.defineProperty(exports, "AuthMethodPriority", { enumerable: true, get: function () { return interfaces_1.AuthMethodPriority; } });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/header-validator",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Header validator for MCP ABAP ADT - validates and prioritizes authentication headers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -44,6 +44,9 @@
44
44
  "engines": {
45
45
  "node": ">=18.0.0"
46
46
  },
47
+ "dependencies": {
48
+ "@mcp-abap-adt/interfaces": "^0.1.0"
49
+ },
47
50
  "devDependencies": {
48
51
  "@types/jest": "^30.0.0",
49
52
  "@types/node": "^24.2.1",