<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>GitHub Copilot on Azure Readiness starts here...</title><link>https://www.pdtit.be/tags/github-copilot/</link><description>Recent content in GitHub Copilot on Azure Readiness starts here...</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 13 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://www.pdtit.be/tags/github-copilot/index.xml" rel="self" type="application/rss+xml"/><item><title>How I used GitHub Copilot to migrate my Hugo website to GitHub Pages</title><link>https://www.pdtit.be/post/how-i-used-github-copilot-to-migrate-my-hugo-website-to-github-pages/</link><pubDate>Sat, 13 Jun 2026 00:00:00 +0000</pubDate><guid>https://www.pdtit.be/post/how-i-used-github-copilot-to-migrate-my-hugo-website-to-github-pages/</guid><description>&lt;p&gt;If you&amp;rsquo;ve followed this blog for a while, you know it runs on &lt;strong&gt;&lt;a class="link" href="https://www.gohugo.io" target="_blank" rel="noopener"
 &gt;Hugo&lt;/a&gt;&lt;/strong&gt;, a fantastic static site generator that turns a folder of Markdown files into a fast, fully static website. For the past couple of years that site has been hosted on &lt;strong&gt;Azure Static Web Apps&lt;/strong&gt;, with an &lt;strong&gt;Azure DevOps pipeline&lt;/strong&gt; doing the heavy lifting of compiling Hugo and publishing the output.&lt;/p&gt;
&lt;p&gt;That setup served me well. After migration my old site templates and structure to a new one last February (see:https://www.pdtit.be/post/how-i-used-github-copilot-to-modernize-my-8-year-old-hugo-website/), I now wanted to consolidate everything into a single ecosystem and move the site to &lt;strong&gt;GitHub Pages&lt;/strong&gt;, built and deployed by &lt;strong&gt;GitHub Actions&lt;/strong&gt;. And because I wanted to see how far the tooling has come, I did the entire migration side-by-side with &lt;strong&gt;GitHub Copilot in agent mode&lt;/strong&gt; inside VS Code.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apart from moving the site, I also made the decision to decommission the 007FFFLearning.com brand, and go back to the &amp;ldquo;good-old&amp;rdquo; pdtit.be&amp;quot; one I had since the early days of the internet in 1996 already.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This post walks through &lt;em&gt;why&lt;/em&gt; I made the move, &lt;em&gt;what&lt;/em&gt; my old setup looked like, the &lt;em&gt;plan&lt;/em&gt; I followed, and &lt;em&gt;how&lt;/em&gt; the migration actually went, including the DNS part that always makes people nervous. I&amp;rsquo;m deliberately keeping it conceptual rather than copy-paste exact, so you can map it to your own site.&lt;/p&gt;
&lt;p&gt;Here&amp;rsquo;s the prompt I used:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;/plan a migration from this repo from azure static webapps with Hugo to a GitHub Pages environment. The goal is to migrate away from Azure hosting, Azure DevOps pipeline for the blog post updates and run everything in GitHub. The new domain name should be www.pdtit.be instead of www.007FFFLearning.com, as a redirection from pdtit.github.io default Pages name. Prepare a migration plan, identify the successes, potential risks and evaluation criteria to move the site.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;If a migration is technically possible without losing functionality, highlight the actual steps to migrate, knowing we want a smooth migration with minimal downtime
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="../images/placeholder-copilot-agent-mode.png"
 
 
 loading="lazy"
 
 alt="GitHub Copilot agent mode in VS Code"
 
 
 
 &gt;&lt;/p&gt;
&lt;p&gt;// change draft: true to false&lt;/p&gt;
&lt;h2 id="1-why-github-pages-for-a-static-site"&gt;1. Why GitHub Pages for a static site?
&lt;/h2&gt;&lt;p&gt;A Hugo blog is, by definition, just a pile of static HTML, CSS, JavaScript and images once it&amp;rsquo;s built. You don&amp;rsquo;t need servers, containers, or databases to host it, you only need somewhere that can serve files over HTTPS. That&amp;rsquo;s exactly what &lt;strong&gt;GitHub Pages&lt;/strong&gt; does, for free, directly from a repository.&lt;/p&gt;
&lt;p&gt;A few reasons it&amp;rsquo;s a great fit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One place for everything.&lt;/strong&gt; Your content, your build pipeline, and your hosting all live in the same GitHub repo. No jumping between portals.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Build on push.&lt;/strong&gt; With &lt;strong&gt;GitHub Actions&lt;/strong&gt;, every push to your main branch rebuilds the site and publishes it automatically. Write a post, commit, done.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Free TLS and custom domains.&lt;/strong&gt; GitHub Pages provisions a certificate for your own domain at no cost and can enforce HTTPS.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;No build output in source control.&lt;/strong&gt; The generated site is produced fresh in CI on every push, so you never have to commit the compiled HTML again.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a personal blog, this removes a surprising amount of moving parts.&lt;/p&gt;
&lt;h2 id="2-where-i-was-coming-from-the-old-setup"&gt;2. Where I was coming from (the old setup)
&lt;/h2&gt;&lt;p&gt;Before the migration, the moving pieces looked like this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hugo&lt;/strong&gt; as the site generator, with content written in Markdown.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Azure Static Web Apps&lt;/strong&gt; as the host, serving the compiled site and handling TLS for my custom domain.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;Azure DevOps pipeline&lt;/strong&gt; that triggered on every commit, installed the right Hugo version, built the site, ran a couple of validation checks (more on that later), and deployed it.&lt;/li&gt;
&lt;li&gt;The compiled output and even a copy of the Hugo binary were &lt;strong&gt;committed into the repository&lt;/strong&gt;, a habit from earlier days that I wanted to leave behind.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Nothing here was &lt;em&gt;broken&lt;/em&gt;. But it meant my publishing flow spanned two platforms (Azure DevOps for builds, Azure for hosting), and the repo carried a lot of generated weight it didn&amp;rsquo;t need.&lt;/p&gt;
&lt;h2 id="3-the-migration-plan"&gt;3. The migration plan
&lt;/h2&gt;&lt;p&gt;Rather than diving straight into edits, I asked Copilot Plan mode to help me draft a phased plan first. Having a clear plan up front is what keeps a migration like this calm instead of chaotic. At a high level it broke down into:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 1 - Prepare the repository.&lt;/strong&gt;
Stop committing build output. Add a &lt;code&gt;.gitignore&lt;/code&gt; for the generated site, the Hugo cache, and any local binaries. Pin the exact Hugo version so local builds and CI builds always match.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 2 - Recreate the build in GitHub Actions.&lt;/strong&gt;
Replace the Azure DevOps pipeline with a GitHub Actions workflow that installs Hugo, builds the site, and deploys it to Pages. Importantly, I carried over the &lt;strong&gt;validation checks&lt;/strong&gt; my old pipeline did, things like confirming the search index was generated and that newly added posts actually appear in the output, so I didn&amp;rsquo;t lose that safety net.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 3 - Publish to GitHub.&lt;/strong&gt;
Create the GitHub repository, point the local repo at it, rename the default branch to &lt;code&gt;main&lt;/code&gt;, and push. Then flip the repo&amp;rsquo;s Pages setting to build from &lt;strong&gt;GitHub Actions&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 4 - Custom domain.&lt;/strong&gt;
Tell GitHub which domain to serve, update the site&amp;rsquo;s base URL, configure DNS, and enforce HTTPS once the certificate is issued.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 5 - Redirect the old domain.&lt;/strong&gt;
Keep the previous domain alive for a while and forward it to the new one so existing links and search engine results don&amp;rsquo;t break.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Phase 6 - Decommission the old platform.&lt;/strong&gt;
Once the new site is confirmed healthy, retire the Azure DevOps pipeline and the Azure Static Web App.&lt;/p&gt;
&lt;p&gt;&lt;img src="../images/placeholder-migration-plan.png"
 
 
 loading="lazy"
 
 alt="The phased migration plan drafted with Copilot"
 
 
 
 &gt;&lt;/p&gt;
&lt;p&gt;Writing the plan down also surfaced the &lt;em&gt;risks&lt;/em&gt; early, the trickiest being deep links from the old domain and making sure the search feature kept working. Knowing those in advance meant I could check for them deliberately instead of discovering them after go-live.&lt;/p&gt;
&lt;h2 id="4-how-the-migration-actually-went"&gt;4. How the migration actually went
&lt;/h2&gt;&lt;p&gt;With the plan agreed, the execution was honestly the easy part, this is where working alongside Copilot in agent mode really paid off. Instead of me hand-editing dozens of files, I described the outcome I wanted and reviewed the changes it proposed.&lt;/p&gt;
&lt;p&gt;A few highlights of the actual approach:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cleaning up the repo.&lt;/strong&gt;
The first win was getting the compiled site and the committed Hugo binaries &lt;em&gt;out&lt;/em&gt; of version control. Going forward, the build output is regenerated by CI on every push, so the repository only contains what it should: content and configuration.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A single build-and-deploy workflow.&lt;/strong&gt;
The GitHub Actions workflow became the new &amp;ldquo;engine&amp;rdquo; of the site, the direct replacement for the old pipeline. On every push to &lt;code&gt;main&lt;/code&gt; it installs the pinned Hugo version, builds the site, runs the validation checks, and deploys the result to Pages. The very first run going green was the moment I knew the new platform was viable.&lt;/p&gt;
&lt;p&gt;&lt;img src="../images/placeholder-actions-run-green.png"
 
 
 loading="lazy"
 
 alt="The GitHub Actions build and deploy workflow running green"
 
 
 
 &gt;&lt;/p&gt;
&lt;p&gt;Check out the actual GitHub Actions YAML if you want:&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/pdtit/pdtit.github.io/actions/runs/27473418917/workflow" target="_blank" rel="noopener"
 &gt;https://github.com/pdtit/pdtit.github.io/actions/runs/27473418917/workflow&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Fixing up internal links.&lt;/strong&gt;
Because the domain was changing, I had Copilot sweep the content for hard-coded absolute links pointing at the old address and make them relative instead. Relative links survive a domain change without any further edits, which is exactly what you want.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Validating before trusting.&lt;/strong&gt;
Before pushing anything live, I rebuilt the site locally from a clean slate and verified the important things: the homepage and posts pointed at the new address, the client-side search index was present and valid, and no stale references lingered in the output. Only then did I publish.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;The DNS part (kept high-level on purpose).&lt;/strong&gt;
This is the step people worry about most, but the pattern is well-trodden:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;strong&gt;&lt;code&gt;www&lt;/code&gt; subdomain&lt;/strong&gt; gets a &lt;strong&gt;CNAME&lt;/strong&gt; pointing at the GitHub Pages host.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;root (apex) domain&lt;/strong&gt; gets a set of &lt;strong&gt;A records&lt;/strong&gt; (and IPv6 &lt;code&gt;AAAA&lt;/code&gt; records) pointing at GitHub&amp;rsquo;s Pages addresses. GitHub publishes the exact values, you just enter them at your registrar.&lt;/li&gt;
&lt;li&gt;GitHub then automatically redirects the apex to your &lt;code&gt;www&lt;/code&gt; canonical address.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="../images/placeholder-dns-records.png"
 
 
 loading="lazy"
 
 alt="The DNS records configured at the registrar"
 
 
 
 &gt;&lt;/p&gt;
&lt;p&gt;One gotcha worth calling out: if your registrar had any leftover parking or forwarding records on the apex, remove them, otherwise the certificate can struggle to provision. Once DNS resolved cleanly, GitHub issued the TLS certificate within minutes, and I switched on &lt;strong&gt;Enforce HTTPS&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Don&amp;rsquo;t strand the old domain.&lt;/strong&gt;
Finally, the previous domain stays alive and forwards to the new one. The key detail is to use a &lt;strong&gt;path-preserving redirect&lt;/strong&gt;, so an old deep link to a specific article lands on that same article on the new domain, not just the homepage. That protects your existing readers and your search rankings while everything propagates.&lt;/p&gt;
&lt;h2 id="wrapping-up"&gt;Wrapping up
&lt;/h2&gt;&lt;p&gt;The migration itself, prepare, rebuild in Actions, publish, wire up the domain, redirect the old one, was conceptually simple, but having GitHub Copilot do the repetitive editing, validation, and verification turned what could have been a tense weekend into a calm afternoon. The site you&amp;rsquo;re reading this on right now is the result: a Hugo blog, built by GitHub Actions, served from GitHub Pages, on my own domain.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;re sitting on a static site hosted somewhere heavier than it needs to be, this is a very approachable move, and a great little project to try out agent-mode tooling on a real, low-risk task.&lt;/p&gt;
&lt;p&gt;// should always be the last section of the blog&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.buymeacoffee.com/pdtit" target="_blank" rel="noopener"
 &gt;&lt;img src="../images/buy_me_a_coffee.png"
 
 
 loading="lazy"
 
 alt="BuyMeACoffee"
 
 
 
 &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cheers!!&lt;/p&gt;
&lt;p&gt;/Peter&lt;/p&gt;</description></item><item><title>How I used GitHub Copilot to modernize my 8 year old Hugo website</title><link>https://www.pdtit.be/post/how-i-used-github-copilot-to-modernize-my-8-year-old-hugo-website/</link><pubDate>Sat, 28 Feb 2026 00:00:00 +0000</pubDate><guid>https://www.pdtit.be/post/how-i-used-github-copilot-to-modernize-my-8-year-old-hugo-website/</guid><description>&lt;p&gt;I recently decided to give my 8 year old Hugo website a serious refresh. The trigger was simple: I still used the first Hugo theme I picked up 8 years ago, some content menu options didn&amp;rsquo;t actually do anything or were no longer relevant. Then I also had screenshots and other image files all over the place (5 different folder locations, duplicate image file names and alike).&lt;/p&gt;
&lt;p&gt;Instead of doing this modernization and cleanup manually over a few weekends, I used &lt;strong&gt;GitHub Copilot&lt;/strong&gt; as an active engineering partner to accelerate the full modernization journey.&lt;/p&gt;
&lt;h2 id="where-it-started-search-was-broken-in-production"&gt;Where it started: search was broken in production
&lt;/h2&gt;&lt;p&gt;The first issue looked small, but it exposed deeper reliability problems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The search JSON endpoint existed&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;/search/&lt;/code&gt; page in production was effectively empty&lt;/li&gt;
&lt;li&gt;The pipeline still reported success&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So this wasnâ€™t a single typo. It was a classic â€œgreen pipeline, broken runtime behaviorâ€ scenario.&lt;/p&gt;
&lt;p&gt;With Copilot, I moved from guessing to structured troubleshooting:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Validate generated Hugo output&lt;/li&gt;
&lt;li&gt;Compare source routing/content metadata with deployed artifacts&lt;/li&gt;
&lt;li&gt;Harden the pipeline to fail fast when critical pages are missing&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That immediately changed the workflow from reactive debugging to proactive validation.&lt;/p&gt;
&lt;h2 id="copilot-helped-me-modernize-beyond-just-the-bug"&gt;Copilot helped me modernize beyond just the bug
&lt;/h2&gt;&lt;p&gt;Once search was fixed, I used the same momentum to clean up years of accumulated content and asset drift.&lt;/p&gt;
&lt;h3 id="1-build-and-deployment-reliability"&gt;1) Build and deployment reliability
&lt;/h3&gt;&lt;p&gt;I updated the Azure Static Web Apps pipeline to be more explicit and defensive:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build validation checks for critical output files&lt;/li&gt;
&lt;li&gt;Safer prebuilt artifact deployment behavior&lt;/li&gt;
&lt;li&gt;Better guardrails so partial site generation doesnâ€™t silently pass&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Result: deployment confidence went up significantly.&lt;/p&gt;
&lt;h3 id="1-build-and-deployment-reliability-1"&gt;1) Build and deployment reliability
&lt;/h3&gt;&lt;p&gt;I updated the Azure Static Web Apps pipeline to be more explicit and defensive:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt; 1
&lt;/span&gt;&lt;span class="lnt"&gt; 2
&lt;/span&gt;&lt;span class="lnt"&gt; 3
&lt;/span&gt;&lt;span class="lnt"&gt; 4
&lt;/span&gt;&lt;span class="lnt"&gt; 5
&lt;/span&gt;&lt;span class="lnt"&gt; 6
&lt;/span&gt;&lt;span class="lnt"&gt; 7
&lt;/span&gt;&lt;span class="lnt"&gt; 8
&lt;/span&gt;&lt;span class="lnt"&gt; 9
&lt;/span&gt;&lt;span class="lnt"&gt;10
&lt;/span&gt;&lt;span class="lnt"&gt;11
&lt;/span&gt;&lt;span class="lnt"&gt;12
&lt;/span&gt;&lt;span class="lnt"&gt;13
&lt;/span&gt;&lt;span class="lnt"&gt;14
&lt;/span&gt;&lt;span class="lnt"&gt;15
&lt;/span&gt;&lt;span class="lnt"&gt;16
&lt;/span&gt;&lt;span class="lnt"&gt;17
&lt;/span&gt;&lt;span class="lnt"&gt;18
&lt;/span&gt;&lt;span class="lnt"&gt;19
&lt;/span&gt;&lt;span class="lnt"&gt;20
&lt;/span&gt;&lt;span class="lnt"&gt;21
&lt;/span&gt;&lt;span class="lnt"&gt;22
&lt;/span&gt;&lt;span class="lnt"&gt;23
&lt;/span&gt;&lt;span class="lnt"&gt;24
&lt;/span&gt;&lt;span class="lnt"&gt;25
&lt;/span&gt;&lt;span class="lnt"&gt;26
&lt;/span&gt;&lt;span class="lnt"&gt;27
&lt;/span&gt;&lt;span class="lnt"&gt;28
&lt;/span&gt;&lt;span class="lnt"&gt;29
&lt;/span&gt;&lt;span class="lnt"&gt;30
&lt;/span&gt;&lt;span class="lnt"&gt;31
&lt;/span&gt;&lt;span class="lnt"&gt;32
&lt;/span&gt;&lt;span class="lnt"&gt;33
&lt;/span&gt;&lt;span class="lnt"&gt;34
&lt;/span&gt;&lt;span class="lnt"&gt;35
&lt;/span&gt;&lt;span class="lnt"&gt;36
&lt;/span&gt;&lt;span class="lnt"&gt;37
&lt;/span&gt;&lt;span class="lnt"&gt;38
&lt;/span&gt;&lt;span class="lnt"&gt;39
&lt;/span&gt;&lt;span class="lnt"&gt;40
&lt;/span&gt;&lt;span class="lnt"&gt;41
&lt;/span&gt;&lt;span class="lnt"&gt;42
&lt;/span&gt;&lt;span class="lnt"&gt;43
&lt;/span&gt;&lt;span class="lnt"&gt;44
&lt;/span&gt;&lt;span class="lnt"&gt;45
&lt;/span&gt;&lt;span class="lnt"&gt;46
&lt;/span&gt;&lt;span class="lnt"&gt;47
&lt;/span&gt;&lt;span class="lnt"&gt;48
&lt;/span&gt;&lt;span class="lnt"&gt;49
&lt;/span&gt;&lt;span class="lnt"&gt;50
&lt;/span&gt;&lt;span class="lnt"&gt;51
&lt;/span&gt;&lt;span class="lnt"&gt;52
&lt;/span&gt;&lt;span class="lnt"&gt;53
&lt;/span&gt;&lt;span class="lnt"&gt;54
&lt;/span&gt;&lt;span class="lnt"&gt;55
&lt;/span&gt;&lt;span class="lnt"&gt;56
&lt;/span&gt;&lt;span class="lnt"&gt;57
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;trigger&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;main&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;pool&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;vmImage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;ubuntu-latest&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;steps&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;UseHugoExtended@1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;latest&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hugo --minify&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Build Hugo site&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ðŸ›¡ï¸ GUARDRAIL: Validate critical output files exist&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;script&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="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; if [ ! -f &amp;#34;public/search/index.json&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;ERROR: Search JSON endpoint missing!&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; fi
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; if [ ! -f &amp;#34;public/index.html&amp;#34; ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;ERROR: Homepage not generated!&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; fi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Validate critical pages exist&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ðŸ›¡ï¸ GUARDRAIL: Check for empty or malformed search index&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;script&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="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; SIZE=$(wc -c &amp;lt; public/search/index.json)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; if [ $SIZE -lt 100 ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;ERROR: Search index is suspiciously small ($SIZE bytes)!&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; fi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Validate search index integrity&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ðŸ›¡ï¸ GUARDRAIL: Verify content pages were generated&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;script&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="sd"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; COUNT=$(find public -name &amp;#34;index.html&amp;#34; -type f | wc -l)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; if [ $COUNT -lt 10 ]; then
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; echo &amp;#34;WARNING: Only $COUNT pages generated (expected more)&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; exit 1
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="sd"&gt; fi&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Verify minimum content threshold&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;PublishBuildArtifacts@1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;pathToPublish&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;artifactName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hugo-site&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Publish build artifacts&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;task&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;AzureStaticWebApp@0&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;inputs&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;azure_static_web_apps_api_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;$(AZURE_STATIC_WEB_APPS_TOKEN)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;repo_token&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;$(GITHUB_TOKEN)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;upload&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app_location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;public&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Deploy to Azure Static Web Apps&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Key safety improvements:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;âœ… Explicit validation that search index exists and has realistic content&lt;/li&gt;
&lt;li&gt;âœ… Minimum page count check to catch silent generation failures&lt;/li&gt;
&lt;li&gt;âœ… Pipeline fails fast instead of deploying broken output&lt;/li&gt;
&lt;li&gt;âœ… Clear error messages for debugging&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Result: deployment confidence went up significantly.&lt;/p&gt;
&lt;h3 id="2-content-architecture-cleanup"&gt;2) Content architecture cleanup
&lt;/h3&gt;&lt;p&gt;Over time, I had duplicate and legacy routes (especially around books and videos). Copilot helped audit what was truly used versus what was just historical baggage.&lt;/p&gt;
&lt;p&gt;I then:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Redesigned the Books page into a cleaner 2-column layout (cover + title/description)&lt;/li&gt;
&lt;li&gt;Removed duplicate &lt;code&gt;publications&lt;/code&gt; pages where canonical pages already existed&lt;/li&gt;
&lt;li&gt;Reviewed and cleaned aliases to keep routing intentional&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Result: fewer moving parts and clearer content ownership.&lt;/p&gt;
&lt;h3 id="3-image-and-asset-governance"&gt;3) Image and asset governance
&lt;/h3&gt;&lt;p&gt;This was the biggest hidden technical debt.&lt;/p&gt;
&lt;p&gt;I had images spread across multiple legacy folders with overlapping filenames. That made reference checks noisy and risky. Copilot helped me run source-scoped audits, identify true usage, and avoid false positives from generated output.&lt;/p&gt;
&lt;p&gt;I used that to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Move post-related images into &lt;code&gt;content/post/images&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Rewrite Markdown links in affected posts&lt;/li&gt;
&lt;li&gt;Handle filename collisions safely&lt;/li&gt;
&lt;li&gt;Remove unused files/folders only after reference validation&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Result: cleaner repository, fewer dead assets, and lower risk of accidental content breakage.&lt;/p&gt;
&lt;h2 id="what-i-liked-most-about-using-copilot-on-an-older-codebase"&gt;What I liked most about using Copilot on an older codebase
&lt;/h2&gt;&lt;p&gt;The biggest value wasnâ€™t â€œAI wrote code for me.â€ It was this:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Faster root-cause analysis&lt;/li&gt;
&lt;li&gt;Safer bulk refactoring with validation checkpoints&lt;/li&gt;
&lt;li&gt;Less context switching for repetitive search/update tasks&lt;/li&gt;
&lt;li&gt;Better confidence to remove legacy clutter without fear&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For old websites, this matters a lot. Most of the work is not feature development â€” itâ€™s careful archaeology.&lt;/p&gt;
&lt;h2 id="practical-lessons-if-you-want-to-modernize-your-own-hugo-site"&gt;Practical lessons if you want to modernize your own Hugo site
&lt;/h2&gt;&lt;p&gt;If your site is aging and you donâ€™t know where to start, this sequence worked very well for me:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fix one visible production issue first (high leverage)&lt;/li&gt;
&lt;li&gt;Add pipeline checks for critical pages/artifacts&lt;/li&gt;
&lt;li&gt;Identify canonical routes and remove duplicates&lt;/li&gt;
&lt;li&gt;Consolidate assets by usage domain (e.g., post images)&lt;/li&gt;
&lt;li&gt;Delete only after source-level reference validation&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Small, verified steps beat one giant risky migration every time.&lt;/p&gt;
&lt;h2 id="final-thoughts"&gt;Final thoughts
&lt;/h2&gt;&lt;p&gt;This modernization started as a broken search page after switching to a new Hugo theme and ended as a full site health upgrade and removal of technical debt.&lt;/p&gt;
&lt;p&gt;GitHub Copilot didnâ€™t replace engineering judgment â€” it amplified it. For me, that was the real win: I could move faster &lt;strong&gt;and&lt;/strong&gt; be more careful at the same time.&lt;/p&gt;
&lt;p&gt;If you have an older Hugo site (or any long-running static site), this is absolutely worth doing.&lt;/p&gt;
&lt;p&gt;By the way, this whole process took less than 2 hours, and about 20 prompts in a continuous conversational approach. Are you a fan of GitHub Copilot? Let me know what your coolest use case has been so far!&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.buymeacoffee.com/pdtit" target="_blank" rel="noopener"
 &gt;&lt;img src="../images/screenshot-2026-02-28-17f576e7.png"
 
 
 loading="lazy"
 
 alt="BuyMeACoffee"
 
 
 
 &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cheers!!&lt;/p&gt;
&lt;p&gt;/Peter&lt;/p&gt;</description></item><item><title>Keeping MCP Server config in sync between VS Code and GitHub Copilot CLI</title><link>https://www.pdtit.be/post/keeping-mcp-config-in-sync-between-vscode-and-cli/</link><pubDate>Thu, 26 Feb 2026 00:00:00 +0000</pubDate><guid>https://www.pdtit.be/post/keeping-mcp-config-in-sync-between-vscode-and-cli/</guid><description>&lt;p&gt;&lt;img src="../images/screenshot-2026-02-26-0bb14fbf.png"
 
 
 loading="lazy"
 
 alt="Keeping MCP config in sync between VS Code and Copilot CLI"
 
 
 
 &gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Syncing MCP config from VS Code to Copilot CLI in a few simple steps.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Hey awesome people,&lt;/p&gt;
&lt;p&gt;Over the last weeks, Iâ€™ve been jumping between VS Code and Copilot CLI a lot more than usual. One thing kept annoying me: my MCP setup was perfect in VS Code, but I had to keep tweaking pieces again in CLI.&lt;/p&gt;
&lt;p&gt;If that sounds familiar, good news: if you already have MCP servers working in VS Code, you can reuse most of that setup in GitHub Copilot CLI.&lt;/p&gt;
&lt;p&gt;In this post, Iâ€™ll show you the fastest way to &amp;lsquo;keep both in sync&amp;rsquo; from VS Code &lt;code&gt;mcp.json&lt;/code&gt; to Copilot CLI &lt;code&gt;mcp-config.json&lt;/code&gt;, with the necessary commands for both PowerShell and Bash.&lt;/p&gt;
&lt;h2 id="why-this-matters"&gt;Why this matters
&lt;/h2&gt;&lt;p&gt;When you move between VS Code and terminal workflows, you really donâ€™t want to rebuild MCP config from scratch every time.&lt;/p&gt;
&lt;p&gt;I made that mistake once (probably multiple times, but didn&amp;rsquo;t want to exagerate too much&amp;hellip;), and it was exactly as fun as it sounds: tiny syntax differences, one invalid server name, one missing env var, and suddenly youâ€™re troubleshooting config instead of actually building. (Although I can tell you that this would be a perfect use case for GenAI GitHub Copilot, lol)&lt;/p&gt;
&lt;p&gt;This guide helps you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;keep one consistent MCP setup style (VSCode MCP Config as the &amp;lsquo;main&amp;rsquo; source)&lt;/li&gt;
&lt;li&gt;avoid common config parsing errors in Copilot CLI&lt;/li&gt;
&lt;li&gt;&amp;lsquo;keep-in-sync&amp;rsquo; in a few minutes, even if youâ€™re new to MCP&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="mcp-in-plain-english"&gt;MCP in plain English
&lt;/h2&gt;&lt;p&gt;MCP (Model Context Protocol) lets AI tools connect to external capabilities.&lt;/p&gt;
&lt;p&gt;Think of MCP servers as â€œskill pluginsâ€ for your assistant, like:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;documentation search&lt;/li&gt;
&lt;li&gt;GitHub actions (issues, PRs, code search)&lt;/li&gt;
&lt;li&gt;browser automation&lt;/li&gt;
&lt;li&gt;Azure DevOps operations&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="file-locations-you-should-know"&gt;File locations you should know
&lt;/h2&gt;&lt;p&gt;VS Code MCP config is typically found at:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Workspace: &lt;code&gt;./mcp.json&lt;/code&gt; or &lt;code&gt;./.vscode/mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;User-level:
&lt;ul&gt;
&lt;li&gt;Windows: &lt;code&gt;%APPDATA%\\Code\\User\\mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;macOS: &lt;code&gt;~/Library/Application Support/Code/User/mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Linux: &lt;code&gt;~/.config/Code/User/mcp.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Copilot CLI MCP config lives here on all platforms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;~/.copilot/mcp-config.json&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="vs-code-vs-copilot-cli-the-important-differences"&gt;VS Code vs Copilot CLI: the important differences
&lt;/h2&gt;&lt;p&gt;The two formats are very close, but not identical:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Top-level key&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VS Code: &lt;code&gt;servers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copilot CLI: &lt;code&gt;mcpServers&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Server ID naming rules in Copilot CLI&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;allowed: letters, numbers, &lt;code&gt;_&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;server keys with &lt;code&gt;/&lt;/code&gt; must be renamed&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;VS Code &lt;code&gt;inputs&lt;/code&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;supported in VS Code flow&lt;/li&gt;
&lt;li&gt;not used in Copilot CLI config&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Placeholder syntax&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;VS Code often uses &lt;code&gt;${env:VAR}&lt;/code&gt; and &lt;code&gt;${input:name}&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Copilot CLI expects &lt;code&gt;$VAR&lt;/code&gt; and supports explicit &lt;code&gt;env&lt;/code&gt; mappings&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;None of this is hard, but itâ€™s just different enough to break things when done manually in a hurry.&lt;/p&gt;
&lt;h2 id="fast-path-recommended-automate-conversion"&gt;Fast path (recommended): automate conversion
&lt;/h2&gt;&lt;p&gt;You can grab both scripts directly from my GitHub repo:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/petender/MCP-Clone_VSCode2CLI" target="_blank" rel="noopener"
 &gt;https://github.com/petender/MCP-Clone_VSCode2CLI&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Quick clone commands (PowerShell, bash, zsh):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;git clone https://github.com/petender/MCP-Clone_VSCode2CLI.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; MCP-Clone_VSCode2CLI
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;Use one of these scripts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;convert-mcp-config.ps1&lt;/code&gt; (Windows/macOS/Linux with &lt;code&gt;pwsh&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;convert-mcp-config.sh&lt;/code&gt; (macOS/Linux with &lt;code&gt;bash&lt;/code&gt; + &lt;code&gt;python3&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I personally use the PowerShell version most of the time, no surprise I&amp;rsquo;m primarily a Windows-guy, but didn&amp;rsquo;t want to assume everyone is using PowerShell nowadays. (You should though ;)&lt;/p&gt;
&lt;h3 id="what-the-script-converts-automatically"&gt;What the script converts automatically
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;servers&lt;/code&gt; -&amp;gt; &lt;code&gt;mcpServers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;removes VS Code-only &lt;code&gt;inputs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;converts &lt;code&gt;${input:name}&lt;/code&gt; -&amp;gt; &lt;code&gt;$NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;converts &lt;code&gt;${env:VAR}&lt;/code&gt; -&amp;gt; &lt;code&gt;$VAR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;adds/merges &lt;code&gt;env&lt;/code&gt; mappings like &lt;code&gt;&amp;quot;VAR&amp;quot;: &amp;quot;$VAR&amp;quot;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;renames invalid server IDs (for example containing &lt;code&gt;/&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is exactly the part that saves the most time (and avoids the most headaches).&lt;/p&gt;
&lt;h2 id="powershell-usage-convert-mcp-configps1"&gt;PowerShell usage (&lt;code&gt;convert-mcp-config.ps1&lt;/code&gt;)
&lt;/h2&gt;&lt;p&gt;The script supports cross-platform defaults using your home path.&lt;/p&gt;
&lt;h3 id="easiest-mode"&gt;Easiest mode
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;Bypass&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="nb"&gt;convert-mcp&lt;/span&gt;&lt;span class="n"&gt;-config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;This auto-discovers common input locations and writes to &lt;code&gt;~/.copilot/mcp-config.json&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="explicit-input-default-output"&gt;Explicit input, default output
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;Bypass&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="nb"&gt;convert-mcp&lt;/span&gt;&lt;span class="n"&gt;-config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InputPath&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="explicit-input-and-output"&gt;Explicit input and output
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;Bypass&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="nb"&gt;convert-mcp&lt;/span&gt;&lt;span class="n"&gt;-config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InputPath&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;json&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-OutputPath&lt;/span&gt; &lt;span class="p"&gt;~/.&lt;/span&gt;&lt;span class="n"&gt;copilot&lt;/span&gt;&lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;mcp-config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;json&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="custom-home-path"&gt;Custom home path
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;pwsh&lt;/span&gt; &lt;span class="n"&gt;-ExecutionPolicy&lt;/span&gt; &lt;span class="n"&gt;Bypass&lt;/span&gt; &lt;span class="o"&gt;-File&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="nb"&gt;convert-mcp&lt;/span&gt;&lt;span class="n"&gt;-config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;ps1&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-InputPath&lt;/span&gt; &lt;span class="p"&gt;./&lt;/span&gt;&lt;span class="n"&gt;mcp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="py"&gt;json&lt;/span&gt; &lt;span class="p"&gt;\&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;-UserHome&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="p"&gt;/&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="bash-usage-convert-mcp-configsh"&gt;Bash usage (&lt;code&gt;convert-mcp-config.sh&lt;/code&gt;)
&lt;/h2&gt;&lt;p&gt;First run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;chmod +x ./convert-mcp-config.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="easiest-mode-1"&gt;Easiest mode
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./convert-mcp-config.sh
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="explicit-input-default-output-1"&gt;Explicit input, default output
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./convert-mcp-config.sh --input ./mcp.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="explicit-input-and-output-1"&gt;Explicit input and output
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./convert-mcp-config.sh --input ./mcp.json --output ~/.copilot/mcp-config.json
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="custom-home-path-1"&gt;Custom home path
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;./convert-mcp-config.sh --input ./mcp.json --user-home /home/&amp;lt;user&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h3 id="bash-prerequisites"&gt;Bash prerequisites
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;&lt;code&gt;bash&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;python3&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="verify-in-copilot-cli"&gt;Verify in Copilot CLI
&lt;/h2&gt;&lt;p&gt;After conversion, open Copilot CLI and run:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;/mcp reload&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;/mcp show&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If your config is valid, your servers should load without parsing errors.&lt;/p&gt;
&lt;p&gt;If they donâ€™t show up right away, donâ€™t panic. In most cases itâ€™s either naming rules or env vars (both covered below).&lt;/p&gt;
&lt;h2 id="required-environment-variables-for-the-sample-config"&gt;Required environment variables (for the sample config)
&lt;/h2&gt;&lt;p&gt;Set these before launching Copilot CLI:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;GITHUB_PERSONAL_ACCESS_TOKEN&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ADO_ORG&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ADO_DOMAIN&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;PowerShell session example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-powershell" data-lang="powershell"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$env:GITHUB_PERSONAL_ACCESS_TOKEN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;your_token&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$env:ADO_ORG&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;your_ado_org&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;$env:ADO_DOMAIN&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;core&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;bash/zsh example:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;GITHUB_PERSONAL_ACCESS_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;your_token&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;ADO_ORG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&amp;lt;your_ado_org&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;export&lt;/span&gt; &lt;span class="nv"&gt;ADO_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;core&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;h2 id="manual-conversion-checklist"&gt;Manual conversion checklist
&lt;/h2&gt;&lt;p&gt;If you want to edit by hand, follow this sequence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rename &lt;code&gt;servers&lt;/code&gt; to &lt;code&gt;mcpServers&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;remove &lt;code&gt;inputs&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;replace &lt;code&gt;${input:name}&lt;/code&gt; with &lt;code&gt;$NAME&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;replace &lt;code&gt;${env:VAR}&lt;/code&gt; with &lt;code&gt;$VAR&lt;/code&gt; and add/update &lt;code&gt;env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;rename server IDs that contain unsupported characters&lt;/li&gt;
&lt;li&gt;keep operational properties unchanged (&lt;code&gt;type&lt;/code&gt;, &lt;code&gt;command&lt;/code&gt;, &lt;code&gt;args&lt;/code&gt;, &lt;code&gt;url&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt;, &lt;code&gt;gallery&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This checklist is also useful as a review checklist in PRs.&lt;/p&gt;
&lt;h2 id="common-errors-and-quick-fixes"&gt;Common errors and quick fixes
&lt;/h2&gt;&lt;h3 id="mcp-server-name-must-only-contain-alphanumeric-characters-underscores-and-hyphens"&gt;&lt;code&gt;MCP server name must only contain alphanumeric characters, underscores, and hyphens&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Cause: a server key still contains &lt;code&gt;/&lt;/code&gt; (or another invalid character).&lt;/p&gt;
&lt;p&gt;Fix example:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;microsoft/playwright-mcp&lt;/code&gt; -&amp;gt; &lt;code&gt;microsoft_playwright_mcp&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="server-starts-but-fails-with-authruntime-errors"&gt;Server starts but fails with auth/runtime errors
&lt;/h3&gt;&lt;p&gt;Cause: missing or wrong environment variable values.&lt;/p&gt;
&lt;p&gt;Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;validate variable names exactly&lt;/li&gt;
&lt;li&gt;confirm values exist in the active shell session&lt;/li&gt;
&lt;li&gt;run &lt;code&gt;/mcp reload&lt;/code&gt; again&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also make sure you start Copilot CLI from the same shell/session where variables are set.&lt;/p&gt;
&lt;h3 id="command-not-found"&gt;&lt;code&gt;command not found&lt;/code&gt;
&lt;/h3&gt;&lt;p&gt;Cause: one of the required tools is missing.&lt;/p&gt;
&lt;p&gt;Fix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;install dependencies used by your config (&lt;code&gt;docker&lt;/code&gt;, &lt;code&gt;npx&lt;/code&gt;, &lt;code&gt;uvx&lt;/code&gt;, etc.)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;Most VS Code MCP config can be reused in Copilot CLI. The key differences are server ID naming, top-level key name, and placeholder handling.&lt;/p&gt;
&lt;p&gt;While one could assume that the MCP Configuration should be standardized across platforms, once I figured out the key syntax differences (GitHub Copilot for the win!), it ended up being much easier than I expected once I stopped doing it manually and automated the boring parts (GitHub Copilot for the win x2!).&lt;/p&gt;
&lt;p&gt;If this helped you, feel free to share it with your team so everyone can standardize MCP config faster. And maybe give a little GitHub Star on the repo, so I know you like it ;).&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.buymeacoffee.com/pdtit" target="_blank" rel="noopener"
 &gt;&lt;img src="../images/screenshot-2026-02-26-17f576e7.png"
 
 
 loading="lazy"
 
 alt="BuyMeACoffee"
 
 
 
 &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cheers!!&lt;/p&gt;
&lt;p&gt;/Peter&lt;/p&gt;</description></item><item><title>Using GitHub Copilot Agent Mode to vibe code a Python shooting game</title><link>https://www.pdtit.be/post/using-github-copilot-agent-mode-to-vibe-code-a-python-shooting-game/</link><pubDate>Fri, 28 Nov 2025 00:00:00 +0000</pubDate><guid>https://www.pdtit.be/post/using-github-copilot-agent-mode-to-vibe-code-a-python-shooting-game/</guid><description>&lt;p&gt;For about a year now, I&amp;rsquo;ve been teaching a lot on &lt;a class="link" href="https://github.com/copilot" target="_blank" rel="noopener"
 &gt;GitHub Copilot&lt;/a&gt; as part of my Microsoft role. Our program offers 2 different learning paths, one created by the Microsoft Content developers, &lt;a class="link" href="https://learn.microsoft.com/en-us/training/paths/accelerate-app-development-using-github-copilot/" target="_blank" rel="noopener"
 &gt;AZ-2007&lt;/a&gt;, and the other one is managed by GitHub Content team, known as &lt;a class="link" href="https://learn.microsoft.com/en-us/training/paths/copilot/" target="_blank" rel="noopener"
 &gt;GH-300&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you know my approach to teaching tech a bit, which a learner in my class lately called &lt;strong&gt;inspiring through technology&lt;/strong&gt;, it means I&amp;rsquo;m trying to explain as much as possible through compelling, live demos. After walking learners through different GitHub Copilot features such as documenting/explaining code, generate application code (on different development frameworks), but also Azure CLI, CI/CD pipeline, Dockerfile, YAML, JSON and alike, I usually close with &lt;a class="link" href="https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent" target="_blank" rel="noopener"
 &gt;Agent Mode&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Having showed the first time in April when GitHub Copilot was still in preview, I usually show a demo where the Agent Mode builds me an ASP.NET webapp, modifies the Welcome home page, creates some sample employee data in a json-file, which then gets displayed in a table view in the webapp. If time allows, I also ask to then migrate everything into a SQLite setup, which brings in more complexity such as Entity Framework, SQL data migration steps and interaction with Azure KeyVault, since I specify I want to run this in Azure SQL, but not allowing connection strings in my appsettings.json.&lt;/p&gt;
&lt;p&gt;(Now that I think about it, it might be another great blog post to write on in the near future&amp;hellip;)&lt;/p&gt;
&lt;p&gt;Earlier this week however, I came up with a new scenario, asking Agent Mode to develop a &lt;em&gt;shooter game&lt;/em&gt; using Python code, bringing me back to my youth in the mid-80&amp;rsquo;s when I was playing such games on my first 486-PC.&lt;/p&gt;
&lt;h2 id="agent-mode-prompt"&gt;Agent Mode Prompt
&lt;/h2&gt;&lt;p&gt;The prompt I used was this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;
&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1
&lt;/span&gt;&lt;span class="lnt"&gt;2
&lt;/span&gt;&lt;span class="lnt"&gt;3
&lt;/span&gt;&lt;span class="lnt"&gt;4
&lt;/span&gt;&lt;span class="lnt"&gt;5
&lt;/span&gt;&lt;span class="lnt"&gt;6
&lt;/span&gt;&lt;span class="lnt"&gt;7
&lt;/span&gt;&lt;span class="lnt"&gt;8
&lt;/span&gt;&lt;span class="lnt"&gt;9
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;
&lt;td class="lntd"&gt;
&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;*&lt;span class="s2"&gt;&amp;#34;as a kid, I played arcade games a lot. I want to build a Python app, which tests me on my shooting reflexes. Help me developing a game which does the following:
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;1. aks for player name input
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;2. bottom middle of the game screen shows a shooter 
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;3. anywhere random on screen appears a target
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;4. player uses the space bar to simulate a shoot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;5. calculate the time between the target appearing and player pressing the space bar to shoot
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;6. if that time is less than 0.3 sec, player wins, otherwise computer wins
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;7. show a &amp;#34;&lt;/span&gt;YOU WIN&lt;span class="s2"&gt;&amp;#34; or &amp;#34;&lt;/span&gt;YOU ARE TOO SLOW&lt;span class="s2"&gt;&amp;#34; dpeending on the outcome&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-018f3ae5.png"
 
 
 loading="lazy"
 
 alt="Agent Mode Prompt Used"
 
 
 
 &gt;&lt;/p&gt;
&lt;h2 id="agent-mode-processing"&gt;Agent Mode Processing
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;From here, the &lt;strong&gt;Agent started rolling&amp;hellip;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Confirming with some sort of understanding what I asked for, followed by &lt;em&gt;creating 3 todos&lt;/em&gt;:&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;Set Up Python Environment&lt;/li&gt;
&lt;li&gt;Creating the Reflex Shooting Game&lt;/li&gt;
&lt;li&gt;Testing the Game&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-88f73764.png"
 
 
 loading="lazy"
 
 alt="Agent Mode Starting"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;This process took less than &lt;strong&gt;1 minute&lt;/strong&gt;, can you imagine? From there, it continued with providing detailed instructions on how to the game works.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-f533f735.png"
 
 
 loading="lazy"
 
 alt="Game Playing Instructions"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Next, it also had a list of features included:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-dc0b721b.png"
 
 
 loading="lazy"
 
 alt="Game Features Included"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Time to start the game!!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-c3d6fa9b.png"
 
 
 loading="lazy"
 
 alt="Start Game"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Which allowed me to play exactly as I asked for. When I was too slow, it would tell me&amp;hellip;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-df129c5d.png"
 
 
 loading="lazy"
 
 alt="Too Slow"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;And several attempts later, I finally managed to win a game!!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-11-28-8d0ea57f.png"
 
 
 loading="lazy"
 
 alt="You Win"
 
 
 
 &gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;I thought after using GitHub Copilot for training our customers and showing capabilities using live demos for about 5 hours per class, as well as using it for a lot of &amp;ldquo;coding&amp;rdquo; tasks as part of my role as trainer, mainly creating more demo scenarios (see &lt;a class="link" href="https://aka.ms/trainer-demo-deploy" target="_blank" rel="noopener"
 &gt;Trainer-Demo-Deploy&lt;/a&gt; to get an idea what that means&amp;hellip;), I thought I&amp;rsquo;d seen it all.&lt;/p&gt;
&lt;p&gt;Yet, &lt;strong&gt;it keeps suprising me every single day when I try something new.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If I had access to this technology in the mid 80&amp;rsquo;s, I guess I would have spent more time learning about coding than playing games&amp;hellip; although this game is actually pretty addicting already. Time to wrap up this post and go play a bit more! And feel 12 years old again.&lt;/p&gt;
&lt;p&gt;If you want to see some similar version of this templategame in action, head over to &lt;a class="link" href="https://github.com/petender/vibeshooter" target="_blank" rel="noopener"
 &gt;my github repo&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.buymeacoffee.com/pdtit" target="_blank" rel="noopener"
 &gt;&lt;img src="../images/screenshot-2025-11-28-17f576e7.png"
 
 
 loading="lazy"
 
 alt="BuyMeACoffee"
 
 
 
 &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cheers!!&lt;/p&gt;
&lt;p&gt;/Peter&lt;/p&gt;</description></item><item><title>Using GitHub Copilot Agent Mode to transform ARM templates to BICEP</title><link>https://www.pdtit.be/post/using-github-copilot-agent-mode-to-transform-arm-templates-to-bicep/</link><pubDate>Sat, 12 Jul 2025 00:00:00 +0000</pubDate><guid>https://www.pdtit.be/post/using-github-copilot-agent-mode-to-transform-arm-templates-to-bicep/</guid><description>&lt;p&gt;Over the last few months, I&amp;rsquo;ve been working on an exciting project for our Microsoft Technical Trainer team, known as &lt;a class="link" href="https://aka.ms/trainer-demo-deploy" target="_blank" rel="noopener"
 &gt;&amp;ldquo;Trainer-Demo-Deploy&amp;rdquo;&lt;/a&gt;, a catalog of Azure end-to-end demo scenarios, available as an Open-Source project.&lt;/p&gt;
&lt;p&gt;While we managed to get about 50 templates live, there can never be enough scenarios to integrate into your Azure classes or POC activities if you ask me. One of the challenging tasks in the project is not only coming up with demo ideas, but also creating the actual artifacts, such as Azure templates with Bicep, sample apps and sample data.&lt;/p&gt;
&lt;p&gt;I had an &lt;a class="link" href="https://azure.microsoft.com/en-us/products/site-recovery/" target="_blank" rel="noopener"
 &gt;Azure Site Recovery Services&lt;/a&gt; scenario from a few years ago, written in modular ARM templates. With Bicep providing a great way to &lt;a class="link" href="https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/decompile?tabs=azure-cli" target="_blank" rel="noopener"
 &gt;transform your ARM to Bicep&lt;/a&gt;, I could have gone through each template file and convert them. Have done several of those over the last few months.&lt;/p&gt;
&lt;p&gt;But out of teaching &lt;a class="link" href="https://learn.microsoft.com/en-us/training/paths/accelerate-app-development-using-github-copilot/" target="_blank" rel="noopener"
 &gt;AZ-2007 Accelerate app development by using GitHub Copilot&lt;/a&gt;, where I integrate a - what I think amazing demo on how to use Agent mode to deploy a sample web app - I started thinking about testing if &lt;strong&gt;Agent Mode&lt;/strong&gt; could help me with this transformation project.&lt;/p&gt;
&lt;p&gt;Fact I&amp;rsquo;m dedicating a blog post to it, is mainly to confirm it worked amazingly well, as well as sharing my excitement and some steps of what the process looked like. Hopefully this post inspires you to start embracing &lt;a class="link" href="https://github.blog/ai-and-ml/github-copilot/agent-mode-101-all-about-github-copilots-powerful-mode/" target="_blank" rel="noopener"
 &gt;&lt;strong&gt;GitHub Copilot Agent Mode&lt;/strong&gt;&lt;/a&gt; into your own tasks.&lt;/p&gt;
&lt;h2 id="my-starting-templates"&gt;My starting templates
&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;My original setup was pretty straightforward, having a folder &amp;ldquo;templates&amp;rdquo;, in which I have modular templates for each part of my Azure Site Recovery Vault deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Each templates hold a snippet of ARM / JSON structured code to deploy one or more Azure Resources.&lt;/p&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-677ae48c.png"
 
 
 loading="lazy"
 
 alt="Current ARM Templates"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="2"&gt;
&lt;li&gt;I opened up the folder structure in my Visual Studio Code, and opened GitHub Copilot, selecting &lt;strong&gt;Agent Mode&lt;/strong&gt;. I &lt;em&gt;clearly described in a prompt&lt;/em&gt;, what I wanted the Agent to perform as tasks. I didn&amp;rsquo;t provide much detail to be honest, as initially, I was merily experimenting to try and find out if Agent could actually help with this, or how far it would go in the process.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id="agent-mode-prompt"&gt;Agent Mode Prompt
&lt;/h2&gt;&lt;p&gt;The prompt I used was this:
&lt;em&gt;for each file in the templates folder, convert to azure bicep. create a new bicep file for each, keeping the same name as the original json file. the azuredeploy should be transformed to main.bicep. validate all pointers to all new bicep files to be correct&amp;quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-0e6d3c1f.png"
 
 
 loading="lazy"
 
 alt="Agent Mode Prompt Used"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="3"&gt;
&lt;li&gt;
&lt;p&gt;From here, the &lt;strong&gt;Agent started rolling&amp;hellip;&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Informing me about the different steps it would take to handle this task, starting with &lt;em&gt;exploring the templates folder to see all the files that need to be converted&lt;/em&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-9563c155.png"
 
 
 loading="lazy"
 
 alt="Agent Mode Starting"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="5"&gt;
&lt;li&gt;
&lt;p&gt;Followed by going more in-depth into each and every template JSON file&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Followed by &lt;em&gt;starting the conversion process to Bicep files&lt;/em&gt;. Before doing that, it also highlighted it would check the Azure deployment best practices (although I didn&amp;rsquo;t explicitly asked to do that, nice one! )&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-23d6f25c.png"
 
 
 loading="lazy"
 
 alt="Agent Mode inspecting files"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="7"&gt;
&lt;li&gt;It felt like it &lt;em&gt;learned&lt;/em&gt; from the best practices, by starting with the azuredeploy.json conversion to main.bicep first. It could also be that it started with this, because I mentioned this in the prompt itself.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-048d528f.png"
 
 
 loading="lazy"
 
 alt="Agent Mode creating main.bicep"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="8"&gt;
&lt;li&gt;As the main.bicep conversion took a bit longer than normal - although it was only running over it for about a minute, it &lt;em&gt;prompted&lt;/em&gt;, asking if it was ok to continue. Obviously, I confirmed to &lt;strong&gt;continue&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-9f313718.png"
 
 
 loading="lazy"
 
 alt="Agent Mode pending task"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="9"&gt;
&lt;li&gt;From here, it nicely continued looping through all smaller json-files, and transforming them into corresponding bicep-files. Since each file typically had only 1 or 2 resource references, the conversion went really smooth.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-cc406a5d.png"
 
 
 loading="lazy"
 
 alt="Agent Mode conversion"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="10"&gt;
&lt;li&gt;After a bit, it had finished the transformation of each ARM to Bicep, and &lt;em&gt;started on updating the template links&lt;/em&gt;, as I asked for in my prompt, to also validate the references to all the deployment files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-3a52cde6.png"
 
 
 loading="lazy"
 
 alt="Agent Mode updating file links"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="11"&gt;
&lt;li&gt;With all references updated, it continued with its own &lt;em&gt;error checking&lt;/em&gt;, and validating the different templates for any possible errors. Even more interesting, &lt;strong&gt;without me specifying&lt;/strong&gt;, it detected an error in the &lt;strong&gt;azure.yaml&lt;/strong&gt;, which I had in my project folder, from a baseline started AZD-template we use to create all our Trainer-Demo-Deploy scenarios.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-c58c585e.png"
 
 
 loading="lazy"
 
 alt="Agent Mode create azure.yaml"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="12"&gt;
&lt;li&gt;Last, it also created a &lt;em&gt;main.parameters.json&lt;/em&gt;, to capture any specific Parameters for the deployment.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-52ae46b8.png"
 
 
 loading="lazy"
 
 alt="Agent Mode create main.parameters.json"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="13"&gt;
&lt;li&gt;From here, it went back to &lt;em&gt;validating&lt;/em&gt; the Bicep templates again, where it detected a few different issues. (I didn&amp;rsquo;t check in detail what got identified as issue, as it didn&amp;rsquo;t prompt me to validate anything on my end&amp;hellip;); based on the &lt;em&gt;next informational message&lt;/em&gt;, it struggled with missing an output for &lt;strong&gt;myWorkspaceKey&lt;/strong&gt;, in the deploy-infra.bicep file.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images2025-07-04-bicep-error.png"
 
 
 loading="lazy"
 
 alt="Agent Mode validating infra"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="14"&gt;
&lt;li&gt;&lt;em&gt;Chewing&lt;/em&gt; a bit on the myWorkspaceKey problem, it managed to find its own work-around to solve the problem. It even provided a clear explaining on why, identifying the dependency on the parent template.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-7c9e3eae.png"
 
 
 loading="lazy"
 
 alt="Agent Mode detecting an issue"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="15"&gt;
&lt;li&gt;Feeling we were close to the end of the process, it &lt;strong&gt;continued amazing&lt;/strong&gt; me, as it now also created its &lt;strong&gt;own documentation&lt;/strong&gt; in a BICEP_CONVERSION_SUMMARY.md Markdown file, in which it listed up what conversations it did.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-233210a1.png"
 
 
 loading="lazy"
 
 alt="Agent Mode creating conversion documentation"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="16"&gt;
&lt;li&gt;With all that out of the way, it ran another &lt;strong&gt;final validation&lt;/strong&gt; to conclude there were no more issues, closing the task with creating another README-BICEP.md file, describing how to run the actual deployment, using AZD.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-ea367bd4.png"
 
 
 loading="lazy"
 
 alt="Agent Mode creating readme"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="17"&gt;
&lt;li&gt;Finally, the Agent &lt;strong&gt;provided a description within the Chat Agent window&lt;/strong&gt;, clearly describing all the tasks accomplished with the necessary file references included:&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-12b1b132.png"
 
 
 loading="lazy"
 
 alt="Agent Mode Chat description"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="18"&gt;
&lt;li&gt;As well as adding additional details on the &lt;strong&gt;task validation&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-8a5606f6.png"
 
 
 loading="lazy"
 
 alt="Agent Mode creating task validation"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="19"&gt;
&lt;li&gt;Finishing with describing different ways on how to run the actual deployment, using &lt;strong&gt;azd&lt;/strong&gt;, &lt;strong&gt;Azure CLI&lt;/strong&gt; and &lt;strong&gt;Azure PowerShell&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-1a21a2ce.png"
 
 
 loading="lazy"
 
 alt="Agent Mode explaining how to run the deployment"
 
 
 
 &gt;&lt;/p&gt;
&lt;ol start="20"&gt;
&lt;li&gt;Last step was running the deployment, and this worked without any hiccups!&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src="../images/screenshot-2025-07-12-b01b0623.png"
 
 
 loading="lazy"
 
 alt="Deployment completed"
 
 
 
 &gt;&lt;/p&gt;
&lt;h2 id="summary"&gt;Summary
&lt;/h2&gt;&lt;p&gt;As mentioned earlier, I didn&amp;rsquo;t intend to go through this process as part of writing a blog post. Yet, the fact that the &lt;strong&gt;GitHub Copilot Agent Mode&lt;/strong&gt; happily suprised me once more, I wanted to share my joy and excitement about this.&lt;/p&gt;
&lt;p&gt;Starting from a &lt;em&gt;somewhat complex&lt;/em&gt; JSON ARM template folder with about 10 modular arm-json files, it managed to nicely convert all of them into the new Bicep template language, with only a few minor issues throughout the process. Without asking assistance or halting the process, it ran its own troubleshooting and issue resolution, resulting in a 100% successful transformation.&lt;/p&gt;
&lt;p&gt;Apart from the technical success of the task, what surprised me even more, is &lt;strong&gt;it only took the agent barely 5 minutes&lt;/strong&gt; and only had to &lt;strong&gt;prompt me twice&lt;/strong&gt; during the whole process!!&lt;/p&gt;
&lt;p&gt;If you want to see this template in action, head over to &lt;a class="link" href="https://github.com/petender/azd-asrdemo" target="_blank" rel="noopener"
 &gt;my github repo&lt;/a&gt; and continue your Azure learning journey with more demo scenarios at &lt;a class="link" href="https://aka.ms/trainer-demo-deploy" target="_blank" rel="noopener"
 &gt;Trainer-Demo-Deploy&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://www.buymeacoffee.com/pdtit" target="_blank" rel="noopener"
 &gt;&lt;img src="../images/screenshot-2025-07-12-17f576e7.png"
 
 
 loading="lazy"
 
 alt="BuyMeACoffee"
 
 
 
 &gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Cheers!!&lt;/p&gt;
&lt;p&gt;/Peter&lt;/p&gt;</description></item></channel></rss>