Skip to main content

Posts

Track used skills, agents and MCP servers with the Copilot usage metrics API

As part of our AI adoption journey, we wanted to know which custom skills, agents and MCP servers were actually in use. So, we built a small tool that scanned our repositories for SKILL.md files, .agent.md files and MCP configuration. It worked, but it answered the wrong question. It told us what existed , not what developers used . The Copilot usage metrics API now closes that gap. What changed? In September the API got CLI customization metrics. The catch: every customer-defined skill, agent and MCP server was grouped under other , and slash commands under custom . Useful for trends, useless for “is our new skill being used?”. Since October 22, the totals_by_skill , totals_by_custom_agent , totals_by_mcp , totals_by_slash_cmd and totals_by_plugin arrays show the configured name of your own items. Each list also holds up to 25 entries per record, up from five. Remark: names are reported from Copilot CLI, the Copilot app, clients built on the Copilot SDK (like VS Code ...
Recent posts

Strongly typed IDs in .NET with Vogen

What does this method signature tell you? public Task<Order> GetOrder(Guid customerId, Guid orderId) Nothing that stops you from calling it like this: await repository.GetOrder(orderId, customerId); It compiles, it runs, and it returns nothing (or worse, the wrong thing). Every ID is a Guid , so the compiler can’t help you. This problem is called primitive obsession. We don't take real advantage of our type system but use any of the built-in primitives instead. The solution The obvious fix is a small wrapper type per ID. A readonly struct or a record does the job, but you end up writing the same boilerplate for every ID: equality, ToString , JSON serialization, a type converter for route binding, an EF Core value converter, Luckily someone already did all the hard work for us and created Vogen. Vogen is a source generator that does all the hard work. Add the package: dotnet add package Vogen And declare your ID: using Vogen; [ValueObject<Guid>]...

Generating architecture diagrams with the Archify skill

Keeping architecture diagrams up to date has always been a lot of work. They are outdated the moment your diagram is saved. Thanks to AI, keeping those diagrams up-to-date became a lot easier. I originally did this just by asking the AI to create and update mermaid diagrams, but recently I started experimenting with Archify . You describe a system in plain words (or point your agent at a codebase), and you get back an interactive HTML diagram. What is Archify? Archify is an agent skill and some tooling around it. It works with Claude Code, Codex, Cursor, OpenCode and GitHub Copilot. What makes it different, is that the agent doesn’t draw the diagram directly. It authors typed JSON that describes the system. Archify then renders that JSON and validates it. The output is a single, self-contained HTML file. Installing the skill For Claude Code: npx -y skills@latest add https://archify.si --skill archify --agent claude-code --global --copy --yes For GitHub Copilot: npx ...

DSC 3.0: running your own PowerShell with getScript, testScript and setScript

Sooner or later you hit a setting that no DSC resource manages. A hosts file entry, a certificate with specific properties, some tweak that only exists as a PowerShell snippet on a wiki page. You know the PowerShell to fix it, but there is no resource to wrap it in. One way is to reach for Microsoft.DSC.Transitional/RunCommandOnSet . It runs a command during the set operation, and that is all it does. There is no test, so DSC cannot tell you whether the machine is in the desired state. But it is a good fit as a temporary transitional resource while migrating DSCv3 resources. There is another option: Microsoft.DSC.Transitional/PowerShellScript . It lets you bring your own getScript , testScript and setScript . Note: If you are new to DSC 3.0 check out my blog series about it: Getting started with DSC 3.0 – Part 1: What is DSC and what changed? Getting started with DSC 3.0 – Part 2: Managing a Windows service end to end Getting started with DSC 3.0 – Part 3: Capturin...

Letting GitHub Copilot drive your desktop with computer use

I'm a big fan of the Automations feature in the GitHub Copilot app. It allows me to focus on the most important stuff and I can schedule and delegate a lot of work in the background. Most of the time, these automations can interact with an API, a CLI or an MCP server. But what if you want to interact with a desktop application that only exists as a GUI, a legacy tool nobody wants to touch, a screen you have to click through by hand every week. That is the gap GitHub is trying to close with the recently introduced 'computer use' feature in the GitHub Copilot app and Copilot CLI. What is computer use? Computer use lets Copilot interact with desktop applications on your behalf. It reads the application content through the accessibility tree of your operating system and takes screenshots when it needs visual context. Then it can click controls, enter and edit text, press keys, scroll and drag. Some facts up front: It is in public preview since October 1, 2026 ...

SQL Server Full-Text Search: when a stop word broke our search

One of our users complained that the search functionality was broken. Searches that should have returned results came back empty. The cause turned out to be a single word: 'both'. If you are new to Full-Text Search, start with my previous post. It explains the building blocks I use below. The problem The search in our application is built on SQL Server Full-Text Search. The terms a user types are combined into a CONTAINS query. A simplified version looks like this: SELECT ProductId, Name FROM dbo.Products WHERE CONTAINS(Description, '"bike" AND "both"'); This returned no rows, even though we knew there were products matching both terms. Searching for "bike" alone worked fine. Looking for the cause The cause is simple:  Stop words Stop words are words that are not indexed because they are considered too common to be useful. SQL Server ships with a system stoplist per language, and 'both' is on the English one: S...

SQL Server Full-Text Search: an overview

Although we have ElasticSearch running in our environment, our most important search functionality is still handled by SQL Server. And no, it is not by writing a statement like WHERE Name LIKE '%term%' . That would probably work on a small table, but we have a lot of data to dig through, so we need something smarter than substring matching. SQL Server has a built-in answer for this: Full-Text Search . In this post I walk through the building blocks and the main features. In the next post I'll show what happened when a customer ran into a surprising side effect. Why do we need this? A LIKE with a leading wildcard can't use an index seek. SQL Server has to scan every row and compare strings. On top of that, LIKE knows nothing about language: searching for 'ride' won't find 'riding', and there is no concept of relevance. Full-text search works differently. It builds an inverted index: a list of words (tokens) and, for every word, the rows wh...