fa-mcp-sdk 0.2.125 → 0.2.132

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 (58) hide show
  1. package/bin/fa-mcp.js +46 -12
  2. package/cli-template/config/_local.yaml +29 -11
  3. package/cli-template/config/custom-environment-variables.yaml +0 -6
  4. package/cli-template/config/default.yaml +34 -14
  5. package/cli-template/fa-mcp-sdk-spec.md +396 -189
  6. package/dist/core/_types_/config.d.ts +4 -17
  7. package/dist/core/_types_/config.d.ts.map +1 -1
  8. package/dist/core/_types_/types.d.ts +12 -15
  9. package/dist/core/_types_/types.d.ts.map +1 -1
  10. package/dist/core/auth/middleware.d.ts +9 -37
  11. package/dist/core/auth/middleware.d.ts.map +1 -1
  12. package/dist/core/auth/middleware.js +31 -146
  13. package/dist/core/auth/middleware.js.map +1 -1
  14. package/dist/core/auth/multi-auth.d.ts +10 -14
  15. package/dist/core/auth/multi-auth.d.ts.map +1 -1
  16. package/dist/core/auth/multi-auth.js +133 -220
  17. package/dist/core/auth/multi-auth.js.map +1 -1
  18. package/dist/core/auth/types.d.ts +1 -7
  19. package/dist/core/auth/types.d.ts.map +1 -1
  20. package/dist/core/auth/types.js +1 -10
  21. package/dist/core/auth/types.js.map +1 -1
  22. package/dist/core/bootstrap/init-config.d.ts.map +1 -1
  23. package/dist/core/bootstrap/init-config.js +4 -0
  24. package/dist/core/bootstrap/init-config.js.map +1 -1
  25. package/dist/core/index.d.ts +6 -6
  26. package/dist/core/index.d.ts.map +1 -1
  27. package/dist/core/index.js +5 -4
  28. package/dist/core/index.js.map +1 -1
  29. package/dist/core/utils/utils.d.ts +6 -0
  30. package/dist/core/utils/utils.d.ts.map +1 -1
  31. package/dist/core/utils/utils.js +25 -0
  32. package/dist/core/utils/utils.js.map +1 -1
  33. package/dist/core/web/server-http.d.ts.map +1 -1
  34. package/dist/core/web/server-http.js +32 -18
  35. package/dist/core/web/server-http.js.map +1 -1
  36. package/package.json +1 -1
  37. package/cli-template/src/_examples/custom-basic-auth-example.ts +0 -252
  38. package/cli-template/src/_examples/multi-auth-examples.ts +0 -333
  39. package/cli-template/src/_types_/common.d.ts +0 -27
  40. package/cli-template/src/api/router.ts +0 -35
  41. package/cli-template/src/api/swagger.ts +0 -167
  42. package/cli-template/src/asset/favicon.svg +0 -3
  43. package/cli-template/src/custom-resources.ts +0 -12
  44. package/cli-template/src/prompts/agent-brief.ts +0 -8
  45. package/cli-template/src/prompts/agent-prompt.ts +0 -10
  46. package/cli-template/src/prompts/custom-prompts.ts +0 -12
  47. package/cli-template/src/start.ts +0 -71
  48. package/cli-template/src/tools/handle-tool-call.ts +0 -55
  49. package/cli-template/src/tools/tools.ts +0 -88
  50. package/cli-template/tests/jest-simple-reporter.js +0 -10
  51. package/cli-template/tests/mcp/sse/mcp-sse-client-handling.md +0 -111
  52. package/cli-template/tests/mcp/sse/test-sse-npm-package.js +0 -96
  53. package/cli-template/tests/mcp/test-cases.js +0 -143
  54. package/cli-template/tests/mcp/test-http.js +0 -63
  55. package/cli-template/tests/mcp/test-sse.js +0 -67
  56. package/cli-template/tests/mcp/test-stdio.js +0 -78
  57. package/cli-template/tests/utils.ts +0 -154
  58. package/cli-template/yarn.lock +0 -6375
@@ -58,16 +58,36 @@ The primary function for starting your MCP server.
58
58
  **Example Usage in `src/start.ts`:**
59
59
 
60
60
  ```typescript
61
- import { initMcpServer, McpServerData, CustomBasicAuthValidator } from 'fa-mcp-sdk';
61
+ import { initMcpServer, McpServerData, CustomAuthValidator } from 'fa-mcp-sdk';
62
62
  import { tools } from './tools/tools.js';
63
63
  import { handleToolCall } from './tools/handle-tool-call.js';
64
64
  import { AGENT_BRIEF } from './prompts/agent-brief.js';
65
65
  import { AGENT_PROMPT } from './prompts/agent-prompt.js';
66
66
 
67
- // Optional: Custom Basic Authentication validator
68
- const customAuthValidator: CustomBasicAuthValidator = async (username: string, password: string): Promise<boolean> => {
69
- // Your custom authentication logic here (database, LDAP, API, etc.)
70
- return await authenticateUser(username, password);
67
+ // Optional: Custom Authentication validator (black box function)
68
+ const customAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
69
+ // Your custom authentication logic here - full request object available
70
+ // Can access headers, IP, user-agent, etc.
71
+ const authHeader = req.headers.authorization;
72
+ const userID = req.headers['x-user-id'];
73
+ const clientIP = req.headers['x-real-ip'] || req.connection?.remoteAddress;
74
+
75
+ // Implement any authentication logic (database, LDAP, API, custom rules, etc.)
76
+ const isAuthenticated = await authenticateRequest(req);
77
+
78
+ if (isAuthenticated) {
79
+ return {
80
+ success: true,
81
+ authType: 'basic',
82
+ tokenType: 'custom',
83
+ username: userID || 'unknown',
84
+ };
85
+ } else {
86
+ return {
87
+ success: false,
88
+ error: 'Custom authentication failed',
89
+ };
90
+ }
71
91
  };
72
92
 
73
93
  const serverData: McpServerData = {
@@ -76,8 +96,8 @@ const serverData: McpServerData = {
76
96
  agentBrief: AGENT_BRIEF,
77
97
  agentPrompt: AGENT_PROMPT,
78
98
 
79
- // Optional: Provide custom Basic Authentication
80
- customBasicAuthValidator: customAuthValidator,
99
+ // Optional: Provide custom authentication function
100
+ customAuthValidator: customAuthValidator,
81
101
 
82
102
  // ... other configuration
83
103
  };
@@ -95,7 +115,7 @@ Main configuration interface for your MCP server.
95
115
  interface McpServerData {
96
116
  // MCP Core Components
97
117
  tools: Tool[]; // Your tool definitions
98
- toolHandler: (params: { name: string; arguments?: any }) => Promise<any>; // Tool execution function
118
+ toolHandler: (params: { name: string; arguments?: any; headers?: Record<string, string> }) => Promise<any>; // Tool execution function
99
119
 
100
120
  // Agent Configuration
101
121
  agentBrief: string; // Brief description of your agent
@@ -107,7 +127,7 @@ interface McpServerData {
107
127
  customResources?: IResourceData[] | null; // Custom resource definitions
108
128
 
109
129
  // Authentication
110
- customBasicAuthValidator?: CustomBasicAuthValidator; // Custom Basic Authentication validator function
130
+ customAuthValidator?: CustomAuthValidator; // Custom authentication validator function
111
131
 
112
132
  // HTTP Server Components (for HTTP transport)
113
133
  httpComponents?: {
@@ -229,11 +249,19 @@ export const tools: Tool[] = [
229
249
  ```typescript
230
250
  import { formatToolResult, ToolExecutionError, logger } from 'fa-mcp-sdk';
231
251
 
232
- export const handleToolCall = async (params: { name: string, arguments?: any }): Promise<any> => {
233
- const { name, arguments: args } = params;
252
+ export const handleToolCall = async (params: { name: string, arguments?: any, headers?: Record<string, string> }): Promise<any> => {
253
+ const { name, arguments: args, headers } = params;
234
254
 
235
255
  logger.info(`Tool called: ${name}`);
236
256
 
257
+ // Access normalized HTTP headers (all header names are lowercase)
258
+ if (headers) {
259
+ const authHeader = headers.authorization;
260
+ const userAgent = headers['user-agent'];
261
+ const customHeader = headers['x-custom-header'];
262
+ logger.info(`Headers available: authorization=${!!authHeader}, user-agent=${userAgent}`);
263
+ }
264
+
237
265
  try {
238
266
  switch (name) {
239
267
  case 'my_custom_tool':
@@ -266,6 +294,88 @@ async function handleMyCustomTool(args: any): Promise<string> {
266
294
  }
267
295
  ```
268
296
 
297
+ #### HTTP Headers in Tool Handler
298
+
299
+ The FA-MCP-SDK automatically passes normalized HTTP headers to your `toolHandler` function, enabling context-aware tool execution based on client information.
300
+
301
+ **Key Features:**
302
+ - All headers are automatically normalized to lowercase
303
+ - Available in both HTTP and SSE transports (SSE provides empty headers object)
304
+ - Headers are sanitized and only string values are passed
305
+ - Array header values are joined with `', '` separator
306
+
307
+ **Example Usage:**
308
+
309
+ ```typescript
310
+ export const handleToolCall = async (params: {
311
+ name: string,
312
+ arguments?: any,
313
+ headers?: Record<string, string>
314
+ }): Promise<any> => {
315
+ const { name, arguments: args, headers } = params;
316
+
317
+ // Access client information via headers
318
+ if (headers) {
319
+ const authHeader = headers.authorization; // Lowercase normalized
320
+ const userAgent = headers['user-agent']; // Browser/client info
321
+ const clientIP = headers['x-real-ip'] || headers['x-forwarded-for']; // Proxy headers
322
+ const customData = headers['x-custom-header']; // Custom headers
323
+
324
+ logger.info(`Tool ${name} called by ${userAgent} from IP ${clientIP}`);
325
+
326
+ // Conditional logic based on client
327
+ if (userAgent?.includes('mobile')) {
328
+ return await handleMobileRequest(args);
329
+ }
330
+
331
+ // Custom authorization beyond standard auth
332
+ if (customData === 'admin-mode' && authHeader) {
333
+ return await handleAdminRequest(args);
334
+ }
335
+ }
336
+
337
+ // Regular tool logic
338
+ switch (name) {
339
+ case 'get_user_data':
340
+ // Use headers for audit logging
341
+ return await getUserData(args, {
342
+ clientIP: headers?.['x-real-ip'],
343
+ userAgent: headers?.['user-agent']
344
+ });
345
+ }
346
+ };
347
+ ```
348
+
349
+ **Header Normalization Details:**
350
+
351
+ ```typescript
352
+ // Original headers from client:
353
+ {
354
+ 'Authorization': 'Bearer token123',
355
+ 'X-Custom-Header': 'value',
356
+ 'USER-AGENT': 'MyClient/1.0'
357
+ }
358
+
359
+ // Normalized headers passed to toolHandler:
360
+ {
361
+ 'authorization': 'Bearer token123',
362
+ 'x-custom-header': 'value',
363
+ 'user-agent': 'MyClient/1.0'
364
+ }
365
+ ```
366
+
367
+ **Transport Differences:**
368
+
369
+ - **HTTP Transport**: Full headers available from Express request object
370
+ - **SSE Transport**: Headers preserved from initial SSE connection establishment (GET /sse request)
371
+
372
+ **Common Use Cases:**
373
+ - Client identification and analytics
374
+ - Custom authorization checks beyond standard authentication
375
+ - Request routing based on client capabilities
376
+ - Audit logging with client context
377
+ - Rate limiting per client type
378
+
269
379
  ### Configuration Management
270
380
 
271
381
  #### Using `appConfig`
@@ -395,25 +505,45 @@ webServer:
395
505
  port: {{port}}
396
506
  # array of hosts that CORS skips
397
507
  originHosts: ['localhost', '0.0.0.0']
508
+ # Authentication is configured here only when accessing the MCP server
509
+ # Authentication in services that enable tools, resources, and prompts
510
+ # is implemented more deeply. To do this, you need to use the information passed in HTTP headers
511
+ # You can also use a custom authorization function
398
512
  auth:
399
- enabled: false # Enables/disables token authorization
400
- # An array of fixed tokens that pass to the MCP (use only for MCPs with green data or for development)
401
- permanentServerTokens: []
513
+ enabled: false # Enables/disables authorization
514
+ # ========================================================================
515
+ # PERMANENT SERVER TOKENS
516
+ # Static tokens for server-to-server communication
517
+ # CPU cost: O(1) - fastest authentication method
518
+ #
519
+ # To enable this authentication, you need to set auth.enabled = true
520
+ # and set one token of at least 20 characters in length
521
+ # ========================================================================
522
+ permanentServerTokens: [ ] # Add your server tokens here: ['token1', 'token2']
523
+
524
+ # ========================================================================
525
+ # JWT TOKEN WITH SYMMETRIC ENCRYPTION
526
+ # Custom JWT tokens with AES-256 encryption
527
+ # CPU cost: Medium - decryption + JSON parsing
528
+ #
529
+ # To enable this authentication, you need to set auth.enabled = true and set
530
+ # encryptKey to at least 20 characters
531
+ # ========================================================================
402
532
  jwtToken:
403
- # Symmetric encryption key to generate a token for this MCP
533
+ # Symmetric encryption key to generate a token for this MCP (minimum 8 chars)
404
534
  encryptKey: '***'
405
535
  # If webServer.auth.enabled and the parameter true, the service name and the service specified in the token will be checked
406
536
  checkMCPName: true
407
- #basic:
408
- # username: '***'
409
- # password: '***'
410
- #oauth2:
411
- # type: 'oauth2';
412
- # clientId: '***'
413
- # clientSecret: '***'
414
- # redirectUri?: 'string'
415
- # tokenEndpoint?: string # For custom OAuth providers // VVR
416
- #pat: string;
537
+
538
+ # ========================================================================
539
+ # Basic Authentication - Base64 encoded username:password
540
+ # CPU cost: Medium - Base64 decoding + string comparison
541
+ # To enable this authentication, you need to set auth.enabled = true
542
+ # and set username and password to valid values
543
+ # ========================================================================
544
+ basic:
545
+ username: ''
546
+ password: '***'
417
547
  ```
418
548
 
419
549
  **`config/local.yaml`** - local overrides. Usually contains secrets.
@@ -739,8 +869,6 @@ addErrorMessage(originalError, 'Database operation failed');
739
869
 
740
870
  ```typescript
741
871
  import {
742
- authByToken,
743
- authTokenMW,
744
872
  ICheckTokenResult,
745
873
  checkToken,
746
874
  generateToken
@@ -788,27 +916,20 @@ const generateToken = (user: string, liveTimeSec: number, payload?: any): string
788
916
  // Example:
789
917
  const token = generateToken('john_doe', 3600, { role: 'admin' }); // 1 hour token
790
918
 
791
- // authByToken - Express route handler for token validation
792
- // Function Signature:
793
- const authByToken = (req: Request, res: Response): boolean {...}
919
+ // Deprecated: authByToken was replaced by createAuthMW universal middleware
920
+ // Use createAuthMW instead for all authentication scenarios:
794
921
 
795
- // Example:
796
- app.post('/api/secure', (req, res) => {
797
- if (!authByToken(req, res)) {
798
- return; // Response already sent with error
799
- }
800
- // User is authenticated, continue with request
801
- res.json({ message: 'Access granted' });
922
+ // Example - Modern approach:
923
+ app.post('/api/secure', createAuthMW(), (req, res) => {
924
+ // User is authenticated, authInfo available on req
925
+ const authInfo = (req as any).authInfo;
926
+ res.json({
927
+ message: 'Access granted',
928
+ authType: authInfo?.authType,
929
+ username: authInfo?.username
930
+ });
802
931
  });
803
932
 
804
- // authTokenMW - Express middleware for token authentication
805
- // Function Signature:
806
- const authTokenMW = (req: Request, res: Response, next: NextFunction): void {...}
807
-
808
- // Example:
809
- import express from 'express';
810
- const app = express();
811
- app.use('/protected', authTokenMW); // Apply to all /protected/* routes
812
933
  ```
813
934
 
814
935
  #### Token Generation
@@ -856,21 +977,20 @@ import {
856
977
  AuthType,
857
978
  AuthResult,
858
979
  AuthDetectionResult,
859
- CustomBasicAuthValidator,
980
+ CustomAuthValidator,
860
981
  checkMultiAuth,
982
+ checkCombinedAuth,
861
983
  detectAuthConfiguration,
862
984
  logAuthConfiguration,
863
- enhancedAuthTokenMW,
864
- createConfigurableAuthMiddleware,
865
- getAuthInfo,
866
- getMultiAuthError
985
+ createAuthMW, // Universal authentication middleware
986
+ getMultiAuthError, // Programmatic authentication checking
867
987
  } from 'fa-mcp-sdk';
868
988
 
869
989
  // Authentication types in CPU priority order (low to high cost)
870
- export type AuthType = 'permanentServerTokens' | 'pat' | 'basic' | 'jwtToken' | 'oauth2';
990
+ export type AuthType = 'permanentServerTokens' | 'basic' | 'jwtToken';
871
991
 
872
- // Custom Basic Authentication validator function
873
- export type CustomBasicAuthValidator = (username: string, password: string) => Promise<boolean> | boolean;
992
+ // Custom Authentication validator function (black box - receives full request)
993
+ export type CustomAuthValidator = (req: any) => Promise<AuthResult> | AuthResult;
874
994
 
875
995
  // Authentication result interface
876
996
  export interface AuthResult {
@@ -879,7 +999,6 @@ export interface AuthResult {
879
999
  authType?: AuthType;
880
1000
  tokenType?: string;
881
1001
  username?: string;
882
- accessToken?: string;
883
1002
  payload?: any;
884
1003
  }
885
1004
 
@@ -894,16 +1013,12 @@ export interface AuthDetectionResult {
894
1013
  ##### Core Multi-Authentication Functions
895
1014
 
896
1015
  ```typescript
897
- // checkMultiAuth - validate token using all configured authentication methods
1016
+ // checkMultiAuth - validate using all configured authentication methods
898
1017
  // Function Signature:
899
- async function checkMultiAuth(
900
- token: string,
901
- authConfig: AppConfig['webServer']['auth']
902
- ): Promise<AuthResult> {...}
1018
+ async function checkMultiAuth(req: Request): Promise<AuthResult> {...}
903
1019
 
904
1020
  // Example:
905
- const authConfig = appConfig.webServer.auth;
906
- const result = await checkMultiAuth('user_token', authConfig);
1021
+ const result = await checkMultiAuth(req);
907
1022
 
908
1023
  if (result.success) {
909
1024
  console.log(`Authenticated via ${result.authType} as ${result.username}`);
@@ -911,27 +1026,44 @@ if (result.success) {
911
1026
  console.log('Authentication failed:', result.error);
912
1027
  }
913
1028
 
1029
+ // checkCombinedAuth - validate using configured auth + custom validator
1030
+ // Function Signature:
1031
+ async function checkCombinedAuth( req: any ): Promise<AuthResult> {...}
1032
+
1033
+ // This is the enhanced function that:
1034
+ // 1. Runs standard MCP auth methods (if configured)
1035
+ // 2. Additionally runs custom validator (if configured)
1036
+ // 3. Can use custom validator as fallback if standard auth fails
1037
+
1038
+ // Example:
1039
+ const authResult = await checkCombinedAuth(req);
1040
+
1041
+ if (authResult.success) {
1042
+ console.log(`Authentication successful via ${authResult.authType}`);
1043
+ } else {
1044
+ console.log('Combined authentication failed:', authResult.error);
1045
+ }
1046
+
914
1047
  // detectAuthConfiguration - analyze auth configuration
915
1048
  // Function Signature:
916
- function detectAuthConfiguration(authConfig: AppConfig['webServer']['auth']): AuthDetectionResult {...}
1049
+ function detectAuthConfiguration(): AuthDetectionResult {...}
917
1050
 
918
1051
  // Example:
919
- const detection = detectAuthConfiguration(appConfig.webServer.auth);
1052
+ const detection = detectAuthConfiguration();
920
1053
  console.log('Configured auth types:', detection.configured);
921
1054
  console.log('Valid auth types:', detection.valid);
922
1055
  console.log('Configuration errors:', detection.errors);
923
1056
 
924
1057
  // logAuthConfiguration - log auth system status (debugging)
925
1058
  // Function Signature:
926
- function logAuthConfiguration(authConfig: AppConfig['webServer']['auth']): void {...}
1059
+ function logAuthConfiguration(): void {...}
927
1060
 
928
1061
  // Example:
929
- logAuthConfiguration(appConfig.webServer.auth);
1062
+ logAuthConfiguration();
930
1063
  // Output:
931
1064
  // Auth system configuration:
932
1065
  // - enabled: true
933
- // - configured types: permanentServerTokens, basic, pat
934
- // - valid types: permanentServerTokens, pat
1066
+ // - configured types: permanentServerTokens, basic
935
1067
  ```
936
1068
 
937
1069
  ##### Multi-Authentication Middleware
@@ -939,16 +1071,16 @@ logAuthConfiguration(appConfig.webServer.auth);
939
1071
  ```typescript
940
1072
  import express from 'express';
941
1073
  import {
942
- enhancedAuthTokenMW,
943
- createConfigurableAuthMiddleware,
1074
+ createAuthMW,
944
1075
  getMultiAuthError,
945
- getAuthInfo
946
1076
  } from 'fa-mcp-sdk';
947
1077
 
948
- // enhancedAuthTokenMW - automatic multi-auth middleware
949
- // Automatically detects if multi-auth is needed based on configuration
1078
+ // Universal authentication middleware with flexible options
950
1079
  const app = express();
951
- app.use('/api', enhancedAuthTokenMW);
1080
+
1081
+ // Basic usage - handles all authentication scenarios automatically
1082
+ const authMW = createAuthMW();
1083
+ app.use('/api', authMW);
952
1084
 
953
1085
  app.get('/api/protected', (req, res) => {
954
1086
  const authInfo = (req as any).authInfo;
@@ -960,24 +1092,34 @@ app.get('/api/protected', (req, res) => {
960
1092
  });
961
1093
  });
962
1094
 
963
- // createConfigurableAuthMiddleware - configurable middleware
964
- // Function Signature:
965
- function createConfigurableAuthMiddleware(options: {
966
- forceMultiAuth?: boolean;
967
- logConfiguration?: boolean;
968
- } = {}): (req: Request, res: Response, next: NextFunction) => void {...}
969
-
970
- // Example:
971
- const authMW = createConfigurableAuthMiddleware({
972
- logConfiguration: true, // Log auth config on first request
973
- forceMultiAuth: false // Auto-detect multi-auth need
1095
+ // Advanced usage with custom options
1096
+ const customAuthMW = createAuthMW({
1097
+ mcpPaths: ['/mcp', '/messages', '/sse', '/custom'], // Custom MCP paths
1098
+ logConfig: true, // Force logging
974
1099
  });
1100
+ app.use('/custom-endpoints', customAuthMW);
975
1101
 
976
- app.use('/api/v2', authMW);
977
-
978
- // getMultiAuthError - programmatic auth checking
1102
+ // createAuthMW - Universal authentication middleware
1103
+ // Function Signature:
1104
+ function createAuthMW(options?: {
1105
+ mcpPaths?: string[]; // Paths to check for public MCP requests (default: ['/mcp', '/messages', '/sse'])
1106
+ logConfig?: boolean; // Log auth configuration on first request (default: from LOG_AUTH_CONFIG env)
1107
+ }): (req: Request, res: Response, next: NextFunction) => Promise<void>
1108
+
1109
+ // Features:
1110
+ // ✅ Combines all authentication methods (standard + custom validator)
1111
+ // ✅ Supports public MCP resources/prompts (requireAuth: false)
1112
+ // ✅ Configurable MCP paths
1113
+ // ✅ CPU-optimized authentication order
1114
+ // ✅ Automatic auth method detection
1115
+ // ✅ Request context enrichment (req.authInfo)
1116
+
1117
+ // getMultiAuthError - Programmatic authentication checking
979
1118
  // Function Signature:
980
- async function getMultiAuthError(req: Request): Promise<{ code: number, message: string } | undefined> {...}
1119
+ async function getMultiAuthError(req: Request): Promise<{ code: number, message: string } | undefined>
1120
+
1121
+ // Returns error object if authentication failed, undefined if successful
1122
+ // Uses checkCombinedAuth internally - supports all authentication methods
981
1123
 
982
1124
  // Example - Custom middleware with different auth levels
983
1125
  app.use('/api/custom', async (req, res, next) => {
@@ -1007,90 +1149,174 @@ app.use('/api/custom', async (req, res, next) => {
1007
1149
  }
1008
1150
  });
1009
1151
 
1010
- // getAuthInfo - get current authentication configuration info
1011
- // Function Signature:
1012
- function getAuthInfo(): {
1013
- enabled: boolean;
1014
- configured: AuthType[];
1015
- valid: AuthType[];
1016
- errors: Record<string, string[]>;
1017
- usingMultiAuth: boolean;
1018
- } {...}
1019
-
1020
- // Example:
1021
- app.get('/auth/info', (req, res) => {
1022
- const authInfo = getAuthInfo();
1023
- res.json(authInfo);
1024
- });
1025
1152
  ```
1026
1153
 
1027
- ##### Custom Basic Authentication
1154
+ ##### Custom Authentication
1028
1155
 
1029
- You can provide custom Basic Authentication validation functions through the `McpServerData` interface:
1156
+ You can provide custom authentication validation functions through the `McpServerData` interface. The custom validator receives the full Express request object, allowing for flexible authentication logic:
1030
1157
 
1031
1158
  ```typescript
1032
- import { McpServerData, CustomBasicAuthValidator } from 'fa-mcp-sdk';
1159
+ import { McpServerData, CustomAuthValidator } from 'fa-mcp-sdk';
1033
1160
 
1034
- // Database-backed authentication
1035
- const databaseAuthValidator: CustomBasicAuthValidator = async (username: string, password: string): Promise<boolean> => {
1161
+ // Database-backed authentication with request context
1162
+ const databaseAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
1036
1163
  try {
1037
- const user = await getUserFromDatabase(username);
1038
- if (!user) return false;
1164
+ // Extract authentication data from various sources
1165
+ const authHeader = req.headers.authorization;
1166
+ const username = req.headers['x-username'];
1167
+ const apiKey = req.headers['x-api-key'];
1168
+
1169
+ if (authHeader?.startsWith('Basic ')) {
1170
+ const [user, pass] = Buffer.from(authHeader.slice(6), 'base64').toString().split(':');
1171
+ const dbUser = await getUserFromDatabase(user);
1172
+
1173
+ if (dbUser && await comparePassword(pass, dbUser.hashedPassword)) {
1174
+ return {
1175
+ success: true,
1176
+ authType: 'basic',
1177
+ tokenType: 'basic',
1178
+ username: dbUser.username,
1179
+ payload: { userId: dbUser.id, roles: dbUser.roles }
1180
+ };
1181
+ }
1182
+ }
1039
1183
 
1040
- return await comparePassword(password, user.hashedPassword);
1184
+ if (apiKey && username) {
1185
+ const isValid = await validateUserApiKey(username, apiKey);
1186
+ if (isValid) {
1187
+ return {
1188
+ success: true,
1189
+ authType: 'basic',
1190
+ tokenType: 'apiKey',
1191
+ username: username,
1192
+ payload: { apiKey: apiKey.substring(0, 8) + '...' }
1193
+ };
1194
+ }
1195
+ }
1196
+
1197
+ return { success: false, error: 'Invalid credentials' };
1041
1198
  } catch (error) {
1042
1199
  console.error('Database authentication error:', error);
1043
- return false;
1200
+ return { success: false, error: 'Database authentication error' };
1044
1201
  }
1045
1202
  };
1046
1203
 
1047
- // LDAP/Active Directory authentication
1048
- const ldapAuthValidator: CustomBasicAuthValidator = async (username: string, password: string): Promise<boolean> => {
1204
+ // IP-based authentication with time restrictions
1205
+ const ipBasedAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
1049
1206
  try {
1050
- const result = await authenticateWithLDAP(username, password);
1051
- return result.success;
1207
+ const clientIP = req.headers['x-real-ip'] || req.connection?.remoteAddress;
1208
+ const userAgent = req.headers['user-agent'];
1209
+
1210
+ // Check IP whitelist
1211
+ if (!isIPAllowed(clientIP)) {
1212
+ return { success: false, error: `IP address ${clientIP} not allowed` };
1213
+ }
1214
+
1215
+ // Block bots and crawlers
1216
+ if (userAgent?.includes('bot') || userAgent?.includes('crawler')) {
1217
+ return { success: false, error: 'Bots and crawlers are not allowed' };
1218
+ }
1219
+
1220
+ // Time-based restrictions (business hours only)
1221
+ const hour = new Date().getHours();
1222
+ if (hour < 9 || hour > 17) {
1223
+ return { success: false, error: 'Access only allowed during business hours (9-17)' };
1224
+ }
1225
+
1226
+ return {
1227
+ success: true,
1228
+ authType: 'basic',
1229
+ tokenType: 'ipBased',
1230
+ username: `ip-${clientIP}`,
1231
+ payload: { clientIP, userAgent, accessTime: new Date().toISOString() }
1232
+ };
1052
1233
  } catch (error) {
1053
- console.error('LDAP authentication error:', error);
1054
- return false;
1234
+ console.error('IP authentication error:', error);
1235
+ return { success: false, error: 'IP authentication error' };
1055
1236
  }
1056
1237
  };
1057
1238
 
1058
- // External API authentication
1059
- const apiAuthValidator: CustomBasicAuthValidator = async (username: string, password: string): Promise<boolean> => {
1239
+ // External service authentication
1240
+ const externalServiceAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
1060
1241
  try {
1242
+ const token = req.headers.authorization?.replace('Bearer ', '');
1243
+ const clientId = req.headers['x-client-id'];
1244
+
1245
+ if (!token || !clientId) {
1246
+ return { success: false, error: 'Missing token or client ID' };
1247
+ }
1248
+
1061
1249
  const response = await fetch('https://auth.example.com/validate', {
1062
1250
  method: 'POST',
1063
1251
  headers: { 'Content-Type': 'application/json' },
1064
- body: JSON.stringify({ username, password })
1252
+ body: JSON.stringify({ token, clientId, ip: req.ip })
1065
1253
  });
1066
1254
 
1067
- if (!response.ok) return false;
1255
+ if (!response.ok) {
1256
+ return { success: false, error: 'External service validation failed' };
1257
+ }
1258
+
1068
1259
  const result = await response.json();
1069
- return result.valid === true;
1260
+ if (result.valid === true) {
1261
+ return {
1262
+ success: true,
1263
+ authType: 'basic',
1264
+ tokenType: 'external',
1265
+ username: result.username || clientId,
1266
+ payload: {
1267
+ clientId,
1268
+ externalUserId: result.userId,
1269
+ scopes: result.scopes
1270
+ }
1271
+ };
1272
+ } else {
1273
+ return { success: false, error: result.error || 'Invalid token' };
1274
+ }
1070
1275
  } catch (error) {
1071
- console.error('API authentication error:', error);
1072
- return false;
1276
+ console.error('External service authentication error:', error);
1277
+ return { success: false, error: 'External service authentication error' };
1073
1278
  }
1074
1279
  };
1075
1280
 
1076
- // Multi-factor authentication
1077
- const mfaAuthValidator: CustomBasicAuthValidator = async (username: string, password: string): Promise<boolean> => {
1281
+ // Multi-factor authentication with context
1282
+ const mfaAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> => {
1078
1283
  try {
1079
- // Password format: "actualPassword:mfaToken"
1080
- const [actualPassword, mfaToken] = password.split(':');
1081
- if (!actualPassword || !mfaToken) return false;
1284
+ const authHeader = req.headers.authorization;
1285
+ const mfaToken = req.headers['x-mfa-token'];
1286
+ const userSession = req.headers['x-session-id'];
1287
+
1288
+ if (!authHeader?.startsWith('Basic ') || !mfaToken) {
1289
+ return { success: false, error: 'Missing basic auth or MFA token' };
1290
+ }
1291
+
1292
+ const [username, password] = Buffer.from(authHeader.slice(6), 'base64').toString().split(':');
1082
1293
 
1083
1294
  // Validate base credentials
1084
1295
  const user = await getUserFromDatabase(username);
1085
- if (!user || !(await comparePassword(actualPassword, user.hashedPassword))) {
1086
- return false;
1296
+ if (!user || !(await comparePassword(password, user.hashedPassword))) {
1297
+ return { success: false, error: 'Invalid username or password' };
1087
1298
  }
1088
1299
 
1089
- // Validate MFA token
1090
- return await validateMFAToken(username, mfaToken);
1300
+ // Validate MFA token and session
1301
+ const mfaValid = await validateMFAToken(username, mfaToken, userSession);
1302
+ if (mfaValid) {
1303
+ return {
1304
+ success: true,
1305
+ authType: 'basic',
1306
+ tokenType: 'mfa',
1307
+ username: username,
1308
+ payload: {
1309
+ userId: user.id,
1310
+ sessionId: userSession,
1311
+ mfaMethod: 'totp' // or whatever MFA method was used
1312
+ }
1313
+ };
1314
+ } else {
1315
+ return { success: false, error: 'Invalid MFA token or session' };
1316
+ }
1091
1317
  } catch (error) {
1092
1318
  console.error('MFA authentication error:', error);
1093
- return false;
1319
+ return { success: false, error: 'MFA authentication error' };
1094
1320
  }
1095
1321
  };
1096
1322
 
@@ -1101,8 +1327,8 @@ const serverData: McpServerData = {
1101
1327
  agentBrief: 'My MCP Server',
1102
1328
  agentPrompt: 'Server with custom authentication',
1103
1329
 
1104
- // Provide custom basic auth validator
1105
- customBasicAuthValidator: databaseAuthValidator, // or ldapAuthValidator, apiAuthValidator, mfaAuthValidator
1330
+ // Provide custom authentication validator (black box function)
1331
+ customAuthValidator: databaseAuthValidator, // or ipBasedAuthValidator, externalServiceAuthValidator, mfaAuthValidator
1106
1332
 
1107
1333
  // ... other configuration
1108
1334
  };
@@ -1110,46 +1336,6 @@ const serverData: McpServerData = {
1110
1336
  await initMcpServer(serverData);
1111
1337
  ```
1112
1338
 
1113
- ##### Authentication Configuration
1114
-
1115
- Multi-authentication is configured in `config/default.yaml`:
1116
-
1117
- ```yaml
1118
- webServer:
1119
- auth:
1120
- enabled: true
1121
-
1122
- # Permanent server tokens (CPU priority: 1 - fastest)
1123
- permanentServerTokens:
1124
- - 'server-token-1'
1125
- - 'server-token-2'
1126
-
1127
- # Personal Access Tokens (CPU priority: 2)
1128
- pat: 'ATATT3xFfGF0...'
1129
-
1130
- # Basic Authentication (CPU priority: 3)
1131
- basic:
1132
- type: 'basic'
1133
- username: 'admin'
1134
- password: 'password'
1135
- # Note: When using customBasicAuthValidator, username/password can be omitted
1136
-
1137
- # JWT Tokens (CPU priority: 4)
1138
- jwtToken:
1139
- encryptKey: 'your-secret-key'
1140
- checkMCPName: true
1141
-
1142
- # OAuth2 (CPU priority: 5 - most expensive)
1143
- oauth2:
1144
- type: 'oauth2'
1145
- clientId: 'your-client-id'
1146
- clientSecret: 'your-client-secret'
1147
- accessToken: 'your-access-token'
1148
- refreshToken: 'your-refresh-token'
1149
- redirectUri: 'https://example.com/callback'
1150
- tokenEndpoint: 'https://auth.provider.com/token'
1151
- ```
1152
-
1153
1339
  ##### Usage Examples
1154
1340
 
1155
1341
  ```typescript
@@ -1161,7 +1347,7 @@ app.post('/test-token', async (req, res) => {
1161
1347
  }
1162
1348
 
1163
1349
  try {
1164
- const result = await checkMultiAuth(token, appConfig.webServer.auth);
1350
+ const result = await checkMultiAuth(req);
1165
1351
  res.json({
1166
1352
  valid: result.success,
1167
1353
  authType: result.authType,
@@ -1176,11 +1362,29 @@ app.post('/test-token', async (req, res) => {
1176
1362
  });
1177
1363
 
1178
1364
  // Different authentication requirements for different endpoints
1179
- app.use('/rest', enhancedAuthTokenMW); // Any valid auth
1180
- app.use('/graphql', userLevelAuthOnly); // No server tokens
1181
- app.use('/websocket', sessionTokensOnly); // JWT/OAuth2 only
1365
+ app.use('/rest', createAuthMW()); // Standard auth with all methods
1366
+ app.use('/graphql', createAuthMW({ logConfig: false })); // Silent auth
1367
+ app.use('/websocket', createAuthMW({ mcpPaths: [] })); // No public MCP paths
1182
1368
  ```
1183
1369
 
1370
+ **Authentication Logic Flow:**
1371
+
1372
+ The enhanced authentication system follows this logic:
1373
+
1374
+ 1. **If configured auth methods exist** (permanentServerTokens, jwtToken, basic + auth.enabled = true):
1375
+ - Standard MCP authentication runs first
1376
+ - If successful AND custom validator exists → run custom validator additionally
1377
+ - Both must pass for authentication to succeed
1378
+
1379
+ 2. **If no standard auth OR standard auth fails:**
1380
+ - Custom validator runs as fallback (if configured)
1381
+ - Can authenticate using custom logic alone
1382
+
1383
+ 3. **Custom validator is completely independent:**
1384
+ - Receives full Express request object
1385
+ - Can implement any authentication/authorization logic
1386
+ - Works as black box as requested
1387
+
1184
1388
  **Client Usage Examples:**
1185
1389
 
1186
1390
  ```bash
@@ -1193,14 +1397,17 @@ curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhb..." http://localhost:3000/m
1193
1397
  # Using Basic Authentication
1194
1398
  curl -H "Authorization: Basic $(echo -n 'admin:password' | base64)" http://localhost:3000/mcp
1195
1399
 
1196
- # Using PAT
1197
- curl -H "Authorization: Bearer ATATT3xFfGF0..." http://localhost:3000/mcp
1198
-
1199
- # Using OAuth2
1200
- curl -H "Authorization: Bearer ya29.A0AfH6..." http://localhost:3000/mcp
1201
-
1202
- # Using MFA Basic Auth (if custom validator supports it)
1203
- curl -H "Authorization: Basic $(echo -n 'admin:password:123456' | base64)" http://localhost:3000/mcp
1400
+ # Using custom headers for custom validator
1401
+ curl -H "X-User-ID: john.doe" \
1402
+ -H "X-API-Key: custom-api-key-12345" \
1403
+ -H "X-Client-IP: 192.168.1.10" \
1404
+ http://localhost:3000/mcp
1405
+
1406
+ # Using custom authentication with context
1407
+ curl -H "Authorization: Bearer token123" \
1408
+ -H "X-MFA-Token: 123456" \
1409
+ -H "X-Session-ID: sess_abc123" \
1410
+ http://localhost:3000/mcp
1204
1411
  ```
1205
1412
 
1206
1413
  The multi-authentication system automatically tries authentication methods in CPU-optimized order (fastest first) and returns on the first successful match, providing both performance and flexibility.