<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Julien Landuré</title>
    <description>The latest articles on DEV Community by Julien Landuré (@jlandure).</description>
    <link>https://dev.to/jlandure</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192673%2Fa76bdc95-3cd3-4bd5-b177-01f02dabcdb3.jpg</url>
      <title>DEV Community: Julien Landuré</title>
      <link>https://dev.to/jlandure</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jlandure"/>
    <language>en</language>
    <item>
      <title>Google Antigravity &amp; Gemini 4 Argon: How to Load Your Global ~/.agents Skills</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Wed, 14 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/gde/google-antigravity-gemini-4-argon-how-to-load-your-global-agents-skills-2cek</link>
      <guid>https://dev.to/gde/google-antigravity-gemini-4-argon-how-to-load-your-global-agents-skills-2cek</guid>
      <description>&lt;p&gt;Reuse your global Agent Skills in Google Antigravity with Gemini 4 Argon using a single symbolic link, and clean up your legacy Gemini configuration.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://agentskills.io/specification" rel="noopener noreferrer"&gt;Agent Skills specification&lt;/a&gt; has quickly become the standard way to package reusable instructions, scripts, and references for coding agents. Across tools listed on the &lt;a href="https://agentskills.io/clients" rel="noopener noreferrer"&gt;Agent Skills clients page&lt;/a&gt; such as Claude Code, Cursor, Gemini CLI, and OpenAI Codex, the community has converged on a shared directory convention: &lt;code&gt;.agents/skills/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;When you test Gemini 4 Argon inside Google Antigravity (AGY), project-level skills inside &lt;code&gt;&amp;lt;workspace&amp;gt;/.agents/skills/&lt;/code&gt; work out of the box. Global skills stored in &lt;code&gt;~/.agents/skills/&lt;/code&gt;, however, do not appear at startup.&lt;/p&gt;

&lt;p&gt;In this article, we’ll explore how the &lt;code&gt;.agents/skills/&lt;/code&gt; convention works, why our first configuration attempt with &lt;code&gt;skills.json&lt;/code&gt; failed in Antigravity, how to fix global skill discovery with a symbolic link, and how to clean up your &lt;code&gt;~/.gemini&lt;/code&gt; directory when migrating from Gemini CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 1. How the Agent Skills standard organizes your skills
&lt;/h2&gt;

&lt;p&gt;An &lt;a href="https://agentskills.io/" rel="noopener noreferrer"&gt;Agent Skill&lt;/a&gt; is a directory containing a &lt;code&gt;SKILL.md&lt;/code&gt; file (with &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; in YAML frontmatter, followed by Markdown instructions) and optional subdirectories like &lt;code&gt;scripts/&lt;/code&gt;, &lt;code&gt;references/&lt;/code&gt;, and &lt;code&gt;assets/&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.agents/skills/
├── ai-seo/
│ ├── SKILL.md
│ └── references/
├── blog-post-writer/
│ ├── SKILL.md
│ └── references/
└── humanizer/
    └── SKILL.md
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Instead of loading every instruction file into the prompt at once, compatible agents use progressive disclosure in three steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;At session start, the agent reads only the &lt;code&gt;name&lt;/code&gt; and &lt;code&gt;description&lt;/code&gt; of each skill (around 50 to 100 tokens per skill).&lt;/li&gt;
&lt;li&gt;When your prompt matches a skill’s description, or when you invoke it explicitly with &lt;code&gt;/skill-name&lt;/code&gt;, the agent loads the full &lt;code&gt;SKILL.md&lt;/code&gt; file.&lt;/li&gt;
&lt;li&gt;The agent reads supporting files in &lt;code&gt;references/&lt;/code&gt; or runs helper scripts in &lt;code&gt;scripts/&lt;/code&gt; only when needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For cross-client interoperability, the &lt;a href="https://agentskills.io/client-implementation/adding-skills-support" rel="noopener noreferrer"&gt;Agent Skills implementation guide&lt;/a&gt; recommends scanning two &lt;code&gt;.agents/skills/&lt;/code&gt; locations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;&amp;lt;project&amp;gt;/.agents/skills/&lt;/code&gt; for project-specific skills shared through Git.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.agents/skills/&lt;/code&gt; for user-level skills shared across all projects on your machine (including skills installed with &lt;code&gt;npx skills&lt;/code&gt; and tracked in &lt;code&gt;~/.agents/.skill-lock.json&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;According to the &lt;a href="https://antigravity.google/docs/skills" rel="noopener noreferrer"&gt;Google Antigravity Skills documentation&lt;/a&gt;, Antigravity scans &lt;code&gt;&amp;lt;workspace-root&amp;gt;/.agents/skills/&lt;/code&gt; at the workspace level (with backward compatibility for &lt;code&gt;.agent/skills/&lt;/code&gt;), whereas its global scope is set to &lt;code&gt;~/.gemini/config/skills/&lt;/code&gt; (unlike Gemini CLI, which already supports &lt;code&gt;~/.agents/skills/&lt;/code&gt; as a global alias).&lt;/p&gt;

&lt;h2&gt;
  
  
  ❌ 2. Attempt #1: Why &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt; fails
&lt;/h2&gt;

&lt;p&gt;If you check Antigravity’s built-in customization documentation (&lt;code&gt;agy-customizations&lt;/code&gt;), you will find a JSON configuration mechanism called &lt;code&gt;skills.json&lt;/code&gt; designed to register skills stored outside default locations.&lt;/p&gt;

&lt;p&gt;Our first try was to create &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt; pointing to &lt;code&gt;~/.agents/skills&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"entries"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"path"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"~/.agents/skills"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After restarting Antigravity and opening a new conversation, none of the skills from &lt;code&gt;~/.agents/skills&lt;/code&gt; were loaded.&lt;/p&gt;

&lt;p&gt;The reason is a subtle rule in Antigravity’s customization loader:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Config files are also honored on explicitly configured customization paths […] when the configured path points directly at a config file (e.g. &lt;code&gt;path/to/skills.json&lt;/code&gt;). A configured directory keeps its regular one-level scan; a manifest inside it is not expanded.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside a project repository, &lt;code&gt;&amp;lt;workspace&amp;gt;/.agents/skills.json&lt;/code&gt; is explicitly checked and expanded.&lt;/li&gt;
&lt;li&gt;Inside the global &lt;code&gt;~/.gemini/config/&lt;/code&gt; directory, Antigravity performs a direct one-level directory scan on &lt;code&gt;~/.gemini/config/skills/&lt;/code&gt; and &lt;code&gt;~/.gemini/config/plugins/&lt;/code&gt;. It ignores &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  ⚡️ 3. Attempt #2: Link &lt;code&gt;~/.gemini/config/skills&lt;/code&gt; to &lt;code&gt;~/.agents/skills&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Since Antigravity expects global standalone skills inside &lt;code&gt;~/.gemini/config/skills/&amp;lt;skill-name&amp;gt;/SKILL.md&lt;/code&gt;, the cleanest fix is a symbolic link from &lt;code&gt;~/.gemini/config/skills&lt;/code&gt; to &lt;code&gt;~/.agents/skills&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Before creating the symlink, check if you already have an existing &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt; file or a &lt;code&gt;~/.gemini/config/skills/&lt;/code&gt; folder:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you created &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt; during your own tests and it only points to &lt;code&gt;~/.agents/skills&lt;/code&gt;, you can remove it (&lt;code&gt;rm -f ~/.gemini/config/skills.json&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If your &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt; listed other custom directories, move that &lt;code&gt;skills.json&lt;/code&gt; into your project’s &lt;code&gt;&amp;lt;workspace&amp;gt;/.agents/skills.json&lt;/code&gt; (where Antigravity does expand it), or symlink those individual skill folders inside &lt;code&gt;~/.agents/skills/&lt;/code&gt; so everything stays in one place.&lt;/li&gt;
&lt;li&gt;If you already have a non-empty &lt;code&gt;~/.gemini/config/skills/&lt;/code&gt; directory, move its skill subfolders into &lt;code&gt;~/.agents/skills/&lt;/code&gt; first, then remove the empty directory (&lt;code&gt;mv ~/.gemini/config/skills/* ~/.agents/skills/ &amp;amp;&amp;amp; rmdir ~/.gemini/config/skills&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then create the symbolic link in your terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Point Antigravity's global skills folder to ~/.agents/skills&lt;/span&gt;
&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; ~/.agents/skills ~/.gemini/config/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify that the symbolic link points to your &lt;code&gt;.agents/skills&lt;/code&gt; directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; ~/.gemini/config/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will see output similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lrwxr-xr-x 1 user staff 26 Oct 13 11:05 /Users/user/.gemini/config/skills -&amp;gt; /Users/user/.agents/skills
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reload Antigravity. Your &lt;code&gt;~/.agents/skills&lt;/code&gt; catalog is now injected at startup, and you can trigger any skill directly in chat with &lt;code&gt;/skill-name&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9euelajirzl44vb3577.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl9euelajirzl44vb3577.webp" alt="Google Antigravity chat prompt with Gemini 4 Argon High selected and / for actions" width="800" height="467"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💪 ProTip: Watch out for nested plugin symlinks inside &lt;code&gt;~/.agents/skills/&lt;/code&gt;. Antigravity scans &lt;code&gt;~/.gemini/config/skills/&lt;/code&gt; only one level deep (&lt;code&gt;skills/&amp;lt;skill-name&amp;gt;/SKILL.md&lt;/code&gt;). If &lt;code&gt;~/.agents/skills/my-plugin&lt;/code&gt; is a symlink to a plugin repository where the skill file is nested at &lt;code&gt;my-plugin/skills/my-skill/SKILL.md&lt;/code&gt;, Antigravity will skip it. Point your symlink directly to the folder that immediately contains &lt;code&gt;SKILL.md&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🧹 4. Bonus: Clean up &lt;code&gt;~/.gemini&lt;/code&gt; when coming from Gemini CLI
&lt;/h2&gt;

&lt;p&gt;Because Google Antigravity and Gemini CLI both store state under &lt;code&gt;~/.gemini&lt;/code&gt;, that directory often accumulates old folders over time.&lt;/p&gt;

&lt;p&gt;First, a warning: do not delete &lt;code&gt;~/.gemini&lt;/code&gt;! Antigravity stores its active data there:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.gemini/antigravity/&lt;/code&gt; holds your conversation history (&lt;code&gt;conversations/&lt;/code&gt;, &lt;code&gt;conversation_summaries.db&lt;/code&gt;), generated artifacts (&lt;code&gt;brain/&lt;/code&gt;), and internal binaries.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.gemini/config/&lt;/code&gt; holds your active Antigravity configuration (&lt;code&gt;config.json&lt;/code&gt;), Model Context Protocol (MCP) server definitions (&lt;code&gt;mcp_config.json&lt;/code&gt;), installed &lt;code&gt;plugins/&lt;/code&gt;, and your new &lt;code&gt;skills&lt;/code&gt; symlink.&lt;/li&gt;
&lt;li&gt;Root files such as &lt;code&gt;oauth_creds.json&lt;/code&gt;, &lt;code&gt;google_accounts.json&lt;/code&gt;, &lt;code&gt;settings.json&lt;/code&gt;, and &lt;code&gt;GEMINI.md&lt;/code&gt; are still used if you run Gemini CLI in the terminal.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, you can inspect &lt;code&gt;~/.gemini&lt;/code&gt; and remove obsolete migration backups and temporary folders:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-al&lt;/span&gt; ~/.gemini
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is what you can safely clean up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;~/.gemini/antigravity-backup/&lt;/code&gt; and &lt;code&gt;~/.gemini/antigravity-ide/&lt;/code&gt;: migration snapshots created when upgrading from earlier builds. Once you have confirmed your conversations and settings work in Antigravity, you can delete both folders.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.gemini/tmp/&lt;/code&gt;: leftover temporary session directories.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.gemini/skills/&lt;/code&gt;: an older root-level skills folder sometimes populated by Gemini CLI extensions (for example, GCP Data Cloud skills listed in &lt;code&gt;.datacloud_skills_manifest&lt;/code&gt;). Antigravity does not scan &lt;code&gt;~/.gemini/skills/&lt;/code&gt;. If you have custom skills stored there, move them into &lt;code&gt;~/.agents/skills/&lt;/code&gt; first, then remove the obsolete folder.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Remove obsolete migration backups and temporary directories&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.gemini/antigravity-backup &lt;span class="se"&gt;\&lt;/span&gt;
       ~/.gemini/antigravity-ide &lt;span class="se"&gt;\&lt;/span&gt;
       ~/.gemini/tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: Before removing &lt;code&gt;~/.gemini/skills&lt;/code&gt;, run &lt;code&gt;ls -al ~/.gemini/skills&lt;/code&gt; to make sure you do not lose any custom skill you created for Gemini CLI.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion
&lt;/h2&gt;

&lt;p&gt;Making your &lt;code&gt;~/.agents/skills&lt;/code&gt; setup work with Google Antigravity and Gemini 4 Argon takes a single symbolic link once you know how its global loader operates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keep your shared skills in &lt;code&gt;~/.agents/skills/&lt;/code&gt; so they work across all &lt;a href="https://agentskills.io/clients" rel="noopener noreferrer"&gt;Agent Skills compatible clients&lt;/a&gt; and CLI tools.&lt;/li&gt;
&lt;li&gt;Skip &lt;code&gt;~/.gemini/config/skills.json&lt;/code&gt;, which is not expanded in the global configuration directory.&lt;/li&gt;
&lt;li&gt;Link &lt;code&gt;~/.gemini/config/skills&lt;/code&gt; to &lt;code&gt;~/.agents/skills&lt;/code&gt; with &lt;code&gt;ln -s ~/.agents/skills ~/.gemini/config/skills&lt;/code&gt; and reload Antigravity.&lt;/li&gt;
&lt;li&gt;Keep &lt;code&gt;~/.gemini/antigravity/&lt;/code&gt; and &lt;code&gt;~/.gemini/config/&lt;/code&gt;, and clean up legacy migration folders inside &lt;code&gt;~/.gemini/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;👉 Give it a try in your terminal and type &lt;code&gt;/&lt;/code&gt; in your next Antigravity chat session to see all your global skills in action!&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Go further with Antigravity
&lt;/h2&gt;

&lt;p&gt;Want to master autonomous coding agents in your daily workflow? In one day, the &lt;strong&gt;&lt;a href="https://techtown.fr/formations/coder-avec-antigravity/" rel="noopener noreferrer"&gt;Code with Antigravity training by TechTown&lt;/a&gt;&lt;/strong&gt; covers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;discovering Antigravity and the agentic paradigm;&lt;/li&gt;
&lt;li&gt;delegating development tasks to autonomous agents;&lt;/li&gt;
&lt;li&gt;validating agent work via Artifacts;&lt;/li&gt;
&lt;li&gt;customizing behavior with Rules, Skills, and MCP servers.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devaugmenté</category>
      <category>agentsia</category>
    </item>
    <item>
      <title>Opus génère ma vidéo de motion design en 20 minutes</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 13 Oct 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/techtown-fr/opus-genere-ma-video-de-motion-design-en-20-minutes-254n</link>
      <guid>https://dev.to/techtown-fr/opus-genere-ma-video-de-motion-design-en-20-minutes-254n</guid>
      <description>&lt;p&gt;Tout le monde teste des prompts pour qu’Opus 5.5 génère une vidéo. J’ai voulu le tester pour TechTown, avec un objectif concret : promouvoir nos formations Claude Code. À vrai dire, je suis bluffé par le résultat ⭐️&lt;/p&gt;

&lt;p&gt;En une vingtaine de minutes, Claude Code m’a livré un showreel de 21 secondes, en 1080p à 60 images par seconde, avec sa propre bande-son. Je n’ai ouvert aucun logiciel de montage.&lt;/p&gt;

&lt;p&gt;Dans cet article, nous allons voir le but de cette vidéo, le principe du prompt, puis ce que Claude a réellement fait sous le capot. Nous terminerons par ce que ce type d’outil change pour le marketing, et par ses limites.&lt;/p&gt;


  


&lt;p&gt;&lt;em&gt;Le showreel de 21 secondes généré par Claude Code avec Opus 5.5. La vidéo est aussi à voir &lt;a href="https://www.linkedin.com/posts/jlandure_tout-le-monde-teste-des-prompts-pour-que-ugcPost-7515341522557534208-Blpi/" rel="noopener noreferrer"&gt;sur LinkedIn&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Le but : une vidéo pour nos formations Claude Code
&lt;/h2&gt;

&lt;p&gt;Nous animons une formation Claude Code par mois, et nous avons déjà formé 175 personnes. Pour donner envie à d’autres équipes de se lancer, il nous fallait un format court et visuel, facile à partager sur LinkedIn.&lt;/p&gt;

&lt;p&gt;Habituellement, ce genre de vidéo passe par un motion designer et plusieurs allers-retours. Je voulais savoir si un agent de code pouvait tenir ce rôle, avec un brief aussi court que celui que j’enverrais à un prestataire.&lt;/p&gt;

&lt;h2&gt;
  
  
  💬 Le prompt
&lt;/h2&gt;

&lt;p&gt;Voici le prompt complet, envoyé tel quel à Claude Code :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;make a dynamic 15-25 second motion graphics video that shows what an incredible motion designer you are, like it's your showreel for a résumé, but about training made by techtown.fr -- go all out.
highlight : formation Claude Code.
Info : https://techtown.fr/formations/
Use /techtown-brand-guidelines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Le prompt ne dit rien de la technique : ni outil, ni format de fichier, ni découpage en scènes. Il donne en revanche tout ce qu’un bon brief créatif contient. Nous retrouvons d’ailleurs la méthode des 3C (Contexte, Clarté, Contraintes) que nous enseignons en formation :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;le contexte : un showreel de motion designer, sur les formations TechTown ;&lt;/li&gt;
&lt;li&gt;la clarté : un sujet à mettre en avant, la formation Claude Code ;&lt;/li&gt;
&lt;li&gt;les contraintes : une durée entre 15 et 25 secondes et la charte graphique TechTown.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;L’URL de la page formations donne à Claude une source de vérité. Il a récupéré lui-même les 16 formations, leurs catégories et leurs durées, au lieu d’inventer un catalogue.&lt;/p&gt;

&lt;p&gt;Le skill &lt;code&gt;/techtown-brand-guidelines&lt;/code&gt; contient notre charte : la police Poppins, le bleu &lt;code&gt;#1C62ED&lt;/code&gt;, les règles du logo, le style des boutons. Claude le charge avant de dessiner quoi que ce soit, et chaque couleur de la vidéo en vient.&lt;/p&gt;

&lt;p&gt;Enfin, « like it’s your showreel » et « go all out » fixent le niveau d’exigence. Ces quelques mots autorisent Claude à investir du temps dans les détails : flou de mouvement, sound design, transitions travaillées.&lt;/p&gt;

&lt;p&gt;💪 ProTip: écrivez votre prompt comme un brief à un professionnel. Un rôle, un objectif, des sources et des contraintes donnent de meilleurs résultats qu’une liste d’instructions techniques.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚙️ Sous le capot : comment Claude a travaillé
&lt;/h2&gt;

&lt;p&gt;C’est la partie qui m’a le plus impressionné. Claude ne dispose d’aucun outil vidéo dédié. Il a donc écrit la vidéo sous forme de code, puis l’a rendue avec des outils déjà installés sur mon Mac.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Rassembler la matière
&lt;/h3&gt;

&lt;p&gt;Claude a d’abord chargé le skill de charte graphique. Il a ensuite téléchargé la &lt;a href="https://techtown.fr/formations/" rel="noopener noreferrer"&gt;page formations&lt;/a&gt; avec &lt;code&gt;curl&lt;/code&gt; pour en extraire le texte, puis récupéré le logo SVG officiel du site. Le tracé de ce logo est réutilisé tel quel dans l’animation finale.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Choisir une technique : la vidéo comme une fonction du temps
&lt;/h3&gt;

&lt;p&gt;L’animation est une page HTML avec un &lt;code&gt;&amp;lt;canvas&amp;gt;&lt;/code&gt; de 1920×1080 pixels, dessiné en JavaScript avec l’API Canvas 2D. Le principe est simple : chaque image est une fonction pure du temps. La fonction &lt;code&gt;drawFrame(t)&lt;/code&gt; dessine exactement ce que l’on doit voir à &lt;code&gt;t&lt;/code&gt; secondes, et la même valeur de &lt;code&gt;t&lt;/code&gt; donne toujours la même image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;2.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;s1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// le point bleu du logo&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;5.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;s2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// le manifeste typographique&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;8.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;s3&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// le catalogue des formations&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;15.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;s4&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// le terminal Claude Code&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mf"&gt;18.0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;s5&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// les chiffres clés&lt;/span&gt;
  &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="nf"&gt;s6&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// le logo et l'appel à l'action&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ce déterminisme permet d’exporter la vidéo image par image, sans dépendre de la vitesse de la machine.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Construire un storyboard calé sur le rythme
&lt;/h3&gt;

&lt;p&gt;Claude a découpé la vidéo en six scènes, toutes calées sur un tempo de 120 BPM. Chaque coupe tombe sur un temps de la musique.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faxuaqndistxgw8mkahyd.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Faxuaqndistxgw8mkahyd.webp" alt="Six images extraites du showreel : le point bleu, le manifeste typographique, le catalogue des 16 formations, le terminal Claude Code, le compteur 175+ et le logo final" width="800" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Les six scènes du showreel, de la genèse du point bleu jusqu’au logo TechTown.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La scène la plus longue, de 8,5 à 15,5 secondes, est consacrée à Claude Code. Une carte du catalogue « Coder avec Claude Code » se détache, s’agrandit et devient un terminal. On y voit taper &lt;code&gt;claude&lt;/code&gt;, puis la demande « Forme mon équipe à Claude Code ». L’agent active ensuite le Plan Mode, génère un &lt;code&gt;CLAUDE.md&lt;/code&gt;, connecte un serveur MCP et lance des sous-agents. Le terminal se range ensuite sur le côté pour présenter les deux niveaux de formation.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Les techniques d’un motion designer
&lt;/h3&gt;

&lt;p&gt;Claude a appliqué les techniques d’un vrai showreel :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;des courbes d’accélération (&lt;code&gt;outExpo&lt;/code&gt;, &lt;code&gt;inOutCubic&lt;/code&gt;, &lt;code&gt;outBack&lt;/code&gt;) pour chaque mouvement ;&lt;/li&gt;
&lt;li&gt;des textes révélés par masque, et un effet de texte « décodé » où des glyphes aléatoires se résolvent de gauche à droite ;&lt;/li&gt;
&lt;li&gt;un compteur à rouleaux pour le nombre de formations ;&lt;/li&gt;
&lt;li&gt;des secousses de caméra synchronisées avec les impacts sonores ;&lt;/li&gt;
&lt;li&gt;un grain, un vignettage et un habillage de type caméra (repères d’angle, timecode, barre de progression).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Le flou de mouvement est le détail le plus technique. Pour chaque image, Claude dessine six sous-images réparties sur une demi-durée d’image, puis les moyenne. C’est l’équivalent d’un obturateur à 180° en cinéma.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;subs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;subs&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nx"&gt;shutter&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="nx"&gt;FPS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nf"&gt;drawScene&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ts&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;globalAlpha&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// moyenne glissante&lt;/span&gt;
  &lt;span class="nx"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;scene&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjuzgnokz6tow2fnky0zt.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fjuzgnokz6tow2fnky0zt.webp" alt="La carte Coder avec Claude Code se transforme en terminal, avec un flou de mouvement visible sur les contours" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;La transition entre le catalogue et le terminal : le flou de mouvement vient de la moyenne de six sous-images.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  5. Le son, synthétisé de A à Z
&lt;/h3&gt;

&lt;p&gt;La bande-son ne réutilise aucun sample. Un second script Node.js, &lt;code&gt;audio.mjs&lt;/code&gt;, calcule l’onde sonore échantillon par échantillon : 48 000 valeurs par seconde, en stéréo.&lt;/p&gt;

&lt;p&gt;Chaque instrument est une petite fonction mathématique. La grosse caisse est une sinusoïde dont la fréquence chute très vite. Les charleys et les claps sont du bruit blanc filtré. La basse et les nappes sont des ondes en dents de scie filtrées, sur une grille d’accords la mineur, fa, do, sol. Le script ajoute des montées, des impacts et des souffles pour les transitions.&lt;/p&gt;

&lt;p&gt;Côté mixage, la musique baisse à chaque coup de grosse caisse (le fameux &lt;em&gt;sidechain&lt;/em&gt; de la musique électronique), et une réverbération inspirée de Freeverb donne de l’espace au tout. Le script écrit enfin un fichier WAV.&lt;/p&gt;

&lt;p&gt;La synchronisation repose sur les mêmes horaires que l’animation. Il y a même un clic de clavier par caractère tapé dans le terminal, calculé avec la même formule que l’effet de frappe.&lt;/p&gt;

&lt;h3&gt;
  
  
  6. Le rendu : Chrome headless et ffmpeg
&lt;/h3&gt;

&lt;p&gt;Pour transformer la page en vidéo, Claude a écrit un petit serveur Node.js, sans aucune dépendance. Il sert la page et enregistre chaque image PNG que le navigateur lui envoie. Google Chrome, lancé en mode headless, calcule alors les 1 260 images de la vidéo :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="s2"&gt;"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"&lt;/span&gt; &lt;span class="nt"&gt;--headless&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;new &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s2"&gt;"http://localhost:8123/?render&amp;amp;subs=6"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ffmpeg assemble ensuite les images et le son :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ffmpeg &lt;span class="nt"&gt;-framerate&lt;/span&gt; 60 &lt;span class="nt"&gt;-i&lt;/span&gt; frames/%05d.png &lt;span class="nt"&gt;-i&lt;/span&gt; audio.wav &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt;:v libx264 &lt;span class="nt"&gt;-crf&lt;/span&gt; 16 &lt;span class="nt"&gt;-pix_fmt&lt;/span&gt; yuv420p &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-c&lt;/span&gt;:a aac &lt;span class="nt"&gt;-b&lt;/span&gt;:a 256k &lt;span class="nt"&gt;-movflags&lt;/span&gt; +faststart techtown-showreel-formations.mp4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Au total, le projet représente environ 1 000 lignes de code réparties dans quatre fichiers : la page HTML, l’animation, la bande-son et le serveur de rendu.&lt;/p&gt;

&lt;p&gt;À noter : je pensais que Claude utiliserait le serveur MCP Playwright pour piloter le navigateur. Il n’était pas disponible dans la session. Claude s’est adapté en lançant directement Chrome headless, sans installer le moindre paquet.&lt;/p&gt;

&lt;h3&gt;
  
  
  7. Se relire avant de livrer
&lt;/h3&gt;

&lt;p&gt;Avant le rendu final, Claude a vérifié son travail dans le navigateur intégré à l’application Claude. Il a ajouté à sa page un mode « planche contact » (&lt;code&gt;?sheet=0.3,0.9,1.3,...&lt;/code&gt;) qui affiche seize vignettes de la vidéo sur un seul écran, puis il a contrôlé certaines images en plein écran.&lt;/p&gt;

&lt;p&gt;Cette relecture lui a permis de corriger deux défauts avant le rendu : un vignettage trop fort qui grisait les textes en bord d’écran, et une liste à puces trop serrée dans les cartes de formation. Après le rendu, il a encore relu quelques images exportées pour vérifier le flou de mouvement.&lt;/p&gt;

&lt;h2&gt;
  
  
  🔁 Une correction, trois secondes re-rendues
&lt;/h2&gt;

&lt;p&gt;La première version contenait une erreur de fond. La scène des chiffres clés reprenait la préparation aux certifications Google Cloud, présente sur la page formations. Dans une vidéo consacrée à Claude Code, ce mélange entre Google Cloud et Anthropic n’avait pas de sens.&lt;/p&gt;

&lt;p&gt;Je l’ai signalé en une phrase, avec une capture d’écran : « change ce slide. Tu mélanges GCP et Anthropic. Tu peux juste dire 175+ formés cette année avec une courbe par exemple. »&lt;/p&gt;

&lt;p&gt;Claude a réécrit la scène avec un compteur 175+ et une courbe de progression qui se dessine mois après mois. La forme de la courbe est illustrative : seul le total de 175 personnes formées est un chiffre réel. Il n’a ensuite re-rendu que les images concernées, autour de la plage de 15,5 à 18 secondes, avant de réencoder la vidéo. Le reste du film n’a pas bougé.&lt;/p&gt;

&lt;p&gt;Claude avait bien trouvé l’information sur notre site, sans voir qu’elle n’avait pas sa place dans ce message. La relecture humaine des faits reste indispensable.&lt;/p&gt;

&lt;h2&gt;
  
  
  📣 Ce que ça change pour le marketing
&lt;/h2&gt;

&lt;p&gt;Ce test m’a fait voir la vidéo marketing autrement.&lt;/p&gt;

&lt;p&gt;Le délai entre l’idée et la vidéo se compte en minutes. Le brief tient en quatre lignes, et le premier rendu arrive dans la même session. Il devient possible de tester une idée de vidéo avant même d’en parler à une agence.&lt;/p&gt;

&lt;p&gt;La vidéo devient du code. Elle se versionne dans Git, se relit dans une pull request et se reproduit à l’identique. Pour changer un chiffre, il suffit de modifier une ligne et de relancer le rendu de quelques secondes.&lt;/p&gt;

&lt;p&gt;Les déclinaisons deviennent peu coûteuses. Une fois l’animation écrite, nous pouvons demander une version par formation, une version en anglais ou un format vertical pour les stories, sans repartir de zéro.&lt;/p&gt;

&lt;p&gt;La charte graphique s’applique d’elle-même. En la décrivant une fois dans un skill, l’équipe marketing la rend disponible pour chaque prompt. Chaque vidéo, page ou visuel généré en hérite.&lt;/p&gt;

&lt;p&gt;Le contenu reste aligné avec la réalité. Claude lit le site et les données réelles au lieu de recopier un texte figé. Dans notre cas, les 16 formations affichées dans la vidéo sont celles du catalogue.&lt;/p&gt;

&lt;p&gt;Il y a aussi des limites, et il faut les garder en tête :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;les faits doivent être relus par un humain, comme l’a montré l’erreur sur les certifications ;&lt;/li&gt;
&lt;li&gt;les marques tierces demandent de la vigilance : la vidéo reproduit l’interface de Claude Code et une couleur de la charte d’Anthropic, ce qui mérite une vérification avant une diffusion large ;&lt;/li&gt;
&lt;li&gt;le son synthétisé fonctionne bien pour un format court, mais ne remplace pas un compositeur ni une voix off ;&lt;/li&gt;
&lt;li&gt;pour une campagne haut de gamme, un motion designer garde l’avantage sur la direction artistique.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pour du contenu fréquent, court et cohérent avec la marque, en revanche, l’écart avec un agent de code devient très faible.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion
&lt;/h2&gt;

&lt;p&gt;Avec un brief de quatre lignes, Opus 5.5 et Claude Code ont produit une vidéo de motion design complète : storyboard, animation, sound design et rendu final. Sous le capot, rien de magique : du Canvas 2D, du Node.js, Chrome headless et ffmpeg, orchestrés par un agent qui sait aussi relire son propre travail.&lt;/p&gt;

&lt;p&gt;Mon takeaway : rédigez vos prompts comme des briefs créatifs, donnez une source de vérité et une charte sous forme de skill, puis gardez la main sur la relecture des faits.&lt;/p&gt;

&lt;p&gt;👉 Et vous, quel contenu marketing confieriez-vous à un agent de code ?&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Aller plus loin avec Claude Code
&lt;/h2&gt;

&lt;p&gt;Vous voulez apprendre à travailler ainsi avec un agent de code ? En une journée, la &lt;strong&gt;&lt;a href="https://techtown.fr/formations/coder-avec-claude-debutant/" rel="noopener noreferrer"&gt;formation Coder avec Claude Code - Débutant de TechTown&lt;/a&gt;&lt;/strong&gt; couvre :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l’installation et la configuration de Claude Code ;&lt;/li&gt;
&lt;li&gt;les commandes essentielles et la méthode des 3C pour formuler des prompts ;&lt;/li&gt;
&lt;li&gt;la personnalisation de l’environnement de travail (CLAUDE.md, permissions, Plan Mode) ;&lt;/li&gt;
&lt;li&gt;l’intégration de Claude dans un workflow Git professionnel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Quelques retours de participants :&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;« Sujet passionnant, formateur passionné. »&lt;/p&gt;

&lt;p&gt;« Cette formation m’a donné les bases de la bonne utilisation de Claude pour mon métier de dev. Je me sens prêt à l’utiliser quotidiennement et je connais la bonne direction pour progresser en autonomie sur cet outil. »&lt;/p&gt;

&lt;p&gt;« J’ai apprécié le fait que l’on puisse approfondir certains sujets lors des questionnements ainsi que de voir des cas pratiques et des démos et TPs. »&lt;/p&gt;

&lt;p&gt;« Bonne introduction, sujets pertinents et dans un ordre accessible. Explications théoriques claires et en accord avec la suite des TP. »&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>devaugmenté</category>
      <category>ia</category>
    </item>
    <item>
      <title>Gouvernance MCP : qui choisit les tools de vos agents de code ?</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 29 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/techtown-fr/gouvernance-mcp-qui-choisit-les-tools-de-vos-agents-de-code--be4</link>
      <guid>https://dev.to/techtown-fr/gouvernance-mcp-qui-choisit-les-tools-de-vos-agents-de-code--be4</guid>
      <description>&lt;p&gt;Les agents de code comme GitHub Copilot, Claude Code ou Cursor se branchent sur des &lt;strong&gt;serveurs MCP&lt;/strong&gt; (Model Context Protocol). Ces serveurs leur apportent des &lt;em&gt;tools&lt;/em&gt; : chercher un ticket Jira, lire une page Confluence, ouvrir une pull request…&lt;/p&gt;

&lt;p&gt;C’est très pratique. Mais pour une entreprise, une question arrive vite : &lt;strong&gt;qui décide de ce que l’agent a le droit de faire ?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Dans cet article, nous allons partir d’un cas concret : Jira, Confluence et Context7 placés derrière une gateway MCP. Nous verrons les trois besoins de gouvernance, puis les trois solutions possibles aujourd’hui, avec leurs limites.&lt;/p&gt;

&lt;h2&gt;
  
  
  📌 Les trois besoins de gouvernance
&lt;/h2&gt;

&lt;p&gt;Côté entreprise, nous voulons trois choses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A. Choisir les serveurs MCP autorisés.&lt;/strong&gt; Pas n’importe quel serveur trouvé sur Internet, mais une liste validée. Elle est publiée par une &lt;strong&gt;registry MCP&lt;/strong&gt; interne, ou imposée par des &lt;strong&gt;managed settings&lt;/strong&gt; , c’est-à-dire des réglages déployés par l’entreprise sur les postes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;B. Préférer les serveurs MCP distants officiels.&lt;/strong&gt; Un serveur &lt;em&gt;remote&lt;/em&gt; hébergé par l’éditeur (GitHub, Atlassian…) apporte :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;rien à installer sur les postes : ni &lt;code&gt;docker run&lt;/code&gt;, ni &lt;code&gt;npx&lt;/code&gt; ;&lt;/li&gt;
&lt;li&gt;des mises à jour gérées par l’éditeur ;&lt;/li&gt;
&lt;li&gt;une simple URL à déclarer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;C. Filtrer les tools proposés par ces serveurs.&lt;/strong&gt; Un serveur d’éditeur expose souvent &lt;strong&gt;des dizaines de tools&lt;/strong&gt; , dont beaucoup en écriture. Le serveur open source &lt;a href="https://github.com/sooperset/mcp-atlassian" rel="noopener noreferrer"&gt;mcp-atlassian&lt;/a&gt;, que nous utilisons dans notre test, en expose 97, dont une quarantaine qui modifient des données : &lt;code&gt;jira_create_issue&lt;/code&gt;, &lt;code&gt;confluence_delete_page&lt;/code&gt;… Nous voulons n’en garder qu’une partie, par exemple la lecture seule.&lt;/p&gt;

&lt;p&gt;A et B sont bien outillés aujourd’hui. &lt;strong&gt;C est le vrai problème.&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Rappel : registry et managed settings pour le besoin A
&lt;/h3&gt;

&lt;p&gt;Deux mécanismes permettent déjà de choisir les serveurs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Côté Copilot&lt;/strong&gt; , une politique d’organisation GitHub impose à VS Code une registry MCP interne qui liste les serveurs approuvés. C’est ce que nous faisons. Une entrée de registry ressemble à ceci :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"server"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"io.github.upstash/context7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"remotes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"streamable-http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.context7.com/mcp"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Chaque entrée décrit un serveur et la façon de s’y connecter. Ici, Context7 est déclaré comme serveur distant, sans installation locale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Côté Claude Code&lt;/strong&gt; , nous faisons de même avec des managed settings. L’entreprise dépose un fichier de réglages, dans &lt;code&gt;/Library/Application Support/ClaudeCode/&lt;/code&gt; sur macOS (&lt;a href="https://code.claude.com/docs/en/managed-mcp" rel="noopener noreferrer"&gt;documentation&lt;/a&gt;) :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"allowedMcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"serverUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.context7.com/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"serverUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp-gateway.example.com/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"deniedMcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"serverUrl"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://untrusted.example.com/*"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La liste de refus est évaluée en premier. Ensuite, seuls les serveurs présents dans la liste d’autorisation peuvent être chargés.&lt;/p&gt;

&lt;p&gt;À noter : ces deux mécanismes filtrent &lt;strong&gt;des serveurs&lt;/strong&gt; , pas &lt;strong&gt;des tools&lt;/strong&gt;. Une fois un serveur autorisé, tous ses tools arrivent chez l’agent.&lt;/p&gt;

&lt;h2&gt;
  
  
  1️⃣ Solution 1 : le filtrage proposé par l’éditeur
&lt;/h2&gt;

&lt;p&gt;Certains éditeurs permettent de choisir les tools… &lt;strong&gt;dans la configuration du client&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Le serveur MCP distant de GitHub lit par exemple des en-têtes HTTP maison, décrits dans sa &lt;a href="https://github.com/github/github-mcp-server/blob/main/docs/remote-server.md" rel="noopener noreferrer"&gt;documentation&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"servers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"github"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://api.githubcopilot.com/mcp/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-MCP-Toolsets"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"repos,issues"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-MCP-Tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"get_file_contents,issue_read"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"X-MCP-Readonly"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"true"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;X-MCP-Toolsets&lt;/code&gt; et &lt;code&gt;X-MCP-Tools&lt;/code&gt; limitent les tools exposés, et &lt;code&gt;X-MCP-Readonly&lt;/code&gt; ne garde que les tools de lecture.&lt;/p&gt;

&lt;p&gt;Autre exemple : quand nous installons nous-mêmes mcp-atlassian, nous le configurons par variables d’environnement :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;--rm&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;READ_ONLY_MODE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;ENABLED_TOOLS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;jira_search,confluence_search &lt;span class="se"&gt;\&lt;/span&gt;
  ...image-mcp-atlassian...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pourquoi cette approche est fragile
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;C’est de la configuration client.&lt;/strong&gt; Le réglage vit dans le &lt;code&gt;mcp.json&lt;/code&gt; de chaque utilisateur. Il suffit de retirer &lt;code&gt;X-MCP-Readonly&lt;/code&gt; ou &lt;code&gt;READ_ONLY_MODE&lt;/code&gt; pour retrouver tous les tools. Ce n’est pas une barrière, c’est une préférence.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le serveur accepte toujours tout.&lt;/strong&gt; Avec le même token, un autre client, ou un simple &lt;code&gt;curl&lt;/code&gt;, obtient l’ensemble des tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chaque éditeur a sa propre convention.&lt;/strong&gt; &lt;code&gt;X-MCP-Tools&lt;/code&gt; chez GitHub, variables d’environnement chez mcp-atlassian, rien chez d’autres. Aucune règle commune n’est possible.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cela ne fonctionne qu’avec les éditeurs qui l’ont prévu.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitHub le précise d’ailleurs à propos de &lt;code&gt;X-MCP-Lockdown&lt;/code&gt; : c’est un filtre « best-effort », pas une frontière de sécurité.&lt;/p&gt;

&lt;h2&gt;
  
  
  2️⃣ Solution 2 : une gateway MCP entre les agents et les serveurs
&lt;/h2&gt;

&lt;p&gt;L’idée est simple : les agents ne parlent plus directement aux serveurs MCP. Ils passent par une &lt;strong&gt;gateway&lt;/strong&gt; qui applique la politique de l’entreprise.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxv9amz9q2fdtjdf3puy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvxv9amz9q2fdtjdf3puy.webp" alt="Schéma : les agents de code passent par agentgateway, qui filtre les tools de Context7 et d’Atlassian" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Tous les agents passent par la même gateway, qui applique la liste blanche des tools et journalise chaque appel.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Nous avons testé cette approche avec &lt;a href="https://agentgateway.dev" rel="noopener noreferrer"&gt;agentgateway&lt;/a&gt;, un projet open source de l’Agentic AI Foundation (&lt;a href="https://aaif.io" rel="noopener noreferrer"&gt;AAIF&lt;/a&gt;), en version 1.5.0. Toute la politique tient dans un fichier de configuration :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;routes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlassian&lt;/span&gt;
  &lt;span class="na"&gt;matches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;pathPrefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;/atlassian/mcp&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
  &lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;mcpAuthorization&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# Liste blanche : seuls ces tools existent pour l'agent&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;mcp.tool.name&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;["jira_get_issue",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"jira_get_all_projects",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"confluence_search",&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;"confluence_get_page"]'&lt;/span&gt;
  &lt;span class="na"&gt;backends&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;atlassian&lt;/span&gt;
        &lt;span class="na"&gt;mcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{&lt;/span&gt; &lt;span class="nv"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;//localhost&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;&lt;span class="nv"&gt;8000/mcp&lt;/span&gt; &lt;span class="pi"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Les règles &lt;code&gt;mcpAuthorization&lt;/code&gt; sont des expressions CEL (Common Expression Language). Ici, un seul tableau suffit à décrire la liste blanche de la route Atlassian.&lt;/p&gt;

&lt;p&gt;⚡️ Voici ce que nous avons constaté pendant le test :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Les tools refusés disparaissent&lt;/strong&gt; de la liste envoyée à l’agent (&lt;code&gt;tools/list&lt;/code&gt;). Le modèle ne sait même pas qu’ils existent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Un appel direct à un tool refusé est rejeté&lt;/strong&gt; , même forcé à la main avec &lt;code&gt;curl&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Le token de l’utilisateur est relayé tel quel.&lt;/strong&gt; Atlassian voit toujours la vraie personne, avec ses propres droits.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chaque appel est journalisé&lt;/strong&gt;  : quel serveur, quel tool, quand. Nous obtenons des statistiques d’usage sans effort supplémentaire.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Voici un extrait réel des logs d’accès, simplifié :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"route"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"default/context7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"http.status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mcp.method.name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"tools/call"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
 &lt;/span&gt;&lt;span class="nl"&gt;"mcp_target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"context7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"mcp_tool"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"resolve-library-id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="nl"&gt;"duration"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="s2"&gt;"2274ms"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enfin, &lt;strong&gt;côté poste, rien à installer.&lt;/strong&gt; L’agent déclare simplement l’URL de la gateway :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"atlassian"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp-gateway.example.com/atlassian/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Basic &amp;lt;base64(email:token)&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Les limites de la gateway
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Il faut héberger et maintenir un composant de plus&lt;/strong&gt; , dans notre cas un service Cloud Run.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Il faut tenir la liste blanche à jour&lt;/strong&gt; quand l’éditeur ajoute ou renomme des tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Il faut penser la sécurité réseau.&lt;/strong&gt; Par exemple, ne jamais transmettre le token Atlassian d’un utilisateur à un autre serveur :
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;policies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;requestHeaderModifier&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;remove&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;authorization&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# sur la route Context7&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Le filtrage se fait au niveau du tool&lt;/strong&gt; , pas de ses paramètres. Pour aller plus loin, par exemple réécrire un schéma ou limiter un projet Jira, agentgateway propose des &lt;em&gt;MCP guardrails&lt;/em&gt;. Il faut alors développer un service externe.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;💪 ProTip : gardez une route par serveur MCP plutôt qu’une route unique qui les agrège. Les noms de tools restent inchangés, et un token ne peut pas fuiter d’un serveur à l’autre.&lt;/p&gt;

&lt;p&gt;C’est la seule solution où &lt;strong&gt;la règle est appliquée côté serveur, au même endroit pour tous les agents&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  3️⃣ Solution 3 : choisir les tools dans chaque agent de code
&lt;/h2&gt;

&lt;p&gt;Troisième voie : garder la connexion directe et restreindre les tools &lt;strong&gt;dans la configuration de l’agent&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Côté Copilot CLI&lt;/strong&gt; , c’est simple : une liste blanche par serveur.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"mcpServers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"atlassian"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"http"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://mcp.example.com/mcp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"tools"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"jira_get_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"confluence_search"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;VS Code&lt;/strong&gt; propose en plus un sélecteur de tools dans l’interface du chat. Mais c’est à chaque développeur de choisir les tools qu’il charge dans sa fenêtre de contexte : l’entreprise ne peut pas imposer ce choix.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Côté Claude Code&lt;/strong&gt; , c’est plus pénible. Il n’existe pas de liste &lt;code&gt;tools&lt;/code&gt; par serveur, il faut passer par les &lt;a href="https://code.claude.com/docs/en/permissions" rel="noopener noreferrer"&gt;règles de permissions&lt;/a&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"deny"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp __atlassian__ jira_create_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp __atlassian__ jira_update_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp __atlassian__ jira_delete_issue"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp __atlassian__ confluence_create_page"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"mcp __atlassian__ confluence_delete_page"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Un tool refusé est bien retiré du contexte du modèle, et une règle posée dans les managed settings ne peut pas être contournée par l’utilisateur. Mais deux points compliquent la maintenance :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;une règle &lt;code&gt;deny&lt;/code&gt; l’emporte toujours sur une règle &lt;code&gt;allow&lt;/code&gt;. Impossible d’écrire « tout refuser sauf ces quatre tools » avec &lt;code&gt;deny: mcp __atlassian__ *&lt;/code&gt; puis &lt;code&gt;allow: …&lt;/code&gt;. Il faut &lt;strong&gt;lister un par un&lt;/strong&gt; tout ce que nous refusons, et revoir la liste à chaque nouveau tool de l’éditeur ;&lt;/li&gt;
&lt;li&gt;les règles dépendent du &lt;strong&gt;nom local&lt;/strong&gt; donné au serveur (&lt;code&gt;mcp __atlassian__ …&lt;/code&gt;). Si l’utilisateur l’appelle &lt;code&gt;jira&lt;/code&gt;, la règle ne s’applique plus.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Surtout, &lt;strong&gt;chaque agent a sa propre syntaxe&lt;/strong&gt;. Une entreprise qui autorise Copilot, Claude Code et Cursor doit écrire et maintenir trois politiques différentes, puis vérifier qu’elles disent la même chose.&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚖️ Comparatif des trois solutions
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;1. Filtrage éditeur (client)&lt;/th&gt;
&lt;th&gt;2. Gateway MCP&lt;/th&gt;
&lt;th&gt;3. Réglage par agent&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Où vit la règle&lt;/td&gt;
&lt;td&gt;config client&lt;/td&gt;
&lt;td&gt;serveur (gateway)&lt;/td&gt;
&lt;td&gt;config de chaque agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contournable par l’utilisateur&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;td&gt;non&lt;/td&gt;
&lt;td&gt;oui, sauf managed settings Claude Code&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marche avec tous les éditeurs&lt;/td&gt;
&lt;td&gt;non&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Marche avec tous les agents&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;td&gt;une syntaxe par agent&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Statistiques d’usage&lt;/td&gt;
&lt;td&gt;non&lt;/td&gt;
&lt;td&gt;oui&lt;/td&gt;
&lt;td&gt;non&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Coût&lt;/td&gt;
&lt;td&gt;nul&lt;/td&gt;
&lt;td&gt;un service à héberger&lt;/td&gt;
&lt;td&gt;maintenance de N configurations&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion : il manque une brique dans la spec MCP
&lt;/h2&gt;

&lt;p&gt;Aujourd’hui, &lt;strong&gt;la seule manière fiable&lt;/strong&gt; de filtrer les tools des éditeurs est d’ajouter une gateway. Elle fonctionne bien, mais c’est une infrastructure de plus pour un besoin que toutes les entreprises partagent.&lt;/p&gt;

&lt;p&gt;La dernière version de la &lt;a href="https://modelcontextprotocol.io/specification/2026-07-28/basic/transports/streamable-http" rel="noopener noreferrer"&gt;spécification MCP&lt;/a&gt; (2026-07-28) fait déjà un pas vers les intermédiaires :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;chaque requête HTTP porte des en-têtes standard comme &lt;code&gt;Mcp-Method: tools/call&lt;/code&gt; et &lt;code&gt;Mcp-Name: jira_search&lt;/code&gt;, pour qu’une gateway puisse router sans lire le corps de la requête ;&lt;/li&gt;
&lt;li&gt;un serveur peut demander au client de recopier certains paramètres dans des en-têtes &lt;code&gt;Mcp-Param-*&lt;/code&gt;, grâce à l’extension &lt;code&gt;x-mcp-header&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mais rien de tout cela ne permet de dire au serveur «  &lt;strong&gt;n’expose que ces tools&lt;/strong&gt;  » ou «  &lt;strong&gt;lecture seule&lt;/strong&gt;  ». Chacun invente sa convention : &lt;code&gt;X-MCP-Tools&lt;/code&gt; et &lt;code&gt;X-MCP-Readonly&lt;/code&gt; chez GitHub, &lt;code&gt;READ_ONLY_MODE&lt;/code&gt; et &lt;code&gt;ENABLED_TOOLS&lt;/code&gt; chez mcp-atlassian.&lt;/p&gt;

&lt;p&gt;Ce qui manque, c’est une &lt;strong&gt;généralisation de ces réglages dans la spécification&lt;/strong&gt;. Par exemple, avec des en-têtes qui n’existent pas encore :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;POST&lt;/span&gt; &lt;span class="nn"&gt;/mcp&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;tools/list&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Tools&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jira_get_issue,confluence_search&lt;/span&gt;
&lt;span class="na"&gt;Mcp-Read-Only&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Trois règles simples suffiraient :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Des en-têtes standard&lt;/strong&gt; , compris par tous les serveurs, pour restreindre les tools et imposer la lecture seule.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ils ne peuvent que restreindre&lt;/strong&gt; , jamais élargir ce que le serveur autorise déjà. GitHub applique déjà ce principe à son mode lockdown.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Les intermédiaires doivent les propager&lt;/strong&gt; , et peuvent les imposer. Une gateway ou un proxy d’entreprise pourrait alors ajouter &lt;code&gt;Mcp-Read-Only: true&lt;/code&gt; à toutes les requêtes, sans maintenir de liste de tools, et chaque serveur l’appliquerait lui-même.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Avec cette brique, la gouvernance deviendrait simple :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;la &lt;strong&gt;registry&lt;/strong&gt; choisit les serveurs (A) ;&lt;/li&gt;
&lt;li&gt;les &lt;strong&gt;serveurs distants officiels&lt;/strong&gt; évitent toute installation sur les postes (B) ;&lt;/li&gt;
&lt;li&gt;un &lt;strong&gt;en-tête standard&lt;/strong&gt; , posé par l’entreprise et impossible à retirer côté poste, filtre les tools (C).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;En attendant, la gateway reste la solution la plus robuste. C’est aussi un excellent point d’observation pour mesurer ce que les agents font vraiment de ces serveurs.&lt;/p&gt;

&lt;p&gt;👉 Et vous, comment filtrez-vous les tools MCP de vos agents de code ?&lt;/p&gt;

&lt;h2&gt;
  
  
  🎓 Aller plus loin avec MCP
&lt;/h2&gt;

&lt;p&gt;Vous voulez comprendre MCP de l’intérieur ? En une journée, la &lt;strong&gt;&lt;a href="https://techtown.fr/formations/mcp/" rel="noopener noreferrer"&gt;formation Model Context Protocol (MCP) de TechTown&lt;/a&gt;&lt;/strong&gt; couvre :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;l’architecture client-serveur ;&lt;/li&gt;
&lt;li&gt;les tools, les resources et les prompts ;&lt;/li&gt;
&lt;li&gt;le cycle de vie d’une requête ;&lt;/li&gt;
&lt;li&gt;la création de serveurs MCP en Python et en Java ;&lt;/li&gt;
&lt;li&gt;l’intégration avec des agents IA et le déploiement dans le Cloud.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;La journée alterne théorie et ateliers pratiques.&lt;/p&gt;

</description>
      <category>devaugmenté</category>
      <category>agentsia</category>
    </item>
    <item>
      <title>My first talk at AWS Summit Paris: steer your code agents with Kiro</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Thu, 17 Sep 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/techtown-fr/my-first-talk-at-aws-summit-paris-steer-your-code-agents-with-kiro-hce</link>
      <guid>https://dev.to/techtown-fr/my-first-talk-at-aws-summit-paris-steer-your-code-agents-with-kiro-hce</guid>
      <description>&lt;p&gt;I presented Spec-Driven Development with Benjamin Bourgeois. The room was full, and the feedback made me very happy.&lt;/p&gt;

&lt;p&gt;I had the opportunity to give my first talk at &lt;a href="https://aws.amazon.com/events/summits/paris/" rel="noopener noreferrer"&gt;AWS Summit Paris&lt;/a&gt;, on 1 April 2026, at the Palais des Congrès. I presented with &lt;a href="https://bbourgeois.dev" rel="noopener noreferrer"&gt;Benjamin Bourgeois&lt;/a&gt;, Cloud and AI consultant at TechTown.&lt;/p&gt;

&lt;p&gt;Our session was &lt;strong&gt;DEV211&lt;/strong&gt; , in the Developer Community Zone, from 13:00 to 13:45. The title was &lt;em&gt;Kiro : Ne codez plus seul, pilotez vos agents de code&lt;/em&gt;. The &lt;a href="https://jlandure.dev/slides/2026-04-01-kiro-ne-codez-plus-seul-pilotez-vos-agents-de-code-at-aws-summit-paris/" rel="noopener noreferrer"&gt;slides&lt;/a&gt; are online if you want to replay the deck.&lt;/p&gt;

&lt;p&gt;I was very happy to host this talk. The room was more than full. The official feedback was excellent. In this article, I’ll share what we presented about &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;, and what I take from this first Summit stage.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foov6h0z9w4inrp6e7fnh.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Foov6h0z9w4inrp6e7fnh.webp" alt="Julien Landuré and Benjamin Bourgeois, speakers at AWS Summit Paris 2026" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Benjamin and I, before our first AWS Summit talk together.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  A packed Developer Community Zone
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;235 people&lt;/strong&gt; came into a room built for &lt;strong&gt;100&lt;/strong&gt;. That is a &lt;strong&gt;235%&lt;/strong&gt; fill rate.&lt;/p&gt;

&lt;p&gt;Kiro was also easy to find around the event. In the AWS Village, this wall summed up the product in one sentence: an agentic AI development environment that brings structure to AI coding.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feoxuj2xw5d9yemdei7iy.webp" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Feoxuj2xw5d9yemdei7iy.webp" alt="AWS Village wall presenting Kiro as an agentic AI development environment" width="800" height="1067"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Kiro in the AWS Village at AWS Summit Paris 2026.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;If you want the photos from the day, they are on my LinkedIn posts: the &lt;a href="https://www.linkedin.com/posts/jlandure_awssummit-aws-kiro-activity-7442583036245790740-rOK6" rel="noopener noreferrer"&gt;session announcement&lt;/a&gt; and the &lt;a href="https://www.linkedin.com/posts/jlandure_awssummit-aws-awscommunitybuilders-activity-7447531637208178688-_XMN" rel="noopener noreferrer"&gt;recap after the Summit&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Don’t code alone, steer your agents
&lt;/h2&gt;

&lt;p&gt;Kiro is the agentic IDE from AWS. It is more than a code editor. It helps you build applications with an AI-native workflow, while you keep control of quality.&lt;/p&gt;

&lt;p&gt;We started with a simple story of the last few years:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We &lt;strong&gt;ask&lt;/strong&gt; an AI for help.&lt;/li&gt;
&lt;li&gt;We use &lt;strong&gt;autocomplete&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;We now work with &lt;strong&gt;code agents&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Chat-only workflows have limits:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new conversation can lose the context.&lt;/li&gt;
&lt;li&gt;The context window is limited.&lt;/li&gt;
&lt;li&gt;The context is hard to share with the team.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is why we talked about the &lt;strong&gt;Augmented Developer&lt;/strong&gt;. You do not let the agent guess. You steer it. You write the intent first. Then the agent writes the code.&lt;/p&gt;

&lt;p&gt;The developer becomes the person who guides the agents, not the person who types every line.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spec-Driven Development in Kiro
&lt;/h2&gt;

&lt;p&gt;The main idea of the talk was &lt;strong&gt;Spec-Driven Development (SDD)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In Kiro, a prompt can become a structured specification &lt;strong&gt;before&lt;/strong&gt; any code is generated. You can review the intent, the design, and the tasks. Then you let agents implement the work.&lt;/p&gt;

&lt;p&gt;A spec in Kiro usually has three files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;requirements.md&lt;/code&gt; — what we want, with user stories and acceptance criteria&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;design.md&lt;/code&gt; — how we will build it&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tasks.md&lt;/code&gt; — the steps to implement&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You validate the specification first. You generate code second. That is the difference with a long prompt that jumps straight to files.&lt;/p&gt;

&lt;p&gt;One attendee summed it up well: they understood how Kiro turns development into an AI-native workflow, where prompts become structured specs before code generation. They also learned how to steer code agents, automate tests and documentation, and keep control while going faster.&lt;/p&gt;

&lt;p&gt;The result we wanted people to leave with: more automation, less guesswork, and better control of the final code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Practices of the Augmented Developer
&lt;/h2&gt;

&lt;p&gt;We also shared practices that help you get useful code from agents, not just fast code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Context Engineering&lt;/strong&gt; : give the agent the right context, not a bigger prompt.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/strong&gt; : a project file that is added to the agent context automatically.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steering&lt;/strong&gt; : markdown files that keep your team rules and conventions across chats.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Skills&lt;/strong&gt; : reusable packages with a prompt, examples, and scripts.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;MCP (Model Context Protocol)&lt;/strong&gt;: connect the agent to external tools and data.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hooks&lt;/strong&gt; : run tests, linters, or docs when a file is saved or a task ends.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We showed this live on stage. The goal was not to replace developers. The goal was to help developers stay the person who decides.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ProTip:&lt;/strong&gt; before you ask Kiro to generate a feature, ask it to write the spec. Read &lt;code&gt;requirements.md&lt;/code&gt; and &lt;code&gt;design.md&lt;/code&gt;. If the intent is wrong, fix the spec. Do not fix 20 generated files.&lt;/p&gt;

&lt;h2&gt;
  
  
  The feedback that made me smile
&lt;/h2&gt;

&lt;p&gt;AWS later sent us the official session scores. They are based on &lt;strong&gt;19 survey responses&lt;/strong&gt; :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Content: &lt;strong&gt;4.74 / 5&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Speaker: &lt;strong&gt;4.79 / 5&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tech depth: &lt;strong&gt;4.68 / 5&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Learned something new: &lt;strong&gt;94.7%&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;One written comment was simple and kind: &lt;em&gt;“Très instructif.”&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;AWS also turned the feedback into a song title, just for fun: &lt;em&gt;Come Together&lt;/em&gt;, by The Beatles. Two speakers, one story, and a room that came together. That made me smile.&lt;/p&gt;

&lt;p&gt;I ended the day at a community dinner with the AWS team. After a first Summit talk, it was a pleasure to meet people, talk about Kiro, and hear what developers want to try next.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I take from this first Summit talk
&lt;/h2&gt;

&lt;p&gt;I am very proud of this first talk at AWS Summit. Sharing the stage with Benjamin felt natural. Seeing 235 people in a 100-seat room was a strong signal: developers want to understand how to work with agents, not only how to prompt them.&lt;/p&gt;

&lt;p&gt;If you missed the session, start here:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://jlandure.dev/slides/2026-04-01-kiro-ne-codez-plus-seul-pilotez-vos-agents-de-code-at-aws-summit-paris/" rel="noopener noreferrer"&gt;slides&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Open &lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;kiro.dev&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Sign in with an AWS Builder ID.&lt;/li&gt;
&lt;li&gt;Write a small spec before you generate code.&lt;/li&gt;
&lt;li&gt;Add an &lt;code&gt;AGENTS.md&lt;/code&gt; or a steering file, so the agent knows your project rules.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Have you tried Spec-Driven Development yet? I would love to hear how you steer your agents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Useful links&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://jlandure.dev/slides/2026-04-01-kiro-ne-codez-plus-seul-pilotez-vos-agents-de-code-at-aws-summit-paris/" rel="noopener noreferrer"&gt;Slides: Kiro at AWS Summit Paris&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev" rel="noopener noreferrer"&gt;Kiro&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/specs/" rel="noopener noreferrer"&gt;Kiro specs documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kiro.dev/docs/steering/" rel="noopener noreferrer"&gt;Kiro steering and AGENTS.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jlandure.dev" rel="noopener noreferrer"&gt;Julien Landuré&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://bbourgeois.dev" rel="noopener noreferrer"&gt;Benjamin Bourgeois&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/posts/jlandure_awssummit-aws-kiro-activity-7442583036245790740-rOK6" rel="noopener noreferrer"&gt;LinkedIn: session announcement&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.linkedin.com/posts/jlandure_awssummit-aws-awscommunitybuilders-activity-7447531637208178688-_XMN" rel="noopener noreferrer"&gt;LinkedIn: Summit recap&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>événements</category>
      <category>devaugmenté</category>
    </item>
    <item>
      <title>Your Face on a World Cup Sticker: Our Nano Banana Story</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 28 Jul 2026 11:09:48 +0000</pubDate>
      <link>https://dev.to/gde/your-face-on-a-world-cup-sticker-our-nano-banana-story-43p6</link>
      <guid>https://dev.to/gde/your-face-on-a-world-cup-sticker-our-nano-banana-story-43p6</guid>
      <description>&lt;p&gt;There is something timeless about opening a pack of football stickers. You never know which player, celebration, or rare edition you will find next.&lt;/p&gt;

&lt;p&gt;For the 2026 World Cup, we wanted to recreate that feeling with a personal twist: what if the player on the sticker was you, your friends, or your family? That simple idea became &lt;a href="https://sticker.techtown.fr/" rel="noopener noreferrer"&gt;Sticker Studio&lt;/a&gt;, a small experiment powered by Google's Nano Banana image model.&lt;/p&gt;

&lt;p&gt;Take a photo, choose a national team, add your name, and the application transforms your face into a playful cartoon football sticker. In this article, we'll explore the idea, the prompt system behind every scenario, the reference images that fixed our 2026 jerseys, and why we upgraded from Nano Banana 1 to Nano Banana 2.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrecuitwfii2npmnwyet.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fnrecuitwfii2npmnwyet.png" alt="Friends and family become World Cup 2026 cartoon collectibles with Sticker Studio." width="800" height="972"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ⚽ 1. The idea: putting our own faces on the stickers
&lt;/h2&gt;

&lt;p&gt;The starting point was not a technical challenge. It was fun.&lt;/p&gt;

&lt;p&gt;We wanted the nostalgia of football sticker albums without simply reproducing a traditional player portrait. Each result had to feel personal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the face should remain recognizable.&lt;/li&gt;
&lt;li&gt;the rendering should look like a playful children's illustration, not a photorealistic edit.&lt;/li&gt;
&lt;li&gt;the person should wear their selected national team's kit.&lt;/li&gt;
&lt;li&gt;every sticker should tell a small story.&lt;/li&gt;
&lt;li&gt;rare borders and special effects should bring back the excitement of opening a pack.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The experience is deliberately simple. You provide a photo and a name, choose your team, and let the application create the surprise.&lt;/p&gt;

&lt;p&gt;Behind that simple flow, the application runs an Astro frontend on Firebase Hosting and an API on Cloud Run. The browser sends the photo and the user's choices to the backend. The backend selects the scenario, builds the complete prompt, attaches the relevant references, and calls the Gemini image-generation API.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv06iv7dlpbncobde433n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fv06iv7dlpbncobde433n.png" alt="The public Studio keeps the experience simple: one photo, one team, one personalized sticker." width="609" height="866"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The AI is asked to preserve the real identity cues visible in the photo — face shape, skin tone, hairstyle, facial hair, eyebrows, expression, and accessories such as glasses. At the same time, it must turn that identity into a simplified, expressive cartoon.&lt;/p&gt;

&lt;p&gt;This balance matters. Too realistic, and the result loses its friendly illustrated character. Too generic, and the person no longer recognizes themselves. The fun happens in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  ✍️ 2. One prompt engine, dozens of scenarios
&lt;/h2&gt;

&lt;p&gt;A prompt like “turn this person into a football sticker” can produce an image. It does not produce a consistent collection.&lt;/p&gt;

&lt;p&gt;To make stickers generated for different people feel as if they belong to the same album, we built every prompt from the same six-part structure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Subject and identity&lt;/strong&gt; — who the person is and which visible traits must be preserved.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kit&lt;/strong&gt; — the selected national team and its official football jersey.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action&lt;/strong&gt; — the humorous pose or celebration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Environment&lt;/strong&gt; — the stadium, pitch, city, or imaginary setting around the player.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Style and card layout&lt;/strong&gt; — the shared visual rules, border, typography, and rarity effects.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output constraints&lt;/strong&gt; — a centered, vertical, full-bleed composition suitable for a sticker.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In the backend, those layers are assembled in TypeScript. Here is a shortened version of the production function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildStickerPrompt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PromptOptions&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;userName&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PLAYER&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;border&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;styleBible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;styleBible&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;DEFAULT_STYLE_BIBLE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Using the attached real photo as the identity reference...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`The character wears the official kit of &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;teamName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referenceInstruction&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;referenceInstruction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()]&lt;/span&gt;
      &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
    &lt;span class="s2"&gt;`Scenario: the character is &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;scenario&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`Presented inside a trading-card layout with &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nf"&gt;borderPhrase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;border&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s2"&gt;`At the bottom, render the name '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;' on one readable line.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;styleBible&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Center the subject. Use a vertical, full-bleed composition.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The function keeps the permanent art direction in one place, then injects only the values that make a sticker unique. It also makes optional instructions explicit: a jersey reference, trophy, ball, or rare effect is included only when the selected slot needs it.&lt;/p&gt;

&lt;p&gt;The scenarios provide variety. One person can perform a bicycle kick in a packed stadium. Another can ride a rocket-powered football through space, take a victory selfie, attempt a Panenka, meditate on the pitch, or lift the trophy under golden confetti.&lt;/p&gt;

&lt;p&gt;Here is a shortened example based on the real prompt structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Using the attached real photo as the identity reference, transform this exact person into a humorous parodic cartoon caricature. Match their real face closely: face shape, skin tone, hairstyle, facial hair, eyebrows, and expression.

The character is a professional soccer player wearing the official kit of the France national football team.

Scenario: the character is performing a spectacular overhead bicycle
kick in mid-air, with the ball blurring off the foot and a packed
stadium behind.

Presented inside a rectangular trading-card layout with a vibrant blue parallel border. At the top, arched text reads 'WORLD CUP 2026'. At the bottom, the name 'JULIEN' is fully readable.

Art style: die-cut sticker design, bold clean white outer outline,
2D vector illustration, flat colors, simple cel shading, thick black
ink outlines, high contrast, and playful humorous cartoon caricature.

Center the subject. Use a vertical, full-bleed composition.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The shared visual instructions are our &lt;strong&gt;Style Bible&lt;/strong&gt;. They keep the thick outlines, flat colors, card frame, typography, and composition stable across all generations.&lt;/p&gt;

&lt;p&gt;We also randomize collectible details. Common stickers appear more frequently, while gold, silver, black, and holographic borders are less common. Some generations receive a World Cup trophy or official ball as a secondary prop. Around one sticker in twenty gets a golden &lt;code&gt;LEGENDAIRE&lt;/code&gt; badge.&lt;/p&gt;

&lt;p&gt;The result is more than a photo filter. The same input can become a genuinely different collectible each time.&lt;/p&gt;

&lt;p&gt;💪 &lt;strong&gt;ProTip:&lt;/strong&gt; separate stable visual rules from variable scenario instructions. It makes your prompt system easier to tune and prevents a change to one celebration from altering the visual identity of the whole collection.&lt;/p&gt;

&lt;h2&gt;
  
  
  👕 3. When the AI remembered the wrong jerseys
&lt;/h2&gt;

&lt;p&gt;The first stickers looked promising, but one detail immediately broke the illusion: the kits were wrong. For example, this result clearly uses an older France jersey.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyajox2vwz1xzxvycwyb1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fyajox2vwz1xzxvycwyb1.jpg" alt="Without a jersey reference, Nano Banana falls back to an older France kit." width="651" height="771"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nano Banana often generated older designs, including 2024-era jerseys. That is understandable — a text prompt can describe “the official 2026 France home kit,” but the model still needs reliable visual information about a new design.&lt;/p&gt;

&lt;p&gt;We solved this with multimodal reference images.&lt;/p&gt;

&lt;p&gt;For France, England, Spain, and Argentina, the backend now attaches a packshot of the official 2026 home jersey to the generation request. The prompt explicitly tells Nano Banana to reproduce its colors, pattern, collar, marks, and crest placement in the cartoon style.&lt;/p&gt;

&lt;p&gt;It also draws a strict boundary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;This image is ONLY a clothing reference. Ignore any person, mannequin,
face, body, pose, or background shown in it, and use it solely for the
jersey design.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We applied the same technique to the official 2026 match ball. Whenever a scenario includes a football, the backend sends the ball image and asks the model to reproduce its base color, panel shapes, colored zones, and line patterns.&lt;/p&gt;

&lt;p&gt;The complete request can therefore contain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the user's portrait for identity;&lt;/li&gt;
&lt;li&gt;a 2026 jersey packshot for clothing;&lt;/li&gt;
&lt;li&gt;the official match-ball image for the ball design;&lt;/li&gt;
&lt;li&gt;the assembled text prompt for the scene and visual rules.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The actual Gemini request follows that order:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;unknown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;portrait&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;jersey&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;jersey&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
  &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;ball&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;inlineData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ball&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[]),&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;models&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateContent&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;model&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DEFAULT_MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parts&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
  &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;imageConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;3:4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The ordering is intentional: the portrait remains the primary identity reference, followed by controlled visual assets and finally the instructions that explain how to use them. All of this happens on the backend. The browser sends the photo and business choices, but it never receives the Gemini API key or controls the raw production prompt.&lt;/p&gt;

&lt;p&gt;There was one important gotcha. A jersey reference containing a recognizable face or head could make the image request fail with &lt;code&gt;finishReason=IMAGE_OTHER&lt;/code&gt; and no generated image.&lt;/p&gt;

&lt;p&gt;⚡️ &lt;strong&gt;ProTip:&lt;/strong&gt; prepare product references as clean packshots. Crop jersey images to the torso, remove faces and heads, keep the background simple, and use a reasonably sized compressed image. Reference quality directly affects both reliability and adherence.&lt;/p&gt;

&lt;h2&gt;
  
  
  🍌 4. Moving from Nano Banana 1 to Nano Banana 2
&lt;/h2&gt;

&lt;p&gt;Our first production version used Nano Banana 1 through &lt;code&gt;gemini-2.5-flash-image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It proved that the concept worked. It could transform a portrait, create the cartoon style, and follow imaginative football scenarios. But as our prompts gained more constraints, we needed stronger adherence: preserve the person's identity, copy a new jersey, use the correct ball, render the name, respect the card frame, and still create a dynamic scene.&lt;/p&gt;

&lt;p&gt;We upgraded the production model to Nano Banana 2, exposed through &lt;code&gt;gemini-3.1-flash-image&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The improvement was not about adding more adjectives to the prompt. Nano Banana 2 followed the combined identity, reference-image, text, and layout instructions more reliably. That made it a better fit for a product in which small details — the correct collar, visible glasses, or an uncropped name — determine whether the sticker feels right.&lt;/p&gt;

&lt;p&gt;We compared both versions with the same portraits, scenarios, and prompts before switching. Keeping the model name configurable also means we can repeat those A/B tests as the Gemini image family evolves.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6qjzcltd9mu07e1acsg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fp6qjzcltd9mu07e1acsg.png" alt="A controlled A/B comparison made reference adherence and layout differences easier to evaluate." width="583" height="592"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generation still uses a 1K output. For a fast, playful web experience, this gives us a practical balance between visual quality, latency, and cost.&lt;/p&gt;

&lt;p&gt;You can learn more about image generation in the &lt;a href="https://ai.google.dev/gemini-api/docs/image-generation" rel="noopener noreferrer"&gt;official Gemini API documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 5. From a side project to real API limits
&lt;/h2&gt;

&lt;p&gt;The first audience was exactly who we built it for: friends and family.&lt;/p&gt;

&lt;p&gt;Then people started generating several versions, trying another team, looking for a rarer border, and sharing the funniest results. That is great product feedback — and a fast way to discover API quotas.&lt;/p&gt;

&lt;p&gt;At Gemini API Tier 1, our daily request limits became a real constraint. The API key is only the credential; the effective quota comes from the associated Gemini API project and usage tier. Once the studio became active, generation capacity mattered as much as prompt quality.&lt;/p&gt;

&lt;p&gt;A big thank you to &lt;strong&gt;Guillaume Vernade&lt;/strong&gt;, who helped us move to Tier 3 and give the experiment enough room for more people to play.&lt;/p&gt;

&lt;p&gt;This was a useful reminder: when an AI demo becomes shareable, usage can grow differently from a traditional application. One visit may trigger several expensive generation calls because exploration is part of the experience. Quotas, concurrency, retries, and graceful error messages belong in the product design from the beginning.&lt;/p&gt;

&lt;p&gt;Thanks to a spending cap, we can also control how much we spend on new generations. Each sticker costs approximately &lt;strong&gt;$0.20&lt;/strong&gt; to generate, so the cap protects the experiment from an unexpectedly high bill.&lt;/p&gt;

&lt;h2&gt;
  
  
  🎯 Conclusion: now create your World Cup sticker
&lt;/h2&gt;

&lt;p&gt;Sticker Studio started with one simple question: what if our friends and family were the players inside the pack?&lt;/p&gt;

&lt;p&gt;Building it taught us three practical lessons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;structured prompts turn isolated images into a coherent collection;&lt;/li&gt;
&lt;li&gt;current visual references are essential when recent jerseys and equipment must be accurate;&lt;/li&gt;
&lt;li&gt;a stronger model and enough API capacity matter once people start experimenting.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But the main result is much simpler: it is fun.&lt;/p&gt;

&lt;p&gt;Friends and family have already generated &lt;strong&gt;365 stickers&lt;/strong&gt;. With the World Cup final finished, now is the perfect time to create yours, try another team, compare rare editions, and share the result.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://sticker.techtown.fr/" rel="noopener noreferrer"&gt;Create your own World Cup sticker with Sticker Studio&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Which team, scenario, and rarity will you get?&lt;/p&gt;

&lt;p&gt;&lt;em&gt;IMPORTANT NOTE: Sticker Studio is a playful, unofficial fan project and is not affiliated with Panini or FIFA.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nanobanana</category>
      <category>gemini</category>
      <category>promptengineering</category>
    </item>
    <item>
      <title>How to celebrate an achievement as an open source developer?</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 30 Jun 2020 14:40:05 +0000</pubDate>
      <link>https://dev.to/jlandure/how-to-celebrate-an-achievement-as-an-open-source-developer-19jo</link>
      <guid>https://dev.to/jlandure/how-to-celebrate-an-achievement-as-an-open-source-developer-19jo</guid>
      <description>&lt;p&gt;✏️ As an open source developer, I've maintained on GitHub a docker image since July 2017: an &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;"Alpine Chrome"&lt;/a&gt; and now the image has been pulled more than 🎉 &lt;strong&gt;1 000 000 times&lt;/strong&gt; 🎉 on the Docker Hub.&lt;/p&gt;

&lt;p&gt;I wrote about it recently on Dev.to 👇&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/zenika" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__org__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F1234%2F61529416-7337-4c59-ab27-aaa666d850fc.png" alt="Zenika" width="800" height="703"&gt;
      &lt;div class="ltag__link__user__pic"&gt;
        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F192673%2Fa76bdc95-3cd3-4bd5-b177-01f02dabcdb3.jpg" alt="" width="450" height="450"&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Crafting the perfect container to play with a Headless Chrome&lt;/h2&gt;
      &lt;h3&gt;Julien Landuré for Zenika ・ May 26 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#docker&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#testing&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#javascript&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  🎯 What can I do to celebrate this achievement?
&lt;/h1&gt;

&lt;p&gt;🤔 I do not know what kind of celebration I could do! It's a big achievement from my point of view.&lt;br&gt;
🚀 I've started with a tweet here&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1277969952415215617-374" src="https://platform.twitter.com/embed/Tweet.html?id=1277969952415215617"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1277969952415215617-374');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1277969952415215617&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;😊 &lt;em&gt;Disclaimer: I am not a designer and I've spent too much time designing the arrows to highlight the &lt;code&gt;docker pulls: 1M&lt;/code&gt; information. Please do not judge it... and I've successfully added a &lt;code&gt;:rocket:&lt;/code&gt; emoji on the screenshot.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  🎉 What else?
&lt;/h1&gt;

&lt;p&gt;💚 Do you have any kind of ideas or stories to share about celebrating your open source achievements?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a GIF?&lt;/li&gt;
&lt;li&gt;Invite my friends to celebrate it in a restaurant?&lt;/li&gt;
&lt;li&gt;&lt;em&gt;[your turn]&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>opensource</category>
      <category>github</category>
      <category>motivation</category>
      <category>docker</category>
    </item>
    <item>
      <title>⏱ 3 minutes to deploy your static Nuxt.js app on AppEngine</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 09 Jun 2020 12:53:17 +0000</pubDate>
      <link>https://dev.to/zenika/3-minutes-to-deploy-your-static-nuxt-js-app-on-appengine-2a2n</link>
      <guid>https://dev.to/zenika/3-minutes-to-deploy-your-static-nuxt-js-app-on-appengine-2a2n</guid>
      <description>&lt;p&gt;I’ve been deploying applications on &lt;a href="https://en.wikipedia.org/wiki/Serverless_computing" rel="noopener noreferrer"&gt;serverless solutions&lt;/a&gt; since 2010. Recently, I discovered &lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; and I wanted to create this post on how to quickly set up a static Nuxt.js app on &lt;a href="https://cloud.google.com/appengine" rel="noopener noreferrer"&gt;AppEngine, the Google Cloud PaaS&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  📝 Getting started with Nuxt.js
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://nuxtjs.org/" rel="noopener noreferrer"&gt;Nuxt.js&lt;/a&gt; is described as &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The Intuitive Vue Framework&lt;br&gt;
Build your next Vue.js application with confidence using NuxtJS.&lt;br&gt;
An open source framework making web development simple and powerful. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It's very powerful! You can use it to create various architectures: &lt;a href="https://nuxtjs.org/guide#server-rendered-universal-ssr-" rel="noopener noreferrer"&gt;SSR&lt;/a&gt;, &lt;a href="https://nuxtjs.org/guide#single-page-applications-spa-" rel="noopener noreferrer"&gt;SPA&lt;/a&gt; or &lt;a href="https://nuxtjs.org/guide#static-generated-pre-rendering-" rel="noopener noreferrer"&gt;Static&lt;/a&gt; renderings.&lt;br&gt;
For this demo, we will be focused on the &lt;strong&gt;static feature&lt;/strong&gt; using the &lt;code&gt;nuxt generate&lt;/code&gt; command.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⚗ Create the project
&lt;/h2&gt;

&lt;p&gt;For the demo, we will use a fresh new project. To do this, execute the &lt;code&gt;npx create-nuxt-app&lt;/code&gt; command to create a &lt;a href="https://nuxtjs.org/guide/installation#using-code-create-nuxt-app-code-" rel="noopener noreferrer"&gt;simple Nuxt.js application&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node &lt;span class="nt"&gt;-v&lt;/span&gt;
v12.16.3

&lt;span class="nv"&gt;$ &lt;/span&gt;npx create-nuxt-app

create-nuxt-app v2.15.0
✨  Generating Nuxt.js project &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
? Project name nuxt-static-deploy-appengine
? Project description My glorious Nuxt.js project
? Author name Julien Landuré
? Choose programming language TypeScript
? Choose the package manager Npm
? Choose UI framework None
? Choose custom server framework None &lt;span class="o"&gt;(&lt;/span&gt;Recommended&lt;span class="o"&gt;)&lt;/span&gt;
? Choose the runtime &lt;span class="k"&gt;for &lt;/span&gt;TypeScript Default
? Choose Nuxt.js modules &lt;span class="o"&gt;(&lt;/span&gt;Press &amp;lt;space&amp;gt; to &lt;span class="k"&gt;select&lt;/span&gt;, &amp;lt;a&amp;gt; to toggle all, &amp;lt;i&amp;gt; to inv
ert selection&lt;span class="o"&gt;)&lt;/span&gt;
? Choose linting tools Prettier
? Choose &lt;span class="nb"&gt;test &lt;/span&gt;framework None
? Choose rendering mode Universal &lt;span class="o"&gt;(&lt;/span&gt;SSR&lt;span class="o"&gt;)&lt;/span&gt;
? Choose development tools &lt;span class="o"&gt;(&lt;/span&gt;Press &amp;lt;space&amp;gt; to &lt;span class="k"&gt;select&lt;/span&gt;, &amp;lt;a&amp;gt; to toggle all, &amp;lt;i&amp;gt; to invert selection&lt;span class="o"&gt;)&lt;/span&gt;
...
🎉  Successfully created project nuxt-static-deploy-appengine
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✅ Test locally
&lt;/h2&gt;

&lt;p&gt;As described in the command line, run the &lt;code&gt;npm run start&lt;/code&gt; &lt;a href="https://nuxtjs.org/guide/commands#development-environment" rel="noopener noreferrer"&gt;script to get your app running&lt;/a&gt; on &lt;code&gt;http://localhost:3000/&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's edit the &lt;code&gt;index.vue&lt;/code&gt; and write this html:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"subtitle"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Hello Nuxt on AppEngine 🎉&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it works! Very easy to see our modification instantly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7a5a4pg96lydb6y02cky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7a5a4pg96lydb6y02cky.png" alt="Alt Text" width="800" height="375"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🏗 Build it
&lt;/h2&gt;

&lt;p&gt;To &lt;a href="https://nuxtjs.org/guide/commands#static-generated-deployment-pre-rendered-" rel="noopener noreferrer"&gt;build the app statically&lt;/a&gt;, run the &lt;code&gt;npm run generate&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npm run generate

&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nuxt-static-deploy-appengine@1.0.0 generate .../nuxt-static-deploy-appengine
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; nuxt generate

ℹ Production build                                                    10:52:36
✔ Builder initialized                                                 10:52:37
✔ Nuxt files generated                                                10:52:37
ℹ Starting &lt;span class="nb"&gt;type &lt;/span&gt;checking service...                   nuxt:typescript 10:52:39

✔ Client
  Compiled successfully &lt;span class="k"&gt;in &lt;/span&gt;6.95s

✔ Server
  Compiled successfully &lt;span class="k"&gt;in &lt;/span&gt;1.13s


Hash: c5e482cb9772cddb1178
Version: webpack 4.43.0
Time: 6951ms
Built at: 05/27/2020 10:52:46 AM
                         Asset       Size  Chunks                         Chunk Names
../server/client.manifest.json    6.6 KiB          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
       98f4a70ff71f0382866c.js   2.31 KiB       3  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  runtime
       9fc3f5e914943c5b3a9b.js   2.97 KiB       2  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  pages/index
                      LICENSES  389 bytes          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
       a51444c83f14a6afaeb5.js    153 KiB       1  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  commons.app
       d267ba93006b46dadaa8.js   49.2 KiB       0  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  app
 + 2 hidden assets
Entrypoint app &lt;span class="o"&gt;=&lt;/span&gt; 98f4a70ff71f0382866c.js a51444c83f14a6afaeb5.js d267ba93006b46dadaa8.js

Hash: fbd0c6af7579246a49e7
Version: webpack 4.43.0
Time: 1127ms
Built at: 05/27/2020 10:52:47 AM
                  Asset       Size  Chunks                         Chunk Names
818a9680ee5eeafdfb4b.js   3.05 KiB       1  &lt;span class="o"&gt;[&lt;/span&gt;emitted] &lt;span class="o"&gt;[&lt;/span&gt;immutable]  pages/index
              server.js   26.7 KiB       0  &lt;span class="o"&gt;[&lt;/span&gt;emitted]              app
   server.manifest.json  145 bytes          &lt;span class="o"&gt;[&lt;/span&gt;emitted]              
Entrypoint app &lt;span class="o"&gt;=&lt;/span&gt; server.js
ℹ Generating pages                                                    
✔ Generated /                                                        
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;✨ Congratulations, we built our first Nuxt.js application! &lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: We can check the files in the &lt;code&gt;dist&lt;/code&gt; folder or run it with &lt;code&gt;npx http-server dist&lt;/code&gt;.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  ☁️ Deploy it on AppEngine
&lt;/h1&gt;

&lt;p&gt;Then, to share the project with the rest of the world, we can deploy it on AppEngine. &lt;a href="https://cloud.google.com/appengine" rel="noopener noreferrer"&gt;Appengine&lt;/a&gt; comes with a lot of features and quotas. &lt;/p&gt;

&lt;p&gt;It is free for a basic usage thanks to its quotas BUT we have to enable billing to enjoy it. As a new customer, we get &lt;a href="https://cloud.google.com/free" rel="noopener noreferrer"&gt;$300 to spend on Google Cloud Platform products&lt;/a&gt; during your free trial.&lt;/p&gt;

&lt;h2&gt;
  
  
  🛒 Create a project
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="//console.cloud.google.com"&gt;&lt;code&gt;console.cloud.google.com&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/building-app/creating-gcp-project" rel="noopener noreferrer"&gt;create a new Google Cloud Platform project&lt;/a&gt;.&lt;br&gt;
Please save the [PROJECT_ID] of the project: you have to replace the &lt;code&gt;nuxt-static-deploy&lt;/code&gt; with your [PROJECT_ID] for the rest of the post.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: we can retrieve the [PROJECT_ID] in the url of the web console like &lt;code&gt;https://console.cloud.google.com/appengine/start?project=nuxt-static-deploy&lt;/code&gt; where nuxt-static-deploy is my [PROJECT_ID].&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: To create a project to host your app, we can also use the &lt;code&gt;gcloud projects create&lt;/code&gt; command. we need to enter a [PROJECT_ID], &lt;code&gt;nuxt-static-deploy&lt;/code&gt; for example.&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a project &lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud projects create nuxt-static-deploy 
Create &lt;span class="k"&gt;in &lt;/span&gt;progress &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;https://cloudresourcemanager.googleapis.com/v1/projects/nuxt-static-deploy].
Waiting &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;operations/cp.4929250122387435991] to finish...done.              
Enabling service &lt;span class="o"&gt;[&lt;/span&gt;cloudapis.googleapis.com] on project &lt;span class="o"&gt;[&lt;/span&gt;nuxt-static-deploy]...
Operation &lt;span class="s2"&gt;"operations/acf.803672e3-74b0-43e1-8a6b-3d37a4a0975b"&lt;/span&gt; finished successfully.

&lt;span class="c"&gt;# List the billing accounts&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud beta billing accounts list
ACCOUNT_ID            NAME                                          OPEN
00ADEE-870384-XXXXXX  my_account                                    True

&lt;span class="c"&gt;# Link the billing account to my project&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud beta billing projects &lt;span class="nb"&gt;link &lt;/span&gt;nuxt-static-deploy &lt;span class="nt"&gt;--billing-account&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;00ADEE-870384-XXXXXX
billingAccountName: billingAccounts/00ADEE-870384-XXXXXX
billingEnabled: &lt;span class="nb"&gt;true
&lt;/span&gt;name: projects/nuxt-static-deploy/billingInfo
projectId: nuxt-static-deploy

&lt;span class="c"&gt;# Enable Cloud Build API&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud &lt;span class="nt"&gt;--project&lt;/span&gt; nuxt-static-deploy services &lt;span class="nb"&gt;enable &lt;/span&gt;cloudbuild.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ✏️ Write the descriptor &lt;code&gt;app.yaml&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;AppEngine supports various programming languages: python, go, java, php, nodejs etc.&lt;br&gt;
To deploy static files, the best practice is to use the &lt;code&gt;python37&lt;/code&gt; runtime.&lt;/p&gt;

&lt;p&gt;Let's create a file in &lt;code&gt;.appengine/app.yaml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;python37&lt;/span&gt;

&lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
  &lt;span class="na"&gt;static_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.html&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/(.*)&lt;/span&gt;
  &lt;span class="na"&gt;static_files&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;\1&lt;/span&gt;
  &lt;span class="na"&gt;upload&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;(.*)&lt;/span&gt;
  &lt;span class="na"&gt;secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For more information on the descriptor file, &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/config/appref" rel="noopener noreferrer"&gt;please read this doc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Don't forget to copy the file in our &lt;code&gt;dist&lt;/code&gt; folder using this command &lt;code&gt;cp .appengine/app.yaml dist/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;em&gt;ProTips: We can add it to the &lt;code&gt;build&lt;/code&gt; script in your &lt;code&gt;package.json&lt;/code&gt; file.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🚀 Deploy your app
&lt;/h2&gt;

&lt;p&gt;If this is the first you are using the Google Cloud Platform, you have to install the &lt;code&gt;gcloud&lt;/code&gt; command line utility following &lt;a href="https://cloud.google.com/sdk/docs/quickstarts" rel="noopener noreferrer"&gt;the official page&lt;/a&gt; or &lt;a href="https://formulae.brew.sh/cask/google-cloud-sdk" rel="noopener noreferrer"&gt;using Homebrew&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Deploy our app using the &lt;code&gt;gcloud app deploy&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app deploy &lt;span class="nt"&gt;--project&lt;/span&gt; nuxt-static-deploy dist 

You are creating an app &lt;span class="k"&gt;for &lt;/span&gt;project nuxt-static-deploy.
WARNING: Creating an App Engine application &lt;span class="k"&gt;for &lt;/span&gt;a project is irreversible and the region
cannot be changed. More information about regions is at
&amp;lt;https://cloud.google.com/appengine/docs/locations&amp;gt;.

Please choose the region where you want your App Engine application 
located:

 &lt;span class="o"&gt;[&lt;/span&gt;1] asia-east2    &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;2] asia-northeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;3] asia-northeast2 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;4] asia-northeast3 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;5] asia-south1   &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;6] australia-southeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;7] europe-west   &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;8] europe-west2  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;9] europe-west3  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;10] europe-west6  &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;11] northamerica-northeast1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;12] southamerica-east1 &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;13] us-central    &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;14] us-east1      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;15] us-east4      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;16] us-west2      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;17] us-west3      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;18] us-west4      &lt;span class="o"&gt;(&lt;/span&gt;supports standard and flexible&lt;span class="o"&gt;)&lt;/span&gt;
 &lt;span class="o"&gt;[&lt;/span&gt;19] cancel
Please enter your numeric choice:  7

Creating App Engine application &lt;span class="k"&gt;in &lt;/span&gt;project nuxt-static-deploy and region &lt;span class="o"&gt;[&lt;/span&gt;
europe-west]....done.                                                          
Services to deploy:

descriptor:      &lt;span class="o"&gt;[&lt;/span&gt;.../nuxt-static-deploy-appengine/dist/app.yaml]
&lt;span class="nb"&gt;source&lt;/span&gt;:          &lt;span class="o"&gt;[&lt;/span&gt;.../nuxt-static-deploy-appengine/dist]
target project:  &lt;span class="o"&gt;[&lt;/span&gt;nuxt-static-deploy]
target service:  &lt;span class="o"&gt;[&lt;/span&gt;default]
target version:  &lt;span class="o"&gt;[&lt;/span&gt;20200602t094056]
target url:      &lt;span class="o"&gt;[&lt;/span&gt;https://20200602t094056-dot-nuxt-static-deploy.ew.r.appspot.com]


     &lt;span class="o"&gt;(&lt;/span&gt;add &lt;span class="nt"&gt;--promote&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;you also want to make this service available from
     &lt;span class="o"&gt;[&lt;/span&gt;https://nuxt-static-deploy.ew.r.appspot.com]&lt;span class="o"&gt;)&lt;/span&gt;

Do you want to &lt;span class="k"&gt;continue&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;Y/n&lt;span class="o"&gt;)&lt;/span&gt;?   

Beginning deployment of service &lt;span class="o"&gt;[&lt;/span&gt;default]...
╔════════════════════════════════════════════════════════════╗
╠═ Uploading 12 files to Google Cloud Storage               ═╣
╚════════════════════════════════════════════════════════════╝
File upload &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Updating service &lt;span class="o"&gt;[&lt;/span&gt;default]...done.                                             
Deployed service &lt;span class="o"&gt;[&lt;/span&gt;default] to &lt;span class="o"&gt;[&lt;/span&gt;https://20200602t094056-dot-nuxt-static-deploy.ew.r.appspot.com]

You can stream logs from the &lt;span class="nb"&gt;command &lt;/span&gt;line by running:
  &lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app logs &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; default

To view your application &lt;span class="k"&gt;in &lt;/span&gt;the web browser run:
  &lt;span class="nv"&gt;$ &lt;/span&gt;gcloud app browse

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  🎉 Enjoy
&lt;/h1&gt;

&lt;p&gt;Go &lt;a href="https://nuxt-static-deploy.appspot.com/" rel="noopener noreferrer"&gt;here at https://nuxt-static-deploy.appspot.com/&lt;/a&gt; to see our first Nuxt.js app deployed with AppEngine.&lt;/p&gt;

&lt;h1&gt;
  
  
  🔍 Conclusion
&lt;/h1&gt;

&lt;p&gt;Nuxt.js is a cool technology to create Vue.js app and is trending with the release of &lt;code&gt;@nuxt/content&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1263795755954909184-839" src="https://platform.twitter.com/embed/Tweet.html?id=1263795755954909184"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1263795755954909184-839');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1263795755954909184&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;AppEngine comes with a lot of features:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🔁 Multiple versions available to do A/B testing or Traffic Splitting (each version has a different timestamp on the &lt;a href="https://console.cloud.google.com/appengine/versions" rel="noopener noreferrer"&gt;Versions Page on Appengine&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;🔐 &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/mapping-custom-domains" rel="noopener noreferrer"&gt;Custom Domain Mapping&lt;/a&gt; &amp;amp; auto-renew, free, &lt;a href="https://cloud.google.com/appengine/docs/standard/python3/securing-custom-domains-with-ssl" rel="noopener noreferrer"&gt;managed SSL certificates&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💸 Free quota (1 GB bandwidth)&lt;/li&gt;
&lt;li&gt;⭐️ No instance used with Python37 runtime and our configuration because we have only static content &lt;/li&gt;
&lt;li&gt;🌐 Static content available through Google CDN and &lt;a href="https://peering.google.com/#/infrastructure" rel="noopener noreferrer"&gt;its Edge Network&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;⚡️ Managed network: Surprise 🎉 our project uses the http3 procotol &lt;code&gt;h3-Q050&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm0egf2fxzh3xsxcvp46g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fm0egf2fxzh3xsxcvp46g.png" alt="Alt Text" width="800" height="361"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to contact me on &lt;a href="https://twitter.com/jlandure" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt; if you have questions!&lt;/p&gt;

&lt;p&gt;NB: You can find another guide on Nuxt.js with AppEngine &lt;a href="https://nuxtjs.org/faq/appengine-deployment" rel="noopener noreferrer"&gt;here in the official documentation&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>googlecloud</category>
      <category>serverless</category>
      <category>vue</category>
    </item>
    <item>
      <title>Crafting the perfect container to play with a Headless Chrome</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 26 May 2020 06:19:17 +0000</pubDate>
      <link>https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp</link>
      <guid>https://dev.to/zenika/crafting-the-perfect-container-to-play-with-a-headless-chrome-ilp</guid>
      <description>&lt;p&gt;Since July 30th 2017, I have been contributing to a side project: the idea from the beginning was the ability to use Chrome to make some tests in headless mode and avoid a long queue of CI builds using a &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;tiny Docker image&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  🤔 Why use a Headless Chrome
&lt;/h1&gt;

&lt;p&gt;In the world of webdev, the ability to run quickly end-to-end tests are important. Popular technologies like Puppeteer enable developers to make fun things like testing, automating forms, crawling, generating screenshots, capturing timeline... And there is a secret: some of these features are directly available on Chrome! 🙌&lt;/p&gt;

&lt;p&gt;For more information on why headless mode is a cool feature, please read a good example on what you can achieve here:&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/reverentgeek" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3194%2Feaf08445-1922-4191-bb07-8b5a3cd4657f.png" alt="reverentgeek"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="https://dev.to/reverentgeek/convert-text-html-to-pdf-with-node-js-and-puppeteer-4c53" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Create PDFs with Node.js and Puppeteer&lt;/h2&gt;
      &lt;h3&gt;David Neal ・ Feb 6 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#node&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;h1&gt;
  
  
  💡 Crafting the perfect container
&lt;/h1&gt;

&lt;p&gt;With 28 contributors, I have been working on this project to create the perfect image to run Chromium in Headless mode:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📦 Tiniest Headless Chrome (&lt;a href="https://github.com/Zenika/alpine-chrome#image-disk-size" rel="noopener noreferrer"&gt;393 MB&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;🐳 Easy to use, ephemeral and reproducible Headless Chrome with Docker&lt;/li&gt;
&lt;li&gt;📝 Doc-friendly with examples for printing the DOM, generating an image with a mobile ratio or generating a PDF.&lt;/li&gt;
&lt;li&gt;👷‍♂️ Autobuild with the &lt;a href="https://hub.docker.com/repository/docker/zenika/alpine-chrome" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt; to sync the project and ship the images with confidence&lt;/li&gt;
&lt;li&gt;📌 Up-to-date with Chromium 81 (81.0.4044.138) and tags available to test different versions of Chromium&lt;/li&gt;
&lt;li&gt;🔐 Secure with the best way to use Chrome and Docker - &lt;a href="https://github.com/Zenika/alpine-chrome#3-ways-to-securely-use-chrome-headless-with-this-image" rel="noopener noreferrer"&gt;See "3 ways to securely use Chrome Headless" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;🌐 Ready for internationalisation use: support for asian characters - &lt;a href="https://github.com/Zenika/alpine-chrome/blob/master/with-puppeteer/src/screenshot-asia.js" rel="noopener noreferrer"&gt;See "screenshot-asia.js" file&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💄 Ready for design use: support for WebGL - &lt;a href="https://github.com/Zenika/alpine-chrome#how-to-use-with-webgl" rel="noopener noreferrer"&gt;See "How to use with WebGL" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;📄 Open Source with an Apache2 licence&lt;/li&gt;
&lt;li&gt;👥 Community-built with 28 contributors - &lt;a href="https://github.com/Zenika/alpine-chrome#-contributors" rel="noopener noreferrer"&gt;See "✨ Contributors" section&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;💚 Dev-friendly with examples using NodeJS, Puppeteer, docker-compose and also a test with a X11 display - &lt;a href="https://github.com/Zenika/alpine-chrome#run-examples" rel="noopener noreferrer"&gt;See "Run examples" section&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  💚 Open Source &amp;amp; Fun
&lt;/h1&gt;

&lt;p&gt;Some open source &amp;amp; fun projects have been set up and used for this open source side project. I want to share it with you: it adds value and confidence to maintain a GitHub repository. 👌 &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Updates to follow the &lt;a href="https://opensource.guide/" rel="noopener noreferrer"&gt;recommended community standards&lt;/a&gt; from the GitHub community profile&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gitmoji.carloscuesta.me/" rel="noopener noreferrer"&gt;Gitmoji&lt;/a&gt; for commit messages &lt;em&gt;where I have contributed in the past&lt;/em&gt; 😊&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://allcontributors.org/" rel="noopener noreferrer"&gt;All Contributors&lt;/a&gt; to thanks everyone who contributes to the project&lt;/li&gt;
&lt;li&gt;Inspiration from &lt;a href="https://github.com/kefranabg/readme-md-generator" rel="noopener noreferrer"&gt;readme-md-generator&lt;/a&gt; made by my friend &lt;a href="https://dev.to/kefranabg"&gt;Franck Abgrall&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Inspiration from this cheatsheet on &lt;a href="https://dev.to/kefranabg/cheat-sheet-how-to-promote-your-open-source-project-4flh"&gt;"How to promote your open-source project?"&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://renovate.whitesourcesoftware.com/" rel="noopener noreferrer"&gt;Renovate&lt;/a&gt; for dependency updates&lt;/li&gt;
&lt;li&gt;Many reads on dev.to to know what web developers are doing with Chrome and Puppeteer&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  🚀 Reaching a point
&lt;/h1&gt;

&lt;p&gt;Here is the most interesting insight for a tool: to be used and useful! In almost two weeks, this image will be pulled on the &lt;a href="https://hub.docker.com/repository/docker/zenika/alpine-chrome" rel="noopener noreferrer"&gt;Docker Hub&lt;/a&gt;... &lt;/p&gt;

&lt;p&gt;🎉 &lt;strong&gt;a million times&lt;/strong&gt;! 🎉 &lt;/p&gt;

&lt;p&gt;&lt;em&gt;there is currently ~922k downloads and it grows ~6k by day&lt;/em&gt;&lt;br&gt;
On &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, the repository gets ~3000 views, ~1000 unique visitors every two weeks and ~464 stars.&lt;/p&gt;

&lt;p&gt;After 3 years maintaining it, I cannot thank everyone using this container. This is the first time a side project has become so popular.&lt;/p&gt;

&lt;h1&gt;
  
  
  🗓 The Future
&lt;/h1&gt;

&lt;p&gt;✨ Here is some features in the roadmap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;with-playwright&lt;/code&gt; version with &lt;a href="https://playwright.dev/" rel="noopener noreferrer"&gt;Playwright&lt;/a&gt; to reuse the installed headless Chromium (&lt;a href="https://github.com/Zenika/alpine-chrome/pull/85" rel="noopener noreferrer"&gt;PR in draft&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with-deno&lt;/code&gt; version with &lt;a href="https://deno.land/" rel="noopener noreferrer"&gt;Deno&lt;/a&gt; to create a useful and fun things to do with deno&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;with-lighthouse&lt;/code&gt; version with &lt;a href="https://developers.google.com/web/tools/lighthouse/" rel="noopener noreferrer"&gt;Lighthouse&lt;/a&gt; to be able to monitor your website performance in CI builds&lt;/li&gt;
&lt;li&gt;Create a dedicated homepage and documentation website: the &lt;a href="https://github.com/Zenika/alpine-chrome/blob/master/README.md" rel="noopener noreferrer"&gt;README.md&lt;/a&gt; begins to be complicated to navigate and update&lt;/li&gt;
&lt;li&gt;Create a gallery with the name of the project or product using this container&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;📣 Please test this container or play with it!&lt;br&gt;
💻 If you are interested to help me maintain this image, we are welcome! &lt;em&gt;There is some "Good first issue" labelled issue on the repository.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Disclaimer: these terms in this post are synonyms: "Chrome" vs "Chromium" and "image" vs "container".&lt;/em&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>docker</category>
      <category>testing</category>
      <category>javascript</category>
    </item>
    <item>
      <title>State of Cloud Communities in France</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 14 Apr 2020 06:52:54 +0000</pubDate>
      <link>https://dev.to/zenika/state-of-cloud-communities-in-france-7ko</link>
      <guid>https://dev.to/zenika/state-of-cloud-communities-in-france-7ko</guid>
      <description>&lt;p&gt;&lt;a href="https://www.linkedin.com/in/karentamrazyan/" rel="noopener noreferrer"&gt;Karen Tamrazyan&lt;/a&gt; wrote a good article that analyzes local communities of 3 major public cloud providers AWS, Azure and Google Cloud in Germany, Austria and Switzerland (German-speaking countries) here:&lt;br&gt;
&lt;a href="https://www.tamrazyan.com/state-of-cloud-communities-in-german-speaking-countries/" rel="noopener noreferrer"&gt;https://www.tamrazyan.com/state-of-cloud-communities-in-german-speaking-countries/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;His article made me think about France. He helped me to create a similar sheet to superficially compare regional communities behind the three major public cloud providers: Amazon Web Services (aka AWS), Microsoft Azure and Google Cloud.&lt;/p&gt;
&lt;h1&gt;
  
  
  Comparison
&lt;/h1&gt;

&lt;p&gt;The top right quadrant from the latest Gartner cloud market analysis clearly indicates three global cloud leaders: Amazon Web Services, Microsoft Azure, Google Cloud.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2AHLk4BgFc7-QM6iWx" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2AHLk4BgFc7-QM6iWx" alt="quadrant" width="1536" height="1536"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;How are we going to compare the communities with each other? As Karen did in his article, we will compare them by the amount of registered members or the group size in other words. It is obvious that the group size is just one metric of many, like e.g. the activity rate (how often do events take place), the average number of participants, the date of the last event and so on… The members count should serve as a simplified way of measurement of a group success or importance.&lt;/p&gt;

&lt;p&gt;The internet is big. What kind of groups are we going to count? Indeed, there are cloud communities as Facebook groups, on different messenger networks like e.g. Telegram, independently hosted forums and so on. In this work we will count only the communities, which are hosted on the Meetup.com platform. Meetup is at the moment a de facto most popular community platform in those 3 countries.&lt;/p&gt;

&lt;p&gt;We will only consider active groups. It means that a group should have at least 1 registered past or upcoming event. In the locations where multiple communities dedicated to the same cloud technology are present, only the biggest one will be considered because of the very high probability of members overlap between those groups.&lt;/p&gt;

&lt;p&gt;The article gives only a snapshot of the situation as of April 12, 2020. The author doesn’t intend to update it as the situation changes. It’s a photo, not a video.&lt;/p&gt;
&lt;h1&gt;
  
  
  Cloud communities in France
&lt;/h1&gt;

&lt;p&gt;France has a population of over 67 million people. It is one of the most important technological hubs of Europe (competing with Germany and England). Plenty of universities, industrial and service companies belong to the landscape of most of the cities in this country. Even small places can often have lively user groups here like in Brest, Tours or Cannes.&lt;/p&gt;


&lt;div class="ltag_gist-liquid-tag"&gt;
  
&lt;/div&gt;


&lt;h1&gt;
  
  
  Analysis of Cloud Impact
&lt;/h1&gt;

&lt;p&gt;As we can clearly see from the table, AWS has far away the biggest communities in France : it demonstrates a tendency to go on this Cloud Provider. This popularity is clearly brought from the location of &lt;a href="https://aws.amazon.com/blogs/aws/now-open-aws-eu-paris-region/" rel="noopener noreferrer"&gt;one datacenter in Paris, France at the end of 2017&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Azure have broad community networks that cover the top 5 most populated cities. Azure is never the biggest community in the city.&lt;/p&gt;

&lt;p&gt;Google Cloud groups (aka “GDG Cloud” chapters) have the weakest cloud community network in France among the 3 contenders. However, one should not forget the much wider network of general Google Developer Groups (abbreviated “GDG”), which also on occasions run Google Cloud themed meetups.&lt;/p&gt;

&lt;p&gt;We can observe that Paris, Lyon and Nantes are the top 3 cities where the aws, azure and google cloud communities can enjoy events and meet together. &lt;/p&gt;

&lt;p&gt;Lyon is quite particular : it’s the only city where Google Cloud has more members than the others Cloud communities.&lt;/p&gt;

&lt;p&gt;This article highlights that cloud communities don’t match the &lt;a href="https://medium.com/frenchtech/bienvenue-aux-communaut%C3%A9s-et-capitales-french-tech-labellis%C3%A9es-le-3-avril-2019-f417539d3bd0" rel="noopener noreferrer"&gt;top “La French Tech” cities&lt;/a&gt;. However, many cloud providers provide Cloud credits for Startups like this &lt;a href="https://cloud.google.com/developers/startups" rel="noopener noreferrer"&gt;GCP initiative&lt;/a&gt;: &lt;a href="https://goo.gl/zY4pH2" rel="noopener noreferrer"&gt;check this page to redeem it&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2A7kDNA0EXz_-qRuUo" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1600%2F0%2A7kDNA0EXz_-qRuUo" alt="frenchtech" width="1400" height="656"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some large cities in the country like Montpellier, Bordeaux or Lille and a couple more, where none of Azure or Google Cloud have community footprints. These are opportunities for the future to grow.&lt;/p&gt;

&lt;h1&gt;
  
  
  What about GDGs?
&lt;/h1&gt;

&lt;p&gt;GDGs, Google Developer Groups, are too general to be studied in this article but GDGs bring together tons of passionate developers in France : 4134 members for GDG Paris, 2360 for GDG Nantes, 2213 for GDG Lille and 2133 for GDG Toulouse. &lt;/p&gt;

&lt;p&gt;These numbers are boosted by the organization of a DevFest each year, a day or 2-days conference dedicated to share about the technology and its impact. And GDGs run some Cloud events sometimes, please check out the &lt;a href="https://developers.google.com/community/gdg/groups" rel="noopener noreferrer"&gt;listing of GDGs here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0i8dd17r1a35ox395i6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx0i8dd17r1a35ox395i6.png" alt="gdg-france" width="447" height="349"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Disclaimer
&lt;/h1&gt;

&lt;p&gt;Author of this article runs voluntarily the &lt;a href="https://meetup.com/fr-FR/GDG-Nantes" rel="noopener noreferrer"&gt;Google Cloud developer group in Nantes (France)&lt;/a&gt; and also the &lt;a href="https://www.meetup.com/fr-FR/GDG-Cloud-Nantes" rel="noopener noreferrer"&gt;GDG Cloud Nantes&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;He’s also a &lt;a href="https://developers.google.com/community/experts/directory/profile/profile-julien_landur_C3_A9" rel="noopener noreferrer"&gt;GDE Cloud&lt;/a&gt;, a community reward from Google Cloud. His opinions were intended to be honest and unbiased. Apart from this research, the following official sources were used: &lt;a href="https://aws.amazon.com/developer/community/usergroups/europe/" rel="noopener noreferrer"&gt;AWS&lt;/a&gt;, Azure &amp;amp; &lt;a href="https://cloud.google.com/community/meetups" rel="noopener noreferrer"&gt;Google Cloud&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/KarenTamrazyan" rel="noopener noreferrer"&gt;Karen Tamrazyan&lt;/a&gt; &amp;amp; &lt;a href="https://twitter.com/charlyx" rel="noopener noreferrer"&gt;Charles-Henri Guérin&lt;/a&gt; for the review of the article.&lt;/p&gt;

</description>
      <category>cloud</category>
      <category>community</category>
      <category>meetup</category>
    </item>
    <item>
      <title>Awesome Remote Work</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Tue, 17 Mar 2020 14:45:07 +0000</pubDate>
      <link>https://dev.to/zenika/awesome-remote-work-1cfd</link>
      <guid>https://dev.to/zenika/awesome-remote-work-1cfd</guid>
      <description>&lt;p&gt;I discovered the book &lt;a href="https://basecamp.com/books/remote" rel="noopener noreferrer"&gt;"Remote"&lt;/a&gt; written by &lt;a href="https://twitter.com/dhh" rel="noopener noreferrer"&gt;David Heinemeier Hansson&lt;/a&gt; and &lt;a href="https://twitter.com/jasonfried" rel="noopener noreferrer"&gt;Jason Fried&lt;/a&gt; in 2014 when I joined &lt;a href="https://oss.zenika.com/" rel="noopener noreferrer"&gt;Zenika&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Since then, I love to read books on the basecamp culture like &lt;a href="https://basecamp.com/books/rework" rel="noopener noreferrer"&gt;"Rework"&lt;/a&gt; or &lt;a href="https://basecamp.com/books/calm" rel="noopener noreferrer"&gt;"It Doesn't Have to Be Crazy at Work"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;With the arrival of &lt;a href="https://en.wikipedia.org/wiki/Coronavirus_disease_2019" rel="noopener noreferrer"&gt;COVID-19&lt;/a&gt;, I decided to create a &lt;a href="https://github.com/zenika-open-source/awesome-remote-work" rel="noopener noreferrer"&gt;list of relevant links&lt;/a&gt; to share the culture from "remote" companies to everyone.&lt;/p&gt;

&lt;h2&gt;
  
  
  ☁️ Company Culture
&lt;/h2&gt;

&lt;p&gt;Since the beginning of &lt;a href="http://zenika.com/" rel="noopener noreferrer"&gt;Zenika&lt;/a&gt;, there is a "Cloud-first" culture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Gsuite for collaboration&lt;/li&gt;
&lt;li&gt;GitHub for code&lt;/li&gt;
&lt;li&gt;Slack for 1-to-1 communication&lt;/li&gt;
&lt;li&gt;Workplace for streamline communication&lt;/li&gt;
&lt;li&gt;SaaS tools for HR, for Sales&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All of these tools seem to be "normal" to be used inside our company and every employee has the right to be in remote when they wish &lt;em&gt;(and notify your manager)&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  🏠 The path to become a remote company
&lt;/h2&gt;

&lt;p&gt;We are an IT Services company so we share our people skills to our customers to build and deliver software.&lt;br&gt;
To keep our own culture safe, &lt;a href="https://twitter.com/CallMeGoodWater" rel="noopener noreferrer"&gt;my manager&lt;/a&gt; has found a cool way to make people work all together: everyone on Friday will not stay in the customer office. He encourages employees to stay remote (from our own office or from their home). &lt;/p&gt;

&lt;p&gt;First reaction from our customers: they were not able to work remotely. So we helped some of them to adopt a remote culture with the associated tools.&lt;/p&gt;
&lt;h2&gt;
  
  
  ⏰ The "COVID-19" effect
&lt;/h2&gt;

&lt;p&gt;With the arrival of the Coronavirus disease, some of them didn't take the right patterns and even worse, a few of them panics.&lt;/p&gt;

&lt;p&gt;To empower the remote culture, I just compiled a list of great articles on "How to remote work?" creating an awesome repository on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1239515937193238528-84" src="https://platform.twitter.com/embed/Tweet.html?id=1239515937193238528"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1239515937193238528-84');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1239515937193238528&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  💚 Open source
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/zenika-open-source/awesome-remote-work" rel="noopener noreferrer"&gt;The repository is open-source&lt;/a&gt; and inspired by the &lt;a href="https://github.com/sindresorhus/awesome" rel="noopener noreferrer"&gt;awesome lists from sindresorhus&lt;/a&gt;.&lt;br&gt;
I've added a specific README for 🇫🇷 french advices and links. &lt;br&gt;
If you wish to contribute for your own country, you're welcome! 🙌&lt;/p&gt;

&lt;p&gt;🚀 Feel free to share your links!&lt;/p&gt;

</description>
      <category>remote</category>
      <category>culture</category>
      <category>dev</category>
      <category>streaming</category>
    </item>
    <item>
      <title>How to backup your Firestore data automatically</title>
      <dc:creator>Julien Landuré</dc:creator>
      <pubDate>Thu, 01 Aug 2019 16:15:03 +0000</pubDate>
      <link>https://dev.to/zenika/how-to-backup-your-firestore-data-automatically-48em</link>
      <guid>https://dev.to/zenika/how-to-backup-your-firestore-data-automatically-48em</guid>
      <description>&lt;p&gt;With my team, we use a lot of Firebase features like Firestore.&lt;/p&gt;

&lt;p&gt;But there is no simple way to backup the data regularly.&lt;/p&gt;

&lt;p&gt;We created a tiny &lt;a href="https://hub.docker.com/r/zenika/alpine-firestore-backup/" rel="noopener noreferrer"&gt;Docker image&lt;/a&gt; &lt;code&gt;zenika/alpine-firestore-backup&lt;/code&gt; and this simple tutorial to perform backups automatically on the Google Cloud Platform with Serverless services like &lt;code&gt;Cloud Run&lt;/code&gt; and &lt;code&gt;Cloud Scheduler&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step1: Create a bucket on GCP
&lt;/h1&gt;

&lt;p&gt;Create a &lt;a href="https://cloud.google.com/storage/docs/storage-classes" rel="noopener noreferrer"&gt;GCP coldline bucket&lt;/a&gt; and save the name of your bucket.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step2: Create a service account
&lt;/h1&gt;

&lt;p&gt;Create a &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-accounts" rel="noopener noreferrer"&gt;GCP Service account&lt;/a&gt; with the following rights:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Owner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Datastore Owner&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Cloud Datastore Import Export Admin&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Storage Admin&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then, download the &lt;a href="https://cloud.google.com/iam/docs/creating-managing-service-account-keys" rel="noopener noreferrer"&gt;JSON private key file&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Step3: Create your env variables for Cloud Run
&lt;/h1&gt;

&lt;p&gt;Please fill in the following information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_PROJECT_ID&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_BUCKET_NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;GCLOUD_SERVICE_KEY&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For the &lt;code&gt;GCLOUD_SERVICE_KEY&lt;/code&gt;, make a base64 encoded string using this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;key.json | &lt;span class="nb"&gt;base64&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Step4: Set up Cloud Run
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/run/docs/deploying" rel="noopener noreferrer"&gt;Cloud Run&lt;/a&gt; is a serverless service to automatically serve your containers using http.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;Cloud Run service&lt;/code&gt; using the public image &lt;code&gt;gcr.io/zenika-hub/alpine-firestore-backup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Be careful to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose your newly image in &lt;code&gt;latest&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Choose "Cloud Run (fully managed)" and a location&lt;/li&gt;
&lt;li&gt;Enter a service name&lt;/li&gt;
&lt;li&gt;Select "Allow unauthenticated invocations"&lt;/li&gt;
&lt;li&gt;In the "Show optional settings / Environment variables", set the 3 environment variables seen in the previous section&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can test the service using your browser: &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Save the url created to call your Cloud Run Service.&lt;br&gt;
For example: &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/backup&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fja62bl6ygmompxymp26j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fja62bl6ygmompxymp26j.png" alt="cloud-run" width="544" height="973"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step5: Launch with Cloud Scheduler
&lt;/h1&gt;

&lt;p&gt;&lt;a href="https://cloud.google.com/scheduler/docs/" rel="noopener noreferrer"&gt;Cloud Scheduler&lt;/a&gt; allow you to schedule a cronjob in order to call a https endpoint at regular intervals.&lt;/p&gt;

&lt;p&gt;Prepare a &lt;code&gt;Cloud Scheduler&lt;/code&gt; to send a request to your &lt;code&gt;Cloud Run Service&lt;/code&gt; every time you need.&lt;/p&gt;

&lt;p&gt;For example, every Monday at 3:00am &lt;code&gt;0 3 * * 1&lt;/code&gt; a backup will be done and stored in your bucket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q4j84uwyuja7nk5dw37.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2q4j84uwyuja7nk5dw37.png" alt="cloud-scheduler" width="510" height="598"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Step6: Monitor the backup operations
&lt;/h1&gt;

&lt;p&gt;You can also check the current status of each backup operation using the following url &lt;code&gt;https://alpine-firestore-backup-XXX-run.app/list&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;Feel free to have a look at the Docker image we created for this operation &lt;a href="https://github.com/Zenika/alpine-firestore-backup" rel="noopener noreferrer"&gt;here on github&lt;/a&gt; or &lt;a href="https://hub.docker.com/r/zenika/alpine-firestore-backup/" rel="noopener noreferrer"&gt;here on dockerhub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We also have others images we maintained like a popular and small &lt;a href="https://github.com/Zenika/alpine-chrome" rel="noopener noreferrer"&gt;Chromium Headless image&lt;/a&gt; called &lt;code&gt;zenika/alpine-chrome&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>serverless</category>
      <category>docker</category>
    </item>
  </channel>
</rss>
