@imenam/mcp-github 1.1.54 → 1.1.56

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 (68) hide show
  1. package/README.md +280 -191
  2. package/dist/__tests__/config-server.guard.test.d.ts +2 -0
  3. package/dist/__tests__/config-server.guard.test.d.ts.map +1 -0
  4. package/dist/__tests__/config-server.guard.test.js +135 -0
  5. package/dist/__tests__/config-server.guard.test.js.map +1 -0
  6. package/dist/__tests__/gateway-exec/session.test.d.ts +2 -0
  7. package/dist/__tests__/gateway-exec/session.test.d.ts.map +1 -0
  8. package/dist/__tests__/gateway-exec/session.test.js +72 -0
  9. package/dist/__tests__/gateway-exec/session.test.js.map +1 -0
  10. package/dist/__tests__/git-proxy/pkt-line.test.d.ts +2 -0
  11. package/dist/__tests__/git-proxy/pkt-line.test.d.ts.map +1 -0
  12. package/dist/__tests__/git-proxy/pkt-line.test.js +93 -0
  13. package/dist/__tests__/git-proxy/pkt-line.test.js.map +1 -0
  14. package/dist/__tests__/git-proxy/proxy.e2e.test.d.ts +2 -0
  15. package/dist/__tests__/git-proxy/proxy.e2e.test.d.ts.map +1 -0
  16. package/dist/__tests__/git-proxy/proxy.e2e.test.js +206 -0
  17. package/dist/__tests__/git-proxy/proxy.e2e.test.js.map +1 -0
  18. package/dist/__tests__/handlers/agent-git.test.d.ts +2 -0
  19. package/dist/__tests__/handlers/agent-git.test.d.ts.map +1 -0
  20. package/dist/__tests__/handlers/agent-git.test.js +124 -0
  21. package/dist/__tests__/handlers/agent-git.test.js.map +1 -0
  22. package/dist/__tests__/utils/git.test.js +9 -2
  23. package/dist/__tests__/utils/git.test.js.map +1 -1
  24. package/dist/config-server.d.ts.map +1 -1
  25. package/dist/config-server.js +11 -2
  26. package/dist/config-server.js.map +1 -1
  27. package/dist/gateway-exec/session.d.ts +36 -0
  28. package/dist/gateway-exec/session.d.ts.map +1 -0
  29. package/dist/gateway-exec/session.js +116 -0
  30. package/dist/gateway-exec/session.js.map +1 -0
  31. package/dist/git-proxy/pkt-line.d.ts +50 -0
  32. package/dist/git-proxy/pkt-line.d.ts.map +1 -0
  33. package/dist/git-proxy/pkt-line.js +110 -0
  34. package/dist/git-proxy/pkt-line.js.map +1 -0
  35. package/dist/git-proxy/server.d.ts +27 -0
  36. package/dist/git-proxy/server.d.ts.map +1 -0
  37. package/dist/git-proxy/server.js +216 -0
  38. package/dist/git-proxy/server.js.map +1 -0
  39. package/dist/handlers/agent-git.d.ts +5 -0
  40. package/dist/handlers/agent-git.d.ts.map +1 -0
  41. package/dist/handlers/agent-git.js +205 -0
  42. package/dist/handlers/agent-git.js.map +1 -0
  43. package/dist/handlers/git.d.ts +1 -1
  44. package/dist/handlers/git.d.ts.map +1 -1
  45. package/dist/handlers/git.js +75 -121
  46. package/dist/handlers/git.js.map +1 -1
  47. package/dist/handlers/github.d.ts.map +1 -1
  48. package/dist/handlers/github.js +12 -7
  49. package/dist/handlers/github.js.map +1 -1
  50. package/dist/handlers/info.d.ts.map +1 -1
  51. package/dist/handlers/info.js +14 -3
  52. package/dist/handlers/info.js.map +1 -1
  53. package/dist/index.js +41 -3
  54. package/dist/index.js.map +1 -1
  55. package/dist/utils/git.d.ts +4 -1
  56. package/dist/utils/git.d.ts.map +1 -1
  57. package/dist/utils/git.js +30 -20
  58. package/dist/utils/git.js.map +1 -1
  59. package/dist/utils/role.d.ts +15 -0
  60. package/dist/utils/role.d.ts.map +1 -0
  61. package/dist/utils/role.js +18 -0
  62. package/dist/utils/role.js.map +1 -0
  63. package/dist/utils/target.d.ts +13 -0
  64. package/dist/utils/target.d.ts.map +1 -0
  65. package/dist/utils/target.js +38 -0
  66. package/dist/utils/target.js.map +1 -0
  67. package/package.json +2 -2
  68. package/public/index.html +5 -1
package/README.md CHANGED
@@ -1,191 +1,280 @@
1
- # Restricted GitHub MCP Server
2
-
3
- Un serveur MCP (Model Context Protocol) spécialisé pour automatiser le workflow Git local vers GitHub, avec délégation des compilations/tests soit à un serveur distant via SSH, soit directement en local sur la même machine. Conçu pour être minimaliste et sécurisé.
4
-
5
- ## 🚀 Outils disponibles
6
-
7
- ### Git local → GitHub
8
-
9
- - **`commit`** : Exécute `git add .` puis crée un commit avec le message fourni. *Ne pousse pas vers le remote.*
10
- - **`push`** : Pousse la branche actuelle vers `TARGET_BRANCH`. *Ne fait pas de commit.*
11
- - **`commit_and_push`** : Enchaîne `git add .`, commit et push vers `TARGET_BRANCH`.
12
- - **`pull_request`** : Crée une Pull Request GitHub de `TARGET_BRANCH` vers `BASE_BRANCH`.
13
- - **`clone`** : Clone `TARGET_REPO` dans `CLONE_DIR/<TARGET_BRANCH>/`. Crée la branche distante si elle n'existe pas encore.
14
- - **`get_info`** : Affiche la configuration active (dépôt, branches, chemins) sans exposer les secrets.
15
- - **`get_logs`** : Récupère les dernières lignes du fichier de log serveur. Argument optionnel : `lines` (défaut : 50).
16
-
17
- ### Déploiement & test (serveur distant ou local)
18
-
19
- Selon la configuration active, ces outils s'exécutent soit sur un serveur distant via SSH, soit directement en local sur la machine du serveur MCP (interrupteur `LOCAL_DEPLOY`).
20
-
21
- - **`deploy`** : Exécute `git fetch --all`, `git checkout TARGET_BRANCH`, `git pull` dans `SSH_REMOTE_PATH`. Le repo doit être préalablement cloné à cet emplacement (sur le serveur distant, ou en local).
22
- - **`test`** : Exécute `TEST_COMMAND` (cwd = `SSH_REMOTE_PATH`) et renvoie stdout, stderr et exit code. Stocke la sortie pour `test_logs`.
23
- - **`test_logs`** : Renvoie la sortie du dernier `test` exécuté sans relancer la commande.
24
-
25
- > **Mode local (`LOCAL_DEPLOY`)** : quand il est activé, aucune connexion SSH n'est ouverte — la commande est lancée directement dans le shell de la machine, dans le dossier `SSH_REMOTE_PATH` (interprété comme un chemin local). Pratique lorsque le serveur MCP tourne sur le serveur de déploiement lui-même.
26
-
27
- ### Flux typique agent
28
-
29
- ```
30
- clone → (modifier des fichiers) → commit_and_push → deploy → test
31
- ↑ |
32
- └─── (corriger) ────────┘ (si exit code ≠ 0)
33
- ```
34
-
35
- ## 🖥️ Interface de Configuration (GUI)
36
-
37
- Le serveur lance automatiquement une interface graphique au démarrage (si `PROXY_URL` est défini). Elle permet de :
38
-
39
- - Gérer plusieurs configurations nommées (multi-projets).
40
- - Sélectionner le dépôt et les branches via des menus déroulants GitHub.
41
- - Configurer le déploiement **par projet** : serveur SSH (hôte, utilisateur, port, clé privée) et commande de test, ou bascule en **déploiement local** sans SSH.
42
- - Visualiser la configuration active en temps réel.
43
-
44
- Le formulaire est organisé en sections repliables : **Dépôt & branches** (toujours dépliée, cœur de l'usage), **Options** et **Déploiement & test** (repliées par défaut, le paramétrage serveur n'étant fait qu'une fois). Activer l'interrupteur **Déployer en local** masque automatiquement les champs de connexion SSH devenus inutiles.
45
-
46
- > **Proxy dynamique** : La GUI s'enregistre auprès du reverse proxy défini dans `PROXY_URL` et reçoit un port dynamique. Elle est alors accessible sous le chemin effectivement enregistré — `GUI_PATH` seul par défaut, éventuellement préfixé par `APP_PATH_PREFIX` (voir *Variables GUI / Proxy*). L'enregistrement passe par le client partagé [`@imenam/mcp-gui-interface`](https://www.npmjs.com/package/@imenam/mcp-gui-interface), commun à tous les MCP à interface ; le timeout d'enregistrement est de **1 s** (il était de 5 s avec l'ancien client interne).
47
-
48
- ## ⚙️ Configuration
49
-
50
- Les variables **globales** (token, dossiers, proxy) sont définies dans un fichier `.env` ou dans la section `env` du client MCP. Les **paramètres par projet** (dépôt, branches, déploiement SSH ou local…) sont gérés dans l'interface GUI et stockés dans `configs.json`.
51
-
52
- ### Variables GitHub
53
-
54
- | Variable | Description | Exemple |
55
- | :--- | :--- | :--- |
56
- | `GITHUB_TOKEN` | Personal Access Token GitHub (PAT) | `github_pat_...` |
57
- | `MCP_DATA_DIR` | Dossier contenant `configs.json` (multi-configs) | `./data` |
58
- | `CLONE_DIR` | Dossier racine dans lequel les branches seront clonées | `./cloned_repos` |
59
-
60
- > Les configurations par projet (dépôt, branches, mode lecture seule…) sont gérées dans l'interface GUI et stockées dans `configs.json`.
61
-
62
- ### Paramètres de déploiement (outils `deploy` / `test`)
63
-
64
- Ces paramètres sont définis **par projet** dans l'interface GUI et stockés dans `configs.json` (et non dans le `.env` global). Chaque configuration peut donc viser un serveur — et une clé — différents, ou basculer en mode local.
65
-
66
- | Paramètre | Description | Exemple |
67
- | :--- | :--- | :--- |
68
- | `LOCAL_DEPLOY` | Si activé, exécution locale sans SSH (la machine du serveur MCP) | `false` |
69
- | `SSH_HOST` | Adresse IP ou hostname du serveur distant | `mon-serveur.example.com` |
70
- | `SSH_USER` | Utilisateur SSH | `deploy` |
71
- | `SSH_PORT` | Port SSH (défaut : 22) | `22` |
72
- | `SSH_KEY_PATH` | Chemin absolu vers la clé privée SSH | `C:/Users/.../.ssh/id_ed25519` |
73
- | `SSH_REMOTE_PATH` | Chemin du repo (distant en mode SSH, **local** si `LOCAL_DEPLOY`) | `/var/www/mon-projet` |
74
- | `TEST_COMMAND` | Commande de test à exécuter | `npx tsc --noEmit --pretty` |
75
-
76
- > En mode local, seuls `SSH_REMOTE_PATH` (qui doit exister sur le disque) et `TEST_COMMAND` sont utilisés ; les champs de connexion SSH sont ignorés.
77
-
78
- > **Note** : Pour `tsc`, utilisez `--pretty` pour obtenir la sortie complète avec contexte de code (sans cette option, `tsc` passe en format compact une ligne par erreur quand il n'y a pas de TTY).
79
-
80
- > **Migration** : ces clés peuvent aussi être fournies via le `.env` ou la section `env` du client MCP. Elles ne sont alors lues **qu'une seule fois** au premier démarrage (si `configs.json` n'existe pas encore) pour amorcer la configuration par défaut ; ensuite, c'est la GUI qui fait foi.
81
-
82
- ### Variables GUI / Proxy
83
-
84
- | Variable | Description | Exemple |
85
- | :--- | :--- | :--- |
86
- | `PROXY_URL` | URL du reverse proxy central | `http://localhost:3000` |
87
- | `APP_PATH` | Chemin d'accès via le proxy. Nom canonique, commun à tous les MCP. | `/github-mcp` |
88
- | `APP_NAME` | Nom affiché dans le proxy. Nom canonique. | `GitHub MCP Config` |
89
- | `GUI_PATH` | Ancien nom de `APP_PATH`, toujours accepté (utilisé si `APP_PATH` et `PROXY_APP_PATH` sont absents) | `/github-mcp` (défaut) |
90
- | `GUI_NAME` | Ancien nom de `APP_NAME`, toujours accepté (même ordre de priorité) | `GitHub MCP Config` (défaut) |
91
- | `APP_PATH_PREFIX` | Préfixe de namespace ajouté au chemin **par défaut** uniquement | `/abc123` → `/abc123/github-mcp` |
92
- | `AGENT_NAME` | Nom de l'agent affiché dans la GUI | `Agent Zero` |
93
- | `APP_GROUP` | Section repliable (repliée par défaut) du dashboard proxy dans laquelle placer l'app. Les apps partageant la même valeur sont regroupées ; si non défini, l'app apparaît dans la section « non groupées ». | `Développement` |
94
-
95
- `APP_PATH` / `APP_NAME` sont les noms canoniques de l'écosystème ; `PROXY_APP_*` puis `GUI_*` restent acceptés, dans cet ordre de priorité décroissante. `APP_PATH_PREFIX` **ne préfixe que le chemin par défaut** : si `APP_PATH` (ou `GUI_PATH`) est renseigné, sa valeur est prise telle quelle, sans préfixe — la saisie manuelle l'emporte. Les chemins sont normalisés (un seul `/` de séparation, pas de `/` final).
96
-
97
- ### L'environnement du process l'emporte sur le `.env`
98
-
99
- Une variable déjà présente dans l'environnement du process qui lance ce MCP
100
- (`export` dans un shell, bloc `env` d'une configuration MCP, ou superviseur comme
101
- `@imenam/mcp-gateway-manager`) **l'emporte sur la même clé du `.env` de ce
102
- dépôt**. C'est le comportement par défaut de dotenv, et la convention Unix
103
- habituelle : l'appelant gagne.
104
-
105
- Le `.env` reste rechargé à chaud — la GUI de configuration l'édite et la valeur
106
- est reprise sans redémarrer le serveur — mais ce rechargement ne peut plus
107
- écraser ce que l'appelant a injecté. Concrètement, une ligne
108
- `APP_GROUP=Développement` dans ce `.env` ne remplace plus un `APP_GROUP` fourni
109
- par un superviseur : elle ne s'applique que si personne n'en a fourni.
110
-
111
- Pour changer une variable que l'appelant fournit, changez-la chez l'appelant :
112
- l'éditer depuis la GUI n'aura aucun effet sur cette clé.
113
-
114
- ## 📝 Journalisation (Logs)
115
-
116
- - **Windows** : `C:\var\log\restricted-github-mcp\server.log`
117
- - **Linux/macOS** : `/var/log/restricted-github-mcp/server.log`
118
-
119
- Le dossier est créé automatiquement. Les logs du processus principal (`[MASTER]`) et de la GUI (`[GUI]`) sont centralisés dans le même fichier.
120
-
121
- ## 🖥️ Exemple de Configuration MCP
122
-
123
- ### Via npx (Recommandé)
124
-
125
- ```json
126
- {
127
- "mcpServers": {
128
- "@imenam/mcp-github": {
129
- "command": "npx",
130
- "args": ["-y", "@imenam/mcp-github"],
131
- "env": {
132
- "GITHUB_TOKEN": "votre_token_github_ici",
133
- "CLONE_DIR": "/chemin/vers/dossier-de-clonage",
134
- "MCP_DATA_DIR": "./data",
135
- "PROXY_URL": "http://localhost:3000",
136
- "GUI_PATH": "/github-mcp",
137
- "GUI_NAME": "GitHub MCP Config",
138
- "AGENT_NAME": "Mon Agent",
139
- "SSH_HOST": "mon-serveur.example.com",
140
- "SSH_USER": "ubuntu",
141
- "SSH_PORT": "22",
142
- "SSH_KEY_PATH": "/chemin/vers/.ssh/id_ed25519",
143
- "SSH_REMOTE_PATH": "/var/www/mon-projet",
144
- "TEST_COMMAND": "npx tsc --noEmit --pretty"
145
- }
146
- }
147
- }
148
- }
149
- ```
150
-
151
- > Les variables `PROXY_URL`, `GUI_*` et `AGENT_NAME` sont optionnelles selon votre usage. Les variables SSH (`SSH_HOST`, `SSH_USER`, `SSH_PORT`, `SSH_KEY_PATH`, `SSH_REMOTE_PATH`, `TEST_COMMAND`) ainsi que `LOCAL_DEPLOY` sont désormais gérées **par projet via la GUI** ; les inclure ici ne sert qu'à amorcer la configuration par défaut au premier démarrage (voir la note Migration ci-dessus).
152
-
153
- ### Via installation locale
154
-
155
- ```json
156
- {
157
- "mcpServers": {
158
- "@imenam/mcp-github": {
159
- "command": "node",
160
- "args": ["C:/chemin/vers/gitHubMCP/dist/index.js"],
161
- "env": {
162
- "GITHUB_TOKEN": "votre_token_github_ici",
163
- "CLONE_DIR": "./cloned_repos"
164
- }
165
- }
166
- }
167
- }
168
- ```
169
-
170
- ## 🛠️ Installation locale & build
171
-
172
- ```bash
173
- git clone https://github.com/votre-pseudo/gitHubMCP.git
174
- cd gitHubMCP
175
- npm install
176
- cp .env.example .env
177
- # Éditez .env avec vos valeurs
178
- npm run build
179
- ```
180
-
181
- ## 📦 Publication Automatisée
182
-
183
- Ce projet est configuré pour être publié sur npm via **GitHub Actions** à chaque nouvelle Release :
184
-
185
- 1. Mettez à jour la version dans `package.json`.
186
- 2. Poussez sur GitHub et créez une Release.
187
- 3. Le workflow OIDC publie automatiquement sur npm.
188
-
189
- ## 📄 Licence
190
-
191
- ISC
1
+ # Restricted GitHub MCP Server
2
+
3
+ Un serveur MCP (Model Context Protocol) spécialisé pour automatiser le workflow Git local vers GitHub, avec délégation des compilations/tests soit à un serveur distant via SSH, soit directement en local sur la même machine. Conçu pour être minimaliste et sécurisé.
4
+
5
+ ## 🚀 Outils disponibles
6
+
7
+ ### Git local → GitHub
8
+
9
+ - **`commit`** : Exécute `git add .` puis crée un commit avec le message fourni. *Ne pousse pas vers le remote.*
10
+ - **`push`** : Pousse la branche actuelle vers `TARGET_BRANCH`. *Ne fait pas de commit.*
11
+ - **`commit_and_push`** : Enchaîne `git add .`, commit et push vers `TARGET_BRANCH`.
12
+ - **`pull_request`** : Crée une Pull Request GitHub de `TARGET_BRANCH` vers `BASE_BRANCH`.
13
+ - **`clone`** : Clone `TARGET_REPO` dans `CLONE_DIR/<TARGET_BRANCH>/`. Crée la branche distante si elle n'existe pas encore.
14
+ - **`get_info`** : Affiche la configuration active (dépôt, branches, chemins) sans exposer les secrets.
15
+ - **`get_logs`** : Récupère les dernières lignes du fichier de log serveur. Argument optionnel : `lines` (défaut : 50).
16
+
17
+ ### Déploiement & test (serveur distant ou local)
18
+
19
+ Selon la configuration active, ces outils s'exécutent soit sur un serveur distant via SSH, soit directement en local sur la machine du serveur MCP (interrupteur `LOCAL_DEPLOY`).
20
+
21
+ - **`deploy`** : Exécute `git fetch --all`, `git checkout TARGET_BRANCH`, `git pull` dans `SSH_REMOTE_PATH`. Le repo doit être préalablement cloné à cet emplacement (sur le serveur distant, ou en local).
22
+ - **`test`** : Exécute `TEST_COMMAND` (cwd = `SSH_REMOTE_PATH`) et renvoie stdout, stderr et exit code. Stocke la sortie pour `test_logs`.
23
+ - **`test_logs`** : Renvoie la sortie du dernier `test` exécuté sans relancer la commande.
24
+
25
+ > **Mode local (`LOCAL_DEPLOY`)** : quand il est activé, aucune connexion SSH n'est ouverte — la commande est lancée directement dans le shell de la machine, dans le dossier `SSH_REMOTE_PATH` (interprété comme un chemin local). Pratique lorsque le serveur MCP tourne sur le serveur de déploiement lui-même.
26
+
27
+ ### Flux typique agent
28
+
29
+ ```
30
+ clone → (modifier des fichiers) → commit_and_push → deploy → test
31
+ ↑ |
32
+ └─── (corriger) ────────┘ (si exit code ≠ 0)
33
+ ```
34
+
35
+ ## 🔀 Rôles : faire tourner le MCP derrière mcp-http-gateway
36
+
37
+ Déclaré directement dans la configuration de l'agent, le MCP expose à l'agent son token GitHub, le dépôt et la branche cibles : l'agent peut les lire, et les modifier. Pour l'empêcher, le MCP tourne dans [mcp-http-gateway](https://www.npmjs.com/package/@imenam/mcp-http-gateway), sur un autre serveur. La variable `GITHUB_MCP_ROLE` fixe le rôle de l'instance :
38
+
39
+ | Rôle | Où | Git s'exécute |
40
+ | :--- | :--- | :--- |
41
+ | `standalone` (défaut) | Machine de l'agent | Sur la même machine, en direct vers GitHub avec le token |
42
+ | `remote` | Dans mcp-http-gateway | Sur la machine de l'agent, lancé par le relais agent, à travers le proxy git de l'instance |
43
+
44
+ En rôle `remote`, l'instance est la seule à connaître le token, le dépôt et les branches. Elle expose tous les outils : l'agent les appelle à travers le relais agent du gateway (`mcp-http-gateway --mcp`), comme n'importe quel outil du gateway.
45
+
46
+ ### Le proxy git
47
+
48
+ L'instance ouvre un proxy git sur `127.0.0.1:<MCP_HTTP_RELAY_PORT>`, que le gateway rend joignable à `<gateway>/<route>/http/*` après avoir authentifié la requête. Ce proxy :
49
+
50
+ - envoie toujours les requêtes vers `TARGET_REPO`, quel que soit le chemin demandé par git ;
51
+ - lit les commandes de chaque push avant de contacter GitHub, et refuse tout push qui vise une autre ref que `refs/heads/<TARGET_BRANCH>` ou qui la supprime. GitHub n'est alors jamais contacté, et git affiche `! [remote rejected] … (seule la branche <TARGET_BRANCH> est autorisée)` ;
52
+ - refuse tout push quand la configuration est en lecture seule ;
53
+ - ajoute le token GitHub, que la machine de l'agent ne voit jamais ;
54
+ - rend un refus du token par GitHub (`401`) comme une erreur `502` du proxy, pour que git ne demande pas d'identifiants.
55
+
56
+ Le proxy ne bloque pas les push forcés sur `TARGET_BRANCH`. Pour les interdire, activez une règle de protection de branche côté GitHub.
57
+
58
+ ### Git sur la machine de l'agent
59
+
60
+ Les outils `clone`, `init_working_dir`, `commit`, `push`, `commit_and_push` et `check_status` font exécuter git par le relais agent, selon la convention d'exécution de mcp-http-gateway (voir son README, *Exécution sur la machine de l'agent*) :
61
+
62
+ 1. L'outil répond au relais agent par une demande d'exécution : des commandes `git`, lancées sans shell.
63
+ 2. Le relais agent les exécute sur la machine de l'agent, puis rappelle l'outil avec les résultats.
64
+ 3. L'outil enchaîne les étapes suivantes, puis rend son résultat final. L'agent ne voit que ce résultat.
65
+
66
+ - **Copie de travail :** `REPO_PATH` s'il est configuré sur l'instance, sinon `<dossier de lancement du relais agent>/<nom du dépôt>`.
67
+ - **Remote git :** `<GATEWAY_URL><MCP_SERVER_ROUTE>/http/repo.git`. `clone` et `init_working_dir` y font pointer `origin`.
68
+ - **Jeton du gateway :** passé à git par variables d'environnement (`GIT_CONFIG_*`), limité aux URL du gateway. Il n'apparaît ni dans les arguments des commandes, ni dans `.git/config`.
69
+ - **Prérequis :** la route de l'instance (ex. `/github`) doit figurer dans `GATEWAY_EXEC_ROUTES`, la liste blanche du relais agent. Sinon, ces outils refusent l'appel en nommant la route à ajouter.
70
+
71
+ Un `git push` lancé à la main par l'agent vers `origin` passe par le même proxy et subit les mêmes contrôles.
72
+
73
+ Les outils `pull_request`, `deploy`, `test` et `test_logs` s'exécutent entièrement sur l'instance.
74
+
75
+ ### Configuration de l'instance `remote` (dans le gateway)
76
+
77
+ ```json
78
+ {
79
+ "mcpServers": {
80
+ "github": {
81
+ "command": "npx",
82
+ "args": ["-y", "@imenam/mcp-github"],
83
+ "env": {
84
+ "MCP_SERVER_ROUTE": "/github",
85
+ "MCP_HTTP_RELAY_PORT": "4610",
86
+ "GITHUB_MCP_ROLE": "remote",
87
+ "GITHUB_TOKEN": "votre_token_github_ici",
88
+ "MCP_DATA_DIR": "/chemin/vers/data",
89
+ "PROXY_URL": "http://localhost:3000"
90
+ }
91
+ }
92
+ }
93
+ }
94
+ ```
95
+
96
+ - `MCP_SERVER_ROUTE` et `MCP_HTTP_RELAY_PORT` sont des directives du gateway. L'instance lit la route pour construire l'URL du remote git, et le port pour ouvrir son proxy. Elle refuse de démarrer sans l'une ou l'autre.
97
+ - `REPO_PATH` (optionnel) fixe la copie de travail sur la machine de l'agent.
98
+ - Le dépôt, les branches et le déploiement se règlent dans la GUI de l'instance. En rôle `remote`, la GUI ne propose ni le clonage ni la suppression du dossier de travail : la copie de travail est sur la machine de l'agent.
99
+
100
+ ### Configuration du relais agent (chez l'agent)
101
+
102
+ Le relais agent est celui du gateway ; il suffit d'y autoriser la route de l'instance :
103
+
104
+ ```json
105
+ {
106
+ "mcpServers": {
107
+ "gateway": {
108
+ "command": "npx",
109
+ "args": ["-y", "@imenam/mcp-http-gateway", "--mcp"],
110
+ "env": {
111
+ "GATEWAY_URL": "https://gateway.example.com",
112
+ "GATEWAY_TOKEN": "jeton_du_gateway",
113
+ "GATEWAY_EXEC_ROUTES": "/github"
114
+ }
115
+ }
116
+ }
117
+ }
118
+ ```
119
+
120
+ ## 🖥️ Interface de Configuration (GUI)
121
+
122
+ Le serveur lance automatiquement une interface graphique au démarrage (si `PROXY_URL` est défini). Elle permet de :
123
+
124
+ - Gérer plusieurs configurations nommées (multi-projets).
125
+ - Sélectionner le dépôt et les branches via des menus déroulants GitHub.
126
+ - Configurer le déploiement **par projet** : serveur SSH (hôte, utilisateur, port, clé privée) et commande de test, ou bascule en **déploiement local** sans SSH.
127
+ - Visualiser la configuration active en temps réel.
128
+
129
+ Le formulaire est organisé en sections repliables : **Dépôt & branches** (toujours dépliée, cœur de l'usage), **Options** et **Déploiement & test** (repliées par défaut, le paramétrage serveur n'étant fait qu'une fois). Activer l'interrupteur **Déployer en local** masque automatiquement les champs de connexion SSH devenus inutiles.
130
+
131
+ > **Proxy dynamique** : La GUI s'enregistre auprès du reverse proxy défini dans `PROXY_URL` et reçoit un port dynamique. Elle est alors accessible sous le chemin effectivement enregistré — `GUI_PATH` seul par défaut, éventuellement préfixé par `APP_PATH_PREFIX` (voir *Variables GUI / Proxy*). L'enregistrement passe par le client partagé [`@imenam/mcp-gui-interface`](https://www.npmjs.com/package/@imenam/mcp-gui-interface), commun à tous les MCP à interface ; le timeout d'enregistrement est de **1 s** (il était de 5 s avec l'ancien client interne).
132
+
133
+ ### Exposition réseau
134
+
135
+ La GUI écoute uniquement sur `127.0.0.1`, comme le proxy git : une autre machine y accède par le proxy ou le gateway manager. Un garde de requêtes fourni par [`@imenam/mcp-gui-interface`](https://www.npmjs.com/package/@imenam/mcp-gui-interface) répond `403` à toute requête dont l'en-tête `Host` n'est pas `localhost`, `127.0.0.1` ou `::1` (protection contre le DNS rebinding), ainsi qu'aux requêtes de modification (`POST`, `DELETE`…) émises depuis un autre site (`Sec-Fetch-Site` ou `Origin` étranger). L'hôte de `PROXY_URL` fait partie des origines acceptées.
136
+
137
+ ## ⚙️ Configuration
138
+
139
+ Les variables **globales** (token, dossiers, proxy) sont définies dans un fichier `.env` ou dans la section `env` du client MCP. Les **paramètres par projet** (dépôt, branches, déploiement SSH ou local…) sont gérés dans l'interface GUI et stockés dans `configs.json`.
140
+
141
+ ### Variables GitHub
142
+
143
+ | Variable | Description | Exemple |
144
+ | :--- | :--- | :--- |
145
+ | `GITHUB_TOKEN` | Personal Access Token GitHub (PAT) | `github_pat_...` |
146
+ | `MCP_DATA_DIR` | Dossier contenant `configs.json` (multi-configs) | `./data` |
147
+ | `CLONE_DIR` | Dossier racine dans lequel les branches seront clonées | `./cloned_repos` |
148
+
149
+ > Les configurations par projet (dépôt, branches, mode lecture seule…) sont gérées dans l'interface GUI et stockées dans `configs.json`.
150
+
151
+ ### Paramètres de déploiement (outils `deploy` / `test`)
152
+
153
+ Ces paramètres sont définis **par projet** dans l'interface GUI et stockés dans `configs.json` (et non dans le `.env` global). Chaque configuration peut donc viser un serveur — et une clé — différents, ou basculer en mode local.
154
+
155
+ | Paramètre | Description | Exemple |
156
+ | :--- | :--- | :--- |
157
+ | `LOCAL_DEPLOY` | Si activé, exécution locale sans SSH (la machine du serveur MCP) | `false` |
158
+ | `SSH_HOST` | Adresse IP ou hostname du serveur distant | `mon-serveur.example.com` |
159
+ | `SSH_USER` | Utilisateur SSH | `deploy` |
160
+ | `SSH_PORT` | Port SSH (défaut : 22) | `22` |
161
+ | `SSH_KEY_PATH` | Chemin absolu vers la clé privée SSH | `C:/Users/.../.ssh/id_ed25519` |
162
+ | `SSH_REMOTE_PATH` | Chemin du repo (distant en mode SSH, **local** si `LOCAL_DEPLOY`) | `/var/www/mon-projet` |
163
+ | `TEST_COMMAND` | Commande de test à exécuter | `npx tsc --noEmit --pretty` |
164
+
165
+ > En mode local, seuls `SSH_REMOTE_PATH` (qui doit exister sur le disque) et `TEST_COMMAND` sont utilisés ; les champs de connexion SSH sont ignorés.
166
+
167
+ > **Note** : Pour `tsc`, utilisez `--pretty` pour obtenir la sortie complète avec contexte de code (sans cette option, `tsc` passe en format compact une ligne par erreur quand il n'y a pas de TTY).
168
+
169
+ > **Migration** : ces clés peuvent aussi être fournies via le `.env` ou la section `env` du client MCP. Elles ne sont alors lues **qu'une seule fois** au premier démarrage (si `configs.json` n'existe pas encore) pour amorcer la configuration par défaut ; ensuite, c'est la GUI qui fait foi.
170
+
171
+ ### Variables GUI / Proxy
172
+
173
+ | Variable | Description | Exemple |
174
+ | :--- | :--- | :--- |
175
+ | `PROXY_URL` | URL du reverse proxy central | `http://localhost:3000` |
176
+ | `APP_PATH` | Chemin d'accès via le proxy. Nom canonique, commun à tous les MCP. | `/github-mcp` |
177
+ | `APP_NAME` | Nom affiché dans le proxy. Nom canonique. | `GitHub MCP Config` |
178
+ | `GUI_PATH` | Ancien nom de `APP_PATH`, toujours accepté (utilisé si `APP_PATH` et `PROXY_APP_PATH` sont absents) | `/github-mcp` (défaut) |
179
+ | `GUI_NAME` | Ancien nom de `APP_NAME`, toujours accepté (même ordre de priorité) | `GitHub MCP Config` (défaut) |
180
+ | `APP_PATH_PREFIX` | Préfixe de namespace ajouté au chemin **par défaut** uniquement | `/abc123` → `/abc123/github-mcp` |
181
+ | `AGENT_NAME` | Nom de l'agent affiché dans la GUI | `Agent Zero` |
182
+ | `APP_GROUP` | Section repliable (repliée par défaut) du dashboard proxy dans laquelle placer l'app. Les apps partageant la même valeur sont regroupées ; si non défini, l'app apparaît dans la section « non groupées ». | `Développement` |
183
+
184
+ `APP_PATH` / `APP_NAME` sont les noms canoniques de l'écosystème ; `PROXY_APP_*` puis `GUI_*` restent acceptés, dans cet ordre de priorité décroissante. `APP_PATH_PREFIX` **ne préfixe que le chemin par défaut** : si `APP_PATH` (ou `GUI_PATH`) est renseigné, sa valeur est prise telle quelle, sans préfixe — la saisie manuelle l'emporte. Les chemins sont normalisés (un seul `/` de séparation, pas de `/` final).
185
+
186
+ ### L'environnement du process l'emporte sur le `.env`
187
+
188
+ Une variable déjà présente dans l'environnement du process qui lance ce MCP
189
+ (`export` dans un shell, bloc `env` d'une configuration MCP, ou superviseur comme
190
+ `@imenam/mcp-gateway-manager`) **l'emporte sur la même clé du `.env` de ce
191
+ dépôt**. C'est le comportement par défaut de dotenv, et la convention Unix
192
+ habituelle : l'appelant gagne.
193
+
194
+ Le `.env` reste rechargé à chaud — la GUI de configuration l'édite et la valeur
195
+ est reprise sans redémarrer le serveur — mais ce rechargement ne peut plus
196
+ écraser ce que l'appelant a injecté. Concrètement, une ligne
197
+ `APP_GROUP=Développement` dans ce `.env` ne remplace plus un `APP_GROUP` fourni
198
+ par un superviseur : elle ne s'applique que si personne n'en a fourni.
199
+
200
+ Pour changer une variable que l'appelant fournit, changez-la chez l'appelant :
201
+ l'éditer depuis la GUI n'aura aucun effet sur cette clé.
202
+
203
+ ## 📝 Journalisation (Logs)
204
+
205
+ - **Windows** : `C:\var\log\restricted-github-mcp\server.log`
206
+ - **Linux/macOS** : `/var/log/restricted-github-mcp/server.log`
207
+
208
+ Le dossier est créé automatiquement. Les logs du processus principal (`[MASTER]`) et de la GUI (`[GUI]`) sont centralisés dans le même fichier.
209
+
210
+ ## 🖥️ Exemple de Configuration MCP
211
+
212
+ ### Via npx (Recommandé)
213
+
214
+ ```json
215
+ {
216
+ "mcpServers": {
217
+ "@imenam/mcp-github": {
218
+ "command": "npx",
219
+ "args": ["-y", "@imenam/mcp-github"],
220
+ "env": {
221
+ "GITHUB_TOKEN": "votre_token_github_ici",
222
+ "CLONE_DIR": "/chemin/vers/dossier-de-clonage",
223
+ "MCP_DATA_DIR": "./data",
224
+ "PROXY_URL": "http://localhost:3000",
225
+ "GUI_PATH": "/github-mcp",
226
+ "GUI_NAME": "GitHub MCP Config",
227
+ "AGENT_NAME": "Mon Agent",
228
+ "SSH_HOST": "mon-serveur.example.com",
229
+ "SSH_USER": "ubuntu",
230
+ "SSH_PORT": "22",
231
+ "SSH_KEY_PATH": "/chemin/vers/.ssh/id_ed25519",
232
+ "SSH_REMOTE_PATH": "/var/www/mon-projet",
233
+ "TEST_COMMAND": "npx tsc --noEmit --pretty"
234
+ }
235
+ }
236
+ }
237
+ }
238
+ ```
239
+
240
+ > Les variables `PROXY_URL`, `GUI_*` et `AGENT_NAME` sont optionnelles selon votre usage. Les variables SSH (`SSH_HOST`, `SSH_USER`, `SSH_PORT`, `SSH_KEY_PATH`, `SSH_REMOTE_PATH`, `TEST_COMMAND`) ainsi que `LOCAL_DEPLOY` sont désormais gérées **par projet via la GUI** ; les inclure ici ne sert qu'à amorcer la configuration par défaut au premier démarrage (voir la note Migration ci-dessus).
241
+
242
+ ### Via installation locale
243
+
244
+ ```json
245
+ {
246
+ "mcpServers": {
247
+ "@imenam/mcp-github": {
248
+ "command": "node",
249
+ "args": ["C:/chemin/vers/gitHubMCP/dist/index.js"],
250
+ "env": {
251
+ "GITHUB_TOKEN": "votre_token_github_ici",
252
+ "CLONE_DIR": "./cloned_repos"
253
+ }
254
+ }
255
+ }
256
+ }
257
+ ```
258
+
259
+ ## 🛠️ Installation locale & build
260
+
261
+ ```bash
262
+ git clone https://github.com/votre-pseudo/gitHubMCP.git
263
+ cd gitHubMCP
264
+ npm install
265
+ cp .env.example .env
266
+ # Éditez .env avec vos valeurs
267
+ npm run build
268
+ ```
269
+
270
+ ## 📦 Publication Automatisée
271
+
272
+ Ce projet est configuré pour être publié sur npm via **GitHub Actions** à chaque nouvelle Release :
273
+
274
+ 1. Mettez à jour la version dans `package.json`.
275
+ 2. Poussez sur GitHub et créez une Release.
276
+ 3. Le workflow OIDC publie automatiquement sur npm.
277
+
278
+ ## 📄 Licence
279
+
280
+ ISC
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=config-server.guard.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-server.guard.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/config-server.guard.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,135 @@
1
+ import { describe, it, expect, vi, beforeAll } from "vitest";
2
+ import http from "node:http";
3
+ import net from "node:net";
4
+ import os from "node:os";
5
+ /**
6
+ * Exposition réseau de la GUI de configuration : `startConfigServer` démarre le
7
+ * vrai serveur Express, avec le proxy et la configuration remplacés par des
8
+ * doublures. Les requêtes passent par `node:http`, seul moyen de fixer Host.
9
+ */
10
+ /** URL du proxy connue uniquement de `getConfig()`, pas de `process.env`. */
11
+ const PROXY_URL = "http://proxy.lan:3000";
12
+ let port;
13
+ vi.mock("../logger.js", () => ({ logToFile: vi.fn() }));
14
+ vi.mock("../utils/role.js", () => ({ getRole: () => "standalone" }));
15
+ vi.mock("../utils/config.js", () => ({
16
+ getConfig: () => ({ PROXY_URL, GUI_PATH: "/github-mcp", GUI_NAME: "GitHub MCP Config" }),
17
+ updateEnvFile: vi.fn(),
18
+ safeSend: vi.fn(() => true),
19
+ migrateFromLegacyEnv: vi.fn(),
20
+ getAllConfigNames: () => [],
21
+ getConfigByName: () => null,
22
+ saveConfiguration: vi.fn(),
23
+ deleteConfiguration: () => false,
24
+ setCurrentConfiguration: () => true,
25
+ getCurrentConfigName: () => "demo",
26
+ }));
27
+ vi.mock("@imenam/mcp-gui-interface", async (importOriginal) => {
28
+ const actual = await importOriginal();
29
+ return {
30
+ ...actual,
31
+ ProxyClient: class {
32
+ async register() {
33
+ return { success: true, port, path: "/github-mcp" };
34
+ }
35
+ async unregister() { }
36
+ },
37
+ };
38
+ });
39
+ function freePort() {
40
+ return new Promise((resolve, reject) => {
41
+ const server = net.createServer();
42
+ server.once("error", reject);
43
+ server.listen(0, "127.0.0.1", () => {
44
+ const { port: free } = server.address();
45
+ server.close(() => resolve(free));
46
+ });
47
+ });
48
+ }
49
+ function canConnect(host, target) {
50
+ return new Promise((resolve) => {
51
+ const socket = net.connect({ host, port: target });
52
+ socket.setTimeout(1000);
53
+ socket.once("connect", () => {
54
+ socket.destroy();
55
+ resolve(true);
56
+ });
57
+ socket.once("error", () => resolve(false));
58
+ socket.once("timeout", () => {
59
+ socket.destroy();
60
+ resolve(false);
61
+ });
62
+ });
63
+ }
64
+ async function waitForListening(target) {
65
+ for (let attempt = 0; attempt < 50; attempt++) {
66
+ if (await canConnect("127.0.0.1", target))
67
+ return;
68
+ await new Promise((resolve) => setTimeout(resolve, 100));
69
+ }
70
+ throw new Error(`La GUI n'écoute pas sur 127.0.0.1:${target}`);
71
+ }
72
+ function request(method, path, headers, body) {
73
+ return new Promise((resolve, reject) => {
74
+ const req = http.request({ host: "127.0.0.1", port, method, path, headers }, (res) => {
75
+ let data = "";
76
+ res.setEncoding("utf8");
77
+ res.on("data", (chunk) => (data += chunk));
78
+ res.on("end", () => resolve({ status: res.statusCode ?? 0, body: data }));
79
+ });
80
+ req.on("error", reject);
81
+ if (body !== undefined)
82
+ req.write(body);
83
+ req.end();
84
+ });
85
+ }
86
+ const saveBody = JSON.stringify({ name: "demo", config: { GITHUB_TOKEN: "x" } });
87
+ const jsonHeaders = { "Content-Type": "application/json" };
88
+ /** Première adresse IPv4 non loopback de la machine, s'il y en a une. */
89
+ const externalIpv4 = Object.values(os.networkInterfaces())
90
+ .flat()
91
+ .find((address) => address && address.family === "IPv4" && !address.internal)?.address;
92
+ beforeAll(async () => {
93
+ port = await freePort();
94
+ vi.spyOn(console, "error").mockImplementation(() => { });
95
+ const { startConfigServer } = await import("../config-server.js");
96
+ await startConfigServer();
97
+ await waitForListening(port);
98
+ });
99
+ describe("config-server — exposition réseau", () => {
100
+ it("refuse un Host étranger (DNS rebinding)", async () => {
101
+ const reply = await request("GET", "/api/configs", { Host: `attacker.example:${port}` });
102
+ expect(reply.status).toBe(403);
103
+ expect(JSON.parse(reply.body).error).toMatch(/not allowed/);
104
+ });
105
+ it("refuse une modification cross-site signalée par Sec-Fetch-Site", async () => {
106
+ const reply = await request("POST", "/api/configs", { ...jsonHeaders, Host: `localhost:${port}`, "Sec-Fetch-Site": "cross-site" }, saveBody);
107
+ expect(reply.status).toBe(403);
108
+ });
109
+ it("refuse une modification venant d'une Origin étrangère", async () => {
110
+ const reply = await request("POST", "/api/configs", { ...jsonHeaders, Host: `localhost:${port}`, Origin: "http://attacker.example" }, saveBody);
111
+ expect(reply.status).toBe(403);
112
+ });
113
+ it("filtre avant l'analyse du corps : un JSON invalide cross-site reçoit 403", async () => {
114
+ const reply = await request("POST", "/api/configs", { ...jsonHeaders, Host: `localhost:${port}`, Origin: "http://attacker.example" }, "{not json");
115
+ expect(reply.status).toBe(403);
116
+ });
117
+ it("accepte une modification relayée par le proxy (Origin localhost:3000)", async () => {
118
+ const reply = await request("POST", "/api/configs", {
119
+ ...jsonHeaders,
120
+ Host: `localhost:${port}`,
121
+ Origin: "http://localhost:3000",
122
+ "Sec-Fetch-Site": "same-origin",
123
+ }, saveBody);
124
+ expect(reply.status).toBe(200);
125
+ expect(JSON.parse(reply.body).message).toMatch(/saved/);
126
+ });
127
+ it("fait confiance à l'hôte du PROXY_URL de la configuration", async () => {
128
+ const reply = await request("POST", "/api/configs", { ...jsonHeaders, Host: `localhost:${port}`, Origin: PROXY_URL }, saveBody);
129
+ expect(reply.status).toBe(200);
130
+ });
131
+ it.skipIf(!externalIpv4)("n'est pas joignable sur l'adresse IPv4 externe", async () => {
132
+ expect(await canConnect(externalIpv4, port)).toBe(false);
133
+ });
134
+ });
135
+ //# sourceMappingURL=config-server.guard.test.js.map