@mcp-abap-adt/header-validator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,27 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.1.0] - 2025-11-30
9
+
10
+ ### Added
11
+ - **Header validation** - Validates authentication headers for MCP ABAP ADT servers
12
+ - **Priority system** - Automatically prioritizes authentication methods:
13
+ - Destination-based authentication (highest priority)
14
+ - Direct JWT authentication (medium priority)
15
+ - Basic authentication (lowest priority)
16
+ - **Error reporting** - Detailed error messages for invalid configurations
17
+ - **Warning system** - Warnings for conflicting or ignored headers
18
+ - **TypeScript support** - Full TypeScript definitions and type safety
19
+ - **Unit tests** - Comprehensive test suite with mocks (34 tests)
20
+ - **Documentation** - Complete README with examples and API reference
21
+
22
+ ### Technical Details
23
+ - **Dependencies**: None (zero dependencies)
24
+ - **Node.js version**: >= 18.0.0
25
+ - **Module system**: CommonJS
26
+ - **Build output**: TypeScript compiled to JavaScript with type definitions
27
+
package/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Oleksii Kyslytsia
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
package/README.md ADDED
@@ -0,0 +1,473 @@
1
+ # @mcp-abap-adt/header-validator
2
+
3
+ Header validator for MCP ABAP ADT - validates and prioritizes authentication headers.
4
+
5
+ ## Features
6
+
7
+ - ✅ **Header Validation**: Validates authentication headers for MCP ABAP ADT servers
8
+ - ✅ **Priority System**: Automatically prioritizes authentication methods
9
+ - ✅ **Error Reporting**: Detailed error messages and warnings
10
+ - ✅ **Type Safety**: Full TypeScript support with type definitions
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ npm install @mcp-abap-adt/header-validator
16
+ ```
17
+
18
+ ## Usage
19
+
20
+ ```typescript
21
+ import { validateAuthHeaders } from '@mcp-abap-adt/header-validator';
22
+ import { IncomingHttpHeaders } from 'http';
23
+
24
+ const headers: IncomingHttpHeaders = {
25
+ 'x-sap-url': 'https://test.sap.com',
26
+ 'x-mcp-destination': 'TRIAL',
27
+ // Note: x-sap-auth-type not needed - always uses JWT
28
+ };
29
+
30
+ const result = validateAuthHeaders(headers);
31
+
32
+ if (result.isValid && result.config) {
33
+ console.log('Auth method:', result.config.priority);
34
+ console.log('Destination:', result.config.destination);
35
+ } else {
36
+ console.error('Validation errors:', result.errors);
37
+ }
38
+ ```
39
+
40
+ ## Authentication Methods and Priorities
41
+
42
+ The validator supports four authentication methods, ordered by priority (highest to lowest):
43
+
44
+ ### 1. SAP Destination-Based Authentication (Highest Priority)
45
+
46
+ **Priority**: `AuthMethodPriority.SAP_DESTINATION` (4)
47
+
48
+ **Required Headers**:
49
+ - `x-sap-url` - SAP system URL
50
+ - `x-sap-destination` - Destination name (e.g., "S4HANA_E19")
51
+
52
+ **Optional Headers**:
53
+ - `x-sap-client` - SAP client number
54
+ - `x-sap-login` / `x-sap-password` - Username/password (edge cases)
55
+
56
+ **Description**: Simplest configuration - uses AuthBroker to manage tokens. Always uses JWT authentication. No `x-sap-auth-type` header needed.
57
+
58
+ **Example**:
59
+ ```typescript
60
+ const headers = {
61
+ 'x-sap-url': 'https://test.sap.com',
62
+ 'x-sap-destination': 'S4HANA_E19',
63
+ };
64
+ ```
65
+
66
+ **Notes**:
67
+ - Does NOT require `x-sap-auth-type` (always JWT)
68
+ - If `x-sap-auth-type` is provided, it will be ignored (warning issued)
69
+ - If `x-sap-jwt-token` is also provided, it will be ignored (warning issued)
70
+ - Requires AuthBroker to be initialized in the server
71
+ - Automatically handles token refresh and validation
72
+
73
+ ### 2. MCP Destination-Based Authentication
74
+
75
+ **Priority**: `AuthMethodPriority.MCP_DESTINATION` (3)
76
+
77
+ **Required Headers**:
78
+ - `x-sap-url` - SAP system URL
79
+ - `x-mcp-destination` - Destination name (e.g., "TRIAL", "PRODUCTION")
80
+
81
+ **Optional Headers**:
82
+ - `x-sap-client` - SAP client number
83
+
84
+ **Description**: Uses AuthBroker to manage tokens based on destination. Tokens are loaded from `{destination}.env` files, validated, and automatically refreshed when needed. Always uses JWT authentication.
85
+
86
+ **Example**:
87
+ ```typescript
88
+ const headers = {
89
+ 'x-sap-url': 'https://test.sap.com',
90
+ 'x-mcp-destination': 'TRIAL',
91
+ // Note: x-sap-auth-type not needed - always uses JWT
92
+ };
93
+ ```
94
+
95
+ **Notes**:
96
+ - Does NOT require `x-sap-auth-type` (always JWT)
97
+ - If `x-sap-auth-type` is provided, it will be ignored (warning issued)
98
+ - If `x-sap-jwt-token` is also provided, it will be ignored (warning issued)
99
+ - Requires AuthBroker to be initialized in the server
100
+ - Automatically handles token refresh and validation
101
+
102
+ ### 3. Direct JWT Authentication (Medium Priority)
103
+
104
+ **Priority**: `AuthMethodPriority.DIRECT_JWT` (2)
105
+
106
+ **Required Headers**:
107
+ - `x-sap-url` - SAP system URL
108
+ - `x-sap-auth-type` - Must be `jwt` or `xsuaa`
109
+ - `x-sap-jwt-token` - JWT access token
110
+
111
+ **Optional Headers**:
112
+ - `x-sap-refresh-token` - Refresh token for automatic token renewal
113
+ - `x-sap-uaa-url` / `uaa-url` - UAA URL
114
+ - `x-sap-uaa-client-id` / `uaa-client-id` - UAA Client ID
115
+ - `x-sap-uaa-client-secret` / `uaa-client-secret` - UAA Client Secret
116
+ - `x-sap-client` - SAP client number
117
+
118
+ **Description**: Direct JWT token authentication. Token is provided directly in headers.
119
+
120
+ **Example**:
121
+ ```typescript
122
+ const headers = {
123
+ 'x-sap-url': 'https://test.sap.com',
124
+ 'x-sap-auth-type': 'jwt',
125
+ 'x-sap-jwt-token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
126
+ 'x-sap-refresh-token': 'refresh_token_here', // optional
127
+ };
128
+ ```
129
+
130
+ **Notes**:
131
+ - Token must be at least 10 characters long
132
+ - Refresh token is optional but recommended for automatic token renewal
133
+
134
+ ### 4. Basic Authentication (Lowest Priority)
135
+
136
+ **Priority**: `AuthMethodPriority.BASIC` (1)
137
+
138
+ **Required Headers**:
139
+ - `x-sap-url` - SAP system URL
140
+ - `x-sap-auth-type` - Must be `basic`
141
+ - `x-sap-login` - Username
142
+ - `x-sap-password` - Password
143
+
144
+ **Description**: Basic HTTP authentication with username and password.
145
+
146
+ **Example**:
147
+ ```typescript
148
+ const headers = {
149
+ 'x-sap-url': 'https://test.sap.com',
150
+ 'x-sap-auth-type': 'basic',
151
+ 'x-sap-login': 'username',
152
+ 'x-sap-password': 'password',
153
+ };
154
+ ```
155
+
156
+ **Notes**:
157
+ - Used primarily for on-premise SAP systems
158
+ - Credentials are sent in plain text (use HTTPS in production)
159
+
160
+ ## Valid Header Combinations
161
+
162
+ ### ✅ Valid Combinations
163
+
164
+ #### 1. SAP Destination Only (Simplest - Recommended)
165
+ ```typescript
166
+ {
167
+ 'x-sap-url': 'https://test.sap.com',
168
+ 'x-sap-destination': 'S4HANA_E19',
169
+ }
170
+ // No x-sap-auth-type needed - always JWT
171
+ ```
172
+
173
+ #### 2. MCP Destination Only
174
+ ```typescript
175
+ {
176
+ 'x-sap-url': 'https://test.sap.com',
177
+ 'x-mcp-destination': 'TRIAL',
178
+ // Note: x-sap-auth-type not needed - always uses JWT
179
+ }
180
+ ```
181
+
182
+ #### 3. Direct JWT Only
183
+ ```typescript
184
+ {
185
+ 'x-sap-url': 'https://test.sap.com',
186
+ 'x-sap-auth-type': 'jwt',
187
+ 'x-sap-jwt-token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
188
+ 'x-sap-refresh-token': 'refresh_token', // optional
189
+ }
190
+ ```
191
+
192
+ #### 4. Basic Auth Only
193
+ ```typescript
194
+ {
195
+ 'x-sap-url': 'https://test.sap.com',
196
+ 'x-sap-auth-type': 'basic',
197
+ 'x-sap-login': 'username',
198
+ 'x-sap-password': 'password',
199
+ }
200
+ ```
201
+
202
+ #### 5. SAP Destination + Auth Type (Warning Issued)
203
+ ```typescript
204
+ {
205
+ 'x-sap-url': 'https://test.sap.com',
206
+ 'x-sap-destination': 'S4HANA_E19',
207
+ 'x-sap-auth-type': 'jwt', // ignored (warning)
208
+ }
209
+ ```
210
+ **Result**: SAP Destination auth is used, auth-type is ignored (warning issued)
211
+
212
+ #### 6. Destination + Direct JWT (Warning Issued)
213
+ ```typescript
214
+ {
215
+ 'x-sap-url': 'https://test.sap.com',
216
+ 'x-sap-destination': 'S4HANA_E19',
217
+ 'x-sap-jwt-token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...', // ignored
218
+ }
219
+ ```
220
+ **Result**: SAP Destination auth is used (Priority 4), direct JWT token is ignored (warning issued)
221
+
222
+ #### 7. Multiple Destinations (SAP Takes Priority)
223
+ ```typescript
224
+ {
225
+ 'x-sap-url': 'https://test.sap.com',
226
+ 'x-sap-destination': 'S4HANA_E19', // Priority 4
227
+ 'x-mcp-destination': 'TRIAL', // Priority 3 - ignored
228
+ }
229
+ ```
230
+ **Result**: SAP Destination auth is used, MCP destination is ignored
231
+
232
+ ### ❌ Invalid Combinations
233
+
234
+ #### 1. Missing Required Headers
235
+ ```typescript
236
+ {
237
+ 'x-sap-url': 'https://test.sap.com',
238
+ // Missing x-sap-auth-type
239
+ }
240
+ ```
241
+ **Error**: `x-sap-auth-type header is required`
242
+
243
+ #### 2. Invalid Auth Type
244
+ ```typescript
245
+ {
246
+ 'x-sap-url': 'https://test.sap.com',
247
+ 'x-sap-auth-type': 'invalid',
248
+ }
249
+ ```
250
+ **Error**: `x-sap-auth-type must be one of: jwt, xsuaa, basic`
251
+
252
+ #### 3. JWT Auth Without Token or Destination
253
+ ```typescript
254
+ {
255
+ 'x-sap-url': 'https://test.sap.com',
256
+ 'x-sap-auth-type': 'jwt',
257
+ // Missing x-sap-destination, x-mcp-destination, and x-sap-jwt-token
258
+ }
259
+ ```
260
+ **Error**: `JWT authentication requires either x-sap-destination, x-mcp-destination, or x-sap-jwt-token header`
261
+
262
+ #### 4. Basic Auth Without Credentials
263
+ ```typescript
264
+ {
265
+ 'x-sap-url': 'https://test.sap.com',
266
+ 'x-sap-auth-type': 'basic',
267
+ // Missing x-sap-login and/or x-sap-password
268
+ }
269
+ ```
270
+ **Error**: `Basic authentication requires x-sap-login and x-sap-password headers`
271
+
272
+ #### 5. Empty Values
273
+ ```typescript
274
+ {
275
+ 'x-sap-url': 'https://test.sap.com',
276
+ 'x-sap-destination': ' ', // empty after trim
277
+ }
278
+ ```
279
+ **Error**: `x-sap-destination header is empty`
280
+
281
+ #### 6. Invalid URL
282
+ ```typescript
283
+ {
284
+ 'x-sap-url': 'not-a-valid-url',
285
+ 'x-sap-auth-type': 'jwt',
286
+ 'x-sap-jwt-token': 'token',
287
+ }
288
+ ```
289
+ **Error**: `x-sap-url is not a valid URL`
290
+
291
+ ## Priority Resolution
292
+
293
+ When multiple authentication methods are detected, the validator automatically selects the highest priority method:
294
+
295
+ 1. **SAP Destination** (Priority 4) - Always selected if `x-sap-destination` is present
296
+ 2. **MCP Destination** (Priority 3) - Selected if `x-mcp-destination` is present (always uses JWT, no `x-sap-auth-type` needed)
297
+ 3. **Direct JWT** (Priority 2) - Selected if JWT token is provided (requires `x-sap-auth-type: jwt`)
298
+ 4. **Basic** (Priority 1) - Selected only if basic auth headers are present (requires `x-sap-auth-type: basic`)
299
+
300
+ ### Example: Priority Resolution
301
+
302
+ ```typescript
303
+ const headers = {
304
+ 'x-sap-url': 'https://test.sap.com',
305
+ 'x-sap-destination': 'S4HANA_E19', // Priority 4 (selected)
306
+ 'x-sap-auth-type': 'jwt', // Ignored (warning)
307
+ 'x-mcp-destination': 'TRIAL', // Priority 3 (ignored)
308
+ 'x-sap-jwt-token': 'token', // Priority 2 (ignored)
309
+ 'x-sap-login': 'user', // Priority 1 (ignored)
310
+ 'x-sap-password': 'pass', // Priority 1 (ignored)
311
+ };
312
+
313
+ const result = validateAuthHeaders(headers);
314
+ // result.config.priority === AuthMethodPriority.SAP_DESTINATION (4)
315
+ // result.warnings includes: "x-sap-auth-type is ignored when x-sap-destination is present"
316
+ // result.warnings includes: "x-sap-jwt-token is ignored when x-sap-destination is present"
317
+ ```
318
+
319
+ ## API Reference
320
+
321
+ ### `validateAuthHeaders(headers?: IncomingHttpHeaders): HeaderValidationResult`
322
+
323
+ Validates and prioritizes authentication headers.
324
+
325
+ **Parameters**:
326
+ - `headers` - HTTP headers object (optional)
327
+
328
+ **Returns**: `HeaderValidationResult` object with:
329
+ - `isValid: boolean` - Whether the configuration is valid
330
+ - `config?: ValidatedAuthConfig` - Validated authentication configuration (if valid)
331
+ - `errors: string[]` - List of validation errors
332
+ - `warnings: string[]` - List of warnings (e.g., ignored headers)
333
+
334
+ ### `ValidatedAuthConfig`
335
+
336
+ ```typescript
337
+ interface ValidatedAuthConfig {
338
+ priority: AuthMethodPriority; // Authentication method priority
339
+ authType: AuthType; // 'jwt' | 'xsuaa' | 'basic'
340
+ sapUrl: string; // SAP system URL
341
+ destination?: string; // Destination name (for destination-based auth)
342
+ jwtToken?: string; // JWT token (for direct JWT auth)
343
+ refreshToken?: string; // Refresh token (optional, for JWT auth)
344
+ username?: string; // Username (for basic auth)
345
+ password?: string; // Password (for basic auth)
346
+ errors: string[]; // Validation errors
347
+ warnings: string[]; // Warnings
348
+ }
349
+ ```
350
+
351
+ ### `AuthMethodPriority`
352
+
353
+ Enumeration of authentication method priorities:
354
+
355
+ ```typescript
356
+ enum AuthMethodPriority {
357
+ DESTINATION_BASED = 3, // Highest priority
358
+ DIRECT_JWT = 2, // Medium priority
359
+ BASIC = 1, // Lowest priority
360
+ NONE = 0 // Invalid/No auth
361
+ }
362
+ ```
363
+
364
+ ## Error Handling
365
+
366
+ The validator provides detailed error messages for common issues:
367
+
368
+ - Missing required headers
369
+ - Invalid header values
370
+ - Empty header values (after trimming whitespace)
371
+ - Invalid URL format
372
+ - Invalid authentication type
373
+ - Missing authentication credentials
374
+
375
+ All errors are collected in the `errors` array, and the validation result includes `isValid: false` if any errors are present.
376
+
377
+ ## Warnings
378
+
379
+ The validator issues warnings for:
380
+
381
+ - Conflicting headers (e.g., destination and direct JWT token both present)
382
+ - Multiple authentication methods with the same priority (should not happen in practice)
383
+
384
+ Warnings do not prevent validation from succeeding but indicate potential configuration issues.
385
+
386
+ ## Examples
387
+
388
+ ### Example 1: SAP Destination Auth (Simplest)
389
+
390
+ ```typescript
391
+ import { validateAuthHeaders } from '@mcp-abap-adt/header-validator';
392
+
393
+ const headers = {
394
+ 'x-sap-url': 'https://test.sap.com',
395
+ 'x-sap-destination': 'S4HANA_E19',
396
+ };
397
+
398
+ const result = validateAuthHeaders(headers);
399
+
400
+ if (result.isValid && result.config) {
401
+ console.log('Using SAP destination-based auth');
402
+ console.log('Destination:', result.config.destination);
403
+ console.log('Priority:', result.config.priority);
404
+ console.log('Auth Type:', result.config.authType); // Always 'jwt'
405
+ }
406
+ ```
407
+
408
+ ### Example 2: MCP Destination Auth
409
+
410
+ ```typescript
411
+ const headers = {
412
+ 'x-sap-url': 'https://test.sap.com',
413
+ 'x-mcp-destination': 'TRIAL',
414
+ // Note: x-sap-auth-type not needed - always uses JWT
415
+ };
416
+
417
+ const result = validateAuthHeaders(headers);
418
+
419
+ if (result.isValid && result.config) {
420
+ console.log('Using MCP destination-based auth');
421
+ console.log('Destination:', result.config.destination);
422
+ }
423
+ ```
424
+
425
+ ### Example 3: Direct JWT Auth
426
+
427
+ ```typescript
428
+ const headers = {
429
+ 'x-sap-url': 'https://test.sap.com',
430
+ 'x-sap-auth-type': 'jwt',
431
+ 'x-sap-jwt-token': 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...',
432
+ 'x-sap-refresh-token': 'refresh_token',
433
+ };
434
+
435
+ const result = validateAuthHeaders(headers);
436
+
437
+ if (result.isValid && result.config) {
438
+ console.log('Using direct JWT auth');
439
+ console.log('Token:', result.config.jwtToken);
440
+ }
441
+ ```
442
+
443
+ ### Example 4: Error Handling
444
+
445
+ ```typescript
446
+ const headers = {
447
+ 'x-sap-url': 'https://test.sap.com',
448
+ 'x-sap-auth-type': 'jwt',
449
+ // Missing required headers
450
+ };
451
+
452
+ const result = validateAuthHeaders(headers);
453
+
454
+ if (!result.isValid) {
455
+ console.error('Validation failed:');
456
+ result.errors.forEach(error => console.error(` - ${error}`));
457
+ }
458
+ ```
459
+
460
+ ## Documentation
461
+
462
+ Complete documentation is available in the [`docs/`](docs/) directory:
463
+
464
+ - **[Architecture](docs/architecture/ARCHITECTURE.md)** - System architecture, priority system, and valid header combinations
465
+ - **[Development](docs/development/DEVELOPMENT.md)** - Development guide and testing
466
+ - **[Usage](docs/using/USAGE.md)** - API reference and usage examples
467
+
468
+ See [docs/README.md](docs/README.md) for the complete documentation index.
469
+
470
+ ## License
471
+
472
+ MIT
473
+
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Header validator for MCP ABAP ADT authentication headers
3
+ *
4
+ * Validates and prioritizes authentication headers according to:
5
+ * 1. Destination-based auth (x-mcp-destination) - highest priority
6
+ * 2. Direct JWT token (x-sap-jwt-token) - medium priority
7
+ * 3. Basic auth (x-sap-login + x-sap-password) - lowest priority
8
+ */
9
+ import { IncomingHttpHeaders } from 'http';
10
+ import { HeaderValidationResult } from './types';
11
+ /**
12
+ * Validate and prioritize authentication headers
13
+ *
14
+ * @param headers HTTP headers
15
+ * @returns Validation result with prioritized authentication configuration
16
+ */
17
+ export declare function validateAuthHeaders(headers?: IncomingHttpHeaders): HeaderValidationResult;
@@ -0,0 +1,354 @@
1
+ "use strict";
2
+ /**
3
+ * Header validator for MCP ABAP ADT authentication headers
4
+ *
5
+ * Validates and prioritizes authentication headers according to:
6
+ * 1. Destination-based auth (x-mcp-destination) - highest priority
7
+ * 2. Direct JWT token (x-sap-jwt-token) - medium priority
8
+ * 3. Basic auth (x-sap-login + x-sap-password) - lowest priority
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.validateAuthHeaders = validateAuthHeaders;
12
+ const types_1 = require("./types");
13
+ /**
14
+ * Extract header value (handles array values)
15
+ */
16
+ function getHeaderValue(headers, name) {
17
+ const value = headers[name];
18
+ if (!value) {
19
+ return undefined;
20
+ }
21
+ if (Array.isArray(value)) {
22
+ return value[0]?.trim();
23
+ }
24
+ return String(value).trim();
25
+ }
26
+ /**
27
+ * Validate URL format
28
+ */
29
+ function isValidUrl(url) {
30
+ try {
31
+ const parsed = new URL(url);
32
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
33
+ }
34
+ catch {
35
+ return false;
36
+ }
37
+ }
38
+ /**
39
+ * Validate SAP destination-based authentication (highest priority)
40
+ * x-sap-destination - uses AuthBroker, JWT only, no auth-type needed
41
+ * URL is taken from destination (service key or .env), not from x-sap-url header
42
+ */
43
+ function validateSapDestinationAuth(headers) {
44
+ const destinationRaw = headers['x-sap-destination'];
45
+ if (!destinationRaw) {
46
+ return null;
47
+ }
48
+ const destination = getHeaderValue(headers, 'x-sap-destination');
49
+ const errors = [];
50
+ const warnings = [];
51
+ // Validate destination name (check if empty after trim)
52
+ if (!destination || destination.length === 0) {
53
+ errors.push('x-sap-destination header is empty');
54
+ return {
55
+ priority: types_1.AuthMethodPriority.NONE,
56
+ authType: 'jwt', // SAP destination always uses JWT
57
+ sapUrl: '', // URL will be loaded from destination
58
+ errors,
59
+ warnings,
60
+ };
61
+ }
62
+ // Extract optional SAP client
63
+ const sapClient = getHeaderValue(headers, 'x-sap-client');
64
+ // Optional: x-sap-login and x-sap-password (for cloud systems)
65
+ const username = getHeaderValue(headers, 'x-sap-login');
66
+ const password = getHeaderValue(headers, 'x-sap-password');
67
+ // Warning if x-sap-url is provided (URL comes from destination, not header)
68
+ const sapUrl = getHeaderValue(headers, 'x-sap-url');
69
+ if (sapUrl) {
70
+ warnings.push('x-sap-url is ignored when x-sap-destination is present (URL is loaded from destination service key or .env file)');
71
+ }
72
+ // Warning if direct JWT token is also provided (destination takes priority)
73
+ const jwtToken = getHeaderValue(headers, 'x-sap-jwt-token');
74
+ if (jwtToken) {
75
+ warnings.push('x-sap-jwt-token is ignored when x-sap-destination is present (destination-based auth takes priority)');
76
+ }
77
+ // Warning if auth-type is provided (not needed for x-sap-destination)
78
+ const authType = getHeaderValue(headers, 'x-sap-auth-type');
79
+ if (authType) {
80
+ warnings.push('x-sap-auth-type is ignored when x-sap-destination is present (always uses JWT)');
81
+ }
82
+ return {
83
+ priority: types_1.AuthMethodPriority.SAP_DESTINATION,
84
+ authType: 'jwt', // Always JWT for x-sap-destination
85
+ sapUrl: '', // URL will be loaded from destination (service key or .env)
86
+ sapClient,
87
+ destination,
88
+ username,
89
+ password,
90
+ errors,
91
+ warnings,
92
+ };
93
+ }
94
+ /**
95
+ * Validate MCP destination-based authentication (medium-high priority)
96
+ * x-mcp-destination - uses AuthBroker, always JWT (no x-sap-auth-type needed)
97
+ */
98
+ function validateMcpDestinationAuth(headers, sapUrl) {
99
+ const destinationRaw = headers['x-mcp-destination'];
100
+ if (!destinationRaw) {
101
+ return null;
102
+ }
103
+ const destination = getHeaderValue(headers, 'x-mcp-destination');
104
+ const errors = [];
105
+ const warnings = [];
106
+ // Validate destination name (check if empty after trim)
107
+ if (!destination || destination.length === 0) {
108
+ errors.push('x-mcp-destination header is empty');
109
+ return {
110
+ priority: types_1.AuthMethodPriority.NONE,
111
+ authType: 'jwt', // MCP destination always uses JWT
112
+ sapUrl,
113
+ errors,
114
+ warnings,
115
+ };
116
+ }
117
+ // Warning if x-sap-auth-type is provided (not needed for x-mcp-destination)
118
+ const authType = getHeaderValue(headers, 'x-sap-auth-type');
119
+ if (authType) {
120
+ warnings.push('x-sap-auth-type is ignored when x-mcp-destination is present (always uses JWT)');
121
+ }
122
+ // Extract optional SAP client
123
+ const sapClient = getHeaderValue(headers, 'x-sap-client');
124
+ // Warning if direct JWT token is also provided (destination takes priority)
125
+ const jwtToken = getHeaderValue(headers, 'x-sap-jwt-token');
126
+ if (jwtToken) {
127
+ warnings.push('x-sap-jwt-token is ignored when x-mcp-destination is present (destination-based auth takes priority)');
128
+ }
129
+ return {
130
+ priority: types_1.AuthMethodPriority.MCP_DESTINATION,
131
+ authType: 'jwt', // Always JWT for x-mcp-destination
132
+ sapUrl,
133
+ sapClient,
134
+ destination,
135
+ errors,
136
+ warnings,
137
+ };
138
+ }
139
+ /**
140
+ * Validate direct JWT authentication (medium priority)
141
+ */
142
+ function validateDirectJwtAuth(headers, sapUrl, authType) {
143
+ if (authType !== 'jwt' && authType !== 'xsuaa') {
144
+ return null;
145
+ }
146
+ const jwtToken = getHeaderValue(headers, 'x-sap-jwt-token');
147
+ if (!jwtToken) {
148
+ return null;
149
+ }
150
+ const errors = [];
151
+ const warnings = [];
152
+ // Validate JWT token format (basic check - should start with eyJ)
153
+ if (jwtToken.length < 10) {
154
+ errors.push('x-sap-jwt-token appears to be invalid (too short)');
155
+ }
156
+ // Extract optional tokens and UAA config
157
+ const refreshToken = getHeaderValue(headers, 'x-sap-refresh-token');
158
+ const uaaUrl = getHeaderValue(headers, 'x-sap-uaa-url') || getHeaderValue(headers, 'uaa-url');
159
+ const uaaClientId = getHeaderValue(headers, 'x-sap-uaa-client-id') || getHeaderValue(headers, 'uaa-client-id');
160
+ const uaaClientSecret = getHeaderValue(headers, 'x-sap-uaa-client-secret') || getHeaderValue(headers, 'uaa-client-secret');
161
+ // Extract optional SAP client
162
+ const sapClient = getHeaderValue(headers, 'x-sap-client');
163
+ return {
164
+ priority: types_1.AuthMethodPriority.DIRECT_JWT,
165
+ authType,
166
+ sapUrl,
167
+ sapClient,
168
+ jwtToken,
169
+ refreshToken,
170
+ uaaUrl,
171
+ uaaClientId,
172
+ uaaClientSecret,
173
+ errors,
174
+ warnings,
175
+ };
176
+ }
177
+ /**
178
+ * Validate basic authentication (lowest priority)
179
+ */
180
+ function validateBasicAuth(headers, sapUrl, authType) {
181
+ if (authType !== 'basic') {
182
+ return null;
183
+ }
184
+ const usernameRaw = headers['x-sap-login'];
185
+ const passwordRaw = headers['x-sap-password'];
186
+ if (!usernameRaw || !passwordRaw) {
187
+ return null;
188
+ }
189
+ const username = getHeaderValue(headers, 'x-sap-login');
190
+ const password = getHeaderValue(headers, 'x-sap-password');
191
+ const errors = [];
192
+ const warnings = [];
193
+ // Validate username and password (check if empty after trim)
194
+ if (!username || username.length === 0) {
195
+ errors.push('x-sap-login header is empty');
196
+ }
197
+ if (!password || password.length === 0) {
198
+ errors.push('x-sap-password header is empty');
199
+ }
200
+ // Return config with errors if validation failed
201
+ if (errors.length > 0) {
202
+ return {
203
+ priority: types_1.AuthMethodPriority.NONE,
204
+ authType,
205
+ sapUrl,
206
+ errors,
207
+ warnings,
208
+ };
209
+ }
210
+ return {
211
+ priority: types_1.AuthMethodPriority.BASIC,
212
+ authType,
213
+ sapUrl,
214
+ username,
215
+ password,
216
+ errors,
217
+ warnings,
218
+ };
219
+ }
220
+ /**
221
+ * Validate and prioritize authentication headers
222
+ *
223
+ * @param headers HTTP headers
224
+ * @returns Validation result with prioritized authentication configuration
225
+ */
226
+ function validateAuthHeaders(headers) {
227
+ // No headers provided
228
+ if (!headers) {
229
+ return {
230
+ isValid: false,
231
+ errors: ['No headers provided'],
232
+ warnings: [],
233
+ };
234
+ }
235
+ const errors = [];
236
+ const warnings = [];
237
+ // Check for SAP destination first (doesn't require x-sap-url)
238
+ const sapDestinationConfig = validateSapDestinationAuth(headers);
239
+ if (sapDestinationConfig) {
240
+ // SAP destination found - URL comes from destination, not header
241
+ if (sapDestinationConfig.errors.length === 0) {
242
+ return {
243
+ isValid: true,
244
+ config: sapDestinationConfig,
245
+ errors: [],
246
+ warnings: sapDestinationConfig.warnings,
247
+ };
248
+ }
249
+ else {
250
+ // Has errors, continue to check other methods or return error
251
+ return {
252
+ isValid: false,
253
+ errors: sapDestinationConfig.errors,
254
+ warnings: sapDestinationConfig.warnings,
255
+ };
256
+ }
257
+ }
258
+ // For other auth methods, x-sap-url is required
259
+ const sapUrl = getHeaderValue(headers, 'x-sap-url');
260
+ // Validate required headers
261
+ if (!sapUrl) {
262
+ errors.push('x-sap-url header is required when x-sap-destination is not present');
263
+ return {
264
+ isValid: false,
265
+ errors,
266
+ warnings,
267
+ };
268
+ }
269
+ // Validate URL format
270
+ if (!isValidUrl(sapUrl)) {
271
+ errors.push(`x-sap-url is not a valid URL: ${sapUrl}`);
272
+ return {
273
+ isValid: false,
274
+ errors,
275
+ warnings,
276
+ };
277
+ }
278
+ // Try to validate authentication methods in priority order
279
+ const configs = [];
280
+ // 2. MCP destination-based auth (medium-high priority) - doesn't require x-sap-auth-type
281
+ const mcpDestinationConfig = validateMcpDestinationAuth(headers, sapUrl);
282
+ if (mcpDestinationConfig) {
283
+ configs.push(mcpDestinationConfig);
284
+ }
285
+ // 3. Other auth methods require x-sap-auth-type
286
+ const sapAuthType = getHeaderValue(headers, 'x-sap-auth-type');
287
+ if (sapAuthType) {
288
+ // Validate auth type
289
+ const validAuthTypes = ['jwt', 'xsuaa', 'basic'];
290
+ const authType = sapAuthType.toLowerCase();
291
+ if (!validAuthTypes.includes(authType)) {
292
+ errors.push(`x-sap-auth-type must be one of: ${validAuthTypes.join(', ')}, got: ${sapAuthType}`);
293
+ }
294
+ else {
295
+ // Only validate direct JWT and basic auth if MCP destination is not present
296
+ // (MCP destination already handled above)
297
+ if (!mcpDestinationConfig) {
298
+ // 4. Direct JWT auth (medium priority)
299
+ const jwtConfig = validateDirectJwtAuth(headers, sapUrl, authType);
300
+ if (jwtConfig) {
301
+ configs.push(jwtConfig);
302
+ }
303
+ // 5. Basic auth (lowest priority)
304
+ const basicConfig = validateBasicAuth(headers, sapUrl, authType);
305
+ if (basicConfig) {
306
+ configs.push(basicConfig);
307
+ }
308
+ }
309
+ }
310
+ }
311
+ else {
312
+ // No auth-type provided - check if we have MCP destination or need to error
313
+ if (!mcpDestinationConfig && !sapDestinationConfig) {
314
+ errors.push('x-sap-auth-type header is required when x-sap-destination and x-mcp-destination are not present');
315
+ }
316
+ }
317
+ // No valid authentication method found
318
+ if (configs.length === 0) {
319
+ if (sapAuthType) {
320
+ const authType = sapAuthType.toLowerCase();
321
+ if (authType === 'jwt' || authType === 'xsuaa') {
322
+ errors.push('JWT authentication requires either x-sap-destination, x-mcp-destination, or x-sap-jwt-token header');
323
+ }
324
+ else if (authType === 'basic') {
325
+ errors.push('Basic authentication requires x-sap-login and x-sap-password headers');
326
+ }
327
+ }
328
+ else if (mcpDestinationConfig && mcpDestinationConfig.errors.length > 0) {
329
+ // MCP destination was found but has errors
330
+ errors.push(...mcpDestinationConfig.errors);
331
+ }
332
+ return {
333
+ isValid: false,
334
+ errors,
335
+ warnings,
336
+ };
337
+ }
338
+ // Select highest priority configuration
339
+ const selectedConfig = configs.reduce((prev, current) => current.priority > prev.priority ? current : prev);
340
+ // Check for conflicts (multiple auth methods with same priority shouldn't happen, but check anyway)
341
+ const samePriorityConfigs = configs.filter(c => c.priority === selectedConfig.priority);
342
+ if (samePriorityConfigs.length > 1) {
343
+ warnings.push(`Multiple authentication methods with same priority detected, using: ${types_1.AuthMethodPriority[selectedConfig.priority]}`);
344
+ }
345
+ // Merge errors and warnings
346
+ const allErrors = [...errors, ...selectedConfig.errors];
347
+ const allWarnings = [...warnings, ...selectedConfig.warnings];
348
+ return {
349
+ isValid: allErrors.length === 0,
350
+ config: selectedConfig,
351
+ errors: allErrors,
352
+ warnings: allWarnings,
353
+ };
354
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Header validator for MCP ABAP ADT
3
+ *
4
+ * Validates and prioritizes authentication headers for MCP ABAP ADT servers
5
+ */
6
+ export { validateAuthHeaders } from './headerValidator';
7
+ export * from './types';
package/dist/index.js ADDED
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * Header validator for MCP ABAP ADT
4
+ *
5
+ * Validates and prioritizes authentication headers for MCP ABAP ADT servers
6
+ */
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
19
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
20
+ };
21
+ Object.defineProperty(exports, "__esModule", { value: true });
22
+ exports.validateAuthHeaders = void 0;
23
+ var headerValidator_1 = require("./headerValidator");
24
+ Object.defineProperty(exports, "validateAuthHeaders", { enumerable: true, get: function () { return headerValidator_1.validateAuthHeaders; } });
25
+ __exportStar(require("./types"), exports);
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Types for header validator
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
+ }
package/dist/types.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ /**
3
+ * Types for header validator
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
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 = {}));
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@mcp-abap-adt/header-validator",
3
+ "version": "0.1.0",
4
+ "description": "Header validator for MCP ABAP ADT - validates and prioritizes authentication headers",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "README.md",
10
+ "CHANGELOG.md",
11
+ "LICENSE"
12
+ ],
13
+ "keywords": [
14
+ "abap",
15
+ "sap",
16
+ "adt",
17
+ "mcp",
18
+ "header",
19
+ "validator",
20
+ "authentication",
21
+ "abap-adt"
22
+ ],
23
+ "author": "Oleksii Kyslytsia <oleksij.kyslytsja@gmail.com>",
24
+ "license": "MIT",
25
+ "homepage": "https://github.com/fr0ster/mcp-abap-adt-header-validator#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/fr0ster/mcp-abap-adt-header-validator/issues"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "git+https://github.com/fr0ster/mcp-abap-adt-header-validator.git"
32
+ },
33
+ "publishConfig": {
34
+ "access": "public"
35
+ },
36
+ "scripts": {
37
+ "clean": "rm -rf dist tsconfig.tsbuildinfo",
38
+ "build": "npm run clean --silent && npx tsc -p tsconfig.json",
39
+ "build:fast": "npx tsc -p tsconfig.json",
40
+ "test": "jest",
41
+ "test:check": "npx tsc --noEmit",
42
+ "prepublishOnly": "npm run build"
43
+ },
44
+ "engines": {
45
+ "node": ">=18.0.0"
46
+ },
47
+ "devDependencies": {
48
+ "@types/jest": "^30.0.0",
49
+ "@types/node": "^24.2.1",
50
+ "jest": "^30.2.0",
51
+ "ts-jest": "^29.2.5",
52
+ "typescript": "^5.9.2"
53
+ }
54
+ }