@samanhappy/mcphub 1.0.36 → 1.0.37

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 (84) hide show
  1. package/README.fr.md +47 -17
  2. package/README.md +44 -36
  3. package/README.zh.md +45 -35
  4. package/dist/controllers/groupController.js +63 -14
  5. package/dist/controllers/groupController.js.map +1 -1
  6. package/dist/controllers/serverController.js +18 -0
  7. package/dist/controllers/serverController.js.map +1 -1
  8. package/dist/dao/GroupDaoDbImpl.js +18 -0
  9. package/dist/dao/GroupDaoDbImpl.js.map +1 -1
  10. package/dist/db/connection.js +3 -2
  11. package/dist/db/connection.js.map +1 -1
  12. package/dist/db/entities/Group.js +9 -1
  13. package/dist/db/entities/Group.js.map +1 -1
  14. package/dist/db/groupSchema.js +29 -0
  15. package/dist/db/groupSchema.js.map +1 -0
  16. package/dist/routes/index.js +4 -3
  17. package/dist/routes/index.js.map +1 -1
  18. package/dist/services/groupService.js +57 -23
  19. package/dist/services/groupService.js.map +1 -1
  20. package/dist/services/logService.js +11 -2
  21. package/dist/services/logService.js.map +1 -1
  22. package/dist/services/mcpService.js +49 -12
  23. package/dist/services/mcpService.js.map +1 -1
  24. package/dist/services/oauthClientRegistration.js +22 -7
  25. package/dist/services/oauthClientRegistration.js.map +1 -1
  26. package/dist/services/oauthSettingsStore.js +3 -0
  27. package/dist/services/oauthSettingsStore.js.map +1 -1
  28. package/dist/services/sseService.js +63 -7
  29. package/dist/services/sseService.js.map +1 -1
  30. package/dist/utils/groupAccess.js +17 -0
  31. package/dist/utils/groupAccess.js.map +1 -0
  32. package/dist/utils/migration.js +2 -0
  33. package/dist/utils/migration.js.map +1 -1
  34. package/dist/utils/rateLimit.js +85 -18
  35. package/dist/utils/rateLimit.js.map +1 -1
  36. package/frontend/dist/assets/{ActivityPage-ZRx6b9hm.js → ActivityPage-Bx5-Jrxq.js} +2 -2
  37. package/frontend/dist/assets/{ActivityPage-ZRx6b9hm.js.map → ActivityPage-Bx5-Jrxq.js.map} +1 -1
  38. package/frontend/dist/assets/{CredentialsPage-C9vnh01H.js → CredentialsPage-BGbk_kBx.js} +2 -2
  39. package/frontend/dist/assets/{CredentialsPage-C9vnh01H.js.map → CredentialsPage-BGbk_kBx.js.map} +1 -1
  40. package/frontend/dist/assets/{Dashboard-BDGDQNz1.js → Dashboard-gHeXLPEn.js} +2 -2
  41. package/frontend/dist/assets/{Dashboard-BDGDQNz1.js.map → Dashboard-gHeXLPEn.js.map} +1 -1
  42. package/frontend/dist/assets/{EndpointCopy-BY51t0Ks.js → EndpointCopy-BRwX7Dpv.js} +2 -2
  43. package/frontend/dist/assets/{EndpointCopy-BY51t0Ks.js.map → EndpointCopy-BRwX7Dpv.js.map} +1 -1
  44. package/frontend/dist/assets/GroupsPage-grCAx55k.js +33 -0
  45. package/frontend/dist/assets/GroupsPage-grCAx55k.js.map +1 -0
  46. package/frontend/dist/assets/{LoginPage-CZOKlkR_.js → LoginPage-C7-m2jmB.js} +2 -2
  47. package/frontend/dist/assets/{LoginPage-CZOKlkR_.js.map → LoginPage-C7-m2jmB.js.map} +1 -1
  48. package/frontend/dist/assets/{LogsPage-FDWOOIPg.js → LogsPage-B00XTokA.js} +2 -2
  49. package/frontend/dist/assets/{LogsPage-FDWOOIPg.js.map → LogsPage-B00XTokA.js.map} +1 -1
  50. package/frontend/dist/assets/{MarketPage-DKfZgoIs.js → MarketPage-CVJMdSDD.js} +2 -2
  51. package/frontend/dist/assets/{MarketPage-DKfZgoIs.js.map → MarketPage-CVJMdSDD.js.map} +1 -1
  52. package/frontend/dist/assets/{OAuthConsentPage-Hk_DJ0dW.js → OAuthConsentPage-znW3HtpR.js} +2 -2
  53. package/frontend/dist/assets/{OAuthConsentPage-Hk_DJ0dW.js.map → OAuthConsentPage-znW3HtpR.js.map} +1 -1
  54. package/frontend/dist/assets/{PromptsPage-CT0C0_BI.js → PromptsPage-DGcNS3fO.js} +2 -2
  55. package/frontend/dist/assets/{PromptsPage-CT0C0_BI.js.map → PromptsPage-DGcNS3fO.js.map} +1 -1
  56. package/frontend/dist/assets/{ResourcesPage-CBgu-N5m.js → ResourcesPage-CfyZoadu.js} +2 -2
  57. package/frontend/dist/assets/{ResourcesPage-CBgu-N5m.js.map → ResourcesPage-CfyZoadu.js.map} +1 -1
  58. package/frontend/dist/assets/{ServersPage-CvcP8LFv.js → ServersPage-BlC0RdIu.js} +4 -4
  59. package/frontend/dist/assets/{ServersPage-CvcP8LFv.js.map → ServersPage-BlC0RdIu.js.map} +1 -1
  60. package/frontend/dist/assets/{SettingsPage-DDSVt_xk.js → SettingsPage-CiVNDyr_.js} +2 -2
  61. package/frontend/dist/assets/{SettingsPage-DDSVt_xk.js.map → SettingsPage-CiVNDyr_.js.map} +1 -1
  62. package/frontend/dist/assets/{StatusDot-uIIW4IuH.js → StatusDot-DYO56shE.js} +2 -2
  63. package/frontend/dist/assets/{StatusDot-uIIW4IuH.js.map → StatusDot-DYO56shE.js.map} +1 -1
  64. package/frontend/dist/assets/{ToggleGroup-BToroeZy.js → ToggleGroup-r8yGt_Fq.js} +2 -2
  65. package/frontend/dist/assets/{ToggleGroup-BToroeZy.js.map → ToggleGroup-r8yGt_Fq.js.map} +1 -1
  66. package/frontend/dist/assets/{UsersPage-BjgJjf6L.js → UsersPage-B3muXE90.js} +2 -2
  67. package/frontend/dist/assets/{UsersPage-BjgJjf6L.js.map → UsersPage-B3muXE90.js.map} +1 -1
  68. package/frontend/dist/assets/index-B-aT0HW5.js +3 -0
  69. package/frontend/dist/assets/index-B-aT0HW5.js.map +1 -0
  70. package/frontend/dist/assets/{resourceService-sD9dxx3E.js → resourceService-CSXOSi7M.js} +2 -2
  71. package/frontend/dist/assets/{resourceService-sD9dxx3E.js.map → resourceService-CSXOSi7M.js.map} +1 -1
  72. package/frontend/dist/assets/{useCostData-QoPbsqyD.js → useCostData-BOxUFQAH.js} +2 -2
  73. package/frontend/dist/assets/{useCostData-QoPbsqyD.js.map → useCostData-BOxUFQAH.js.map} +1 -1
  74. package/frontend/dist/assets/useSettingsData-DmpLlRI6.js +2 -0
  75. package/frontend/dist/assets/{useSettingsData-C4JrLqDh.js.map → useSettingsData-DmpLlRI6.js.map} +1 -1
  76. package/frontend/dist/assets/{variableDetection-iFAhGAKs.js → variableDetection-cmwwuSWB.js} +2 -2
  77. package/frontend/dist/assets/{variableDetection-iFAhGAKs.js.map → variableDetection-cmwwuSWB.js.map} +1 -1
  78. package/frontend/dist/index.html +1 -1
  79. package/package.json +1 -1
  80. package/frontend/dist/assets/GroupsPage-DyHyW7NW.js +0 -33
  81. package/frontend/dist/assets/GroupsPage-DyHyW7NW.js.map +0 -1
  82. package/frontend/dist/assets/index-DjNOC0Ov.js +0 -3
  83. package/frontend/dist/assets/index-DjNOC0Ov.js.map +0 -1
  84. package/frontend/dist/assets/useSettingsData-C4JrLqDh.js +0 -2
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { canAccessGroupRoute } from '../utils/groupAccess.js';
2
3
  import { PrincipalRuntimeService } from './principalRuntimeService.js';
3
4
  import { UserContextService } from './userContextService.js';
4
5
  import { deleteCredentialBindings, missingCredentialError } from './credentialBindingService.js';
@@ -1734,9 +1735,14 @@ export const getServersInfo = async (page, limit, user) => {
1734
1735
  const enabled = serverConfig ? serverConfig.enabled !== false : true;
1735
1736
  const resolvedType = inferServerType(serverConfig);
1736
1737
  const oauthConnected = Boolean(serverConfig?.oauth?.accessToken || serverConfig?.oauth?.refreshToken);
1737
- // Add enabled status and custom description to each tool
1738
+ // Add enabled status and custom description to each tool.
1739
+ // toggleTool stores under whatever key the caller passed (prefixed,
1740
+ // as the dashboard sends, or bare, as a direct API call may) with no
1741
+ // normalization — check both so the dashboard reflects a disable set
1742
+ // either way instead of always showing enabled:true for one of them.
1738
1743
  const toolsWithEnabled = tools.map((tool) => {
1739
- const toolConfig = serverConfig?.tools?.[tool.name];
1744
+ const bareToolName = normalizeToolNameForServer(name, tool.name);
1745
+ const toolConfig = serverConfig?.tools?.[tool.name] ?? serverConfig?.tools?.[bareToolName];
1740
1746
  return buildToolWithDescriptionMetadata(tool, toolConfig);
1741
1747
  });
1742
1748
  const promptsWithEnabled = prompts.map((prompt) => {
@@ -1890,7 +1896,11 @@ const filterToolsByConfig = async (serverName, tools) => {
1890
1896
  return tools;
1891
1897
  }
1892
1898
  return tools.filter((tool) => {
1893
- const toolConfig = serverConfig.tools?.[tool.name];
1899
+ // toggleTool stores the disable under whatever key string the caller used
1900
+ // (bare upstream name or server-prefixed name, never normalized) — check
1901
+ // both, same as findToolOnServer's execution-time gate.
1902
+ const bareToolName = normalizeToolNameForServer(serverName, tool.name);
1903
+ const toolConfig = serverConfig.tools?.[tool.name] ?? serverConfig.tools?.[bareToolName];
1894
1904
  // If tool is not in config, it's enabled by default
1895
1905
  return toolConfig?.enabled !== false;
1896
1906
  });
@@ -2482,8 +2492,36 @@ const resolveNameFromGroup = (name, serverName, serverConfig) => {
2482
2492
  return replacePrefixedServerName(name, getExposedServerName(serverName, serverConfig), serverName);
2483
2493
  };
2484
2494
  const findToolOnServer = (serverInfo, toolName, allowRawName) => {
2485
- return serverInfo.tools.find((tool) => tool.name === toolName ||
2495
+ const tool = serverInfo.tools.find((tool) => tool.name === toolName ||
2486
2496
  (allowRawName && normalizeToolNameForServer(serverInfo.name, tool.name) === toolName));
2497
+ if (!tool) {
2498
+ return undefined;
2499
+ }
2500
+ // A tool disabled via POST /api/servers/:server/tools/:tool/toggle must be
2501
+ // unresolvable for execution, not just hidden from tools/list — otherwise a
2502
+ // caller who already knows the tool name can invoke it regardless of the
2503
+ // disabled flag.
2504
+ //
2505
+ // The runtime tool cache (serverInfo.tools, built by normalizeToolForCache)
2506
+ // never carries an `enabled` field — that field only exists on the
2507
+ // *projection* built for the dashboard/API (buildToolWithDescriptionMetadata),
2508
+ // which is a different object. So the persisted per-tool config must be
2509
+ // consulted directly here.
2510
+ //
2511
+ // toggleTool (serverController.ts) stores `tools[toolName] = ...` using
2512
+ // whatever string the caller passed as :toolName, with no normalization.
2513
+ // The dashboard sends the already-prefixed `tool.name`, matching what
2514
+ // getServersInfo's toolsWithEnabled already reads — but any other caller
2515
+ // (REST API used directly, scripts, docs examples) can just as validly
2516
+ // pass the bare upstream name, which then never matches a prefixed-only
2517
+ // lookup. Check both so a disable takes effect regardless of which form
2518
+ // was used to set it.
2519
+ const bareToolName = normalizeToolNameForServer(serverInfo.name, tool.name);
2520
+ const toolConfig = serverInfo.config?.tools?.[tool.name] ?? serverInfo.config?.tools?.[bareToolName];
2521
+ if (toolConfig && toolConfig.enabled === false) {
2522
+ return undefined;
2523
+ }
2524
+ return tool;
2487
2525
  };
2488
2526
  const assertToolAvailableForRoute = (tool, appsRouteContext) => {
2489
2527
  if (isAppOnlyTool(tool) && !appsRouteContext.enabled) {
@@ -3593,13 +3631,8 @@ export const createMcpServer = (name, version, options) => {
3593
3631
  return server;
3594
3632
  };
3595
3633
  export const getFilteredServerInfosForGroup = async (group, options) => {
3596
- // Resolve group server configs. We look up the group directly from the DAO
3597
- // rather than going through getServerConfigsInGroup (which calls getAllGroups
3598
- // and applies filterData on groups). Groups don't carry a visibility field,
3599
- // so admin-owned groups would be filtered out for non-admin users even though
3600
- // bearer-key auth already authorized access and individual servers inside the
3601
- // group may be public. Server-level filterData below still enforces per-server
3602
- // visibility. Fix for #914.
3634
+ // Resolve raw groups so legacy routing remains compatible, then enforce explicit visibility.
3635
+ let matchedGroup = false;
3603
3636
  let serverConfigs = [];
3604
3637
  if (group) {
3605
3638
  const groupDao = getGroupDao();
@@ -3611,6 +3644,10 @@ export const getFilteredServerInfosForGroup = async (group, options) => {
3611
3644
  }
3612
3645
  }
3613
3646
  if (foundGroup) {
3647
+ matchedGroup = true;
3648
+ if (!canAccessGroupRoute(foundGroup)) {
3649
+ return { filteredServerInfos: [], serverConfigsByName: new Map() };
3650
+ }
3614
3651
  serverConfigs = normalizeGroupServers(foundGroup.servers || []);
3615
3652
  }
3616
3653
  }
@@ -3626,7 +3663,7 @@ export const getFilteredServerInfosForGroup = async (group, options) => {
3626
3663
  filteredServerInfos.push(serverInfo);
3627
3664
  continue;
3628
3665
  }
3629
- if (serverNamesInGroup.size === 0) {
3666
+ if (!matchedGroup) {
3630
3667
  if (serverInfo.name === group) {
3631
3668
  filteredServerInfos.push(serverInfo);
3632
3669
  }