@samanhappy/mcphub 1.1.13 → 1.1.15

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 (228) hide show
  1. package/README.fr.md +1 -1
  2. package/README.md +1 -1
  3. package/README.zh.md +1 -1
  4. package/dist/betterAuth.js +3 -3
  5. package/dist/betterAuth.js.map +1 -1
  6. package/dist/cli/commands/keys.js +22 -3
  7. package/dist/cli/commands/keys.js.map +1 -1
  8. package/dist/clients/mcpSdkClient.js +2667 -1448
  9. package/dist/clients/mcpSdkClient.js.map +4 -4
  10. package/dist/config/index.js +1 -1
  11. package/dist/config/index.js.map +1 -1
  12. package/dist/controllers/bearerKeyController.js +49 -3
  13. package/dist/controllers/bearerKeyController.js.map +1 -1
  14. package/dist/controllers/configController.js +1 -0
  15. package/dist/controllers/configController.js.map +1 -1
  16. package/dist/controllers/credentialBindingController.js +36 -12
  17. package/dist/controllers/credentialBindingController.js.map +1 -1
  18. package/dist/controllers/groupController.js +12 -5
  19. package/dist/controllers/groupController.js.map +1 -1
  20. package/dist/controllers/oauthRelayController.js +65 -0
  21. package/dist/controllers/oauthRelayController.js.map +1 -0
  22. package/dist/controllers/personalOAuthController.js +10 -1
  23. package/dist/controllers/personalOAuthController.js.map +1 -1
  24. package/dist/controllers/serverController.js +52 -18
  25. package/dist/controllers/serverController.js.map +1 -1
  26. package/dist/controllers/userController.js +11 -1
  27. package/dist/controllers/userController.js.map +1 -1
  28. package/dist/dao/ActivityDao.js +2 -0
  29. package/dist/dao/ActivityDao.js.map +1 -1
  30. package/dist/dao/BearerKeyDao.js +10 -5
  31. package/dist/dao/BearerKeyDao.js.map +1 -1
  32. package/dist/dao/BearerKeyDaoDbImpl.js +16 -5
  33. package/dist/dao/BearerKeyDaoDbImpl.js.map +1 -1
  34. package/dist/dao/GroupDao.js +24 -0
  35. package/dist/dao/GroupDao.js.map +1 -1
  36. package/dist/dao/GroupDaoDbImpl.js +34 -0
  37. package/dist/dao/GroupDaoDbImpl.js.map +1 -1
  38. package/dist/dao/ServerDao.js +10 -1
  39. package/dist/dao/ServerDao.js.map +1 -1
  40. package/dist/dao/ServerDaoDbImpl.js +4 -0
  41. package/dist/dao/ServerDaoDbImpl.js.map +1 -1
  42. package/dist/dao/SystemConfigDao.js +5 -2
  43. package/dist/dao/SystemConfigDao.js.map +1 -1
  44. package/dist/dao/SystemConfigDaoDbImpl.js +11 -0
  45. package/dist/dao/SystemConfigDaoDbImpl.js.map +1 -1
  46. package/dist/dao/UserDaoDbImpl.js +4 -0
  47. package/dist/dao/UserDaoDbImpl.js.map +1 -1
  48. package/dist/dao/base/JsonFileBaseDao.js +17 -2
  49. package/dist/dao/base/JsonFileBaseDao.js.map +1 -1
  50. package/dist/db/entities/Activity.js +4 -0
  51. package/dist/db/entities/Activity.js.map +1 -1
  52. package/dist/db/entities/BearerKey.js +8 -0
  53. package/dist/db/entities/BearerKey.js.map +1 -1
  54. package/dist/db/entities/Group.js +4 -0
  55. package/dist/db/entities/Group.js.map +1 -1
  56. package/dist/db/entities/Server.js +4 -0
  57. package/dist/db/entities/Server.js.map +1 -1
  58. package/dist/db/entities/SystemConfig.js +8 -0
  59. package/dist/db/entities/SystemConfig.js.map +1 -1
  60. package/dist/db/entities/User.js +4 -0
  61. package/dist/db/entities/User.js.map +1 -1
  62. package/dist/db/repositories/GroupRepository.js +36 -0
  63. package/dist/db/repositories/GroupRepository.js.map +1 -1
  64. package/dist/db/repositories/ServerRepository.js +1 -0
  65. package/dist/db/repositories/ServerRepository.js.map +1 -1
  66. package/dist/db/repositories/VectorEmbeddingRepository.js +11 -1
  67. package/dist/db/repositories/VectorEmbeddingRepository.js.map +1 -1
  68. package/dist/middlewares/auth.js +7 -1
  69. package/dist/middlewares/auth.js.map +1 -1
  70. package/dist/routes/index.js +6 -1
  71. package/dist/routes/index.js.map +1 -1
  72. package/dist/server.js +2 -0
  73. package/dist/server.js.map +1 -1
  74. package/dist/services/activityLoggingService.js +1 -0
  75. package/dist/services/activityLoggingService.js.map +1 -1
  76. package/dist/services/authorizationService.js.map +1 -1
  77. package/dist/services/capabilitySnapshotService.js +2 -1
  78. package/dist/services/capabilitySnapshotService.js.map +1 -1
  79. package/dist/services/contextCostService.js +2 -2
  80. package/dist/services/contextCostService.js.map +1 -1
  81. package/dist/services/credentialBindingService.js +9 -1
  82. package/dist/services/credentialBindingService.js.map +1 -1
  83. package/dist/services/dataService.js +4 -2
  84. package/dist/services/dataService.js.map +1 -1
  85. package/dist/services/groupService.js +21 -8
  86. package/dist/services/groupService.js.map +1 -1
  87. package/dist/services/mcpEventBus.js +6 -0
  88. package/dist/services/mcpEventBus.js.map +1 -0
  89. package/dist/services/mcpService.js +153 -6
  90. package/dist/services/mcpService.js.map +1 -1
  91. package/dist/services/personalOAuthService.js +2 -0
  92. package/dist/services/personalOAuthService.js.map +1 -1
  93. package/dist/services/principalRuntimeService.js +19 -0
  94. package/dist/services/principalRuntimeService.js.map +1 -1
  95. package/dist/services/requestContextService.js +15 -0
  96. package/dist/services/requestContextService.js.map +1 -1
  97. package/dist/services/serverConfigEnvironment.js +2 -1
  98. package/dist/services/serverConfigEnvironment.js.map +1 -1
  99. package/dist/services/smartRoutingService.js +20 -2
  100. package/dist/services/smartRoutingService.js.map +1 -1
  101. package/dist/services/sseService.js +58 -6
  102. package/dist/services/sseService.js.map +1 -1
  103. package/dist/services/templateService.js +11 -5
  104. package/dist/services/templateService.js.map +1 -1
  105. package/dist/services/toolPolicyDiagnostics.js +27 -0
  106. package/dist/services/toolPolicyDiagnostics.js.map +1 -0
  107. package/dist/services/toolPolicyService.js +41 -0
  108. package/dist/services/toolPolicyService.js.map +1 -0
  109. package/dist/services/userService.js +5 -1
  110. package/dist/services/userService.js.map +1 -1
  111. package/dist/services/vectorSearchService.js +4 -2
  112. package/dist/services/vectorSearchService.js.map +1 -1
  113. package/dist/types/toolPolicy.js +2 -0
  114. package/dist/types/toolPolicy.js.map +1 -0
  115. package/dist/utils/groupAccess.js +6 -4
  116. package/dist/utils/groupAccess.js.map +1 -1
  117. package/dist/utils/groupKeyScope.js +34 -0
  118. package/dist/utils/groupKeyScope.js.map +1 -0
  119. package/dist/utils/migration.js +10 -1
  120. package/dist/utils/migration.js.map +1 -1
  121. package/dist/utils/oauthBearer.js +1 -0
  122. package/dist/utils/oauthBearer.js.map +1 -1
  123. package/dist/utils/oauthServerMetadata.js +21 -0
  124. package/dist/utils/oauthServerMetadata.js.map +1 -1
  125. package/dist/utils/serverConfigPersistence.js +16 -1
  126. package/dist/utils/serverConfigPersistence.js.map +1 -1
  127. package/dist/utils/serverConfigValidation.js +1 -0
  128. package/dist/utils/serverConfigValidation.js.map +1 -1
  129. package/dist/utils/serverIdentity.js +7 -0
  130. package/dist/utils/serverIdentity.js.map +1 -0
  131. package/dist/utils/settingsSync.js +14 -1
  132. package/dist/utils/settingsSync.js.map +1 -1
  133. package/dist/utils/systemConfigCache.js +1 -1
  134. package/dist/utils/systemConfigCache.js.map +1 -1
  135. package/dist/utils/tokenCost.js +35 -3
  136. package/dist/utils/tokenCost.js.map +1 -1
  137. package/dist/utils/toolPolicy.js +42 -0
  138. package/dist/utils/toolPolicy.js.map +1 -0
  139. package/frontend/dist/assets/{ActivityPage-C68nRD4J.js → ActivityPage-Bi-TjKez.js} +2 -2
  140. package/frontend/dist/assets/{ActivityPage-C68nRD4J.js.map → ActivityPage-Bi-TjKez.js.map} +1 -1
  141. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js → ConfirmDialog-BJpzQmMw.js} +2 -2
  142. package/frontend/dist/assets/{ConfirmDialog-BYdfb115.js.map → ConfirmDialog-BJpzQmMw.js.map} +1 -1
  143. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js +2 -0
  144. package/frontend/dist/assets/CredentialsPage-C4Ucer3d.js.map +1 -0
  145. package/frontend/dist/assets/{Dashboard-CyTt5-52.js → Dashboard-C_2KkuoD.js} +2 -2
  146. package/frontend/dist/assets/{Dashboard-CyTt5-52.js.map → Dashboard-C_2KkuoD.js.map} +1 -1
  147. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js → DeleteDialog-BoMYLx3J.js} +2 -2
  148. package/frontend/dist/assets/{DeleteDialog-BaCsEkJA.js.map → DeleteDialog-BoMYLx3J.js.map} +1 -1
  149. package/frontend/dist/assets/EndpointCopy-DITVC2YQ.js +2 -0
  150. package/frontend/dist/assets/{EndpointCopy-DxCJY8u6.js.map → EndpointCopy-DITVC2YQ.js.map} +1 -1
  151. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js +33 -0
  152. package/frontend/dist/assets/GroupsPage-sE6YJyuK.js.map +1 -0
  153. package/frontend/dist/assets/LoginPage-CFsd_nY-.js +2 -0
  154. package/frontend/dist/assets/LoginPage-CFsd_nY-.js.map +1 -0
  155. package/frontend/dist/assets/{LogsPage-D3CAJzpq.js → LogsPage-BxZuGDuc.js} +2 -2
  156. package/frontend/dist/assets/LogsPage-BxZuGDuc.js.map +1 -0
  157. package/frontend/dist/assets/MarketPage-CDBs4vMb.js +2 -0
  158. package/frontend/dist/assets/{MarketPage-DsyVFNai.js.map → MarketPage-CDBs4vMb.js.map} +1 -1
  159. package/frontend/dist/assets/{OAuthConsentPage-go6iV2rX.js → OAuthConsentPage-BN_ucViM.js} +2 -2
  160. package/frontend/dist/assets/{OAuthConsentPage-go6iV2rX.js.map → OAuthConsentPage-BN_ucViM.js.map} +1 -1
  161. package/frontend/dist/assets/Pagination-S-7lzy59.js +2 -0
  162. package/frontend/dist/assets/Pagination-S-7lzy59.js.map +1 -0
  163. package/frontend/dist/assets/{PromptsPage-TJYCppKD.js → PromptsPage-Bl3ySXGK.js} +2 -2
  164. package/frontend/dist/assets/{PromptsPage-TJYCppKD.js.map → PromptsPage-Bl3ySXGK.js.map} +1 -1
  165. package/frontend/dist/assets/{ResourcesPage-B9ucLCSU.js → ResourcesPage-BOzQkQ92.js} +2 -2
  166. package/frontend/dist/assets/{ResourcesPage-B9ucLCSU.js.map → ResourcesPage-BOzQkQ92.js.map} +1 -1
  167. package/frontend/dist/assets/ServersPage-DHYwQ5AM.js +39 -0
  168. package/frontend/dist/assets/ServersPage-DHYwQ5AM.js.map +1 -0
  169. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js +14 -0
  170. package/frontend/dist/assets/SettingsPage-CSJ35jlM.js.map +1 -0
  171. package/frontend/dist/assets/{StatusDot-hrwd7ElQ.js → StatusDot-NSjojGCy.js} +2 -2
  172. package/frontend/dist/assets/{StatusDot-hrwd7ElQ.js.map → StatusDot-NSjojGCy.js.map} +1 -1
  173. package/frontend/dist/assets/{ToggleGroup-BXCGM08S.js → ToggleGroup-BIZWpNzm.js} +2 -2
  174. package/frontend/dist/assets/{ToggleGroup-BXCGM08S.js.map → ToggleGroup-BIZWpNzm.js.map} +1 -1
  175. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js +2 -0
  176. package/frontend/dist/assets/UserGroupSelection-CN0weGXJ.js.map +1 -0
  177. package/frontend/dist/assets/UsersPage-DC51v2b8.js +2 -0
  178. package/frontend/dist/assets/UsersPage-DC51v2b8.js.map +1 -0
  179. package/frontend/dist/assets/clipboard-ik5YV95M.js +2 -0
  180. package/frontend/dist/assets/{clipboard-CoLstiwP.js.map → clipboard-ik5YV95M.js.map} +1 -1
  181. package/frontend/dist/assets/{CopyClientConfigDialog-Clr8hk1S.js → costRevision-Bxov9_vH.js} +4 -4
  182. package/frontend/dist/assets/costRevision-Bxov9_vH.js.map +1 -0
  183. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js +61 -0
  184. package/frontend/dist/assets/framework-vendor-D0S_ys6r.js.map +1 -0
  185. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js → i18n-vendor-COTriSc6.js} +2 -2
  186. package/frontend/dist/assets/{i18n-vendor-DVmjrzgW.js.map → i18n-vendor-COTriSc6.js.map} +1 -1
  187. package/frontend/dist/assets/{icons-vendor-CkcU1LMA.js → icons-vendor-DZjjGrOu.js} +3 -3
  188. package/frontend/dist/assets/{icons-vendor-CkcU1LMA.js.map → icons-vendor-DZjjGrOu.js.map} +1 -1
  189. package/frontend/dist/assets/index-6AyPtx0A.js +3 -0
  190. package/frontend/dist/assets/index-6AyPtx0A.js.map +1 -0
  191. package/frontend/dist/assets/index-DrHctFvx.css +1 -0
  192. package/frontend/dist/assets/{resourceService-DXuM3elu.js → resourceService-DMzCAZ0B.js} +2 -2
  193. package/frontend/dist/assets/{resourceService-DXuM3elu.js.map → resourceService-DMzCAZ0B.js.map} +1 -1
  194. package/frontend/dist/assets/useCostData-CcFSYViI.js +2 -0
  195. package/frontend/dist/assets/useCostData-CcFSYViI.js.map +1 -0
  196. package/frontend/dist/assets/variableDetection-CbYSa1ka.js +80 -0
  197. package/frontend/dist/assets/variableDetection-CbYSa1ka.js.map +1 -0
  198. package/frontend/dist/index.html +5 -5
  199. package/mcp_settings.json +2 -0
  200. package/package.json +2 -2
  201. package/frontend/dist/assets/CopyClientConfigDialog-Clr8hk1S.js.map +0 -1
  202. package/frontend/dist/assets/CredentialsPage-WxYmPkS2.js +0 -2
  203. package/frontend/dist/assets/CredentialsPage-WxYmPkS2.js.map +0 -1
  204. package/frontend/dist/assets/EndpointCopy-DxCJY8u6.js +0 -2
  205. package/frontend/dist/assets/GroupsPage-C9uf7qRF.js +0 -33
  206. package/frontend/dist/assets/GroupsPage-C9uf7qRF.js.map +0 -1
  207. package/frontend/dist/assets/LoginPage-CfljHYdH.js +0 -2
  208. package/frontend/dist/assets/LoginPage-CfljHYdH.js.map +0 -1
  209. package/frontend/dist/assets/LogsPage-D3CAJzpq.js.map +0 -1
  210. package/frontend/dist/assets/MarketPage-DsyVFNai.js +0 -2
  211. package/frontend/dist/assets/Pagination-BRqqU5Ct.js +0 -2
  212. package/frontend/dist/assets/Pagination-BRqqU5Ct.js.map +0 -1
  213. package/frontend/dist/assets/ServersPage-BO1e0GJ4.js +0 -39
  214. package/frontend/dist/assets/ServersPage-BO1e0GJ4.js.map +0 -1
  215. package/frontend/dist/assets/SettingsPage-_LJt6T_U.js +0 -14
  216. package/frontend/dist/assets/SettingsPage-_LJt6T_U.js.map +0 -1
  217. package/frontend/dist/assets/UsersPage-CLPsZo0-.js +0 -2
  218. package/frontend/dist/assets/UsersPage-CLPsZo0-.js.map +0 -1
  219. package/frontend/dist/assets/clipboard-CoLstiwP.js +0 -2
  220. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js +0 -61
  221. package/frontend/dist/assets/framework-vendor-CQFm9vKd.js.map +0 -1
  222. package/frontend/dist/assets/index-BE6bm3KN.css +0 -1
  223. package/frontend/dist/assets/index-CKByfAIo.js +0 -3
  224. package/frontend/dist/assets/index-CKByfAIo.js.map +0 -1
  225. package/frontend/dist/assets/useCostData-Br7dI3R9.js +0 -2
  226. package/frontend/dist/assets/useCostData-Br7dI3R9.js.map +0 -1
  227. package/frontend/dist/assets/variableDetection-Civ1fQ-c.js +0 -18
  228. package/frontend/dist/assets/variableDetection-Civ1fQ-c.js.map +0 -1
@@ -1,3 +1,4 @@
1
+ import { getToolPolicy } from './toolPolicyService.js';
1
2
  import { projectGroupToolName, assertValidGroupToolNames } from '../utils/groupToolNames.js';
2
3
  import { isSafePassthroughHeader } from '../utils/passthroughHeaders.js';
3
4
  import { observeToolCall, setMetricsServer } from './metricsService.js';
@@ -38,6 +39,7 @@ import config from '../config/index.js';
38
39
  import { validateMaxBufferSize } from '../utils/stdioOptions.js';
39
40
  import { validateServerName } from '../utils/serverNameValidation.js';
40
41
  import { getGroup } from './sseService.js';
42
+ import { mcpEventBus } from './mcpEventBus.js';
41
43
  import { getServerConfigInGroup, normalizeGroupServers } from './groupService.js';
42
44
  import { removeServerToolEmbeddings, removeToolEmbeddings, saveToolsAsVectorEmbeddings, } from './vectorSearchService.js';
43
45
  import { OpenAPIClient } from '../clients/openapi.js';
@@ -429,6 +431,7 @@ export const notifyToolChanged = async (name, options) => {
429
431
  broadcastToolListChanged();
430
432
  };
431
433
  const broadcastListChanged = (listType, sendNotification) => {
434
+ mcpEventBus.publish({ kind: `${listType}s_list_changed` });
432
435
  Object.values(servers).forEach((server) => {
433
436
  sendNotification(server)
434
437
  .then(() => {
@@ -448,18 +451,20 @@ export const broadcastResourceListChanged = () => {
448
451
  export const broadcastPromptListChanged = () => {
449
452
  broadcastListChanged('prompt', (server) => server.sendPromptListChanged());
450
453
  };
451
- export const updateServerInfoVisibility = (serverName, visibility, sharedWithUsers) => {
454
+ export const updateServerInfoVisibility = (serverName, visibility, sharedWithUsers, sharedWithUserGroups) => {
452
455
  const serverInfo = getServerByName(serverName);
453
456
  if (!serverInfo) {
454
457
  return;
455
458
  }
456
459
  serverInfo.visibility = visibility;
457
460
  serverInfo.sharedWithUsers = sharedWithUsers;
461
+ serverInfo.sharedWithUserGroups = sharedWithUserGroups;
458
462
  if (serverInfo.config) {
459
463
  serverInfo.config = {
460
464
  ...serverInfo.config,
461
465
  visibility,
462
466
  sharedWithUsers,
467
+ sharedWithUserGroups,
463
468
  };
464
469
  }
465
470
  };
@@ -729,7 +734,16 @@ const principalServerScope = new AsyncLocalStorage();
729
734
  // Header-dependent discovery must never publish descriptors or embeddings globally.
730
735
  const requestDiscoveryInfos = new WeakSet();
731
736
  const getVisibleServerInfos = () => {
732
- return getDataService().filterData(principalServerScope.getStore() ?? serverInfos);
737
+ const visible = getDataService().filterData(principalServerScope.getStore() ?? serverInfos);
738
+ // A system key with accessType 'exclude' hides its excluded servers (and the
739
+ // members of excluded groups, already expanded by the auth layer) from every
740
+ // listing, call resolution and smart-routing surface that reads this list.
741
+ const excludedServers = RequestContextService.getInstance().getBearerKeyExclusions();
742
+ if (!excludedServers || excludedServers.length === 0) {
743
+ return visible;
744
+ }
745
+ const excluded = new Set(excludedServers);
746
+ return visible.filter((serverInfo) => !excluded.has(serverInfo.name));
733
747
  };
734
748
  const getVisibleServerByName = (name) => {
735
749
  return getVisibleServerInfos().find((serverInfo) => serverInfo.name === name);
@@ -1698,6 +1712,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1698
1712
  name,
1699
1713
  owner: expandedConf.owner,
1700
1714
  visibility: expandedConf.visibility,
1715
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1701
1716
  sharedWithUsers: expandedConf.sharedWithUsers,
1702
1717
  status: 'disconnected',
1703
1718
  error: null,
@@ -1715,6 +1730,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1715
1730
  name,
1716
1731
  owner: expandedConf.owner,
1717
1732
  visibility: expandedConf.visibility,
1733
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1718
1734
  sharedWithUsers: expandedConf.sharedWithUsers,
1719
1735
  enabled: true,
1720
1736
  status: 'disconnected',
@@ -1749,6 +1765,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1749
1765
  Object.assign(onDemandInfo, {
1750
1766
  owner: expandedConf.owner,
1751
1767
  visibility: expandedConf.visibility,
1768
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1752
1769
  sharedWithUsers: expandedConf.sharedWithUsers,
1753
1770
  enabled: true,
1754
1771
  config: expandedConf,
@@ -1786,6 +1803,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1786
1803
  Object.assign(existingServer, {
1787
1804
  owner: expandedConf.owner,
1788
1805
  visibility: expandedConf.visibility,
1806
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1789
1807
  sharedWithUsers: expandedConf.sharedWithUsers,
1790
1808
  enabled: expandedConf.enabled === undefined ? true : expandedConf.enabled,
1791
1809
  });
@@ -1811,6 +1829,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1811
1829
  name,
1812
1830
  owner: expandedConf.owner,
1813
1831
  visibility: expandedConf.visibility,
1832
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1814
1833
  sharedWithUsers: expandedConf.sharedWithUsers,
1815
1834
  status: 'disconnected',
1816
1835
  error: 'Missing OpenAPI specification URL or schema',
@@ -1826,6 +1845,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1826
1845
  name,
1827
1846
  owner: expandedConf.owner,
1828
1847
  visibility: expandedConf.visibility,
1848
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1829
1849
  sharedWithUsers: expandedConf.sharedWithUsers,
1830
1850
  status: 'connecting',
1831
1851
  error: null,
@@ -1919,6 +1939,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1919
1939
  name,
1920
1940
  owner: expandedConf.owner,
1921
1941
  visibility: expandedConf.visibility,
1942
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1922
1943
  sharedWithUsers: expandedConf.sharedWithUsers,
1923
1944
  status: 'disconnected',
1924
1945
  error: `Failed to create transport: ${formatErrorForLogging(error)}`,
@@ -1953,6 +1974,7 @@ export const initializeClientsFromSettings = async (isInit, serverName, options)
1953
1974
  name,
1954
1975
  owner: expandedConf.owner,
1955
1976
  visibility: expandedConf.visibility,
1977
+ sharedWithUserGroups: expandedConf.sharedWithUserGroups,
1956
1978
  sharedWithUsers: expandedConf.sharedWithUsers,
1957
1979
  status: 'connecting',
1958
1980
  recovery: existingServer?.recovery,
@@ -2202,6 +2224,7 @@ export const getServersInfo = async (page, limit, user) => {
2202
2224
  name: server.name,
2203
2225
  owner: server.owner,
2204
2226
  visibility: server.visibility,
2227
+ sharedWithUserGroups: server.sharedWithUserGroups,
2205
2228
  sharedWithUsers: server.sharedWithUsers,
2206
2229
  // Newly created servers that are enabled should appear as "connecting"
2207
2230
  // until the MCP client initialization completes. Disabled servers remain
@@ -2332,6 +2355,7 @@ export const getServersInfo = async (page, limit, user) => {
2332
2355
  : undefined,
2333
2356
  config: resolvedType || serverConfig?.description || serverConfig?.command
2334
2357
  ? {
2358
+ ...(serverConfig?.id ? { id: serverConfig.id } : {}),
2335
2359
  ...(resolvedType ? { type: resolvedType } : {}),
2336
2360
  ...(hasPersonalAuthentication(serverConfig)
2337
2361
  ? { credentialTemplate: validateCredentialTemplate(serverConfig) }
@@ -3455,12 +3479,14 @@ const handleCallToolRequestImpl = async (request, extra) => {
3455
3479
  const group = getMcpRequestGroup(extra);
3456
3480
  const username = requestContextService.getUsernameContext() ||
3457
3481
  extra?.username ||
3482
+ UserContextService.getInstance().getCurrentUser()?.username ||
3458
3483
  (requestContextService.getKeyKindContext() === 'system' ? 'system' : undefined) ||
3459
3484
  undefined;
3460
3485
  let appsRouteContext;
3461
3486
  const keyId = bearerKeyContext.keyId || extra?.keyId || undefined;
3462
3487
  const keyName = bearerKeyContext.keyName || extra?.keyName || undefined;
3463
3488
  const sourceIp = requestContextService.getRequestContext()?.remoteAddress || undefined;
3489
+ let policyAudit;
3464
3490
  let hostedReservation = null;
3465
3491
  let releaseOnDemandToolCall;
3466
3492
  let clientStateLease;
@@ -3471,6 +3497,19 @@ const handleCallToolRequestImpl = async (request, extra) => {
3471
3497
  return clientStateLease.id;
3472
3498
  };
3473
3499
  const reserveHostedIfNeeded = async (serverName, toolName) => {
3500
+ const policy = await getToolPolicy(group);
3501
+ const decision = policy.authorize(serverName, toolName);
3502
+ policyAudit = { ...decision, serverId: policy.serverId(serverName), groupId: policy.groupId };
3503
+ logger.log('Tool policy decision', {
3504
+ username,
3505
+ callerId: keyId,
3506
+ server: serverName,
3507
+ tool: toolName,
3508
+ ...policyAudit,
3509
+ });
3510
+ if (decision.decision === 'deny') {
3511
+ throw new ToolUnavailableError(`Tool not available: ${toolName}`, decision.reason);
3512
+ }
3474
3513
  const hostedAuth = requestContextService.getHostedAuthContext();
3475
3514
  assertHostedToolAllowed(hostedAuth, serverName, toolName);
3476
3515
  hostedReservation = await reserveHostedToolCall(hostedAuth, serverName, toolName);
@@ -3624,6 +3663,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3624
3663
  // Log successful activity
3625
3664
  const duration = Date.now() - startTime;
3626
3665
  await activityLogger.logToolCall({
3666
+ policy: policyAudit,
3627
3667
  server: targetServerInfo.name,
3628
3668
  tool: cleanToolName,
3629
3669
  duration,
@@ -3698,6 +3738,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3698
3738
  // Log successful activity
3699
3739
  const duration = Date.now() - startTime;
3700
3740
  await activityLogger.logToolCall({
3741
+ policy: policyAudit,
3701
3742
  server: targetServerInfo.name,
3702
3743
  tool: cleanToolName,
3703
3744
  duration,
@@ -3808,6 +3849,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3808
3849
  // Log successful activity
3809
3850
  const duration = Date.now() - startTime;
3810
3851
  await activityLogger.logToolCall({
3852
+ policy: policyAudit,
3811
3853
  server: serverInfo.name,
3812
3854
  tool: cleanToolName,
3813
3855
  duration,
@@ -3866,6 +3908,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3866
3908
  // Log successful activity
3867
3909
  const duration = Date.now() - startTime;
3868
3910
  await activityLogger.logToolCall({
3911
+ policy: policyAudit,
3869
3912
  server: serverInfo.name,
3870
3913
  tool: cleanToolName,
3871
3914
  duration,
@@ -3898,7 +3941,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3898
3941
  callerLabel: keyName,
3899
3942
  toolName: activityToolName,
3900
3943
  ...summarizeErrorForLogging(error),
3901
- ...(unavailable ? { reason: unavailable.reason } : {}),
3944
+ ...(unavailable ? { decision: 'deny', reason: unavailable.reason } : {}),
3902
3945
  });
3903
3946
  // Log error activity
3904
3947
  const duration = Date.now() - startTime;
@@ -3911,6 +3954,7 @@ const handleCallToolRequestImpl = async (request, extra) => {
3911
3954
  getServerByTool(activityToolName);
3912
3955
  const cleanToolName = stripToolServerPrefix(activityToolName, serverInfo?.name);
3913
3956
  await activityLogger.logToolCall({
3957
+ policy: policyAudit ?? (unavailable ? { decision: 'deny', reason: unavailable.reason } : undefined),
3914
3958
  server: serverInfo?.name || 'unknown',
3915
3959
  tool: cleanToolName,
3916
3960
  duration,
@@ -4336,6 +4380,7 @@ const createPrincipalRuntime = async (name, resolvedConfig, scope, toolsOnly = f
4336
4380
  name,
4337
4381
  owner: resolvedConfig.owner,
4338
4382
  visibility: resolvedConfig.visibility,
4383
+ sharedWithUserGroups: resolvedConfig.sharedWithUserGroups,
4339
4384
  sharedWithUsers: resolvedConfig.sharedWithUsers,
4340
4385
  enabled: true,
4341
4386
  config: resolvedConfig,
@@ -4691,6 +4736,7 @@ const listProjectionInputs = async (group, field) => {
4691
4736
  enabled: info.enabled,
4692
4737
  owner: info.owner,
4693
4738
  visibility: info.visibility,
4739
+ sharedWithUserGroups: info.sharedWithUserGroups,
4694
4740
  sharedWithUsers: info.sharedWithUsers,
4695
4741
  config: info.config,
4696
4742
  })),
@@ -4746,9 +4792,109 @@ const withCacheHints = (handler, passthroughTtl = false, listField) => async (re
4746
4792
  // and capabilities. Shared caches must not reuse the gateway result across users.
4747
4793
  return { ...result, ttlMs, cacheScope: 'private' };
4748
4794
  };
4749
- export const handleListToolsRequest = withPrincipalServers(withModernLists(withCacheHints(handleListToolsRequestImpl, false, 'tools'), ['tools']), 'tools-list');
4795
+ const listToolsWithPrincipal = withPrincipalServers(withModernLists(withCacheHints(handleListToolsRequestImpl, false, 'tools'), ['tools']), 'tools-list');
4750
4796
  const callToolWithPrincipal = withPrincipalServers(withModernLists(handleCallToolRequestImpl, ['tools']), 'tool');
4751
- export const handleCallToolRequest = (request, extra) => observeToolCall(() => callToolWithPrincipal(request, extra));
4797
+ // Run before personal-runtime acquisition: rejected targets must not resolve credentials.
4798
+ const withToolPolicy = async (handler, request, extra, invocation) => {
4799
+ const group = getMcpRequestGroup(extra);
4800
+ const policy = await getToolPolicy(group);
4801
+ if (!policy.active)
4802
+ return handler(request, extra);
4803
+ const { filteredServerInfos: candidates, serverConfigsByName } = await getFilteredServerInfosForGroup(getGroupLookupName(group));
4804
+ const username = UserContextService.getInstance().getCurrentUser()?.username;
4805
+ const preflightInfo = (info) => {
4806
+ const tools = username ? principalRuntimes.peekTools(info.name, username) : undefined;
4807
+ return tools ? { ...info, tools } : info;
4808
+ };
4809
+ const lookup = getGroupLookupName(group);
4810
+ const direct = !!lookup && !isSmartRoutingGroup(group) && !(await findRouteGroup(lookup));
4811
+ const upstreamOwnsName = direct &&
4812
+ candidates.map(preflightInfo).some((info) => info.tools.length
4813
+ ? info.tools.some((tool) => normalizeToolNameForServer(info.name, tool.name) === request.params?.name)
4814
+ : // Until discovery disambiguates a raw meta-name, require it to be authorized too.
4815
+ hasPersonalAuthentication(info.config) &&
4816
+ policy.authorize(info.name, request.params?.name).decision === 'deny');
4817
+ const wrapper = !upstreamOwnsName && ['call_tool', 'describe_tool'].includes(request.params?.name);
4818
+ const requested = wrapper ? request.params.arguments?.toolName : request.params?.name;
4819
+ const targetCall = invocation && (upstreamOwnsName || request.params?.name !== 'search_tools');
4820
+ let denied;
4821
+ const eligible = candidates.filter((info) => {
4822
+ if (extra?.server && info.name !== extra.server)
4823
+ return false;
4824
+ const config = serverConfigsByName.get(info.name);
4825
+ const internal = typeof requested === 'string'
4826
+ ? config?.preserveToolNames
4827
+ ? `${info.name}${getNameSeparator()}${requested}`
4828
+ : resolveNameFromGroup(requested, info.name, config)
4829
+ : undefined;
4830
+ const known = internal
4831
+ ? findToolOnServer(preflightInfo(info), internal, direct || !!extra?.server || candidates.length === 1, direct)
4832
+ : undefined;
4833
+ const prefix = `${info.name}${getNameSeparator()}`;
4834
+ const tool = known
4835
+ ? normalizeToolNameForServer(info.name, known.name)
4836
+ : internal?.startsWith(prefix)
4837
+ ? internal.slice(prefix.length)
4838
+ : direct || extra?.server || candidates.length === 1 || config?.preserveToolNames
4839
+ ? internal
4840
+ : undefined;
4841
+ if (targetCall && !tool)
4842
+ return false;
4843
+ let decision = policy.authorize(info.name, targetCall ? tool : undefined, !targetCall);
4844
+ if (targetCall &&
4845
+ direct &&
4846
+ !preflightInfo(info).tools.length &&
4847
+ hasPersonalAuthentication(info.config)) {
4848
+ // Without a catalog, both raw and prefixed/wrapper interpretations must be allowed.
4849
+ for (const alternative of [requested, request.params?.name]) {
4850
+ const alternativeDecision = policy.authorize(info.name, alternative);
4851
+ if (alternativeDecision.decision === 'deny')
4852
+ decision = alternativeDecision;
4853
+ }
4854
+ }
4855
+ if (decision.decision === 'deny') {
4856
+ denied = {
4857
+ server: info,
4858
+ tool: tool || requested || 'unknown',
4859
+ decision: { ...decision, serverId: policy.serverId(info.name), groupId: policy.groupId },
4860
+ };
4861
+ return false;
4862
+ }
4863
+ return true;
4864
+ });
4865
+ if (targetCall && !eligible.length && denied) {
4866
+ const context = RequestContextService.getInstance();
4867
+ const { keyId, keyName } = context.getBearerKeyContext();
4868
+ const username = UserContextService.getInstance().getCurrentUser()?.username;
4869
+ logger.log('Tool policy decision', {
4870
+ username,
4871
+ callerId: keyId,
4872
+ server: denied.server.name,
4873
+ tool: denied.tool,
4874
+ ...denied.decision,
4875
+ });
4876
+ await getActivityLoggingService().logToolCall({
4877
+ server: denied.server.name,
4878
+ tool: denied.tool,
4879
+ duration: 0,
4880
+ status: 'error',
4881
+ username,
4882
+ keyId,
4883
+ keyName,
4884
+ group,
4885
+ policy: denied.decision,
4886
+ sourceIp: context.getRequestContext()?.remoteAddress,
4887
+ hasCredentialTemplate: hasPersonalAuthentication(denied.server.config),
4888
+ });
4889
+ return {
4890
+ content: [{ type: 'text', text: `Error: Tool not available: ${requested}` }],
4891
+ isError: true,
4892
+ };
4893
+ }
4894
+ return principalServerScope.run(eligible, () => handler(request, extra));
4895
+ };
4896
+ export const handleListToolsRequest = (request, extra) => withToolPolicy(listToolsWithPrincipal, request, extra, false);
4897
+ export const handleCallToolRequest = (request, extra) => observeToolCall(() => withToolPolicy(callToolWithPrincipal, request, extra, true));
4752
4898
  export const handleGetPromptRequest = withPrincipalServers(withModernLists(handleGetPromptRequestImpl, ['prompts']), 'prompt');
4753
4899
  export const handleListPromptsRequest = withPrincipalServers(withModernLists(withCacheHints(handleListPromptsRequestImpl, false, 'prompts'), ['prompts']), 'list');
4754
4900
  export const handleListResourcesRequest = withPrincipalServers(withModernLists(withCacheHints(handleListResourcesRequestImpl, false, 'resources'), [
@@ -4853,7 +4999,8 @@ async function filterToolsByGroup(group, serverName, tools, serverConfig) {
4853
4999
  }
4854
5000
  }
4855
5001
  const hostedAuth = RequestContextService.getInstance().getHostedAuthContext();
4856
- return filterHostedTools(hostedAuth, serverName, tools, getNameSeparator());
5002
+ const policy = await getToolPolicy(getMcpRequestGroup() || group);
5003
+ return policy.filterTools(serverName, filterHostedTools(hostedAuth, serverName, tools, getNameSeparator()));
4857
5004
  }
4858
5005
  const normalizePromptNameForGroup = (serverName, promptName) => {
4859
5006
  const prefix = `${serverName}${getNameSeparator()}`;