@samanhappy/mcphub 1.0.36 → 1.0.38

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 (102) 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/constants/oauthServerDefaults.js +1 -0
  5. package/dist/constants/oauthServerDefaults.js.map +1 -1
  6. package/dist/controllers/groupController.js +63 -14
  7. package/dist/controllers/groupController.js.map +1 -1
  8. package/dist/controllers/oauthDynamicRegistrationController.js +6 -32
  9. package/dist/controllers/oauthDynamicRegistrationController.js.map +1 -1
  10. package/dist/controllers/serverController.js +46 -3
  11. package/dist/controllers/serverController.js.map +1 -1
  12. package/dist/dao/GroupDaoDbImpl.js +18 -0
  13. package/dist/dao/GroupDaoDbImpl.js.map +1 -1
  14. package/dist/dao/OAuthClientDaoDbImpl.js +3 -0
  15. package/dist/dao/OAuthClientDaoDbImpl.js.map +1 -1
  16. package/dist/db/connection.js +3 -2
  17. package/dist/db/connection.js.map +1 -1
  18. package/dist/db/entities/Group.js +9 -1
  19. package/dist/db/entities/Group.js.map +1 -1
  20. package/dist/db/entities/OAuthClient.js +4 -0
  21. package/dist/db/entities/OAuthClient.js.map +1 -1
  22. package/dist/db/groupSchema.js +29 -0
  23. package/dist/db/groupSchema.js.map +1 -0
  24. package/dist/models/OAuth.js +98 -1
  25. package/dist/models/OAuth.js.map +1 -1
  26. package/dist/routes/index.js +4 -3
  27. package/dist/routes/index.js.map +1 -1
  28. package/dist/services/groupService.js +57 -23
  29. package/dist/services/groupService.js.map +1 -1
  30. package/dist/services/logService.js +11 -2
  31. package/dist/services/logService.js.map +1 -1
  32. package/dist/services/mcpService.js +177 -17
  33. package/dist/services/mcpService.js.map +1 -1
  34. package/dist/services/oauthClientRegistration.js +22 -7
  35. package/dist/services/oauthClientRegistration.js.map +1 -1
  36. package/dist/services/oauthSettingsStore.js +3 -0
  37. package/dist/services/oauthSettingsStore.js.map +1 -1
  38. package/dist/services/sseService.js +63 -7
  39. package/dist/services/sseService.js.map +1 -1
  40. package/dist/utils/cacheUtils.js +133 -5
  41. package/dist/utils/cacheUtils.js.map +1 -1
  42. package/dist/utils/groupAccess.js +17 -0
  43. package/dist/utils/groupAccess.js.map +1 -0
  44. package/dist/utils/migration.js +3 -0
  45. package/dist/utils/migration.js.map +1 -1
  46. package/dist/utils/rateLimit.js +85 -18
  47. package/dist/utils/rateLimit.js.map +1 -1
  48. package/dist/utils/smartRouting.js +1 -1
  49. package/dist/utils/smartRouting.js.map +1 -1
  50. package/frontend/dist/assets/{ActivityPage-ZRx6b9hm.js → ActivityPage-BZNq-eGE.js} +2 -2
  51. package/frontend/dist/assets/{ActivityPage-ZRx6b9hm.js.map → ActivityPage-BZNq-eGE.js.map} +1 -1
  52. package/frontend/dist/assets/{CredentialsPage-C9vnh01H.js → CredentialsPage-7kyDJLsX.js} +2 -2
  53. package/frontend/dist/assets/{CredentialsPage-C9vnh01H.js.map → CredentialsPage-7kyDJLsX.js.map} +1 -1
  54. package/frontend/dist/assets/{Dashboard-BDGDQNz1.js → Dashboard-CElNx820.js} +2 -2
  55. package/frontend/dist/assets/{Dashboard-BDGDQNz1.js.map → Dashboard-CElNx820.js.map} +1 -1
  56. package/frontend/dist/assets/{EndpointCopy-BY51t0Ks.js → EndpointCopy-8jW5Xtka.js} +2 -2
  57. package/frontend/dist/assets/{EndpointCopy-BY51t0Ks.js.map → EndpointCopy-8jW5Xtka.js.map} +1 -1
  58. package/frontend/dist/assets/GroupsPage-dUkvX_iB.js +33 -0
  59. package/frontend/dist/assets/GroupsPage-dUkvX_iB.js.map +1 -0
  60. package/frontend/dist/assets/{LoginPage-CZOKlkR_.js → LoginPage-D23s7oM0.js} +2 -2
  61. package/frontend/dist/assets/{LoginPage-CZOKlkR_.js.map → LoginPage-D23s7oM0.js.map} +1 -1
  62. package/frontend/dist/assets/{LogsPage-FDWOOIPg.js → LogsPage-BuQMkmV-.js} +2 -2
  63. package/frontend/dist/assets/{LogsPage-FDWOOIPg.js.map → LogsPage-BuQMkmV-.js.map} +1 -1
  64. package/frontend/dist/assets/{MarketPage-DKfZgoIs.js → MarketPage-g9vEYzIq.js} +2 -2
  65. package/frontend/dist/assets/{MarketPage-DKfZgoIs.js.map → MarketPage-g9vEYzIq.js.map} +1 -1
  66. package/frontend/dist/assets/{OAuthConsentPage-Hk_DJ0dW.js → OAuthConsentPage-BiX1lpJR.js} +2 -2
  67. package/frontend/dist/assets/{OAuthConsentPage-Hk_DJ0dW.js.map → OAuthConsentPage-BiX1lpJR.js.map} +1 -1
  68. package/frontend/dist/assets/{PromptsPage-CT0C0_BI.js → PromptsPage-g8SRFY6s.js} +2 -2
  69. package/frontend/dist/assets/{PromptsPage-CT0C0_BI.js.map → PromptsPage-g8SRFY6s.js.map} +1 -1
  70. package/frontend/dist/assets/{ResourcesPage-CBgu-N5m.js → ResourcesPage-Ddhxi9Sf.js} +2 -2
  71. package/frontend/dist/assets/{ResourcesPage-CBgu-N5m.js.map → ResourcesPage-Ddhxi9Sf.js.map} +1 -1
  72. package/frontend/dist/assets/{ServersPage-CvcP8LFv.js → ServersPage-DAmKvrPV.js} +8 -8
  73. package/frontend/dist/assets/ServersPage-DAmKvrPV.js.map +1 -0
  74. package/frontend/dist/assets/SettingsPage-DZXjQZXS.js +12 -0
  75. package/frontend/dist/assets/SettingsPage-DZXjQZXS.js.map +1 -0
  76. package/frontend/dist/assets/{StatusDot-uIIW4IuH.js → StatusDot-CdzMqjTi.js} +2 -2
  77. package/frontend/dist/assets/{StatusDot-uIIW4IuH.js.map → StatusDot-CdzMqjTi.js.map} +1 -1
  78. package/frontend/dist/assets/{ToggleGroup-BToroeZy.js → ToggleGroup-CJd8X_31.js} +2 -2
  79. package/frontend/dist/assets/{ToggleGroup-BToroeZy.js.map → ToggleGroup-CJd8X_31.js.map} +1 -1
  80. package/frontend/dist/assets/{UsersPage-BjgJjf6L.js → UsersPage-CkP_x9Rt.js} +2 -2
  81. package/frontend/dist/assets/{UsersPage-BjgJjf6L.js.map → UsersPage-CkP_x9Rt.js.map} +1 -1
  82. package/frontend/dist/assets/index-Bu4XuAH4.js +3 -0
  83. package/frontend/dist/assets/index-Bu4XuAH4.js.map +1 -0
  84. package/frontend/dist/assets/{resourceService-sD9dxx3E.js → resourceService-w-cZ31FD.js} +2 -2
  85. package/frontend/dist/assets/{resourceService-sD9dxx3E.js.map → resourceService-w-cZ31FD.js.map} +1 -1
  86. package/frontend/dist/assets/{useCostData-QoPbsqyD.js → useCostData-C72NC0PQ.js} +2 -2
  87. package/frontend/dist/assets/{useCostData-QoPbsqyD.js.map → useCostData-C72NC0PQ.js.map} +1 -1
  88. package/frontend/dist/assets/useSettingsData-JTEMu74D.js +2 -0
  89. package/frontend/dist/assets/{useSettingsData-C4JrLqDh.js.map → useSettingsData-JTEMu74D.js.map} +1 -1
  90. package/frontend/dist/assets/{variableDetection-iFAhGAKs.js → variableDetection-DfhNyFy-.js} +2 -2
  91. package/frontend/dist/assets/{variableDetection-iFAhGAKs.js.map → variableDetection-DfhNyFy-.js.map} +1 -1
  92. package/frontend/dist/index.html +1 -1
  93. package/mcp_settings.json +2 -1
  94. package/package.json +1 -1
  95. package/frontend/dist/assets/GroupsPage-DyHyW7NW.js +0 -33
  96. package/frontend/dist/assets/GroupsPage-DyHyW7NW.js.map +0 -1
  97. package/frontend/dist/assets/ServersPage-CvcP8LFv.js.map +0 -1
  98. package/frontend/dist/assets/SettingsPage-DDSVt_xk.js +0 -12
  99. package/frontend/dist/assets/SettingsPage-DDSVt_xk.js.map +0 -1
  100. package/frontend/dist/assets/index-DjNOC0Ov.js +0 -3
  101. package/frontend/dist/assets/index-DjNOC0Ov.js.map +0 -1
  102. package/frontend/dist/assets/useSettingsData-C4JrLqDh.js +0 -2
package/README.fr.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  > Une passerelle et un plan de contrôle MCP open source et auto-hébergés pour connecter, contrôler et exploiter des serveurs MCP.
4
4
 
5
+ [![CI](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml/badge.svg)](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/@samanhappy/mcphub)](https://www.npmjs.com/package/@samanhappy/mcphub)
7
+ [![Docker pulls](https://img.shields.io/docker/pulls/samanhappy/mcphub)](https://hub.docker.com/r/samanhappy/mcphub)
8
+ [![License](https://img.shields.io/github/license/samanhappy/mcphub)](LICENSE)
9
+ [![Discord](https://img.shields.io/badge/discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/2BJehJZVH5)
10
+ [![GitHub stars](https://img.shields.io/github/stars/samanhappy/mcphub?style=social)](https://github.com/samanhappy/mcphub/stargazers)
11
+
5
12
  [English](README.md) | Français | [中文版](README.zh.md)
6
13
 
7
14
  MCPHub offre une manière unifiée de connecter et de gérer plusieurs serveurs MCP, de les organiser en groupes, de contrôler les accès et d'exposer des points de terminaison MCP stables aux clients tels que Claude Code, Cursor, Cherry Studio, OpenWebUI et autres applications compatibles MCP.
@@ -10,33 +17,59 @@ MCPHub offre une manière unifiée de connecter et de gérer plusieurs serveurs
10
17
 
11
18
  ## 🌐 Démo en direct et Documentation
12
19
 
13
- - **Identifiants personnels** - Associez une clé par utilisateur à un serveur partagé, avec stockage chiffré et processus stdio isolés ([En savoir plus](docs/features/per-user-credentials.mdx))
20
+ - **Site web** : [mcphub.app](https://www.mcphub.app/)
14
21
  - **Documentation** : [docs.mcphub.app](https://docs.mcphub.app/)
15
22
  - **Environnement de démo** : [demo.mcphub.app](https://demo.mcphub.app/)
16
23
 
17
24
  ## 🚀 Fonctionnalités
18
25
 
26
+ ### Connecter une fois, exposer partout
27
+
28
+ - **Routage intelligent** ⭐ - Découverte d'outils propulsée par IA utilisant la recherche sémantique vectorielle ([En savoir plus](https://docs.mcphub.app/features/smart-routing))
19
29
  - **Passerelle MCP unifiée** - Exposez tous les serveurs connectés via des points de terminaison MCP stables, avec des routes pour les groupes et les serveurs individuels
20
- - **Gestion des serveurs et des groupes** - Organisez les serveurs en groupes, gérez leur visibilité et contrôlez l'exposition des Tools, Prompts et Resources
30
+ - **Alias et routage des serveurs** - Définissez des alias et routez les clients vers tous les serveurs, des groupes, un serveur individuel ou le routage intelligent
21
31
  - **Prise en charge SSE / Streamable HTTP / stdio** - Connectez des serveurs MCP locaux et distants via les transports pris en charge
32
+ - **Configuration à chaud** - Ajoutez, supprimez ou mettez à jour les serveurs sans temps d'arrêt
33
+
34
+ ### Contrôler les accès et les identifiants
35
+
36
+ - **Identifiants personnels** ⭐ - Associez une clé par utilisateur à un serveur partagé, avec stockage chiffré et processus stdio isolés ([En savoir plus](docs/features/per-user-credentials.mdx))
22
37
  - **Authentification et contrôle d'accès** - Utilisez OAuth 2.0, des bearer keys et la visibilité des serveurs ou groupes pour gérer les accès
23
- - **Alias et routage des serveurs** - Définissez des alias et routez les clients vers tous les serveurs, des groupes, un serveur individuel ou le routage intelligent
38
+ - **Support OAuth 2.0** - Modes client et serveur pour une authentification sécurisée ([En savoir plus](https://docs.mcphub.app/features/oauth))
39
+ - **Connexion sociale** - Support de connexion GitHub et Google via Better Auth (nécessite le mode Base de données)
40
+ - **Gestion des serveurs et des groupes** - Organisez les serveurs en groupes, gérez leur visibilité et contrôlez l'exposition des Tools, Prompts et Resources
41
+
42
+ ### Exploiter en confiance
43
+
24
44
  - **Journaux et observabilité** - Consultez l'activité des appels d'outils, l'état des requêtes, la latence et les journaux des serveurs
25
45
  - **Vérifications de santé** - Surveillez la santé des connexions et l'état des serveurs depuis un même endroit
26
46
  - **Tableau de bord Web** - Gérez la configuration et le fonctionnement des serveurs depuis un navigateur
27
- - **Routage intelligent** - Découverte d'outils propulsée par IA utilisant la recherche sémantique vectorielle ([En savoir plus](https://docs.mcphub.app/features/smart-routing))
28
47
  - **Compression des résultats d'outils** - Réduisez automatiquement les grands résultats texte avant qu'ils n'atteignent les clients
29
- - **Configuration à chaud** - Ajoutez, supprimez ou mettez à jour les serveurs sans temps d'arrêt
30
- - **Support OAuth 2.0** - Modes client et serveur pour une authentification sécurisée ([En savoir plus](https://docs.mcphub.app/features/oauth))
31
- - **Connexion Sociale** - Support de connexion GitHub et Google via Better Auth (nécessite le mode Base de données)
32
48
  - **Mode Base de données** - Stockez la configuration dans PostgreSQL pour les environnements de production ([En savoir plus](https://docs.mcphub.app/configuration/database-configuration))
33
49
  - **Prêt pour Docker** - Déployez instantanément avec la configuration conteneurisée
34
50
 
35
51
  ## 🔧 Démarrage rapide
36
52
 
53
+ ### Prérequis
54
+
55
+ - **Docker** (recommandé) — le moyen le plus rapide d'exécuter MCPHub ; toutes les commandes ci-dessous l'utilisent
56
+ - **Node.js** `^18.0.0 || >=20.0.0` et **pnpm** `10.12.4` — uniquement pour exécuter depuis les sources ou développer localement (voir [Développement local](#développement-local))
57
+
58
+ ### Démarrer avec Docker
59
+
60
+ ```bash
61
+ docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
62
+ ```
63
+
64
+ Ouvrez `http://localhost:3000` et connectez-vous avec le nom d'utilisateur `admin`. Au premier lancement, si la variable d'environnement `ADMIN_PASSWORD` n'est pas définie, un mot de passe aléatoire est généré et affiché dans les logs du serveur.
65
+
66
+ La configuration, les utilisateurs et les identifiants persistent dans `./data` par défaut.
67
+
68
+ Avec vos propres serveurs ? Avant le premier lancement, créez `data/mcp_settings.json` (voir [Configuration](#configuration)). Ensuite, ajoutez des serveurs dans le tableau de bord ou modifiez le fichier existant et redémarrez MCPHub.
69
+
37
70
  ### Configuration
38
71
 
39
- Créez un fichier `mcp_settings.json` :
72
+ Avant le premier lancement, créez `data/mcp_settings.json` :
40
73
 
41
74
  ```json
42
75
  {
@@ -57,13 +90,7 @@ Créez un fichier `mcp_settings.json` :
57
90
 
58
91
  ### Déploiement avec Docker
59
92
 
60
- ```bash
61
- # Exécutez avec une configuration personnalisée (recommandé)
62
- docker run -p 3000:3000 -v ./mcp_settings.json:/app/mcp_settings.json -v ./data:/app/data samanhappy/mcphub
63
-
64
- # Ou exécutez avec les paramètres par défaut (montez ./data pour préserver l'état entre redémarrages)
65
- docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
66
- ```
93
+ Voir [Démarrer avec Docker](#démarrer-avec-docker) pour la commande prête à copier. Gardez `./data` monté pour conserver la configuration, les utilisateurs et les identifiants après la recréation du conteneur.
67
94
 
68
95
  Deux variantes d'image sont publiées sous `samanhappy/mcphub` :
69
96
 
@@ -74,11 +101,11 @@ Voir [Configuration Docker](https://docs.mcphub.app/configuration/docker-setup)
74
101
 
75
102
  ### Accéder au tableau de bord
76
103
 
77
- Ouvrez `http://localhost:3000` et connectez-vous avec le nom d'utilisateur `admin`. Au premier lancement, si la variable d'environnement `ADMIN_PASSWORD` n'est pas définie, un mot de passe aléatoire est généré et affiché dans les logs du serveur. Vous pouvez également prédéfinir le mot de passe :
104
+ Ouvrez `http://localhost:3000` (voir [Démarrer avec Docker](#démarrer-avec-docker) pour la connexion). Vous pouvez également prédéfinir le mot de passe :
78
105
 
79
106
  ```bash
80
107
  # Docker : définir le mot de passe admin via une variable d'environnement
81
- docker run -p 3000:3000 -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
108
+ docker run -p 3000:3000 -v ./data:/app/data -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
82
109
  ```
83
110
 
84
111
  > **Conseil :** Changez le mot de passe admin après la première connexion pour plus de sécurité.
@@ -110,6 +137,7 @@ http://localhost:3000/mcp/$smart/{group} # Routage intelligent dans un groupe
110
137
  | [Mode Base de données](https://docs.mcphub.app/configuration/database-configuration) | Configuration PostgreSQL pour la production |
111
138
  | [OAuth](https://docs.mcphub.app/features/oauth) | Configuration client et serveur OAuth 2.0 |
112
139
  | [Routage intelligent](https://docs.mcphub.app/features/smart-routing) | Découverte d'outils propulsée par IA |
140
+ | [Guide CLI](https://docs.mcphub.app/features/cli) | Gérer et appeler le hub depuis un terminal |
113
141
  | [Configuration Docker](https://docs.mcphub.app/configuration/docker-setup) | Guide de déploiement Docker |
114
142
 
115
143
  ## 🧑‍💻 Développement local
@@ -143,6 +171,8 @@ Les contributions sont les bienvenues ! Rejoignez notre [communauté Discord](ht
143
171
 
144
172
  [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/samanhappy)
145
173
 
174
+ Les utilisateurs en Chine peuvent aussi soutenir via WeChat Pay — voir [中文版](README.zh.md).
175
+
146
176
  ## 🌟 Historique des étoiles
147
177
 
148
178
  [![Historique des étoiles](https://star-history.dera.page/svg?repos=samanhappy/mcphub&type=Date)](https://star-history.dera.page/#samanhappy/mcphub&Date)
package/README.md CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  > An open-source, self-hosted MCP gateway and control plane for connecting, controlling, and operating MCP servers.
4
4
 
5
- [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fsamanhappy%2Fmcphub.svg)](https://mcptoplist.com/server/glama%2Fsamanhappy%2Fmcphub)
5
+ [![CI](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml/badge.svg)](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/@samanhappy/mcphub)](https://www.npmjs.com/package/@samanhappy/mcphub)
7
+ [![Docker pulls](https://img.shields.io/docker/pulls/samanhappy/mcphub)](https://hub.docker.com/r/samanhappy/mcphub)
8
+ [![License](https://img.shields.io/github/license/samanhappy/mcphub)](LICENSE)
9
+ [![Discord](https://img.shields.io/badge/discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/2BJehJZVH5)
10
+ [![GitHub stars](https://img.shields.io/github/stars/samanhappy/mcphub?style=social)](https://github.com/samanhappy/mcphub/stargazers)
6
11
 
7
12
  English | [Français](README.fr.md) | [中文版](README.zh.md)
8
13
 
@@ -20,29 +25,54 @@ It works with MCP clients such as Claude Code, Cursor, Cherry Studio, OpenWebUI,
20
25
 
21
26
  ## 🚀 Features
22
27
 
28
+ ### Connect once, expose everywhere
29
+
30
+ - **Smart Routing** ⭐ - AI-powered tool discovery using vector semantic search ([Learn more](https://docs.mcphub.app/features/smart-routing))
23
31
  - **Unified MCP Gateway** - Expose all connected servers through stable MCP endpoints, including routes for groups and individual servers
32
+ - **Server Aliases and Routing** - Define aliases and route clients to all servers, specific groups, individual servers, or smart routing
33
+ - **SSE / Streamable HTTP / stdio Support** - Connect local and remote MCP servers over the supported transports
34
+ - **Hot-Swappable Config** - Add, remove, or update servers without downtime
35
+
36
+ ### Control access and credentials
37
+
38
+ - **Per-user Credentials** ⭐ - Bind personal keys to one shared server, with encrypted storage and isolated stdio runtimes ([Learn more](docs/features/per-user-credentials.mdx))
24
39
  - **Authentication and Access Control** - Use OAuth 2.0, bearer keys, and server or group visibility controls to manage access
25
- - **Per-user Credentials** - Bind personal keys to one shared server, with encrypted storage and isolated stdio runtimes ([Learn more](docs/features/per-user-credentials.mdx))
40
+ - **OAuth 2.0 Support** - Both client and server modes for secure authentication ([Learn more](https://docs.mcphub.app/features/oauth))
41
+ - **Social Login** - Seamless GitHub and Google login support with Better Auth integration (requires Database Mode)
26
42
  - **Server and Group Management** - Organize servers into groups, manage visibility, and control Tool, Prompt, and Resource exposure
27
- - **SSE / Streamable HTTP / stdio Support** - Connect local and remote MCP servers over the supported transports
28
- - **Server Aliases and Routing** - Define aliases and route clients to all servers, specific groups, individual servers, or smart routing
43
+
44
+ ### Operate with confidence
45
+
29
46
  - **Logs and Observability** - Inspect tool-call activity, request status, latency, and server logs
30
47
  - **Health Checks** - Monitor connection health and server status from one place
31
48
  - **Web Dashboard** - Manage server configuration and runtime operations from a browser
32
- - **Smart Routing** - AI-powered tool discovery using vector semantic search ([Learn more](https://docs.mcphub.app/features/smart-routing))
33
49
  - **MCP Apps Proxy** - Transparently forward interactive MCP Apps on single-server routes ([Learn more](https://docs.mcphub.app/features/mcp-apps))
34
50
  - **Tool Result Compression** - Transparently reduce large text tool outputs before they reach clients
35
- - **Hot-Swappable Config** - Add, remove, or update servers without downtime
36
- - **OAuth 2.0 Support** - Both client and server modes for secure authentication ([Learn more](https://docs.mcphub.app/features/oauth))
37
- - **Social Login** - Seamless GitHub and Google login support with Better Auth integration (requires Database Mode)
38
51
  - **Database Mode** - Store configuration in PostgreSQL for production environments ([Learn more](https://docs.mcphub.app/configuration/database-configuration))
39
52
  - **Docker-Ready** - Deploy instantly with containerized setup
40
53
 
41
54
  ## 🔧 Quick Start
42
55
 
56
+ ### Prerequisites
57
+
58
+ - **Docker** (recommended) — the fastest way to run MCPHub; all commands below use it
59
+ - **Node.js** `^18.0.0 || >=20.0.0` and **pnpm** `10.12.4` — only needed to run from source or develop locally (see [Local Development](#local-development))
60
+
61
+ ### Start with Docker
62
+
63
+ ```bash
64
+ docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
65
+ ```
66
+
67
+ Open `http://localhost:3000` and log in with username `admin`. On first launch, if no `ADMIN_PASSWORD` environment variable is set, a random password is generated and printed to the server logs.
68
+
69
+ Settings, users, and credential bindings persist in `./data` by default.
70
+
71
+ Want your own servers? Before the first launch, create `data/mcp_settings.json` (see [Configuration](#configuration)). After launch, add servers in the dashboard or edit the existing file and restart MCPHub.
72
+
43
73
  ### Configuration
44
74
 
45
- Create a `mcp_settings.json` file:
75
+ Create `data/mcp_settings.json` before the first launch:
46
76
 
47
77
  ```json
48
78
  {
@@ -63,13 +93,7 @@ Create a `mcp_settings.json` file:
63
93
 
64
94
  ### Docker Deployment
65
95
 
66
- ```bash
67
- # Run with custom config (recommended)
68
- docker run -p 3000:3000 -v ./mcp_settings.json:/app/mcp_settings.json -v ./data:/app/data samanhappy/mcphub
69
-
70
- # Or run with default settings (also mount ./data so credentials and state survive restarts)
71
- docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
72
- ```
96
+ See [Start with Docker](#start-with-docker) for the copy-paste command. Keep `./data` mounted so settings, users, and credential bindings survive container recreation.
73
97
 
74
98
  Two image variants are published under `samanhappy/mcphub`:
75
99
 
@@ -80,11 +104,11 @@ See [Docker Setup](https://docs.mcphub.app/configuration/docker-setup) for build
80
104
 
81
105
  ### Access Dashboard
82
106
 
83
- Open `http://localhost:3000` and log in with username `admin`. On first launch, if no `ADMIN_PASSWORD` environment variable is set, a random password is generated and printed to the server logs. You can also pre-set the password:
107
+ Open `http://localhost:3000` (see [Start with Docker](#start-with-docker) for login details). You can also pre-set the password:
84
108
 
85
109
  ```bash
86
110
  # Docker: set admin password via environment variable
87
- docker run -p 3000:3000 -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
111
+ docker run -p 3000:3000 -v ./data:/app/data -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
88
112
  ```
89
113
 
90
114
  > **Tip:** Change the admin password after first login for security.
@@ -107,24 +131,6 @@ http://localhost:3000/mcp/$smart/{group} # Smart routing within group
107
131
 
108
132
  📖 See [API Reference](https://docs.mcphub.app/api-reference) for detailed endpoint documentation.
109
133
 
110
- ### Manage From the Terminal
111
-
112
- The same `mcphub` binary doubles as a CLI for the running hub — no extra install needed.
113
-
114
- ```bash
115
- mcphub login --url http://localhost:3000 --username admin
116
- mcphub servers list
117
- mcphub servers add fetch --type stdio --command uvx --arg mcp-server-fetch
118
- mcphub tools list # discover what tools are available
119
- mcphub tools get fetch_url # see required params + sample command
120
- mcphub call fetch_url url=https://example.com --json
121
- mcphub keys create --name ci --access-type all
122
- ```
123
-
124
- It also speaks the public marketplace API (`mcphub discover`, `mcphub install ...`) so server lookup and one-command install work against any hub with discovery enabled.
125
-
126
- 📖 See [CLI Guide](https://docs.mcphub.app/features/cli) for every subcommand, profiles, and CI usage.
127
-
128
134
  ## 📚 Documentation
129
135
 
130
136
  | Topic | Description |
@@ -178,6 +184,8 @@ Contributions welcome! See our [Discord community](https://discord.gg/2BJehJZVH5
178
184
 
179
185
  [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/samanhappy)
180
186
 
187
+ Chinese users can also support via WeChat Pay — see [中文版](README.zh.md).
188
+
181
189
  ## 🌟 Star History
182
190
 
183
191
  [![Star History Chart](https://star-history.dera.page/svg?repos=samanhappy/mcphub&type=Date)](https://star-history.dera.page/#samanhappy/mcphub&Date)
package/README.zh.md CHANGED
@@ -2,6 +2,13 @@
2
2
 
3
3
  > 开源、自托管的 MCP 网关与控制平面,用于连接、控制和运行 MCP 服务器。
4
4
 
5
+ [![CI](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml/badge.svg)](https://github.com/samanhappy/mcphub/actions/workflows/ci.yml)
6
+ [![npm version](https://img.shields.io/npm/v/@samanhappy/mcphub)](https://www.npmjs.com/package/@samanhappy/mcphub)
7
+ [![Docker pulls](https://img.shields.io/docker/pulls/samanhappy/mcphub)](https://hub.docker.com/r/samanhappy/mcphub)
8
+ [![License](https://img.shields.io/github/license/samanhappy/mcphub)](LICENSE)
9
+ [![Discord](https://img.shields.io/badge/discord-join-5865F2?logo=discord&logoColor=white)](https://discord.gg/2BJehJZVH5)
10
+ [![GitHub stars](https://img.shields.io/github/stars/samanhappy/mcphub?style=social)](https://github.com/samanhappy/mcphub/stargazers)
11
+
5
12
  [English](README.md) | [Français](README.fr.md) | 中文版
6
13
 
7
14
  MCPHub 是 AI 客户端与 MCP 服务器之间的统一控制点。一次连接本地和远程 MCP 服务器,通过稳定端点组织和路由其能力,借助身份认证、限定作用域的凭据和用户级可见性控制访问,并通过集中日志、活动追踪和健康监控统一运行与管理。
@@ -18,28 +25,53 @@ MCPHub 是 AI 客户端与 MCP 服务器之间的统一控制点。一次连接
18
25
 
19
26
  ## 🚀 功能亮点
20
27
 
28
+ ### 一次连接,随处暴露
29
+
30
+ - **智能路由** ⭐ - 基于向量语义搜索的 AI 工具发现 ([了解更多](https://docs.mcphub.app/zh/features/smart-routing))
21
31
  - **统一 MCP 网关** - 通过稳定的 MCP 端点暴露所有已连接的服务器,也支持分组和单服务器路由
32
+ - **服务器别名与路由** - 设置别名,并将客户端路由到所有服务器、指定分组、单个服务器或智能路由
33
+ - **SSE / Streamable HTTP / stdio 支持** - 通过支持的传输方式连接本地和远程 MCP 服务器
34
+ - **热插拔配置** - 无需停机即可添加、移除或更新服务器
35
+
36
+ ### 管控访问与凭据
37
+
38
+ - **个人凭据** ⭐ - 一个共享服务器支持每位用户独立绑定密钥,加密保存并隔离 stdio 运行进程([了解更多](docs/zh/features/per-user-credentials.mdx))
22
39
  - **身份认证与访问控制** - 使用 OAuth 2.0、Bearer Key 以及服务器或分组可见性控制访问权限
23
- - **个人凭据** - 一个共享服务器支持每位用户独立绑定密钥,加密保存并隔离 stdio 运行进程([了解更多](docs/zh/features/per-user-credentials.mdx)
40
+ - **OAuth 2.0 支持** ⭐ - 客户端和服务端模式,实现安全认证 ([了解更多](https://docs.mcphub.app/zh/features/oauth))
41
+ - **社交一键登录** - 通过 Better Auth 集成支持 GitHub 和 Google 快捷登录(需启用数据库模式)
24
42
  - **服务器与分组管理** - 组织服务器分组,管理可见性,并控制 Tool、Prompt 与 Resource 的暴露范围
25
- - **SSE / Streamable HTTP / stdio 支持** - 通过支持的传输方式连接本地和远程 MCP 服务器
26
- - **服务器别名与路由** - 设置别名,并将客户端路由到所有服务器、指定分组、单个服务器或智能路由
43
+
44
+ ### 稳定运行
45
+
27
46
  - **日志与可观测性** - 查看工具调用活动、请求状态、延迟和服务器日志
28
47
  - **健康检查** - 在一个地方监控连接健康状况和服务器状态
29
48
  - **Web 控制台** - 通过浏览器管理服务器配置和运行状态
30
- - **智能路由** - 基于向量语义搜索的 AI 工具发现 ([了解更多](https://docs.mcphub.app/zh/features/smart-routing))
31
49
  - **工具结果压缩** - 在返回客户端前透明压缩大型文本工具输出
32
- - **热插拔配置** - 无需停机即可添加、移除或更新服务器
33
- - **OAuth 2.0 支持** - 客户端和服务端模式,实现安全认证 ([了解更多](https://docs.mcphub.app/zh/features/oauth))
34
- - **社交一键登录** - 通过 Better Auth 集成支持 GitHub 和 Google 快捷登录(需启用数据库模式)
35
50
  - **数据库模式** - 将配置存储在 PostgreSQL 中,适用于生产环境 ([了解更多](https://docs.mcphub.app/zh/configuration/database-configuration))
36
51
  - **Docker 就绪** - 容器化部署,开箱即用
37
52
 
38
53
  ## 🔧 快速开始
39
54
 
55
+ ### 前置条件
56
+
57
+ - **Docker**(推荐)—— 运行 MCPHub 最快的方式,下面的命令都基于它
58
+ - **Node.js** `^18.0.0 || >=20.0.0` 和 **pnpm** `10.12.4` —— 仅从源码运行或参与本地开发时需要(见[本地开发](#本地开发))
59
+
60
+ ### 使用 Docker 启动
61
+
62
+ ```bash
63
+ docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
64
+ ```
65
+
66
+ 打开 `http://localhost:3000`,使用用户名 `admin` 登录。首次启动时,如果未设置 `ADMIN_PASSWORD` 环境变量,系统将自动生成随机密码并输出到服务器日志中。
67
+
68
+ 配置、用户和凭据绑定默认持久化到 `./data`。
69
+
70
+ 想用自己的服务器?首次启动前,创建 `data/mcp_settings.json`(见[配置](#配置))。启动后,可在控制台添加服务器,或编辑现有配置文件并重启 MCPHub。
71
+
40
72
  ### 配置
41
73
 
42
- 创建 `mcp_settings.json` 文件:
74
+ 首次启动前,创建 `data/mcp_settings.json` 文件:
43
75
 
44
76
  ```json
45
77
  {
@@ -60,13 +92,7 @@ MCPHub 是 AI 客户端与 MCP 服务器之间的统一控制点。一次连接
60
92
 
61
93
  ### Docker 部署
62
94
 
63
- ```bash
64
- # 挂载自定义配置运行(推荐)
65
- docker run -p 3000:3000 -v ./mcp_settings.json:/app/mcp_settings.json -v ./data:/app/data samanhappy/mcphub
66
-
67
- # 或使用默认配置运行(仍建议挂载 ./data,避免容器删除后数据丢失)
68
- docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
69
- ```
95
+ 可复制的命令见[使用 Docker 启动](#使用-docker-启动)。请保持挂载 `./data`,这样配置、用户和凭据绑定在容器重建后不会丢失。
70
96
 
71
97
  `samanhappy/mcphub` 提供两种镜像变体:
72
98
 
@@ -77,11 +103,11 @@ docker run -p 3000:3000 -v ./data:/app/data samanhappy/mcphub
77
103
 
78
104
  ### 访问控制台
79
105
 
80
- 打开 `http://localhost:3000`,使用用户名 `admin` 登录。首次启动时,如果未设置 `ADMIN_PASSWORD` 环境变量,系统将自动生成随机密码并输出到服务器日志中。也可以预先设置密码:
106
+ 打开 `http://localhost:3000`(登录方式见[使用 Docker 启动](#使用-docker-启动))。也可以预先设置密码:
81
107
 
82
108
  ```bash
83
109
  # Docker:通过环境变量设置管理员密码
84
- docker run -p 3000:3000 -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
110
+ docker run -p 3000:3000 -v ./data:/app/data -e ADMIN_PASSWORD=your-secure-password samanhappy/mcphub
85
111
  ```
86
112
 
87
113
  > **提示:** 首次登录后请及时修改管理员密码以确保安全。
@@ -104,24 +130,6 @@ http://localhost:3000/mcp/$smart/{group} # 智能路由(特定分组)
104
130
 
105
131
  📖 查看 [API 参考](https://docs.mcphub.app/zh/api-reference)了解详细的端点文档。
106
132
 
107
- ### 终端管理
108
-
109
- `mcphub` 同一个二进制兼任 CLI,无需额外安装。
110
-
111
- ```bash
112
- mcphub login --url http://localhost:3000 --username admin
113
- mcphub servers list
114
- mcphub servers add fetch --type stdio --command uvx --arg mcp-server-fetch
115
- mcphub tools list # 看有哪些 tool 可调
116
- mcphub tools get fetch_url # 看必填参数和样例命令
117
- mcphub call fetch_url url=https://example.com --json
118
- mcphub keys create --name ci --access-type all
119
- ```
120
-
121
- CLI 同样对接公共市场接口(`mcphub discover`、`mcphub install ...`),可对任意开启了 discovery 的 hub 做检索与一键安装。
122
-
123
- 📖 查看 [CLI 指南](https://docs.mcphub.app/zh/features/cli)了解全部子命令、profile 管理与 CI 用法。
124
-
125
133
  ## 📚 文档
126
134
 
127
135
  | 主题 | 描述 |
@@ -176,6 +184,8 @@ pnpm dev
176
184
 
177
185
  <img src="assets/reward.png" width="350">
178
186
 
187
+ 海外用户可通过 [ko-fi](https://ko-fi.com/samanhappy) 支持。
188
+
179
189
  ## 致谢
180
190
 
181
191
  感谢以下朋友的赞赏:小白、唐秀川、琛、孔、黄祥取、兰军飞、无名之辈、Kyle,以及其他匿名支持者。
@@ -10,6 +10,7 @@ export const DEFAULT_OAUTH_SERVER_CONFIG = {
10
10
  enabled: true,
11
11
  allowedGrantTypes: ['authorization_code', 'refresh_token'],
12
12
  requiresAuthentication: false,
13
+ clientTtl: 2592000, // 30 days: reap idle dynamically-registered clients with no live tokens
13
14
  },
14
15
  clientIdMetadata: {
15
16
  enabled: false,
@@ -1 +1 @@
1
- {"version":3,"file":"oauthServerDefaults.js","sourceRoot":"","sources":["../../src/constants/oauthServerDefaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GAAsB;IAC5D,OAAO,EAAE,IAAI;IACb,mBAAmB,EAAE,IAAI;IACzB,oBAAoB,EAAE,OAAO;IAC7B,yBAAyB,EAAE,GAAG;IAC9B,mBAAmB,EAAE,KAAK;IAC1B,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IAChC,YAAY,EAAE,KAAK;IACnB,mBAAmB,EAAE;QACnB,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC1D,sBAAsB,EAAE,KAAK;KAC9B;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,KAAK;QACd,UAAU,EAAE,OAAS;KACtB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAsB,EAAE;IACnE,MAAM,aAAa,GAAG,2BAA2B,CAAC,aAAa;QAC7D,CAAC,CAAC,CAAC,GAAG,2BAA2B,CAAC,aAAa,CAAC;QAChD,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,uBAAuB,GAAG,2BAA2B,CAAC,mBAAmB,IAAI;QACjF,OAAO,EAAE,KAAK;QACd,iBAAiB,EAAE,EAAE;QACrB,sBAAsB,EAAE,KAAK;KAC9B,CAAC;IAEF,MAAM,mBAAmB,GAAG;QAC1B,GAAG,uBAAuB;QAC1B,iBAAiB,EAAE,uBAAuB,CAAC,iBAAiB;YAC1D,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAC,iBAAiB,CAAC;YAChD,CAAC,CAAC,EAAE;KACP,CAAC;IAEF,OAAO;QACL,GAAG,2BAA2B;QAC9B,aAAa;QACb,mBAAmB;QACnB,gBAAgB,EAAE,EAAE,GAAG,2BAA2B,CAAC,gBAAgB,EAAE;KACtE,CAAC;AACJ,CAAC,CAAC"}
1
+ {"version":3,"file":"oauthServerDefaults.js","sourceRoot":"","sources":["../../src/constants/oauthServerDefaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,2BAA2B,GAAsB;IAC5D,OAAO,EAAE,IAAI;IACb,mBAAmB,EAAE,IAAI;IACzB,oBAAoB,EAAE,OAAO;IAC7B,yBAAyB,EAAE,GAAG;IAC9B,mBAAmB,EAAE,KAAK;IAC1B,aAAa,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC;IAChC,YAAY,EAAE,KAAK;IACnB,mBAAmB,EAAE;QACnB,OAAO,EAAE,IAAI;QACb,iBAAiB,EAAE,CAAC,oBAAoB,EAAE,eAAe,CAAC;QAC1D,sBAAsB,EAAE,KAAK;QAC7B,SAAS,EAAE,OAAO,EAAE,wEAAwE;KAC7F;IACD,gBAAgB,EAAE;QAChB,OAAO,EAAE,KAAK;QACd,UAAU,EAAE,OAAS;KACtB;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,6BAA6B,GAAG,GAAsB,EAAE;IACnE,MAAM,aAAa,GAAG,2BAA2B,CAAC,aAAa;QAC7D,CAAC,CAAC,CAAC,GAAG,2BAA2B,CAAC,aAAa,CAAC;QAChD,CAAC,CAAC,EAAE,CAAC;IAEP,MAAM,uBAAuB,GAAG,2BAA2B,CAAC,mBAAmB,IAAI;QACjF,OAAO,EAAE,KAAK;QACd,iBAAiB,EAAE,EAAE;QACrB,sBAAsB,EAAE,KAAK;KAC9B,CAAC;IAEF,MAAM,mBAAmB,GAAG;QAC1B,GAAG,uBAAuB;QAC1B,iBAAiB,EAAE,uBAAuB,CAAC,iBAAiB;YAC1D,CAAC,CAAC,CAAC,GAAG,uBAAuB,CAAC,iBAAiB,CAAC;YAChD,CAAC,CAAC,EAAE;KACP,CAAC;IAEF,OAAO;QACL,GAAG,2BAA2B;QAC9B,aAAa;QACb,mBAAmB;QACnB,gBAAgB,EAAE,EAAE,GAAG,2BAA2B,CAAC,gBAAgB,EAAE;KACtE,CAAC;AACJ,CAAC,CAAC"}
@@ -1,5 +1,7 @@
1
+ import { getGroupDao, getUserDao } from '../dao/index.js';
2
+ import { validateGroupAccess } from '../utils/groupAccess.js';
1
3
  import { logger } from '../utils/logger.js';
2
- import { getAllGroups, getGroupByIdOrName, createGroup, updateGroup, updateGroupServers, deleteGroup, addServerToGroup, removeServerFromGroup, getServerConfigInGroup, getServerConfigsInGroup, updateServerToolsInGroup, } from '../services/groupService.js';
4
+ import { getAllGroups, presentGroup, canMutateGroup, getGroupByIdOrName, createGroup, updateGroup, updateGroupServers, deleteGroup, addServerToGroup, removeServerFromGroup, getServerConfigInGroup, getServerConfigsInGroup, updateServerToolsInGroup, } from '../services/groupService.js';
3
5
  const isValidCapabilitySelection = (value) => {
4
6
  return (value === undefined ||
5
7
  value === 'all' ||
@@ -89,7 +91,12 @@ export const getGroup = async (req, res) => {
89
91
  // Create a new group
90
92
  export const createNewGroup = async (req, res) => {
91
93
  try {
92
- const { name, description, servers } = req.body;
94
+ const { name, description, servers, visibility, sharedWithUsers } = req.body;
95
+ const accessError = validateGroupAccess({ visibility, sharedWithUsers });
96
+ if (accessError) {
97
+ res.status(400).json({ success: false, message: accessError });
98
+ return;
99
+ }
93
100
  if (!name) {
94
101
  res.status(400).json({
95
102
  success: false,
@@ -109,7 +116,10 @@ export const createNewGroup = async (req, res) => {
109
116
  // Set owner property - use current user's username, default to 'admin'
110
117
  const currentUser = req.user;
111
118
  const owner = currentUser?.username || 'admin';
112
- const newGroup = await createGroup(name, description, serverList, owner);
119
+ const newGroup = await createGroup(name, description, serverList, owner, {
120
+ visibility,
121
+ sharedWithUsers,
122
+ });
113
123
  if (!newGroup) {
114
124
  res.status(400).json({
115
125
  success: false,
@@ -119,7 +129,7 @@ export const createNewGroup = async (req, res) => {
119
129
  }
120
130
  const response = {
121
131
  success: true,
122
- data: newGroup,
132
+ data: await presentGroup(newGroup),
123
133
  message: 'Group created successfully',
124
134
  };
125
135
  res.status(201).json(response);
@@ -198,9 +208,12 @@ export const batchCreateGroups = async (req, res) => {
198
208
  const currentUser = req.user;
199
209
  const defaultOwner = currentUser?.username || 'admin';
200
210
  for (const groupData of groups) {
201
- const { name, description, servers } = groupData;
211
+ const { name, description, servers, visibility, sharedWithUsers } = groupData;
202
212
  // Validate group configuration
203
- const validation = validateGroupConfig(groupData);
213
+ const accessError = validateGroupAccess(groupData);
214
+ const validation = accessError
215
+ ? { valid: false, message: accessError }
216
+ : validateGroupConfig(groupData);
204
217
  if (!validation.valid) {
205
218
  results.push({
206
219
  name: name || 'unknown',
@@ -212,7 +225,10 @@ export const batchCreateGroups = async (req, res) => {
212
225
  }
213
226
  try {
214
227
  const serverList = Array.isArray(servers) ? servers : [];
215
- const newGroup = await createGroup(name, description, serverList, defaultOwner);
228
+ const newGroup = await createGroup(name, description, serverList, defaultOwner, {
229
+ visibility,
230
+ sharedWithUsers,
231
+ });
216
232
  if (newGroup) {
217
233
  results.push({
218
234
  name,
@@ -262,7 +278,12 @@ export const batchCreateGroups = async (req, res) => {
262
278
  export const updateExistingGroup = async (req, res) => {
263
279
  try {
264
280
  const { id } = req.params;
265
- const { name, description, servers } = req.body;
281
+ const { name, description, servers, visibility, sharedWithUsers } = req.body;
282
+ const accessError = validateGroupAccess({ visibility, sharedWithUsers });
283
+ if (accessError) {
284
+ res.status(400).json({ success: false, message: accessError });
285
+ return;
286
+ }
266
287
  if (!id) {
267
288
  res.status(400).json({
268
289
  success: false,
@@ -272,6 +293,12 @@ export const updateExistingGroup = async (req, res) => {
272
293
  }
273
294
  // Allow updating servers along with other fields
274
295
  const updateData = {};
296
+ if (visibility !== undefined)
297
+ updateData.visibility = visibility;
298
+ if (sharedWithUsers !== undefined)
299
+ updateData.sharedWithUsers = [
300
+ ...new Set(sharedWithUsers.map((name) => name.trim())),
301
+ ];
275
302
  if (name !== undefined)
276
303
  updateData.name = name;
277
304
  if (description !== undefined)
@@ -290,7 +317,7 @@ export const updateExistingGroup = async (req, res) => {
290
317
  if (Object.keys(updateData).length === 0) {
291
318
  res.status(400).json({
292
319
  success: false,
293
- message: 'At least one field (name, description, or servers) is required to update',
320
+ message: 'At least one field (name, description, servers, visibility, or sharedWithUsers) is required to update',
294
321
  });
295
322
  return;
296
323
  }
@@ -304,7 +331,7 @@ export const updateExistingGroup = async (req, res) => {
304
331
  }
305
332
  const response = {
306
333
  success: true,
307
- data: updatedGroup,
334
+ data: await presentGroup(updatedGroup),
308
335
  message: 'Group updated successfully',
309
336
  };
310
337
  res.json(response);
@@ -373,7 +400,7 @@ export const updateGroupServersBatch = async (req, res) => {
373
400
  }
374
401
  const response = {
375
402
  success: true,
376
- data: updatedGroup,
403
+ data: await presentGroup(updatedGroup),
377
404
  message: 'Group servers updated successfully',
378
405
  };
379
406
  res.json(response);
@@ -445,7 +472,7 @@ export const addServerToExistingGroup = async (req, res) => {
445
472
  }
446
473
  const response = {
447
474
  success: true,
448
- data: updatedGroup,
475
+ data: await presentGroup(updatedGroup),
449
476
  message: 'Server added to group successfully',
450
477
  };
451
478
  res.json(response);
@@ -478,7 +505,7 @@ export const removeServerFromExistingGroup = async (req, res) => {
478
505
  }
479
506
  const response = {
480
507
  success: true,
481
- data: updatedGroup,
508
+ data: await presentGroup(updatedGroup),
482
509
  message: 'Server removed from group successfully',
483
510
  };
484
511
  res.json(response);
@@ -610,7 +637,7 @@ export const updateGroupServerTools = async (req, res) => {
610
637
  }
611
638
  const response = {
612
639
  success: true,
613
- data: updatedGroup,
640
+ data: await presentGroup(updatedGroup),
614
641
  message: 'Server tools updated successfully',
615
642
  };
616
643
  res.json(response);
@@ -622,4 +649,26 @@ export const updateGroupServerTools = async (req, res) => {
622
649
  });
623
650
  }
624
651
  };
652
+ // Username-only candidates are restricted to the group owner/admin, like server sharing.
653
+ export const getGroupShareCandidates = async (req, res) => {
654
+ try {
655
+ const group = await getGroupDao().findById(req.params.id);
656
+ if (!group || !canMutateGroup(group)) {
657
+ res.status(404).json({ success: false, message: 'Group not found' });
658
+ return;
659
+ }
660
+ const users = await getUserDao().findAll();
661
+ res.json({
662
+ success: true,
663
+ data: users
664
+ .map((user) => user.username)
665
+ .filter((username) => username !== group.owner)
666
+ .sort((a, b) => a.localeCompare(b)),
667
+ });
668
+ }
669
+ catch (error) {
670
+ logger.error('Failed to get group share candidates:', error);
671
+ res.status(500).json({ success: false, message: 'Failed to get group share candidates' });
672
+ }
673
+ };
625
674
  //# sourceMappingURL=groupController.js.map