{"id":539,"date":"2026-10-16T12:06:45","date_gmt":"2026-10-16T10:06:45","guid":{"rendered":"https:\/\/dataguide.tech\/?p=539"},"modified":"2026-11-12T23:17:43","modified_gmt":"2026-11-12T22:17:43","slug":"agentic-data-engineering-in-microsoft-fabric-with-claude-code","status":"publish","type":"post","link":"https:\/\/dataguide.tech\/index.php\/2026\/10\/16\/agentic-data-engineering-in-microsoft-fabric-with-claude-code\/","title":{"rendered":"Agentic Data Engineering in Microsoft Fabric with Claude Code"},"content":{"rendered":"\n<blockquote class=\"wp-block-quote is-style-dg-status is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Status check, verified October 2026.<\/strong> The Fabric Core MCP Server is generally available since the September 2026 Fabric update, but the pieces around it move fast. The Fabric skills are open source and get new versions often, the Fabric CLI ships regularly, and the VS Code extension changes its UI from time to time. If something here doesn&#8217;t behave the way I describe, check the <a href=\"https:\/\/learn.microsoft.com\/en-us\/rest\/api\/fabric\/articles\/mcp-servers\/what-is-fabric-mcp-server\" target=\"_blank\" rel=\"noopener\">Microsoft Learn docs<\/a> before assuming your setup is broken. I date-stamp every post in this series and update them when things move.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">Most of a data engineer&#8217;s day in Fabric goes into notebooks and pipelines. And most of the AI help I see people using for that work stops at a chat window: copy the code out, ask a question, paste the answer back into the notebook, run it, repeat. It works. Kind of. But it&#8217;s a long way from an agent that can open your notebook, change it, run it and tell you what broke.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">In this post I&#8217;ll go through the full Data Engineering flow in Microsoft Fabric the way I actually work: notebooks, Fabric skills, pipelines, Dataflow Gen2, and committing all of it to Git at the end.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Two prerequisites. The tool setup from <a href=\"https:\/\/dataguide.tech\/index.php\/2026\/08\/12\/agentic-development-in-microsoft-fabric-part-1\/\">Agentic Development in Microsoft Fabric, Part 1: Setup<\/a>, and the workspace we built in the infrastructure post, <a href=\"https:\/\/dataguide.tech\/index.php\/2026\/10\/16\/one-prompt-one-workspace-agentic-microsoft-fabric-admin\/\">One Prompt, One Workspace: Agentic Microsoft Fabric Admin<\/a>. If you haven&#8217;t done them yet, go there first.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The core of my setup is Visual Studio Code with the Fabric Data Engineering extension. It connects you to your Fabric notebooks, sure. More importantly, it lets your LLM (in my case Claude Code) see and edit those notebooks, and run and test them before the code goes back to the workspace.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Which tool does what<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Same as in the infrastructure post, the agent switches between tools depending on the job. This time there&#8217;s a fourth one in play: the VS Code extension. Quick map, so the rest of the post makes sense:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Work<\/th><th>Tool<\/th><th>Why this one<\/th><\/tr><\/thead><tbody><tr><td>Edit, run and publish notebooks<\/td><td>Fabric Data Engineering VS Code extension<\/td><td>Puts the notebook on your disk as a real file, so any LLM can edit it. It has its own sign-in, separate from the MCP and both CLIs.<\/td><\/tr><tr><td>Notebook code and runs via the <code>spark-authoring-cli<\/code> skill<\/td><td>Azure CLI (<code>az rest<\/code>)<\/td><td>The skill calls the Fabric REST API directly, so it works for every endpoint, MCP or not.<\/td><\/tr><tr><td>Warehouse \/ SQL DB via <code>sqldw-authoring-cli<\/code><\/td><td><code>sqlcmd<\/code> + Azure CLI<\/td><td>T-SQL runs over a SQL connection, not REST. <code>az login<\/code> provides the identity.<\/td><\/tr><tr><td>Create a pipeline from a prompt<\/td><td>Fabric MCP<\/td><td>Built-in <em>create item<\/em> tool, nothing to install.<\/td><\/tr><tr><td>Export, edit and re-import a pipeline, schedules<\/td><td>Fabric CLI (<code>fab<\/code>)<\/td><td>Repeatable, file-based, works with service principals.<\/td><\/tr><tr><td>Dataflow Gen2 via <code>dataflows-cli<\/code><\/td><td>Azure CLI (<code>az rest<\/code>)<\/td><td>Definitions and M preview (<code>executeQuery<\/code>) are REST endpoints the MCP doesn&#8217;t expose.<\/td><\/tr><tr><td>Publish an environment<\/td><td><code>az rest<\/code> \/ <code>fab api<\/code><\/td><td>Staging and publish are separate API calls the MCP doesn&#8217;t have.<\/td><\/tr><tr><td>Commit to Git<\/td><td><code>git-integration-operations-cli<\/code> \u2192 <code>fab api<\/code> (falls back to <code>az rest<\/code>)<\/td><td>No Git tools in the MCP.<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The rule of thumb hasn&#8217;t changed: MCP when there&#8217;s a ready-made tool for it, <code>fab<\/code> for repeatable work, <code>az<\/code> whenever a skill needs a REST endpoint the MCP doesn&#8217;t cover. In practice that means you want all three logged in before you start. <code>\/mcp<\/code> shows the MCP, <code>fab auth status<\/code> shows <code>fab<\/code>, and <code>az account show<\/code> shows <code>az<\/code>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Notebooks in VS Code<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 <strong>VS Code extension<\/strong>, not MCP and not a CLI. This whole notebook section runs through the extension and its own sign-in.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Signing in to the extension<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">First, authorize yourself in the Fabric Data Engineering extension:<\/p>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Open VS Code<\/li>\n\n\n\n<li>Open the Command Palette (usually Ctrl+Shift+P, or Cmd+Shift+P on macOS)<\/li>\n\n\n\n<li>Type &#8220;Fabric Data Engineering&#8221; and select the Sign In option:<\/li>\n<\/ol>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1512\" height=\"822\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260817205725-1.png\" alt=\"Pasted image 20260817205725.png\" class=\"wp-image-630\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260817205725-1.png 1512w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260817205725-1-300x163.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260817205725-1-1024x557.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260817205725-1-768x418.png 768w\" sizes=\"auto, (max-width: 1512px) 100vw, 1512px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Your default browser opens with the standard Microsoft login window. Enter your credentials and sign in. After that, the extension can connect to your workspaces and items:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1113\" height=\"655\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260907225623-1.png\" alt=\"Pasted image 20260907225623.png\" class=\"wp-image-619\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260907225623-1.png 1113w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260907225623-1-300x177.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260907225623-1-1024x603.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260907225623-1-768x452.png 768w\" sizes=\"auto, (max-width: 1113px) 100vw, 1113px\" \/><\/figure>\n\n\n\n<ol class=\"wp-block-list\">\n<li>Choose the extension<\/li>\n\n\n\n<li>Click the select workspace icon<\/li>\n\n\n\n<li>Pick your workspace from the list<\/li>\n<\/ol>\n\n\n\n<p class=\"wp-block-paragraph\">At the bottom (point 4 in the picture) you can check which account is currently logged in to the extension. Once you&#8217;re connected to a workspace, the left-hand menu shows all its Data Engineering items: notebooks, Spark job definitions, lakehouses and environments. There&#8217;s also a section where you can save your prompts, so you don&#8217;t have to type the same thing twice (skills can do something similar).<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"484\" height=\"596\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908094304-1.png\" alt=\"Pasted image 20260908094304.png\" class=\"wp-image-617\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908094304-1.png 484w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908094304-1-244x300.png 244w\" sizes=\"auto, (max-width: 484px) 100vw, 484px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Live or local?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Expand Notebooks and you&#8217;ll see every notebook in the workspace. You can open one in two ways:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Live connection<\/strong> &#8211; you work directly against the workspace, so every change shows up in the portal immediately. The catch: since it all happens online, Claude Code can&#8217;t see or edit the notebook. Claude works on local files.<\/li>\n\n\n\n<li><strong>Local mode<\/strong> &#8211; my preferred one. You download the notebook, make your changes locally, and still run it from VS Code on a Spark cluster in Fabric. When you&#8217;re done, you publish the updated version back to Fabric.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Live connection<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">To open a notebook live, click the &#8220;Open notebook folder&#8221; icon:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"579\" height=\"78\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908100032-3.png\" alt=\"Pasted image 20260908100032.png\" class=\"wp-image-627\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908100032-3.png 579w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908100032-3-300x40.png 300w\" sizes=\"auto, (max-width: 579px) 100vw, 579px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The notebook opens in exactly the state it&#8217;s in online. Here&#8217;s mine:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1083\" height=\"817\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908223745-3.png\" alt=\"Pasted image 20260908223745.png\" class=\"wp-image-629\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908223745-3.png 1083w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908223745-3-300x226.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908223745-3-1024x772.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908223745-3-768x579.png 768w\" sizes=\"auto, (max-width: 1083px) 100vw, 1083px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I changed the script to display only US holidays and ran it. When you run it, VS Code asks you to pick a kernel. Choose Microsoft Fabric Runtime, otherwise it won&#8217;t connect to Fabric:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"608\" height=\"170\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224053-1.png\" alt=\"Pasted image 20260908224053.png\" class=\"wp-image-591\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224053-1.png 608w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224053-1-300x84.png 300w\" sizes=\"auto, (max-width: 608px) 100vw, 608px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then pick the language, PySpark in my case:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"610\" height=\"159\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224221-1.png\" alt=\"Pasted image 20260908224221.png\" class=\"wp-image-590\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224221-1.png 610w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224221-1-300x78.png 300w\" sizes=\"auto, (max-width: 610px) 100vw, 610px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The notebook runs, and for me it produced this output:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1082\" height=\"690\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224317-2.png\" alt=\"Pasted image 20260908224317.png\" class=\"wp-image-618\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224317-2.png 1082w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224317-2-300x191.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224317-2-1024x653.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224317-2-768x490.png 768w\" sizes=\"auto, (max-width: 1082px) 100vw, 1082px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Save the notebook and the changes are immediately visible in Fabric:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1243\" height=\"797\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224934-3.png\" alt=\"Pasted image 20260908224934.png\" class=\"wp-image-628\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224934-3.png 1243w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224934-3-300x192.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224934-3-1024x657.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908224934-3-768x492.png 768w\" sizes=\"auto, (max-width: 1243px) 100vw, 1243px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And since the script actually executes in Fabric, the run shows up in the Monitoring tab:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"703\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1024x703.png\" alt=\"\" class=\"wp-image-542\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1024x703.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-300x206.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-768x527.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1536x1055.png 1536w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image.png 1608w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Now the big caveat of this mode. The only AI that can edit your notebook here is the one native to VS Code notebooks: GitHub Copilot. If that&#8217;s what you use, you&#8217;re fine. Open the chat window on the right and ask it for whatever you need:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"874\" height=\"798\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908225717-1.png\" alt=\"Pasted image 20260908225717.png\" class=\"wp-image-616\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908225717-1.png 874w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908225717-1-300x274.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908225717-1-768x701.png 768w\" sizes=\"auto, (max-width: 874px) 100vw, 874px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Copilot updates the notebook, and you&#8217;re all set for an agentic data engineering workflow:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1374\" height=\"1156\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231711-2.png\" alt=\"Pasted image 20260908231711.png\" class=\"wp-image-623\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231711-2.png 1374w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231711-2-300x252.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231711-2-1024x862.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231711-2-768x646.png 768w\" sizes=\"auto, (max-width: 1374px) 100vw, 1374px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Using a different AI? That&#8217;s where it gets complicated. Other LLMs, even ones installed as VS Code extensions, can&#8217;t see that directory, and you might get an error like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"506\" height=\"104\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231940-2.png\" alt=\"Pasted image 20260908231940.png\" class=\"wp-image-622\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231940-2.png 506w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260908231940-2-300x62.png 300w\" sizes=\"auto, (max-width: 506px) 100vw, 506px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Local mode<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The workaround is the extension&#8217;s second mode. In local mode the extension physically copies the notebook to your disk, which means any LLM can get to it. When you&#8217;re done, you sync it back to the Fabric workspace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Start by clicking a different button on the notebook, <em>Download notebook<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"517\" height=\"442\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225204-1.png\" alt=\"Pasted image 20260909225204.png\" class=\"wp-image-625\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225204-1.png 517w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225204-1-300x256.png 300w\" sizes=\"auto, (max-width: 517px) 100vw, 517px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The icon turns green, meaning your local copy matches the one in the Fabric workspace. A new icon appears too, <em>Publish resource folder<\/em>, which uploads all the files and subfolders under the <strong>builtin<\/strong> folder to the notebook resources:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"514\" height=\"56\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225340-1.png\" alt=\"Pasted image 20260909225340.png\" class=\"wp-image-624\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225340-1.png 514w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225340-1-300x33.png 300w\" sizes=\"auto, (max-width: 514px) 100vw, 514px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To open the downloaded notebook, use the same &#8220;Open notebook folder&#8221; icon as before. This time it asks which mode you want:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"302\" height=\"189\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225723-1.png\" alt=\"Pasted image 20260909225723.png\" class=\"wp-image-593\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225723-1.png 302w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260909225723-1-300x188.png 300w\" sizes=\"auto, (max-width: 302px) 100vw, 302px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Virtual workspace<\/strong> &#8211; the live connection from the previous section<\/li>\n\n\n\n<li><strong>Local folder<\/strong> &#8211; the downloaded copy (pick this one)<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">The notebook opens just like last time:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"2560\" height=\"1267\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1.png\" alt=\"Pasted image 20260914064502.png\" class=\"wp-image-626\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1.png 2560w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1-300x148.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1-1024x507.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1-768x380.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1-1536x760.png 1536w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914064502-scaled-1-2048x1014.png 2048w\" sizes=\"auto, (max-width: 2560px) 100vw, 2560px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">One difference: changes don&#8217;t sync to the workspace automatically. You push them with a button. But the file is now on your disk, so <strong>any<\/strong> LLM can help you write the notebook, including external ones like Claude Code or Codex.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">From the Claude Code extension<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">The easiest route is the Claude Code extension in VS Code. Open it and it sees the notebook context straight away:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"817\" height=\"770\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914072236-1.png\" alt=\"Pasted image 20260914072236.png\" class=\"wp-image-596\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914072236-1.png 817w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914072236-1-300x283.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914072236-1-768x724.png 768w\" sizes=\"auto, (max-width: 817px) 100vw, 817px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">And it worked:<br><\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"622\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-1024x622.png\" alt=\"\" class=\"wp-image-543\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-1024x622.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-767x466.png 767w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-300x182.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-1536x933.png 1536w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image-1-2048x1244.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">I like this approach a lot. The notebook is right there, so you can correct things, execute cells and check the output as you go. One thing to remember: notebooks don&#8217;t save automatically while you edit them. <strong>Hit save.<\/strong> The LLM only sees the last saved version.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">From the terminal<\/h4>\n\n\n\n<p class=\"wp-block-paragraph\">Running the LLM from a terminal works too. First, find where the notebook is stored. Easiest way: right-click the notebook in the VS Code menu and choose <em>Reveal in Finder<\/em> (or <em>File Explorer<\/em>, depending on your OS):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1078\" height=\"1220\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914074040-1.png\" alt=\"Pasted image 20260914074040.png\" class=\"wp-image-594\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914074040-1.png 1078w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914074040-1-265x300.png 265w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914074040-1-905x1024.png 905w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914074040-1-768x869.png 768w\" sizes=\"auto, (max-width: 1078px) 100vw, 1078px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">This is the path to my file: <code>~\/50be2564-b12a-419d-93e8-fce57529a5b2\/SynapseNotebook\/c6be7bf2-3f60-4be6-a573-ffca78078b7f\/NB_AgenticWorkflow\/NB_AgenticWorkflow.ipynb<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Which translates to:<br><code> \/&lt;WorkspaceID&gt;\/&lt;Item Type&gt;\/&lt;ItemID&gt;\/&lt;ItemName&gt;\/&lt;items&gt;<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Other items land under the same main folder (unless you change that in the extension settings), so with the workspace and item IDs you can find anything quickly. The structure is similar to what you see in the code repository when it&#8217;s linked to your Fabric workspace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">From there it&#8217;s simple. Open a terminal, navigate to the folder, and tell the LLM what to do:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"669\" height=\"494\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081113-1.png\" alt=\"Pasted image 20260914081113.png\" class=\"wp-image-598\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081113-1.png 669w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081113-1-300x222.png 300w\" sizes=\"auto, (max-width: 669px) 100vw, 669px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">When it&#8217;s done, check the result by opening the file, or open it in VS Code like before:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1996\" height=\"1180\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1.png\" alt=\"Pasted image 20260914081757.png\" class=\"wp-image-597\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1.png 1996w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1-300x177.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1-1024x605.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1-768x454.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914081757-1-1536x908.png 1536w\" sizes=\"auto, (max-width: 1996px) 100vw, 1996px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\">Publishing back to Fabric<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Time to get the code into Fabric. Go back to the extension and click the <em>Publish notebook<\/em> icon. That&#8217;s it, the whole notebook gets published.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Well, usually. Two things can get in the way.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u26a0\ufe0f Caveat: <code>400 Bad Request<\/code> when publishing notebooks from VS Code<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the extension throws <code>Error running command synapse.publishNotebook: HTTP Error Response: 400 Bad Request<\/code>, in my experience your local <code>.ipynb<\/code> JSON has drifted from the format Fabric expects. It usually happens after editing notebooks with the Jupyter\/NotebookEdit tooling in VS Code, which writes <code>source<\/code> as a single string and injects <code>id<\/code>\/<code>execution_count<\/code> fields Fabric doesn&#8217;t want.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>What to check:<\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>source<\/code> must be a list of strings (one per line), not a single string<\/li>\n\n\n\n<li>Code and markdown cells shouldn&#8217;t carry <code>id<\/code> or <code>execution_count<\/code><\/li>\n\n\n\n<li>Code cell <code>metadata<\/code> needs <code>{\"microsoft\": {\"language\": \"python\", \"language_group\": \"synapse_pyspark\"}}<\/code><\/li>\n\n\n\n<li>The top-level notebook <code>metadata<\/code> needs the Fabric block (<code>kernel_info.name: synapse_pyspark<\/code>, <code>spark_compute<\/code>, <code>dependencies.lakehouse<\/code>), not a generic <code>kernelspec: python3<\/code><\/li>\n\n\n\n<li><strong>Close and reopen the <code>.ipynb<\/code> tab in VS Code before republishing.<\/strong> The extension publishes its in-memory buffer, so it won&#8217;t see fixes made on disk otherwise<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>A prompt you can hand your AI assistant to fix it:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code is-style-dg-prompt\"><code>My Fabric notebook &lt;filename&gt;.ipynb fails to publish from VS Code with HTTP Error Response: 400 Bad Request. Compare it structurally against &lt;a sibling notebook that publishes successfully&gt; and rewrite it to match Fabric's canonical format: strip any id\/execution_count keys from cells, convert source to a list of strings (one per line) if it's a single string, ensure code cells have metadata.microsoft.language = \"python\" and language_group = \"synapse_pyspark\" (preserving existing tags\/cellStatus), and fix the top-level metadata to use the Fabric kernel block instead of a generic python3\/ipykernel one. Validate the JSON loads and each code cell compiles after the rewrite.<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>\u26a0\ufe0f Caveat 2: Editing the same notebook in two places<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">If the notebook changes both locally through the extension and online in the workspace (it happens a lot when several people work in the same workspace), you get a conflict:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"612\" height=\"304\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914204059-1.png\" alt=\"Pasted image 20260914204059.png\" class=\"wp-image-595\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914204059-1.png 612w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260914204059-1-300x149.png 300w\" sizes=\"auto, (max-width: 612px) 100vw, 612px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Click <em>Update<\/em> and a comparison window opens, where you can review the conflict and choose which version to keep. You can also open it with the comparison button highlighted below:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"609\" height=\"407\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260915095629-1.png\" alt=\"Pasted image 20260915095629.png\" class=\"wp-image-601\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260915095629-1.png 609w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260915095629-1-300x200.png 300w\" sizes=\"auto, (max-width: 609px) 100vw, 609px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Microsoft Fabric skills<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">So far the LLM has been writing code with whatever it already knows about Fabric. Skills make it a lot better at that. In my setup post I showed how to install the Fabric skills: [[Starting with agentic workflow in Fabric &#8211; Polished]]. Once they&#8217;re installed your LLM can use them, but what do they actually do, and which ones help with notebook code?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Microsoft quietly open-sourced a whole plugin of Fabric skills, and <code>spark-authoring-cli<\/code> is the one I keep coming back to. It does two jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Job one: control-plane work.<\/strong> Spinning up workspaces, creating schema-enabled lakehouses, deploying notebooks, kicking off runs. All of it through <code>az rest<\/code> against the Fabric REST API, with the long-running-operation dance (poll the <code>Location<\/code> header, wait for <code>Succeeded<\/code>) already figured out for you.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The notebook round-trip is what saved me the most time: <code>getDefinition<\/code> \u2192 base64-decode \u2192 edit the cell array \u2192 re-encode \u2192 <code>updateDefinition<\/code>. Sounds simple. It isn&#8217;t. The skill spells out the bits that actually bite. Every line in a cell&#8217;s <code>source<\/code> array has to end with <code>\\n<\/code>, or your code silently collapses into a single line. And the default lakehouse binding lives in the <code>.ipynb<\/code> metadata but moves to a <code># METADATA<\/code> comment block if you&#8217;re working in the <code>.py<\/code> format. Sound familiar? Same class of formatting drift as the <code>400 Bad Request<\/code> above.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Job two: the code <em>inside<\/em> those cells.<\/strong> PySpark, Scala, SparkR, <code>%%sql<\/code>, plus <code>notebookutils<\/code> patterns for pipeline-context detection, connection credentials, and <code>runMultiple<\/code> DAGs when you need fan-out\/fan-in orchestration instead of hand-rolled threads. Delta MERGE, OPTIMIZE, V-Order tuned per medallion layer. Materialized Lake Views too, including a readiness check that flags which SQL constructs will quietly force a full refresh instead of an incremental one, and why Change Data Feed has to be enabled on every source table before incremental refresh is even possible.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>The part I didn&#8217;t expect: the guardrails.<\/strong> Check for a job instance from the last five minutes before submitting a new run. Capture the job ID immediately, then only ever <code>GET<\/code> it, never retry the <code>POST<\/code>. Never hardcode a workspace or lakehouse ID. Those are rules you normally learn the hard way, around 11pm, after stacking three duplicate Spark sessions on a paused capacity. Here they&#8217;re just written down.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Putting it to work<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Let&#8217;s use it. Open a terminal, or VS Code with an empty session, and create a notebook from scratch that does an extra transformation.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">My prompt:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#a6accd;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#252937;color:#969dc4\">Markdown<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#a6accd;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\u276f \/fabric-skills:spark-authoring-cli create a notebook where you will connect to the lakehouse named\n  LH_AgenticWorkflow and you will create there a code to create a dim_date together with a public\n  holiday flag and a name stored in publicholidays table, please take only US holidays.\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki poimandres\" style=\"background-color: #1b1e28\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E4F0FBD0\">\u276f \/fabric-skills:spark-authoring-cli create a notebook where you will connect to the lakehouse named<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E4F0FBD0\">  LH_AgenticWorkflow and you will create there a code to create a dim_date together with a public<\/span><\/span>\n<span class=\"line\"><span style=\"color: #E4F0FBD0\">  holiday flag and a name stored in publicholidays table, please take only US holidays.<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Before writing anything, the agent checks the prerequisites: whether you&#8217;re logged in to Fabric through <code>az login<\/code>, which workspaces are available, and so on. If it needs something, it asks. Then you either take the action (log in) or point it to the right workspace. Here&#8217;s what the response looks like:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1482\" height=\"1254\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260916233525-1.png\" alt=\"Pasted image 20260916233525.png\" class=\"wp-image-600\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260916233525-1.png 1482w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260916233525-1-300x254.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260916233525-1-1024x866.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260916233525-1-768x650.png 768w\" sizes=\"auto, (max-width: 1482px) 100vw, 1482px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 <strong>Azure CLI<\/strong> (<code>az rest<\/code>). That&#8217;s why the skill checks <code>az login<\/code> first. It doesn&#8217;t go through the MCP or the VS Code extension. It calls the Fabric REST API directly to create the notebook, push the definition and start the run. So the notebook appears in the workspace, not in your local extension folder, until you download it.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The catch: access and capacity<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Here&#8217;s the great part: you can fully vibe code. The LLM has full access to your workspace, because it uses your access.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And here&#8217;s the risk. I do this in my own environment, with no sensitive data in it. On a real environment it&#8217;s a different story. The LLM can and will read data to check whether its code works, so any sensitive information might get pulled into the conversation by accident. My suggestion: set up a dedicated user with limited access and sample or fabricated data, so the agent can write code and validate it against dummy data. If that&#8217;s not possible, fall back to one of the editing options from the notebook section above.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One more cost that&#8217;s easy to miss: capacity. Every test run the agent makes is a real Spark session on your capacity, burning CUs like any other run. An agent that iterates (run, check the output, fix, run again) can start a lot of sessions in a short time, and on a small SKU that&#8217;s enough to throttle everyone else on the capacity. Two habits help. Tell the agent to test on a small sample (a <code>LIMIT<\/code> or a single partition) rather than the full table. And keep the Monitoring hub open for your first few sessions, so you see what it&#8217;s actually starting.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Warehouse and SQL database<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Not everything is Spark. If you work with a warehouse or a SQL database, there&#8217;s <code>sqldw-authoring-cli<\/code>. It&#8217;s built along the same lines as the Spark one, but focuses on Fabric Warehouse and Fabric SQL Database, and helps with T-SQL notebooks, stored procedures, DDL and DML queries and so on.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 It runs T-SQL through <strong><code>sqlcmd<\/code><\/strong> with your <code>az login<\/code> identity, and uses <code>az rest<\/code> for item-level work. SQL goes over a SQL connection, not REST, so neither the MCP nor <code>fab<\/code> is involved.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Spark Job Definitions and Environments<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Two more items show up in the extension tree, and they&#8217;re worth a quick mention.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Spark Job Definitions<\/strong> behave like notebooks in local mode: download the definition, let the agent edit the main file on disk, publish it back. They&#8217;re the better fit once a notebook has settled into a plain batch job that doesn&#8217;t need cells or interactive output.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Environments<\/strong> hold your libraries and Spark settings, and they&#8217;re the one item where the agent&#8217;s edit isn&#8217;t the last step. A change to an environment (a new library, a different Spark property) only takes effect after the environment is published, and publishing takes a few minutes. If the agent adds a library and the next notebook run can&#8217;t import it, that&#8217;s almost always why. Ask it to publish the environment and wait for it to finish before running anything.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 The agent does that with <code>az rest<\/code> or <code>fab api<\/code>, because staging and publishing are separate API calls the MCP doesn&#8217;t have.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pipelines and Dataflow Gen2<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">There&#8217;s more than one way to build notebooks, and we&#8217;ve covered those. So what about the rest, data pipelines and Dataflow Gen2?<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Data pipelines<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">You can create a pipeline with the MCP or with the Fabric CLI. With the MCP, just ask the LLM in a prompt and the pipeline gets created:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1688\" height=\"532\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1.png\" alt=\"Pasted image 20260922231604.png\" class=\"wp-image-602\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1.png 1688w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1-300x95.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1-1024x323.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1-768x242.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231604-1-1536x484.png 1536w\" sizes=\"auto, (max-width: 1688px) 100vw, 1688px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"766\" height=\"492\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231645-1.png\" alt=\"Pasted image 20260922231645.png\" class=\"wp-image-599\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231645-1.png 766w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260922231645-1-300x193.png 300w\" sizes=\"auto, (max-width: 766px) 100vw, 766px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 <strong>Fabric MCP<\/strong> (<code>create_item<\/code>, then <code>update_item_definition<\/code> for the activities). A pipeline is just another Fabric item, so the generic tools handle it, using the MCP auth from the infrastructure post.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Triggers and schedules work the same way, so it&#8217;s a very easy and convenient route. You can get similar results with the CLI, but there are a few caveats worth knowing before you start.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">First, a clarification that confused me for a while. There are two different &#8220;CLI&#8221; routes here, and they authorize differently:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Fabric CLI (<code>fab<\/code>)<\/strong> &#8211; a dedicated tool that treats your tenant like a filesystem. You log in with <code>fab auth login<\/code>, which supports interactive login, service principal and managed identity.<\/li>\n\n\n\n<li><strong><code>az rest<\/code> against the Fabric REST API<\/strong> &#8211; this is what the Fabric skills use under the hood, and this one does run on <code>az login<\/code>.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Both work. <code>fab<\/code> is the friendlier one for pipelines, so that&#8217;s what I&#8217;ll use here.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The command surface looks like this:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#a6accd;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#252937;color:#969dc4\">Bash<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#a6accd;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>fab mkdir ws1.Workspace\/pl1.DataPipeline                 # create an empty pipeline\nfab export ws1.Workspace\/pl1.DataPipeline -o \/tmp        # pull the definition down\nfab import pl1.DataPipeline -i \/tmp\/pl1.DataPipeline -f  # push it back\nfab job run-sch ws1.Workspace\/pl1.DataPipeline           # schedule it\nfab job run-status ...                                   # check on it\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki poimandres\" style=\"background-color: #1b1e28\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #91B4D5\">fab<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">mkdir<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">ws1.Workspace\/pl1.DataPipeline<\/span><span style=\"color: #A6ACCD\">                 <\/span><span style=\"color: #767C9DB0; font-style: italic\"># create an empty pipeline<\/span><\/span>\n<span class=\"line\"><span style=\"color: #91B4D5\">fab<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">export<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">ws1.Workspace\/pl1.DataPipeline<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">-o<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">\/tmp<\/span><span style=\"color: #A6ACCD\">        <\/span><span style=\"color: #767C9DB0; font-style: italic\"># pull the definition down<\/span><\/span>\n<span class=\"line\"><span style=\"color: #91B4D5\">fab<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">import<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">pl1.DataPipeline<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">-i<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">\/tmp\/pl1.DataPipeline<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">-f<\/span><span style=\"color: #A6ACCD\">  <\/span><span style=\"color: #767C9DB0; font-style: italic\"># push it back<\/span><\/span>\n<span class=\"line\"><span style=\"color: #91B4D5\">fab<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">job<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">run-sch<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">ws1.Workspace\/pl1.DataPipeline<\/span><span style=\"color: #A6ACCD\">           <\/span><span style=\"color: #767C9DB0; font-style: italic\"># schedule it<\/span><\/span>\n<span class=\"line\"><span style=\"color: #91B4D5\">fab<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">job<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">run-status<\/span><span style=\"color: #A6ACCD\"> <\/span><span style=\"color: #ADD7FF\">...<\/span><span style=\"color: #A6ACCD\">                                   <\/span><span style=\"color: #767C9DB0; font-style: italic\"># check on it<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">Look familiar? It&#8217;s the same shape as notebook local mode: pull it down, let the LLM work on real files, push it back. That&#8217;s exactly why I like it.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Now the caveats.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Caveat 1: there is no pipeline authoring skill.<\/strong> For notebooks you get <code>spark-authoring-cli<\/code> holding the LLM&#8217;s hand. For data pipelines you get nothing equivalent. The model writes the definition JSON from whatever it already knows. And watch the names, because two skills sound like they&#8217;d help and don&#8217;t. <code>deployment-pipelines-authoring-cli<\/code> is about ALM promotion between dev\/test\/prod stages, not data pipelines at all. <code>pipeline-migration<\/code> converts existing Synapse pipelines rather than creating new ones. I pointed my LLM at the first one by mistake. It was very helpful about something I wasn&#8217;t asking for.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Caveat 2: the definition format is unforgiving.<\/strong> A pipeline is a base64-encoded <code>pipeline-content.json<\/code> with an <code>activities<\/code> array, and every activity type has its own <code>typeProperties<\/code> block. There are roughly 30 of them (<code>TridentNotebook<\/code>, <code>Copy<\/code>, <code>Lookup<\/code>, <code>ForEach<\/code>, <code>IfCondition<\/code>, <code>ExecutePipeline<\/code> and so on), each shaped differently. Nothing validates any of it until you send it, so a plausible-looking guess comes back as a rejected request rather than a helpful error.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Caveat 3: everything is a GUID.<\/strong> Attaching a notebook to a <code>TridentNotebook<\/code> activity means passing <code>notebookId<\/code> and <code>workspaceId<\/code>, not names. Your LLM has to look those up first. If it invents one, you get a pipeline that saves cleanly and fails at runtime.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">The way around all three is the same trick: don&#8217;t ask for a pipeline from scratch. Build a minimal one in the portal, export it, and ask the LLM to extend it. Now it has a working, correctly shaped example to copy instead of writing JSON from memory. Massive difference in hit rate, at least in my case.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">So which one?<\/h4>\n\n\n\n<figure class=\"wp-block-table is-style-dg-comparison\"><table class=\"has-fixed-layout\"><thead><tr><th>Option<\/th><th>MCP<\/th><th>CLI<\/th><\/tr><\/thead><tbody><tr><td>Setup<\/td><td>Installed in part one, authorized in part two<\/td><td><code>fab<\/code> install + <code>fab auth login<\/code>(both covered in part two)<\/td><\/tr><tr><td>How you use it<\/td><td>Ask in plain language<\/td><td>Commands, files, exports<\/td><\/tr><tr><td>Creating from scratch<\/td><td>Easiest route<\/td><td>Needs an example to copy<\/td><\/tr><tr><td>Repeatable \/ scriptable<\/td><td>Not really<\/td><td>Yes &#8211; this is its strength<\/td><\/tr><tr><td>CI\/CD and service principals<\/td><td>No<\/td><td>Yes<\/td><\/tr><tr><td>Bulk changes across items<\/td><td>Painful<\/td><td>Straightforward<\/td><\/tr><tr><td>Fails by<\/td><td>Missing tools<\/td><td>Malformed definition JSON<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Honestly, it&#8217;s not either\/or. I use the MCP when I&#8217;m building something new and thinking out loud, because asking for a pipeline and getting one is hard to beat. I switch to the CLI the moment the work becomes repeatable: the same pipeline across three workspaces, a schedule that needs to exist in dev and prod, anything that should eventually run unattended under a service principal. Even with the Core MCP now GA, I wouldn&#8217;t put it in a deployment process. It&#8217;s built around a browser sign-in, and for anything unattended the CLI with a service principal is the better fit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And one thing worth saying out loud: both routes act with your permissions. Same warning as with the notebooks. Do this in an environment where you don&#8217;t mind what the LLM can reach.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Dataflow Gen2<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Dataflow Gen2 has its own skill, <code>dataflows-cli<\/code>, with three modes (authoring \/ consumption \/ upgrade). It covers M-language semantics, a connector inventory, output destinations, connection management and the Gen1\u2192Gen2.1 upgrade. To talk to Fabric it uses the Azure CLI, not the MCP and not <code>fab<\/code>, so it&#8217;ll ask you to run <code>az login<\/code> first if you haven&#8217;t.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 Why <code>az<\/code>: the skill needs endpoints the MCP doesn&#8217;t expose, like previewing M with <code>executeQuery<\/code> and reading refresh history, and <code>az rest<\/code> reaches any of them with the same login.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">I ran this prompt:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#a6accd;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#252937;color:#969dc4\">Markdown<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#a6accd;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>\/fabric-skills:dataflows-cli create an dataflow that will take data from dim_date and limit it only to 2021 and save as a table in the same lakehouse, add as well week names columns in polish and german\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki poimandres\" style=\"background-color: #1b1e28\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E4F0FBD0\">\/fabric-skills:dataflows-cli create an dataflow that will take data from dim_date and limit it only to 2021 and save as a table in the same lakehouse, add as well week names columns in polish and german<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1688\" height=\"1157\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1.png\" alt=\"Pasted image 20260923234059.png\" class=\"wp-image-603\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1.png 1688w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1-300x206.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1-1024x702.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1-768x526.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234059-1-1536x1053.png 1536w\" sizes=\"auto, (max-width: 1688px) 100vw, 1688px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">First, it picked the authoring mode:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"672\" height=\"29\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234150-1.png\" alt=\"Pasted image 20260923234150.png\" class=\"wp-image-607\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234150-1.png 672w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234150-1-300x13.png 300w\" sizes=\"auto, (max-width: 672px) 100vw, 672px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Then it validated what it needed to work with, and at the end gave me a summary of what it had created:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"699\" height=\"232\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234305-1.png\" alt=\"Pasted image 20260923234305.png\" class=\"wp-image-605\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234305-1.png 699w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234305-1-300x100.png 300w\" sizes=\"auto, (max-width: 699px) 100vw, 699px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The refresh, though, failed, because the connection hadn&#8217;t been authorized:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"721\" height=\"158\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234355-1.png\" alt=\"Pasted image 20260923234355.png\" class=\"wp-image-608\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234355-1.png 721w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234355-1-300x66.png 300w\" sizes=\"auto, (max-width: 721px) 100vw, 721px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">To its credit, it told me exactly how to fix it:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1019\" height=\"77\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234430-1.png\" alt=\"Pasted image 20260923234430.png\" class=\"wp-image-606\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234430-1.png 1019w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234430-1-300x23.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234430-1-768x58.png 768w\" sizes=\"auto, (max-width: 1019px) 100vw, 1019px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Over in Fabric, as expected, there&#8217;s a Dataflow Gen2 with a failed status:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"658\" height=\"100\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234554-1.png\" alt=\"Pasted image 20260923234554.png\" class=\"wp-image-604\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234554-1.png 658w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923234554-1-300x46.png 300w\" sizes=\"auto, (max-width: 658px) 100vw, 658px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Open it and you&#8217;ll see something like this:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1262\" height=\"291\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923235548-1.png\" alt=\"Pasted image 20260923235548.png\" class=\"wp-image-610\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923235548-1.png 1262w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923235548-1-300x69.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923235548-1-1024x236.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260923235548-1-768x177.png 768w\" sizes=\"auto, (max-width: 1262px) 100vw, 1262px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Hit <em>Configure connection<\/em> and authorize, so the dataflow gets permissions to the lakehouse. Then click <em>Save, run &amp; close<\/em>. That triggers the dataflow and closes the window. This time the run succeeded and created the table I asked for:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"631\" height=\"89\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000248-1.png\" alt=\"Pasted image 20260924000248.png\" class=\"wp-image-612\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000248-1.png 631w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000248-1-300x42.png 300w\" sizes=\"auto, (max-width: 631px) 100vw, 631px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"1562\" height=\"485\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1.png\" alt=\"Pasted image 20260924000306.png\" class=\"wp-image-611\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1.png 1562w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1-300x93.png 300w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1-1024x318.png 1024w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1-768x238.png 768w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20260924000306-1-1536x477.png 1536w\" sizes=\"auto, (max-width: 1562px) 100vw, 1562px\" \/><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Closing the loop: committing to Git<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In the previous post we connected the workspace to a repository. Everything we just built, the notebook, the pipeline and the dataflow, now sits in the workspace as uncommitted changes. Nothing has reached the repo yet.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">One thing to keep straight: publishing from VS Code puts your notebook into the <em>workspace<\/em>, not into Git. The local folder the extension downloads to isn&#8217;t a clone of your repo. So the flow is always local copy \u2192 publish to workspace \u2192 commit from workspace.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Open the workspace and the Source control badge shows pending changes:<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"575\" height=\"456\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104229-1.png\" alt=\"Pasted image 20261016104229.png\" class=\"wp-image-613\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104229-1.png 575w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104229-1-300x238.png 300w\" sizes=\"auto, (max-width: 575px) 100vw, 575px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">You can commit from there the normal way: tick the items, add a message, commit. Or, since the <code>git-integration-operations-cli<\/code> skill is already installed, ask the agent:<\/p>\n\n\n\n<div class=\"wp-block-kevinbatdorf-code-block-pro cbp-has-line-numbers\" data-code-block-pro-font-family=\"Code-Pro-JetBrains-Mono\" style=\"font-size:.875rem;font-family:Code-Pro-JetBrains-Mono,ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;--cbp-line-number-color:#a6accd;--cbp-line-number-width:calc(1 * 0.6 * .875rem);line-height:1.25rem;--cbp-tab-width:2;tab-size:var(--cbp-tab-width, 2)\"><span style=\"display:flex;align-items:center;padding:10px 0px 10px 16px;margin-bottom:-2px;width:100%;text-align:left;background-color:#252937;color:#969dc4\">Markdown<\/span><span role=\"button\" tabindex=\"0\" style=\"color:#a6accd;display:none\" aria-label=\"Copy\" class=\"code-block-pro-copy-button\"><pre class=\"code-block-pro-copy-button-pre\" aria-hidden=\"true\"><textarea class=\"code-block-pro-copy-button-textarea\" tabindex=\"-1\" aria-hidden=\"true\" readonly>Commit all pending changes in Blog - Agentic Workflow #1 to Git with the message \"Add dim_date notebook, holiday pipeline and 2021 dataflow\"\n<\/textarea><\/pre><svg xmlns=\"http:\/\/www.w3.org\/2000\/svg\" style=\"width:24px;height:24px\" fill=\"none\" viewBox=\"0 0 24 24\" stroke=\"currentColor\" stroke-width=\"2\"><path class=\"with-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M4.5 12.75l6 6 9-13.5\"><\/path><path class=\"without-check\" stroke-linecap=\"round\" stroke-linejoin=\"round\" d=\"M16.5 8.25V6a2.25 2.25 0 00-2.25-2.25H6A2.25 2.25 0 003.75 6v8.25A2.25 2.25 0 006 16.5h2.25m8.25-8.25H18a2.25 2.25 0 012.25 2.25V18A2.25 2.25 0 0118 20.25h-7.5A2.25 2.25 0 018.25 18v-1.5m8.25-8.25h-6a2.25 2.25 0 00-2.25 2.25v6\"><\/path><\/svg><\/span><pre class=\"shiki poimandres\" style=\"background-color: #1b1e28\" tabindex=\"0\"><code><span class=\"line\"><span style=\"color: #E4F0FBD0\">Commit all pending changes in Blog - Agentic Workflow #1 to Git with the message &quot;Add dim_date notebook, holiday pipeline and 2021 dataflow&quot;<\/span><\/span>\n<span class=\"line\"><\/span><\/code><\/pre><\/div>\n\n\n\n<p class=\"wp-block-paragraph\">It checks the Git status first, commits the pending items, and confirms the workspace and the branch are back on the same commit.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\ud83d\udd27 Same route as in the infrastructure post: the skill calls the Git REST endpoints through <code>fab api<\/code>, falling back to <code>az rest<\/code>, because the MCP has no Git tools.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"756\" height=\"345\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104741-1.png\" alt=\"Pasted image 20261016104741.png\" class=\"wp-image-614\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104741-1.png 756w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104741-1-300x137.png 300w\" sizes=\"auto, (max-width: 756px) 100vw, 756px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"612\" height=\"386\" src=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104754-1.png\" alt=\"Pasted image 20261016104754.png\" class=\"wp-image-609\" srcset=\"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104754-1.png 612w, https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/Pasted-image-20261016104754-1-300x189.png 300w\" sizes=\"auto, (max-width: 612px) 100vw, 612px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The commit is what turns all this into a team workflow. Your agent&#8217;s work goes through a pull request and a review like anyone else&#8217;s. And if it did something you didn&#8217;t want, the previous version is one revert away.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Wrap-up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Quick recap:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Local mode is the key.<\/strong> The Fabric Data Engineering extension puts notebooks on your disk, where any LLM can work on them. Live mode is Copilot-only.<\/li>\n\n\n\n<li><strong>Save before you prompt<\/strong>, and watch for <code>.ipynb<\/code> drift if publishing throws a 400.<\/li>\n\n\n\n<li><strong><code>spark-authoring-cli<\/code><\/strong> knows the notebook round-trip and the guardrails. <code>sqldw-authoring-cli<\/code> does the same for T-SQL.<\/li>\n\n\n\n<li><strong>Watch your data and your CUs.<\/strong> The agent runs with your access, on your capacity.<\/li>\n\n\n\n<li><strong>Pipelines<\/strong>: MCP to explore, <code>fab<\/code> once it&#8217;s repeatable. And give the LLM a working example to extend.<\/li>\n\n\n\n<li><strong>Dataflow Gen2<\/strong> has its own skill. Expect to authorize the connection in the portal yourself.<\/li>\n\n\n\n<li><strong>Commit from the workspace<\/strong>, so the agent&#8217;s work gets reviewed like everyone else&#8217;s.<\/li>\n<\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Which part of your data engineering work would you hand to an agent first? I&#8217;d really like to know where people draw the line.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Next up in the series: Power BI development, with semantic models, DAX and report work.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Status check, verified October 2026. The Fabric Core MCP Server is generally available since the September 2026 Fabric update, but the pieces around it move fast. The Fabric skills are open source and get new versions often, the Fabric CLI ships regularly, and the VS Code extension changes its UI from time to time. If&#8230;<\/p>\n","protected":false},"author":1,"featured_media":584,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_monsterinsights_skip_tracking":false,"_kadence_starter_templates_imported_post":false,"_kad_post_transparent":"","_kad_post_title":"","_kad_post_layout":"","_kad_post_sidebar_id":"","_kad_post_content_style":"","_kad_post_vertical_padding":"","_kad_post_feature":"","_kad_post_feature_position":"","_kad_post_header":false,"_kad_post_footer":false,"_kad_post_classname":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":true,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"dg_series_part":"3","dg_series_short":"Data Engineering with Claude Code","_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[19,20,9],"tags":[18,10],"dg_series":[21],"class_list":["post-539","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-ai","category-data-engineering","category-microsoft-fabric","tag-agenticdevelopment","tag-microsoftfabric","dg_series-agentic-development"],"jetpack_featured_media_url":"https:\/\/dataguide.tech\/wp-content\/uploads\/2026\/10\/image1.jpg","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/posts\/539","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/comments?post=539"}],"version-history":[{"count":5,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/posts\/539\/revisions"}],"predecessor-version":[{"id":632,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/posts\/539\/revisions\/632"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/media\/584"}],"wp:attachment":[{"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/media?parent=539"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/categories?post=539"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/tags?post=539"},{"taxonomy":"dg_series","embeddable":true,"href":"https:\/\/dataguide.tech\/index.php\/wp-json\/wp\/v2\/dg_series?post=539"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}