fa-mcp-sdk 0.2.144 → 0.2.174

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 (153) hide show
  1. package/README.md +1 -1
  2. package/bin/fa-mcp.js +66 -54
  3. package/cli-template/.env.example +2 -2
  4. package/cli-template/README.md +2 -2
  5. package/cli-template/fa-mcp-sdk-spec.md +122 -41
  6. package/cli-template/package.json +3 -3
  7. package/cli-template/r/TEST HTTP.xml +9 -0
  8. package/cli-template/{run/TEST SSE.run.xml → r/TEST SSE.xml } +2 -2
  9. package/cli-template/{run/TEST STDIO.run.xml → r/TEST STDIO.xml } +2 -2
  10. package/cli-template/r/generate-token.xml +14 -0
  11. package/cli-template/{run/kill-server.run.xml → r/kill-server.xml} +2 -2
  12. package/cli-template/{run/kill-token-gen-server.xml → r/remove-nul.xml} +4 -5
  13. package/{cli-template/config → config}/_local.yaml +28 -14
  14. package/{cli-template/config → config}/custom-environment-variables.yaml +3 -0
  15. package/{cli-template/config → config}/default.yaml +50 -10
  16. package/{cli-template/config → config}/development.yaml +4 -4
  17. package/config/local.yaml +81 -0
  18. package/{cli-template/config → config}/production.yaml +4 -4
  19. package/dist/core/_types_/active-directory-config.d.ts +3 -0
  20. package/dist/core/_types_/active-directory-config.d.ts.map +1 -1
  21. package/dist/core/_types_/config.d.ts +5 -1
  22. package/dist/core/_types_/config.d.ts.map +1 -1
  23. package/dist/core/_types_/types.d.ts +5 -1
  24. package/dist/core/_types_/types.d.ts.map +1 -1
  25. package/dist/core/ad/group-checker.d.ts +13 -0
  26. package/dist/core/ad/group-checker.d.ts.map +1 -0
  27. package/dist/core/ad/group-checker.js +86 -0
  28. package/dist/core/ad/group-checker.js.map +1 -0
  29. package/dist/core/auth/admin-auth.d.ts +16 -0
  30. package/dist/core/auth/admin-auth.d.ts.map +1 -0
  31. package/dist/core/auth/admin-auth.js +159 -0
  32. package/dist/core/auth/admin-auth.js.map +1 -0
  33. package/dist/core/auth/basic.d.ts +6 -0
  34. package/dist/core/auth/basic.d.ts.map +1 -0
  35. package/dist/core/auth/basic.js +26 -0
  36. package/dist/core/auth/basic.js.map +1 -0
  37. package/dist/core/auth/{jwt-validation.d.ts → jwt.d.ts} +4 -3
  38. package/dist/core/auth/jwt.d.ts.map +1 -0
  39. package/dist/core/auth/{jwt-validation.js → jwt.js} +9 -19
  40. package/dist/core/auth/jwt.js.map +1 -0
  41. package/dist/core/auth/middleware.d.ts.map +1 -1
  42. package/dist/core/auth/middleware.js +3 -3
  43. package/dist/core/auth/middleware.js.map +1 -1
  44. package/dist/core/auth/multi-auth.d.ts +14 -6
  45. package/dist/core/auth/multi-auth.d.ts.map +1 -1
  46. package/dist/core/auth/multi-auth.js +151 -141
  47. package/dist/core/auth/multi-auth.js.map +1 -1
  48. package/dist/core/auth/permanent.d.ts +6 -0
  49. package/dist/core/auth/permanent.d.ts.map +1 -0
  50. package/dist/core/auth/permanent.js +15 -0
  51. package/dist/core/auth/permanent.js.map +1 -0
  52. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts +1 -1
  53. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.d.ts.map +1 -1
  54. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js +8 -10
  55. package/dist/core/auth/token-generator/ntlm/ntlm-domain-config.js.map +1 -1
  56. package/dist/core/auth/token-generator/ntlm/ntlm-integration.d.ts.map +1 -1
  57. package/dist/core/auth/token-generator/ntlm/ntlm-integration.js +9 -2
  58. package/dist/core/auth/token-generator/ntlm/ntlm-integration.js.map +1 -1
  59. package/dist/core/auth/token-generator/server.d.ts.map +1 -1
  60. package/dist/core/auth/token-generator/server.js +59 -25
  61. package/dist/core/auth/token-generator/server.js.map +1 -1
  62. package/dist/core/auth/types.d.ts +4 -3
  63. package/dist/core/auth/types.d.ts.map +1 -1
  64. package/dist/core/bootstrap/startup-info.d.ts.map +1 -1
  65. package/dist/core/bootstrap/startup-info.js +19 -0
  66. package/dist/core/bootstrap/startup-info.js.map +1 -1
  67. package/dist/core/consul/access-points-updater.js +1 -1
  68. package/dist/core/consul/access-points-updater.js.map +1 -1
  69. package/dist/core/consul/get-consul-api.d.ts +1 -1
  70. package/dist/core/consul/get-consul-api.d.ts.map +1 -1
  71. package/dist/core/consul/get-consul-api.js +1 -1
  72. package/dist/core/consul/get-consul-api.js.map +1 -1
  73. package/dist/core/consul/register.d.ts +1 -1
  74. package/dist/core/consul/register.d.ts.map +1 -1
  75. package/dist/core/index.d.ts +3 -1
  76. package/dist/core/index.d.ts.map +1 -1
  77. package/dist/core/index.js +3 -1
  78. package/dist/core/index.js.map +1 -1
  79. package/dist/core/init-mcp-server.d.ts.map +1 -1
  80. package/dist/core/init-mcp-server.js +1 -1
  81. package/dist/core/init-mcp-server.js.map +1 -1
  82. package/dist/core/utils/testing/McpSseClient.js.map +1 -1
  83. package/dist/core/web/admin-router.d.ts +10 -0
  84. package/dist/core/web/admin-router.d.ts.map +1 -0
  85. package/dist/core/web/admin-router.js +227 -0
  86. package/dist/core/web/admin-router.js.map +1 -0
  87. package/dist/core/web/favicon-svg.d.ts +1 -1
  88. package/dist/core/web/favicon-svg.d.ts.map +1 -1
  89. package/dist/core/web/favicon-svg.js +21 -3
  90. package/dist/core/web/favicon-svg.js.map +1 -1
  91. package/dist/core/web/home-api.d.ts +7 -0
  92. package/dist/core/web/home-api.d.ts.map +1 -0
  93. package/dist/core/web/home-api.js +93 -0
  94. package/dist/core/web/home-api.js.map +1 -0
  95. package/dist/core/web/server-http.d.ts +1 -0
  96. package/dist/core/web/server-http.d.ts.map +1 -1
  97. package/dist/core/web/server-http.js +60 -25
  98. package/dist/core/web/server-http.js.map +1 -1
  99. package/dist/core/web/static/home/index.html +206 -0
  100. package/dist/core/web/static/home/script.js +636 -0
  101. package/dist/core/web/{about-page/css.js → static/styles.css} +435 -105
  102. package/dist/core/web/static/token-gen/index.html +82 -0
  103. package/dist/core/web/static/token-gen/jwt-icon.svg +3 -0
  104. package/dist/core/web/static/token-gen/logout.svg +4 -0
  105. package/dist/core/web/static/token-gen/script.js +365 -0
  106. package/dist/core/web/static/token-gen/user.svg +4 -0
  107. package/dist/core/web/svg-icons.d.ts +7 -0
  108. package/dist/core/web/svg-icons.d.ts.map +1 -0
  109. package/dist/core/web/svg-icons.js +78 -0
  110. package/dist/core/web/svg-icons.js.map +1 -0
  111. package/package.json +7 -3
  112. package/scripts/copy-static.js +31 -0
  113. package/src/template/_examples/multi-auth-examples.ts +14 -47
  114. package/src/template/_types_/custom-config.ts +83 -0
  115. package/src/template/asset/logo.svg +4 -0
  116. package/src/template/start.ts +3 -3
  117. package/src/template/tools/handle-tool-call.ts +2 -1
  118. package/src/tests/mcp/test-http.js +10 -2
  119. package/src/tests/mcp/test-sse.js +10 -2
  120. package/src/tests/mcp/test-stdio.js +1 -2
  121. package/cli-template/run/TEST HTTP.run.xml +0 -5
  122. package/cli-template/run/TEST search.run.xml +0 -11
  123. package/cli-template/run/remove-nul.js.run.xml +0 -5
  124. package/dist/core/auth/jwt-validation.d.ts.map +0 -1
  125. package/dist/core/auth/jwt-validation.js.map +0 -1
  126. package/dist/core/auth/token-generator/html.d.ts +0 -9
  127. package/dist/core/auth/token-generator/html.d.ts.map +0 -1
  128. package/dist/core/auth/token-generator/html.js +0 -862
  129. package/dist/core/auth/token-generator/html.js.map +0 -1
  130. package/dist/core/web/about-page/css.d.ts +0 -2
  131. package/dist/core/web/about-page/css.d.ts.map +0 -1
  132. package/dist/core/web/about-page/css.js.map +0 -1
  133. package/dist/core/web/about-page/render.d.ts +0 -2
  134. package/dist/core/web/about-page/render.d.ts.map +0 -1
  135. package/dist/core/web/about-page/render.js +0 -773
  136. package/dist/core/web/about-page/render.js.map +0 -1
  137. /package/cli-template/{run/== START ==.run.xml → r/== START ==.xml} +0 -0
  138. /package/cli-template/{run/cb.run.xml → r/cb.xml} +0 -0
  139. /package/cli-template/{run/ci.run.xml → r/ci.xml} +0 -0
  140. /package/cli-template/{run/lint.run.xml → r/lint.xml} +0 -0
  141. /package/cli-template/{run/lint_fix.run.xml → r/lint_fix.xml} +0 -0
  142. /package/cli-template/{run/reinstall.run.xml → r/reinstall.xml} +0 -0
  143. /package/{cli-template/config → config}/test.yaml +0 -0
  144. /package/{src/template/asset/favicon.svg → dist/core/web/static/logo.svg} +0 -0
  145. /package/{cli-template/scripts → scripts}/kill-port.js +0 -0
  146. /package/{cli-template/scripts → scripts}/npm/patch_node_modules.js +0 -0
  147. /package/{cli-template/scripts → scripts}/npm/run.js +0 -0
  148. /package/{cli-template/scripts → scripts}/npm/yarn-ci.ps1 +0 -0
  149. /package/{cli-template/scripts → scripts}/npm/yarn-ci.sh +0 -0
  150. /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.ps1 +0 -0
  151. /package/{cli-template/scripts → scripts}/npm/yarn-reinstall.sh +0 -0
  152. /package/{cli-template/scripts → scripts}/pre-commit +0 -0
  153. /package/{cli-template/scripts → scripts}/remove-nul.js +0 -0
package/README.md CHANGED
@@ -174,7 +174,7 @@ npm start
174
174
 
175
175
  Provides endpoints:
176
176
 
177
- - `GET /` - About page with server info
177
+ - `GET /` - Home page with server info
178
178
  - `GET /health` - Health check
179
179
  - `GET /sse` - Server-Sent Events for MCP communication
180
180
  - `POST /mcp` - Direct MCP JSON-RPC endpoint
package/bin/fa-mcp.js CHANGED
@@ -46,6 +46,7 @@ const ALLOWED_FILES = [
46
46
  '__misc',
47
47
  '_tmp',
48
48
  '~last-cli-config.json',
49
+ 'yarn.lock',
49
50
  ];
50
51
 
51
52
  const getAsk = () => {
@@ -227,6 +228,11 @@ class MCPGenerator {
227
228
  defaultValue: '***',
228
229
  title: 'Token for registering service with Consul agent',
229
230
  },
231
+ {
232
+ name: 'consul.agent.reg.host',
233
+ defaultValue: '',
234
+ title: 'The host of the consul agent where the service will be registered',
235
+ },
230
236
  {
231
237
  name: 'consul.envCode.dev',
232
238
  defaultValue: '<envCode.dev>',
@@ -730,6 +736,9 @@ certificate's public and private keys`,
730
736
 
731
737
  async copyDirectory (source, target) {
732
738
  const entries = await fs.readdir(source, { withFileTypes: true });
739
+ if (!fss.existsSync(target)) {
740
+ await fs.mkdir(target, { recursive: true });
741
+ }
733
742
 
734
743
  for (const entry of entries) {
735
744
  if (entry.name === 'node_modules' || entry.name === 'dist') {
@@ -845,6 +854,10 @@ certificate's public and private keys`,
845
854
  content = content.replace(/http:\/\/localhost:9876/g, `http://localhost:${config.port}`);
846
855
  modified = true;
847
856
  }
857
+ if (filePath.endsWith('test-stdio.js')) {
858
+ content = content.replace('../dist/template/start.js', 'dist/src/start.js');
859
+ modified = true;
860
+ }
848
861
 
849
862
  if (modified) {
850
863
  await fs.writeFile(filePath, content, 'utf8');
@@ -854,40 +867,14 @@ certificate's public and private keys`,
854
867
  await this.transformTargetFile(config, '.env', (c) => c.replace(/^(NODE_CONSUL_ENV)=([^\r\n]*)/m, '#$1=$2'));
855
868
  }
856
869
  if (config['claude.isBypassPermissions'] === 'true') {
870
+ const c1 = ['sudo cp', 'sudo', 'bash', 'chmod', 'curl', 'dir', 'echo', 'git', 'find', 'grep', 'jest',
871
+ 'mkdir', 'node', 'npm install', 'npm run', 'npm test', 'npm', 'npx', 'pkill', 'set', 'playwright', 'powershell',
872
+ 'rm', 'taskkill', 'tasklist', 'timeout', 'turbo run', 'wc'];
873
+ const c2 = ['jobs', 'npm start', 'unset http_proxy'];
874
+ const i = ' '.repeat(8);
875
+ const allowBashLines = [...c1.map((c) => `${i}"Bash(${c}:*)",`), ...c2.map((c) => `${i}"Bash(${c})",`)].join('\n');
857
876
  const transformFn = (c) => c.replace('"acceptEdits"', '"bypassPermissions"')
858
- .replace(/"allow": \[\s+"Edit",/, `"allow": [
859
- "Bash(sudo cp:*)",
860
- "Bash(sudo:*)",
861
- "Bash(bash:*)",
862
- "Bash(chmod:*)",
863
- "Bash(curl:*)",
864
- "Bash(dir:*)",
865
- "Bash(echo:*)",
866
- "Bash(git:*)",
867
- "Bash(find:*)",
868
- "Bash(grep:*)",
869
- "Bash(jest:*)",
870
- "Bash(jobs)",
871
- "Bash(mkdir:*)",
872
- "Bash(node:*)",
873
- "Bash(npm install:*)",
874
- "Bash(npm run:*)",
875
- "Bash(npm start)",
876
- "Bash(npm test:*)",
877
- "Bash(npm:*)",
878
- "Bash(npx:*)",
879
- "Bash(pkill:*)",
880
- "Bash(set:*)",
881
- "Bash(playwright:*)",
882
- "Bash(powershell:*)",
883
- "Bash(rm:*)",
884
- "Bash(taskkill:*)",
885
- "Bash(tasklist:*)",
886
- "Bash(timeout:*)",
887
- "Bash(turbo run:*)",
888
- "Bash(unset http_proxy)",
889
- "Bash(wc:*)",
890
- "Edit",`);
877
+ .replace(/"allow": \[\s+"Edit",/, `"allow": [\n${allowBashLines}\n${i}"Edit",`);
891
878
  await this.transformTargetFile(config, '.claude/settings.json', transformFn);
892
879
  }
893
880
  }
@@ -897,12 +884,32 @@ certificate's public and private keys`,
897
884
  // Copy template files
898
885
  await this.copyDirectory(path.join(PROJ_ROOT, 'cli-template'), targetPath);
899
886
  await this.copyDirectory(path.join(PROJ_ROOT, 'src/template'), path.join(targetPath, 'src'));
900
- let testsTargetPath = path.join(targetPath, 'tests');
901
- await fs.mkdir(testsTargetPath, { recursive: true });
887
+
888
+ const testsTargetPath = path.join(targetPath, 'tests');
889
+
902
890
  await this.copyDirectory(path.join(PROJ_ROOT, 'src/tests'), testsTargetPath);
903
891
  await fs.copyFile(path.join(targetPath, '.env.example'), path.join(targetPath, '.env'));
904
892
  await fs.rename(path.join(targetPath, 'gitignore'), path.join(targetPath, '.gitignore'));
905
- await fs.rename(path.join(targetPath, 'run'), path.join(targetPath, '.run'));
893
+ await fs.rename(path.join(targetPath, 'r'), path.join(targetPath, '.run'));
894
+
895
+ await this.copyDirectory(path.join(PROJ_ROOT, 'config'), path.join(targetPath, 'config'));
896
+
897
+ const scriptsTargetPath = path.join(targetPath, 'scripts');
898
+ await this.copyDirectory(path.join(PROJ_ROOT, 'scripts'), scriptsTargetPath);
899
+ await fs.rm(path.join(targetPath, 'scripts/copy-static.js'), { force: true });
900
+
901
+ // Rename all .xml files in .run directory to .run.xml
902
+ const runDirPath = path.join(targetPath, '.run');
903
+ const files = await fs.readdir(runDirPath);
904
+
905
+ for (const file of files) {
906
+ if (file.endsWith('.xml')) {
907
+ const oldFilePath = path.join(runDirPath, file);
908
+ const newFileName = file.slice(0, -4) + '.run.xml';
909
+ const newFilePath = path.join(runDirPath, newFileName);
910
+ await fs.rename(oldFilePath, newFilePath);
911
+ }
912
+ }
906
913
 
907
914
  // Rename mcp-template.com.conf if mcp.domain is provided
908
915
  const mcpDomain = config['mcp.domain'];
@@ -920,15 +927,13 @@ certificate's public and private keys`,
920
927
  }
921
928
 
922
929
  // Read _local.yaml into memory and rename it to local.yaml
923
- let localYamlContent = '';
930
+ let localYamlExampleContent = '';
931
+ const localYamlExamplePath = path.join(targetPath, 'config', '_local.yaml');
932
+ const localYamlPath = path.join(targetPath, 'config', 'local.yaml');
924
933
  try {
925
- const localYamlPath = path.join(targetPath, 'config', '_local.yaml');
926
- const localYamlNewPath = path.join(targetPath, 'config', 'local.yaml');
927
934
 
928
- localYamlContent = await fs.readFile(localYamlPath, 'utf8');
929
- await fs.rename(localYamlPath, localYamlNewPath);
935
+ localYamlExampleContent = await fs.readFile(localYamlExamplePath, 'utf8');
930
936
  } catch (error) {
931
- // _local.yaml doesn't exist, which might be fine
932
937
  console.log('⚠️ Warning: Could not process config/_local.yaml file:', error.message);
933
938
  }
934
939
 
@@ -936,35 +941,40 @@ certificate's public and private keys`,
936
941
  await this.replaceTemplateParameters(config);
937
942
 
938
943
  // Replace template placeholders with defaultValue from optionalParams and save as _local.yaml
939
- if (localYamlContent) {
944
+ if (localYamlExampleContent) {
940
945
  try {
941
- let modifiedContent = localYamlContent;
942
-
946
+ let localYamlExampleModifiedContent = localYamlExampleContent;
947
+ let localYamlModifiedContent = localYamlExampleContent;
943
948
  // Replace with defaultValue from optionalParams
944
949
  for (const param of this.optionalParams) {
945
950
  const template = `{{${param.name}}}`;
946
- if (modifiedContent.includes(template)) {
951
+ if (localYamlExampleModifiedContent.includes(template)) {
947
952
  const defaultValue = param.defaultValue || '';
948
- modifiedContent = modifiedContent.replace(new RegExp(escapeRegExp(template), 'g'), defaultValue);
953
+ localYamlExampleModifiedContent = localYamlExampleModifiedContent.replace(new RegExp(escapeRegExp(template), 'g'), defaultValue);
949
954
  }
950
955
  }
956
+
951
957
  // Replacement of the remaining substitution places with what is in the config
952
958
  for (const [paramName, value] of Object.entries(config)) {
953
959
  const template = `{{${paramName}}}`;
954
- if (modifiedContent.includes(template)) {
955
- modifiedContent = modifiedContent.replace(new RegExp(escapeRegExp(template), 'g'), value);
960
+ if (localYamlExampleModifiedContent.includes(template)) {
961
+ localYamlExampleModifiedContent = localYamlExampleModifiedContent.replace(new RegExp(escapeRegExp(template), 'g'), value);
962
+ }
963
+ if (localYamlModifiedContent.includes(template)) {
964
+ localYamlModifiedContent = localYamlModifiedContent.replace(new RegExp(escapeRegExp(template), 'g'), value);
956
965
  }
957
966
  }
967
+ if (!config['consul.agent.reg.host']) {
968
+ localYamlModifiedContent = localYamlModifiedContent.replace(/(\n +)host: '[^']*'( # The host of the consul agent)/, '$1# host: \'\'$2');
969
+ }
958
970
 
959
- const newLocalYamlPath = path.join(targetPath, 'config', '_local.yaml');
960
- await fs.writeFile(newLocalYamlPath, modifiedContent, 'utf8');
971
+ await fs.writeFile(localYamlPath, localYamlModifiedContent, 'utf8');
972
+ await fs.writeFile(localYamlExamplePath, localYamlExampleModifiedContent, 'utf8');
961
973
  } catch (error) {
962
974
  console.log('⚠️ Warning: Could not create config/_local.yaml file:', error.message);
963
975
  }
964
976
  }
965
977
  const pathsToRemove = [
966
- { rel: 'node_modules' },
967
- { rel: 'yarn.lock' },
968
978
  { rel: 'package-lock.json' },
969
979
  ];
970
980
 
@@ -994,7 +1004,9 @@ certificate's public and private keys`,
994
1004
  process.exit(0);
995
1005
 
996
1006
  } catch (error) {
997
- console.error('\n❌ Error:', error.message);
1007
+ if (error.message && !(error.stack || '').includes(String(error.message))) {
1008
+ console.error('\n❌ Error:', error.message);
1009
+ }
998
1010
  console.error(error.stack);
999
1011
  process.exit(1);
1000
1012
  }
@@ -16,8 +16,8 @@ DEBUG=config-info
16
16
 
17
17
  ## DEBUG patterns ##
18
18
  # AP-UPDATER - consul/access-points-updater
19
- # af-consul:reg | af-consul:* - consul/cyclic-register
20
- # af-consul:curl - consul/prepare-consul-api
19
+ # fa-consul:reg | fa-consul:* - consul/cyclic-register
20
+ # fa-consul:curl - consul/prepare-consul-api
21
21
  # token:auth
22
22
 
23
23
  # The address of the mcp server for testing. Default: http://localhost:<config.webServer.port>
@@ -39,7 +39,7 @@ npm run test:mcp-simple # Simple test
39
39
 
40
40
  **HTTP Mode** (web integration):
41
41
  - HTTP server with Server-Sent Events (SSE)
42
- - About page with server status at `http://localhost:{{port}}/`
42
+ - Home page with server status at `http://localhost:{{port}}/`
43
43
  - Health check endpoint at `/health`
44
44
  - Direct JSON-RPC 2.0 endpoint at `/mcp`
45
45
 
@@ -97,7 +97,7 @@ Add to `claude_desktop_config.json`:
97
97
 
98
98
  ## HTTP Mode Endpoints
99
99
 
100
- - **/** - About page
100
+ - **/** - Home page
101
101
  - **/health** - Health check
102
102
  - **/sse** - Server-Sent Events
103
103
  - **/mcp** - JSON-RPC 2.0
@@ -79,7 +79,6 @@ const customAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult
79
79
  return {
80
80
  success: true,
81
81
  authType: 'basic',
82
- tokenType: 'custom',
83
82
  username: userID || 'unknown',
84
83
  };
85
84
  } else {
@@ -451,7 +450,7 @@ consul:
451
450
  description: <description> # <description> will be replaced by <package.json>.description at initialization
452
451
  tags: [] # If null or empty array - Will be pulled up from package.keywords at initialization
453
452
  meta:
454
- # "About" page link template
453
+ # "Home" page link template
455
454
  who: 'http://{address}:{port}/'
456
455
  envCode: # Used to generate the service ID
457
456
  prod: {{consul.envCode.prod}} # Production environment code
@@ -497,7 +496,7 @@ swagger:
497
496
  description: "PROD server"
498
497
 
499
498
  uiColor:
500
- # Font color of the header and a number of interface elements on the ABOUT page
499
+ # Font color of the header and a number of interface elements on the HOME page
501
500
  primary: '#0f65dc'
502
501
 
503
502
  webServer:
@@ -870,13 +869,12 @@ addErrorMessage(originalError, 'Database operation failed');
870
869
  ```typescript
871
870
  import {
872
871
  ICheckTokenResult,
873
- checkToken,
872
+ checkJwtToken,
874
873
  generateToken
875
874
  } from 'fa-mcp-sdk';
876
875
 
877
876
  // Types used:
878
877
  export interface ICheckTokenResult {
879
- inTokenType?: TTokenType // 'permanent' | 'JWT'
880
878
  payload?: ITokenPayload, // Token payload with user data
881
879
  errorReason?: string, // Error message if validation failed
882
880
  isTokenDecrypted?: boolean, // Whether token was successfully decrypted
@@ -888,16 +886,16 @@ export interface ITokenPayload {
888
886
  [key: string]: any, // Additional payload data
889
887
  }
890
888
 
891
- // checkToken - validate token and return detailed result
889
+ // checkJwtToken - validate token and return detailed result
892
890
  // Function Signature:
893
- const checkToken = (arg: {
891
+ const checkJwtToken = (arg: {
894
892
  token: string,
895
893
  expectedUser?: string,
896
894
  expectedService?: string,
897
895
  }): ICheckTokenResult {...}
898
896
 
899
897
  // Example:
900
- const tokenResult = checkToken({
898
+ const tokenResult = checkJwtToken({
901
899
  token: 'user_provided_token',
902
900
  expectedUser: 'john_doe',
903
901
  expectedService: 'my-mcp-server'
@@ -966,6 +964,86 @@ await generateTokenApp(); // Uses default configuration from appConfig
966
964
  // domainController: 'dc.domain.com'
967
965
  ```
968
966
 
967
+ #### Test Authentication Headers
968
+
969
+ ```typescript
970
+ import { getAuthHeadersForTests } from 'fa-mcp-sdk';
971
+
972
+ // getAuthHeadersForTests - automatically generate authentication headers for testing
973
+ // Function Signature:
974
+ function getAuthHeadersForTests(): object {...}
975
+
976
+ // Determines authentication headers based on appConfig.webServer.auth configuration.
977
+ // Returns Authorization header using the first valid auth method found.
978
+ //
979
+ // Priority order (CPU-optimized, fastest first):
980
+ // 1. permanentServerTokens - if at least one token is defined
981
+ // 2. basic auth - if username AND password are both set
982
+ // 3. JWT token - if jwtToken.encryptKey is set, generates token on the fly
983
+ //
984
+ // Returns empty object if auth is not enabled or no valid method configured.
985
+
986
+ // Examples:
987
+ const headers = getAuthHeadersForTests();
988
+
989
+ // Use in fetch requests
990
+ const response = await fetch('http://localhost:3000/mcp', {
991
+ method: 'POST',
992
+ headers: {
993
+ 'Content-Type': 'application/json',
994
+ ...headers // Automatically adds Authorization header if auth is enabled
995
+ },
996
+ body: JSON.stringify(requestBody)
997
+ });
998
+
999
+ // Use with test clients
1000
+ import { McpHttpClient } from 'fa-mcp-sdk';
1001
+
1002
+ const client = new McpHttpClient('http://localhost:3000');
1003
+ const authHeaders = getAuthHeadersForTests();
1004
+ const result = await client.callTool('my_tool', { query: 'test' }, authHeaders);
1005
+
1006
+ // Return value examples based on configuration:
1007
+
1008
+ // If permanentServerTokens configured:
1009
+ // { Authorization: 'Bearer server-token-1' }
1010
+
1011
+ // If basic auth configured:
1012
+ // { Authorization: 'Basic YWRtaW46cGFzc3dvcmQ=' } // base64 of 'admin:password'
1013
+
1014
+ // If JWT encryptKey configured:
1015
+ // { Authorization: 'Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9...' }
1016
+
1017
+ // If auth.enabled = false or no valid method:
1018
+ // {}
1019
+
1020
+ // Typical test setup:
1021
+ import { getAuthHeadersForTests, appConfig } from 'fa-mcp-sdk';
1022
+
1023
+ describe('MCP Server Tests', () => {
1024
+ const baseUrl = `http://localhost:${appConfig.webServer.port}`;
1025
+ const authHeaders = getAuthHeadersForTests();
1026
+
1027
+ it('should call tool with authentication', async () => {
1028
+ const response = await fetch(`${baseUrl}/mcp`, {
1029
+ method: 'POST',
1030
+ headers: {
1031
+ 'Content-Type': 'application/json',
1032
+ ...authHeaders
1033
+ },
1034
+ body: JSON.stringify({
1035
+ jsonrpc: '2.0',
1036
+ method: 'tools/call',
1037
+ params: { name: 'my_tool', arguments: { query: 'test' } },
1038
+ id: 1
1039
+ })
1040
+ });
1041
+
1042
+ expect(response.ok).toBe(true);
1043
+ });
1044
+ });
1045
+ ```
1046
+
969
1047
  #### Multi-Authentication System
970
1048
 
971
1049
  The FA-MCP-SDK supports a comprehensive multi-authentication system that allows multiple authentication methods to work together with CPU-optimized performance ordering.
@@ -979,7 +1057,6 @@ import {
979
1057
  AuthDetectionResult,
980
1058
  CustomAuthValidator,
981
1059
  checkMultiAuth,
982
- checkCombinedAuth,
983
1060
  detectAuthConfiguration,
984
1061
  logAuthConfiguration,
985
1062
  createAuthMW, // Universal authentication middleware
@@ -994,12 +1071,12 @@ export type CustomAuthValidator = (req: any) => Promise<AuthResult> | AuthResult
994
1071
 
995
1072
  // Authentication result interface
996
1073
  export interface AuthResult {
997
- success: boolean;
998
- error?: string;
999
- authType?: AuthType;
1000
- tokenType?: string;
1001
- username?: string;
1002
- payload?: any;
1074
+ success: boolean;
1075
+ error?: string;
1076
+ authType?: AuthType;
1077
+ username?: string;
1078
+ isTokenDecrypted?: boolean; // only for JWT
1079
+ payload?: any;
1003
1080
  }
1004
1081
 
1005
1082
  // Authentication detection result
@@ -1026,24 +1103,6 @@ if (result.success) {
1026
1103
  console.log('Authentication failed:', result.error);
1027
1104
  }
1028
1105
 
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
-
1047
1106
  // detectAuthConfiguration - analyze auth configuration
1048
1107
  // Function Signature:
1049
1108
  function detectAuthConfiguration(): AuthDetectionResult {...}
@@ -1088,7 +1147,6 @@ app.get('/api/protected', (req, res) => {
1088
1147
  message: 'Access granted',
1089
1148
  authType: authInfo?.authType,
1090
1149
  username: authInfo?.username,
1091
- tokenType: authInfo?.tokenType
1092
1150
  });
1093
1151
  });
1094
1152
 
@@ -1119,7 +1177,7 @@ function createAuthMW(options?: {
1119
1177
  async function getMultiAuthError(req: Request): Promise<{ code: number, message: string } | undefined>
1120
1178
 
1121
1179
  // Returns error object if authentication failed, undefined if successful
1122
- // Uses checkCombinedAuth internally - supports all authentication methods
1180
+ // Uses checkMultiAuth internally - supports all authentication methods
1123
1181
 
1124
1182
  // Example - Custom middleware with different auth levels
1125
1183
  app.use('/api/custom', async (req, res, next) => {
@@ -1174,7 +1232,6 @@ const databaseAuthValidator: CustomAuthValidator = async (req): Promise<AuthResu
1174
1232
  return {
1175
1233
  success: true,
1176
1234
  authType: 'basic',
1177
- tokenType: 'basic',
1178
1235
  username: dbUser.username,
1179
1236
  payload: { userId: dbUser.id, roles: dbUser.roles }
1180
1237
  };
@@ -1187,7 +1244,6 @@ const databaseAuthValidator: CustomAuthValidator = async (req): Promise<AuthResu
1187
1244
  return {
1188
1245
  success: true,
1189
1246
  authType: 'basic',
1190
- tokenType: 'apiKey',
1191
1247
  username: username,
1192
1248
  payload: { apiKey: apiKey.substring(0, 8) + '...' }
1193
1249
  };
@@ -1226,7 +1282,6 @@ const ipBasedAuthValidator: CustomAuthValidator = async (req): Promise<AuthResul
1226
1282
  return {
1227
1283
  success: true,
1228
1284
  authType: 'basic',
1229
- tokenType: 'ipBased',
1230
1285
  username: `ip-${clientIP}`,
1231
1286
  payload: { clientIP, userAgent, accessTime: new Date().toISOString() }
1232
1287
  };
@@ -1261,7 +1316,6 @@ const externalServiceAuthValidator: CustomAuthValidator = async (req): Promise<A
1261
1316
  return {
1262
1317
  success: true,
1263
1318
  authType: 'basic',
1264
- tokenType: 'external',
1265
1319
  username: result.username || clientId,
1266
1320
  payload: {
1267
1321
  clientId,
@@ -1303,7 +1357,6 @@ const mfaAuthValidator: CustomAuthValidator = async (req): Promise<AuthResult> =
1303
1357
  return {
1304
1358
  success: true,
1305
1359
  authType: 'basic',
1306
- tokenType: 'mfa',
1307
1360
  username: username,
1308
1361
  payload: {
1309
1362
  userId: user.id,
@@ -1351,7 +1404,6 @@ app.post('/test-token', async (req, res) => {
1351
1404
  res.json({
1352
1405
  valid: result.success,
1353
1406
  authType: result.authType,
1354
- tokenType: result.tokenType,
1355
1407
  error: result.error,
1356
1408
  username: result.username,
1357
1409
  hasPayload: !!result.payload
@@ -1412,6 +1464,35 @@ curl -H "Authorization: Bearer token123" \
1412
1464
 
1413
1465
  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.
1414
1466
 
1467
+ ### Check if a user belongs to an AD group
1468
+
1469
+ #### Configuration (`config/local.yaml`)
1470
+
1471
+ ```yaml
1472
+ ad:
1473
+ domains:
1474
+ MYDOMAIN:
1475
+ default: true
1476
+ controllers: ['ldap://dc1.corp.com']
1477
+ username: 'svc_account@corp.com'
1478
+ password: '***'
1479
+ # baseDn: 'DC=corp,DC=com' # Optional, auto-derived from controller URL
1480
+ ```
1481
+
1482
+ #### Usage
1483
+
1484
+ ```typescript
1485
+ import { initADGroupChecker } from 'fa-mcp-sdk';
1486
+
1487
+ const { isUserInGroup, groupChecker } = initADGroupChecker();
1488
+
1489
+ const isAdmin = await isUserInGroup('john.doe', 'Admins');
1490
+ const isDeveloper = await isUserInGroup('john.doe', 'Developers');
1491
+
1492
+ groupChecker.clearCache(); // Clear cache if needed
1493
+ ```
1494
+
1495
+
1415
1496
  ### Utility Functions
1416
1497
 
1417
1498
  #### General Utilities
@@ -13,15 +13,15 @@
13
13
  "build": "tsc",
14
14
  "clean": "rimraf dist",
15
15
  "cb": "npm run clean && npm run build",
16
- "ci": "node --no-deprecation ./scripts/npm/run.js",
17
- "reinstall": "node --no-deprecation ./scripts/npm/run.js reinstall",
16
+ "ci": "node --no-deprecation ../scripts/npm/run.js",
17
+ "reinstall": "node --no-deprecation ../scripts/npm/run.js reinstall",
18
18
  "typecheck": "tsc --noEmit",
19
19
  "lint": "eslint .",
20
20
  "lint:fix": "eslint --fix .",
21
21
  "generate-token": "node node_modules/fa-mcp-sdk/dist/core/auth/token-generator/server.js",
22
22
  "dead:exports": "ts-prune",
23
23
  "dead:files": "knip",
24
- "postinstall": "node scripts/npm/patch_node_modules.js",
24
+ "postinstall": "node ../scripts/npm/patch_node_modules.js",
25
25
  "consul:unreg": "node node_modules/fa-mcp-sdk/dist/core/consul/deregister.js",
26
26
  "test": "jest",
27
27
  "test:mcp": "node scripts/test-mcp-tools.js",
@@ -0,0 +1,9 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration
3
+ default="false"
4
+ name="TEST HTTP"
5
+ type="NodeJSConfigurationType"
6
+ path-to-js-file="tests/mcp/test-http.js" working-dir="$PROJECT_DIR$/">
7
+ <method v="2" />
8
+ </configuration>
9
+ </component>
@@ -1,5 +1,5 @@
1
1
  <component name="ProjectRunConfigurationManager">
2
- <configuration default="false" name="TEST SSE" type="NodeJSConfigurationType" path-to-js-file="src/tests/mcp/test-sse.js" working-dir="$PROJECT_DIR$/">
2
+ <configuration default="false" name="TEST SSE" type="NodeJSConfigurationType" path-to-js-file="tests/mcp/test-sse.js" working-dir="$PROJECT_DIR$/">
3
3
  <method v="2" />
4
4
  </configuration>
5
- </component>
5
+ </component>
@@ -1,5 +1,5 @@
1
1
  <component name="ProjectRunConfigurationManager">
2
- <configuration default="false" name="TEST STDIO" type="NodeJSConfigurationType" path-to-js-file="src/tests/mcp/test-stdio.js" working-dir="$PROJECT_DIR$">
2
+ <configuration default="false" name="TEST STDIO" type="NodeJSConfigurationType" path-to-js-file="tests/mcp/test-stdio.js" working-dir="$PROJECT_DIR$">
3
3
  <method v="2" />
4
4
  </configuration>
5
- </component>
5
+ </component>
@@ -0,0 +1,14 @@
1
+ <component name="ProjectRunConfigurationManager">
2
+ <configuration default="false" name="generate-token" type="js.build_tools.npm" nameIsGenerated="true">
3
+ <package-json value="$PROJECT_DIR$/package.json" />
4
+ <command value="run" />
5
+ <scripts>
6
+ <script value="generate-token" />
7
+ </scripts>
8
+ <node-interpreter value="project" />
9
+ <envs>
10
+ <env name="DEBUG" value="ntlm:auth-flow,ntlm:ldap-proxy,ntlm:ldap-proxy-id" />
11
+ </envs>
12
+ <method v="2" />
13
+ </configuration>
14
+ </component>
@@ -4,8 +4,8 @@
4
4
  name="kill-server"
5
5
  type="NodeJSConfigurationType"
6
6
  application-parameters="{{port}}"
7
- path-to-js-file="kill-port.js"
8
- working-dir="$PROJECT_DIR$/scripts"
7
+ path-to-js-file="scripts/kill-port.js"
8
+ working-dir="$PROJECT_DIR$"
9
9
  >
10
10
  <method v="2"/>
11
11
  </configuration>
@@ -1,12 +1,11 @@
1
1
  <component name="ProjectRunConfigurationManager">
2
2
  <configuration
3
3
  default="false"
4
- name="kill-token-gen-server"
4
+ name="remove-nul.js"
5
5
  type="NodeJSConfigurationType"
6
- application-parameters="3030"
7
- path-to-js-file="kill-port.js"
8
- working-dir="$PROJECT_DIR$/scripts"
9
- >
6
+ nameIsGenerated="true"
7
+ path-to-js-file="scripts/remove-nul.js"
8
+ working-dir="$PROJECT_DIR$">
10
9
  <method v="2"/>
11
10
  </configuration>
12
11
  </component>