Pôle Formation UIMM-CVDL
L'objectif final est de pouvoir parler à une IA (Gemini) et lui demander d'automatiser des tâches sur vos fichiers Google, comme : "Va dans ce Google Sheet, trouve toutes les lignes où la colonne B est 'En attente', et mets 'Pour Revue' dans la colonne F."
Le problème de sécurité est que, normalement, pour faire cela, vous devriez donner à l'IA un "pass-partout" (un scope .../auth/drive) qui lui donne accès à tous vos fichiers. C'est très risqué.
Nous allons construire ce "Maître d'Hôtel" sécurisé.
gas-fakes). C'est la base. Nous allons configurer l'outil gas-fakes pour qu'il puisse parler à Google. C'est l'étape la plus complexe, car elle implique la console Google Cloud.gas-fakes.gas-fakes)C'est la fondation. Nous allons obtenir les clés de l'atelier Google Cloud.
@gmail.com personnel est parfait).Dans votre terminal, créez un dossier pour le projet et installez gas-fakes.
mkdir projet-ia-secure
cd projet-ia-secure
npm install @mcpher/gas-fakes
Ensuite, téléchargez le dossier shells depuis le dépôt GitHub (lien corrigé) et placez-le dans votre dossier projet-ia-secure.
C'est ici que vous étiez bloqué. Suivez ce guide précisément. Il est conçu pour contourner les bugs de l'interface Google.
private dans votre projet. Renommez ce fichier credentials.json et placez-le dans projet-ia-secure/private/.@gmail.com) et cliquez sur "Créer".Attention : Vous êtes maintenant sur un formulaire en plusieurs étapes. NE CLIQUEZ PAS SUR LES ONGLETS DU MENU DE GAUCHE (Branding, Audience...). Vous devez suivre le flux avec les boutons en bas de page.
Page 1 : "Branding" (Informations sur l'application)
Mon App de Test IA@gmail.comhttps://www.google.com (juste pour passer).https://policies.google.com/privacy (juste pour passer).+ AJOUTER UN DOMAINE et entrez google.com.Page 2 : "Scopes" (Champs d'application)
https://www.googleapis.com/auth/spreadsheets
https://www.googleapis.com/auth/drive
https://www.googleapis.com/auth/documents
https://www.googleapis.com/auth/userinfo.email
openid
Page 3 : "Test users" (Utilisateurs test)
Puisque l'application est "Externe", elle est en "Mode Test". Seuls les utilisateurs listés ici peuvent l'utiliser.
stephane.jaubert@gmail.com).Page 4 : "Summary" (Résumé)
.env)Nous allons maintenant dire à gas-fakes où trouver vos clés.
projet-ia-secure.shells :
cd shells
./setup.sh
Enter GCP_PROJECT_ID : Entrez l'ID de votre projet (ex: mon-atelier-ia-123456). Vous le trouvez sur la page d'accueil de la console GCP.Enter CLIENT_CREDENTIAL_FILE : Entrez le chemin vers votre fichier JSON : ../private/credentials.json (le ../ est pour "remonter" d'un dossier).DRIVE_TEST_FILE_ID, STORE_TYPE...), appuyez simplement sur Entrée pour accepter les valeurs par défaut.Toujours dans le dossier shells/ :
bash setaccount.sh
bash enable.sh
Retournez à la racine de votre projet (cd ..). Créez deux fichiers de configuration :
appsscript.json (pour lister les permissions) :{
"oauthScopes": [
"https://www.googleapis.com/auth/spreadsheets",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/documents",
"https://www.googleapis.com/auth/userinfo.email",
"openid"
]
}
gasfakes.json (pour donner un "numéro de casier" unique à votre projet) :{
"manifest": "./appsscript.json",
"scriptId": "mon-projet-ia-secure-001"
}
La Partie 1 est terminée. Le "Moteur" est construit et fonctionnel.
Maintenant que le moteur tourne, nous allons y connecter le "Chauffeur" (Gemini).
Dans votre terminal, à la racine de projet-ia-secure, installez les modules pour le serveur MCP :
npm install @modelcontextprotocol/sdk zod
gas-fakes-mcp.js)Créez un fichier nommé gas-fakes-mcp.js à la racine de votre projet. C'est le "Maître d'Hôtel" qui écoutera Gemini.
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { exec } from "child_process";
import fs from "fs/promises";
import { setTimeout } from "node:timers/promises";
import { promisify } from "util";
import { z } from "zod";
const server = new McpServer({
name: "Serveur MCP pour gas-fakes",
version: "0.0.1",
});
const execAsync = promisify(exec);
const tool = {
name: "run_gas_with_gas-fakes", // Le nom de l'outil que Gemini utilisera
schema: {
description:
"Exécute un script Google Apps Script dans un 'sandbox' sécurisé via gas-fakes.",
inputSchema: {
gas_script: z
.string()
.describe(
`Le script Google Apps Script à exécuter.
IMPORTANT: Utiliser 'console.log()' au lieu de 'Logger.log()'.
Si le script est dans une fonction, ne pas oublier de l'appeler (ex: maFonction();).`
),
whitelistItems: z
.array(z.string().describe(`ID de fichier sur Google Drive`))
.describe(
`LISTE BLANCHE : IDs des fichiers (Sheets, Docs...) auxquels ce script a le droit d'accéder.`
)
.optional(),
sandbox: z
.boolean()
.describe(
`'true' (défaut) : Mode sécurisé. Le script ne peut rien faire en dehors de la whitelist.
'false' : Mode non-sécurisé.`
),
},
},
func: async (object = {}) => {
const { sandbox = true, whitelistItems = [], gas_script } = object;
const importFile = "./script_ia_temporaire.mjs"; // Fichier temporaire
function getImportScript() {
const importScriptAr = [
// Importe gas-fakes pour simuler l'environnement
`import "./node_modules/@mcpher/gas-fakes/main.js"`,
"",
];
if (whitelistItems.length === 0) {
// Mode Sandbox STANDARD (autorisé à créer de nouveaux fichiers)
importScriptAr.push(
sandbox ? `ScriptApp.__behavior.sandBoxMode = true;` : "",
`\n\n${gas_script}\n\n`,
// Met à la corbeille tous les fichiers créés pendant ce script
sandbox ? `ScriptApp.__behavior.trash();` : ""
);
} else {
// Mode Sandbox STRICT (uniquement la whitelist)
const wl = whitelistItems
.map((id) => `behavior.newIdWhitelistItem("${id}").setWrite(true)`)
.join(",");
importScriptAr.push(
`const behavior = ScriptApp.__behavior;`,
`behavior.sandboxMode = true;`,
`behavior.strictSandbox = true;`, // N'autorise QUE la whitelist
`behavior.setIdWhitelist([${wl}]);`, // Applique la whitelist
`\n\n${gas_script}\n\n`,
`ScriptApp.__behavior.trash();`
);
}
return importScriptAr.join("\n");
}
try {
// 1. Écrit le script de l'IA dans un fichier
const importScript = getImportScript();
await fs.writeFile(importFile, importScript);
await setTimeout(500); // Laisse le temps au fichier d'être écrit
// 2. Exécute le fichier avec Node.js
const { stdout } = await execAsync(`node ./${importFile}`);
return {
content: [{ type: "text", text: stdout || "Terminé." }],
isError: false,
};
} catch (err) {
// 3. Renvoie une erreur à Gemini si le script échoue
return {
content: [{ type: "text", text: err.message }],
isError: true,
};
} finally {
// 4. Supprime toujours le fichier temporaire
try {
await fs.unlink(importFile);
} catch (err) {
console.error(err.message);
}
}
},
};
const { name, schema, func } = tool;
server.registerTool(name, schema, func);
const transport = new StdioServerTransport();
await server.connect(transport);
settings.json du Gemini CLI (souvent dans votre dossier utilisateur, dans un dossier .gemini).mcpServers.Attention : Vous devez utiliser le chemin absolu vers votre script.
C:\\Utilisateurs\\Stephane\\projet-ia-secure\\gas-fakes-mcp.js (notez les doubles \\)./home/stephane/projet-ia-secure/gas-fakes-mcp.js{
"security": {
"auth": {
"selectedType": "..."
}
},
"ui": {
"theme": "Default"
},
"mcpServers": {
"gas-fakes": {
"command": "node",
"args": ["VOTRE_CHEMIN_ABSOLU_VERS/projet-ia-secure/gas-fakes-mcp.js"]
}
}
}
Tout est prêt. Lançons le test !
projet-ia-secure).gemini
gas-fakes. C'est la preuve que la connexion est réussie.Donnez ce prompt à Gemini :
Ce qui se passe :
DocumentApp.create()).run_gas_with_gas-fakes avec le script.gas-fakes l'exécute, crée le fichier réellement dans votre Google Drive.ScriptApp.__behavior.trash();).C'est le test ultime.
.../d/ID_DU_FICHIER/edit).[VOTRE_ID_DE_FICHIER]) :Ce qui se passe :
SpreadsheetApp.openById(...)).gas_script ET whitelistItems: ["[VOTRE_ID_DE_FICHIER]"].gas-fakes-mcp.js active le mode strict et n'autorise l'accès qu'à cet ID.Vous avez maintenant un assistant IA sécurisé, connecté à votre Google Workspace. Si l'IA avait essayé d'ouvrir un autre fichier, gas-fakes l'aurait bloqué avec une erreur de permission.