Sr. Content Developer at Microsoft, working remotely in PA, TechBash conference organizer, former Microsoft MVP, Husband, Dad and Geek.
160769 stories
·
33 followers

The Rider 2026.3 Early Access Program Is Open

1 Share

The first Early Access build for Rider 2026.3 is now available! It includes rainbow brackets, an easier way to set data breakpoints, a new Game Development plugin category, and filters in code completion. You can get the build from our website or through the Toolbox App.

Here’s a look at what’s new.

Rainbow brackets: You asked for it, and we delivered!

One of the most popular requests in our tracker has been answered! Rider now colors matching brackets by nesting level, making it easier to read deeply nested code, even in one-liners where indent guides don’t help.

This feature is turned off by default, so your editor will look the same until you turn it on. To enable it, go to Settings | Editor | General | Appearance.

Data breakpoints: Easier to set up

If you develop games with Unreal Engine or a custom C++ engine and use data breakpoints for debugging, there’s good news.

Data breakpoints are powerful, but setting them up meant going through the Watches window, where they were hard to find and use.

With Rider 2026.3, you can now simply right-click a variable in the editor and set up a data breakpoint right away.

You can also add a data breakpoint from the Breakpoints tool window. Just click the Plus icon, select Data Breakpoints, and enter a variable name or memory address with the region size.

Oh, and you can still set data breakpoints from the Watches and Evaluate Expression windows if you prefer that route.

Data breakpoints support read and write access, conditions, logging, and other advanced options. You’ll also get input validation as you type.

Game Development category: Find bundled game dev plugins faster

Rider 2026.3 now groups all bundled game development plugins, like Unity Support, UnrealLink, the Godot plugins, and more, into a single Game Development category on the Plugins settings page. This way, you can quickly see everything Rider offers for game developers.

JetBrains Marketplace got a matching update: the new Game Development tag collects everything the community has built for game dev in one place.

If you maintain a game development plugin for Rider, add the Game Development tag in the Marketplace admin panel so your plugin shows up in this filter.

Filters in code completion: See just what you need

The code completion popup now has a filter bar with icons for each kind of suggestion, like namespaces, classes, enums, and events. Left-click an icon to show only suggestions of that kind, or right-click to hide it and keep the rest. These filters come from ReSharper, so they’ll look familiar if you’ve used it.

Test coverage gets TUnit support

Rider’s dotCover integration now measures coverage for unit tests written with TUnit. To enable coverage, install the JetBrains.dotCover.Framework package in your project.

To enable full Testing Platform support in Rider, go to Settings | Build, Execution, Deployment | Unit Testing | Testing Platform and check the Enable Test Platform support box.

Note: TUnit coverage requires Microsoft.Testing.Platform version 2.3.0 or later.


That’s all for the first EAP build! Try it out and let us know what you think in the comments or our issue tracker.

Read the whole story
alvinashcraft
7 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Microsoft Documented Its OpenAPI Extensions For A Decade And Never Registered Them

1 Share

Of all the extension vocabularies I pulled out of the APIs.io catalog, the x-ms- family is the one where the gap makes the least sense. Not because Microsoft did something careless. Because Microsoft did everything right except the last step.

The AutoRest extension vocabulary is documented. Publicly. Thoroughly. It has been for the better part of a decade. There is a specification for it, with prose explaining each key, what it attaches to, what shape its value takes, and what the generator does with it. If you want to know what x-ms-pageable means you can go and read what it means, written down by the people who built it. That puts Microsoft ahead of nearly every vendor in this series, most of whom have keys with no published definition at all.

And it is not in the registry. Not one key.

Let me lay out what is actually riding in these documents. x-ms-examples appears 11,723 times across 621 OpenAPI documents in my catalog. x-ms-pageable appears 8,747 times across 543 documents, carrying values like {nextLinkName: nextLink} — a complete, unambiguous, machine-readable statement of how to page through a collection. x-ms-enum appears 6,723 times across 578 documents. x-ms-discriminator-value 4,051 times. x-ms-client-name 3,616 times. x-ms-parameter-location 3,023 times across 536 documents. x-ms-long-running-operation 2,380 times, x-ms-client-flatten 2,209, x-ms-mutability 1,230, x-ms-azure-resource 543.

Look at what those keys are doing, because it is not decoration. x-ms-pageable solves pagination declaration, which almost nothing else in the OpenAPI world solves. x-ms-long-running-operation declares that an operation is asynchronous and that the response is a handle rather than a result, which is one of the genuinely hard modelling problems in HTTP APIs and one that OpenAPI still does not have a first-class answer for. x-ms-mutability says which fields can be set at create time, which can be updated, and which are read-only — a distinction that every API has and almost no API expresses. x-ms-client-flatten handles the wrapper-object problem that makes generated SDKs miserable.

That is not vendor plumbing. That is a decade of hard-won modelling knowledge about how real, large, long-lived APIs actually behave, encoded in a form a machine can read, sitting in thousands of public documents, and formally invisible to the specification community.

I keep coming back to x-ms-mutability in particular. Every single person who has ever generated a client or built a form against an API has hit the problem it solves. You have a schema with twenty fields. Six of them you set on create and can never change. Four are server-computed and rejecting them is the server’s job. Ten are freely updatable. OpenAPI gives you readOnly and writeOnly and then stops, and everybody fills the rest of the gap with prose in a description field. Microsoft filled it with a key, ten years ago, and has been using it in production ever since.

Now — the honest counterargument, which I want to state properly rather than knock down. Somebody at Microsoft can reasonably say these keys are AutoRest’s, they are internal to a generator pipeline, the documentation exists for the people who need it, and registering them in a public registry implies a stability commitment nobody wants to make. That is a real position. I do not fully agree with it, but it is not a lazy one.

Here is why I think it is wrong anyway. Those documents are public. Azure’s OpenAPI specifications are in a public repository that a very large number of tools consume. The keys have already escaped. They are already being read by things Microsoft did not write and cannot control, and those things are already guessing. Registration does not create an obligation that did not exist — it just writes down what the key means for the readers who are already encountering it. And the registry entry format is deliberately light: a name, a description, a pointer. It is not a conformance claim.

The three asks, and for Microsoft two of them are already done.

Register the keys that carry portable meaning. Not all of them. I would send x-ms-pageable, x-ms-long-running-operation, x-ms-mutability, x-ms-enum, x-ms-parameter-location and x-ms-client-name — the ones whose meaning is about the API rather than about AutoRest’s code output. Each one is a markdown file pointing at documentation Microsoft already wrote. The work is nearly zero because the hard part, the writing-down, happened years ago.

Sponsor the OpenAPI Initiative. Microsoft already does. Microsoft has been part of this from the beginning, and I want to say that plainly rather than let the criticism above stand as the whole picture. The company has put real resources into OpenAPI for a long time.

Get more involved — specifically, bring the modelling problems. This is the ask that actually matters here. Microsoft has the deepest institutional knowledge of what breaks when you describe thousands of services with OpenAPI over ten years, across teams that do not talk to each other, with backward compatibility obligations nobody can walk away from. The long-running-operation problem and the mutability problem are not Microsoft problems. They are everybody’s problems, and Microsoft has shipped an answer to both. Bring those to the Special Interest Groups as proposals rather than leaving them as x-ms- keys, and the specification gets better for everyone — including for Microsoft, who would then get to delete some extensions.

Tomorrow, AWS, whose gateway vocabulary became a de facto standard by pure gravitational pull and has exactly as much registry presence as Microsoft’s.

Read the whole story
alvinashcraft
8 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Jeffrey Palermo: AI-Driven Software Projects - Episode 418

1 Share

https://clearmeasure.com/developers/forums/





Download audio: https://traffic.libsyn.com/clean/secure/azuredevops/Episode_418.mp3?dest-id=768873
Read the whole story
alvinashcraft
8 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

531: How AI Saved Our Lost Podcast Recording

1 Share

In Episode 531 James and Frank recount a near‑disastrous data loss when local recordings died seven minutes in, forcing them to rely on Zencastr's cloud-recovery button and a hopeful rescue email. They walk through the panic and the clever fix—using GitHub Copilot CLI/Baby Luna, FFmpeg and AI-assisted workflows to rebuild a muxed video, repair audio‑video drift, and underscore lessons about testing restores and trusting backups.

Follow Us

⭐⭐ Review Us ⭐⭐

Machine transcription available on http://mergeconflict.fm

Support Merge Conflict





Download audio: https://aphid.fireside.fm/d/1437767933/02d84890-e58d-43eb-ab4c-26bcc8524289/d23283ba-2a44-4f14-ae5c-a0cba82d4255.mp3
Read the whole story
alvinashcraft
9 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Build a digitally sovereign second brain

1 Share

We thought we’d go big this Maker Monday, what with it being the serious business of Back to School and all. Here’s a tutorial from the latest issue of Raspberry Pi Official Magazine that shows you how to keep everything in your brain organised and searchable, from notes to appointments and ideas.

A while ago, we read the book Building a Second Brain by Tiago Forte (Figure 1). It starts with the idea that we are all a little overloaded and bombarded with information, and how overwhelming it can be to not be able to retrieve the information that we need at a later time. The foundation of a Second Brain is to increase the capture and cataloguing rate of information, and the book delves into lots of custom systems to tag, organise, prioritise, and retrieve information. This Personal Knowledge Management (PKM) idea feels similar to the ideas of David Allen and the older, but still brilliant, ‘Getting Things Done’ system. 

Logseq running on Raspberry Pi OS; this is a ‘graph view’ that shows all your tagged information, along with the connections between the tags

At the heart of these PKM systems is the concept of ubiquitous capture: the idea that, as a start, we need to capture the information of interest. This is also an idea in journalling and is basically being proactive about note-taking. The question then is how to store these bits of information. There are lots of options. 

The end of sticky notes

There’s nothing wrong with a pile of sticky notes (or index cards, which are the author’s favourite) and a pen; however, you can quickly end up with a huge pile of notes which need to be physically stored and searched. Of course, lots of people have moved to software applications. Many of the popular operating systems for mobile devices will include some form of note-taking application, and there are many that require you to make an account and store your data in the cloud, so it’s a nice challenge to build a system that doesn’t store your information on another person’s computer. Finally, the solutions we explore here use open source projects so we can examine the code should we need to maximise our privacy and security. 

Figure 1: Inspired by the Tiago Forte book Building a Second Brain, it’s quite possible to create a personal knowledge management system across Raspberry Pi devices

The solution we are going to explore is centred around Logseq, a privacy-focused open source knowledge management system. It’s a really rich piece of software with more features than most people will use, and we’ll only really cover the basics, although the basics are incredibly powerful. Later in the article we’ll use Syncthing, another open source project, to synchronise our second brain created in Logseq across multiple devices. Syncthing is a peer-to-peer continuous file synchronisation tool, which means that it can sync data without passing that data through the cloud/a company’s server. It also encrypts and decrypts the data in transit, so you are nice and secure whilst the data is moving. It’s totally OK to just install Logseq on one machine and have a play with it to see if it can meet your needs.

Figure 2: A Raspberry Pi 500 is a good target for your first attempt at installing Logseq and Syncthing

We began by installing Logseq on a Raspberry Pi 500 (Figure 2), but note that the procedure outlined below is the same for any Raspberry Pi board; so, for example, we have this system currently set up on the Raspberry Pi 500, a Raspberry Pi Zero, and our Uconsole, which contains the Raspberry Pi Compute Module 4 board.

Currently, the only option to install a build of Logseq is via Snap. So, if working from a fresh Raspberry Pi OS installation, we’ll need to install the Snap package manager. If you already have Snap installed on your system, then you can skip to installing Logseq. 

To install Snap and then Logseq, open the Terminal application and, at the prompt, enter: 

$ sudo apt update

We can then install the latest version of Snap using: 

$ sudo apt install snapd

We then need to reboot Raspberry Pi 500:

$ sudo reboot

Once the machine has rebooted, open the Terminal application; we need to install a Snap package called ‘snapd’:

$ sudo snap install snapd

With Snap installed and set up, we can then go ahead and install Logseq using:

$ sudo snap install logseq –channel =og

With Logseq now installed, you will be able to launch it using the desktop applications menu at Accessories > Logseq Desktop. On launch, Logseq will open a demo page showing a journal-style entry for the current date (Figure 3). In this entry is a basic tutorial on how Logseq works and some of its functions. 

Figure 3: When you first open Logseq, you land on an informative demo page

In Logseq terminology, when you create a collection of notes and potentially other assets, you insert them into a ‘graph’. Scrolling on the current demo page, you should find a section where Logseq wants us to specify a folder it can use to contain all our information. You can use an existing folder, but it’s probably better to create a new folder. Click to ‘add a graph’ and then give your folder the title that you want your second brain system to be called; we went with ‘2nd_Brain’, rather unimaginatively. This OG version of Logseq will, in this specified folder, store all the data you input into the system. It essentially creates Markdown files and also stores any uploaded assets (PDFs, pictures, audio, etc.) to an assets subfolder. This means that you could manually copy this entire folder, move it to another machine, and open it in another installation of Logseq. 

Filling the brain with information

With the file specified and a new graph created, let’s put some basic information into our second brain system. Whenever you open Logseq, it will create an empty journal page for the current day. Each of these journal dates will become a page item in our system. So let’s type something in the currently empty journal page. 

As an example, we typed a note: ‘Spotted a book I’d like to read today, called “How to Build a Second Brain” by Tiago Forte’ (Figure 4). Now imagine we wrote a note about a different book every day. Currently the only way to return to this piece of information in the future is if we remember the date or use the search function. If we’d forgotten the name of the book, however, we could only really search for terms like ‘book’, which might generate a lot of returned results. What we need is to tag our information. Tagging is the secret sauce of Logseq that makes it incredibly useful. 


Figure 4: Adding a note without a tag is fine, but the only way to rediscover that tag is to either remember the date it was made or the exact contents

To add a tag to a piece of information, we place the tag term inside two pairs of square brackets. Let’s return to the line we entered earlier and write at the end of the line (though tags can be placed anywhere in text) ‘[[Books To Read]]’ and then press ENTER. Note that the tag text will become a clickable link (Figure 5). If we click that tag, the link then takes us to a new page, as every tag we add to Logseq becomes a new page. On the new [[Books To Read]] page, we will see the reference to our line, ‘… “How to Build a Second Brain” by Tiago Forte’. If we click the back arrow in the upper toolbar, we can navigate back to our journal. Let’s add a second line, ‘Another book I’d like to read, “Design an RP2040 Board with KiCad” by Jo Hinchliffe’. This line will always be listed in the current journal page, but again we can add the [[Books To Read]] tag as we did previously and it will be populated to that tag page. If we write a book title every day into separate journal entries, so long as we use the [[Books To Read]] tag, they will all appear on the tag page (Figure 6). Note that in the image we made a typo in the book title; in a way, this doesn’t matter — just be sure to spell your tags correctly! 

Figure 5: Adding a tag creates a new page for each tag in which tagged information is listed and linked

However, tags are free and unlimited, so it’s worth tagging information widely with lots of tags, thinking about how you might remember them in the future. As an example, in our second brain Logseq setup, we have some Customs and Excise tariff numbers; these numbers are unique for different types of goods that you sell, and it can be quite hard when faced with a new item to research and identify the correct tariff number as they are quite complex and there are a lot of them. It’s definitely the type of information you want to keep note of rather than have to discover every time. If we consider how we might think about these numbers, it’s pretty clear to tag them as [[Tariff]], but sometimes we might think [[Customs]] or might want the tariff number for a particular product or project, so we might use the [[Product Name]] as a tag. Adding multiple tags to items of information increases your chances of finding your way back to the info when needed, as the same piece of information will appear regardless of which tag you remember or use in the future. 


 Figure 6: The ‘Books To Read’ tag page begins to collect all the inputs listed with that tag

We need to move on to other aspects of the system, but it’s worth noting that you can type ‘/Upload an asset’ (or rather start by writing ‘/U’, which will autocomplete) to add files of pretty much any type to an entry. You can then tag these in the usual way, and the asset will be copied to the assets folder in your designated directory. This means you don’t have to worry about copying and pasting items, or losing links to items, as they are all stored in the folder. Finally, note that you can embed links and videos, and even highlight and annotate PDFs, in Logseq, tagging the exact part of a PDF to return to later. 


Figure 7: Syncthing Web UI is the admin console for Syncthing, allowing you to connect devices and share directories and files

So we have the basis of our second brain setup, but we’d like it to be accessible on more than one device. Our approach for this will be to install Logseq on another device, and then use Syncthing to synchronise folders so that our second brain folder is on both devices and up to date (Figure 7). 

To install Syncthing on Raspberry Pi, it’s best to first run:

$ sudo apt update

$ sudo apt upgrade

…to make sure our system is up to date. Then we need to add the ‘apt-transport-https’ package to the apt service by running:

$ sudo apt install apt-transport-https

We then need to store the Syncthing GPG keys on Raspberry Pi in our keyrings directory. In a nutshell, this means that the Syncthing packages we install are legitimate and are sourced from the right place. To do this, run the following command:

$ curl -s https://syncthing.net/release-key.txt | gpg --dearmor | sudo tee /usr/share/keyrings/syncthing-archive-keyring.gpg >/dev/null

We can now add the Syncthing repository with: 

$ echo "deb [signed-by=/usr/share/keyrings/syncthing-archive-keyring.gpg] https://apt.syncthing.net/ syncthing stable" | sudo tee /etc/apt/sources.list.d/syncthing.list

We can then update the packages once again with: 

$ sudo apt update

And then finally install Syncthing with: 

$ sudo apt install syncthing

Once it’s installed, you will find two Syncthing items in the applications menu: one is a simple ‘Start Syncthing’ which sets the service running; the other is ‘Syncthing Web UI’. First, double-click the ‘Start Syncthing’ application, then double-click the ‘Syncthing Web UI’. This should now launch the default web browser and will land on a default Syncthing admin page for the device. From here, we can set up files and directories for synchronisation. 

Figure 8: Adding folders you want to synchronise in the Syncthing Web UI

First, let’s click the ‘Add Folder’ button. In the ‘Add Folder’ dialog box (Figure 8), we can add a folder label — for example, ‘second_brain_directory’, as we are going to share the directory we made for our Logseq second brain files. Leave the Folder ID with the default generated identifier string, and then in the last input, Folder Path, set the exact folder path to match where the Logseq folder you made is. So for example, if you made your Logseq folder in the default user directory of a Raspberry Pi and you called it ‘2nd_Brain’, you can input the tilde (~) character (which means the same as /home/username) and write ~/2nd_Brain. Clicking Save sets this file to be shared. 

Figure 9: Adding devices to your Syncthing setup is straightforward

On another device with Logseq and Syncthing installed (we used our Uconsole), on the same Web UI page you can click on the short name identification in the ‘This Device’ area. Clicking this launches a dialog box with a QR code and a long-form ID string for the device (Figure 9). This is a 56-digit string with dashes. Back on the Raspberry Pi 500, you can click the ‘Add Remote Device’ button and type in the long device ID; note that you don’t need to add the dashes or spaces, just the long string. If you leave the Device Name section empty, it will populate with the random generated device name on the other device; or you can add a more descriptive name such as ‘Uconsole’ or ‘Pi Zero’ here. 

Quick tip

Note that you don’t have to be on the same network for Syncthing to synchronise your files. 

Finally, by clicking on the folder we created to share earlier on the Raspberry Pi 500, and then clicking the Edit button, we can move over to the Sharing tab in the dialog box, where we should be able to see the remote device we just added (Figure 10). If we tick the box to share with that device, we can then click Save. Give everything a couple of minutes to work through the networks and then check on the other device; you should see a notification that the other device wants to share a folder with you. Accept the folder and follow the instructions to set up a path to a directory to synchronise that folder to. All that then remains is to open Logseq on the second device and click ‘add graph’, navigating to the synchronised folder we just made. This means if we add or change anything in Logseq on our first device (Raspberry Pi 500) or our second device (Uconsole), then these changes appear on both devices, keeping our Logseq second brain system up to date. You can of course repeat this process, sharing and synchronising your 2nd_Brain Logseq folder onto as many devices as you want.

Figure 10: Editing a file setting to share with specified devices

Careful now!

Note that synchronising folders is not a backup! If you accidentally delete part or all of your Logseq folder content, this will propagate through Syncthing and delete it everywhere! A solution to this is to, on one or more devices, set up a system that periodically backs up your folder to another folder. 

To do this, we will create a small Bash script that will copy a target folder to a different folder that we won’t sync. We’ll then use the cron service to make this script run periodically so that we don’t have to think about it. You can do this on any of the Raspberry Pi devices, or indeed more than one; however, it would be most useful on a device which can be left always on. In our system, we have it set up on a Raspberry Pi Zero W which runs headless and is an ‘always on’ device in our workspace. However, we’ll set this up here on the Raspberry Pi 500, as it’s easy to do by typing commands into the terminal. 

To begin, launch the Terminal application, and in the root directory of your Raspberry Pi device, launch the nano text editor to write a Bash script:

$ sudo nano

In nano, type the following small Bash script, replacing LOGSEQ_Directory_Path with the path to your Logseq folder. If your folder is in the home directory, you don’t need any leading edge slashes; so, backing up our ‘2nd_brain’ folder in the home directory to ‘backup_2nd_brain’ is really straightforward to input.  

#! /bin/sh 

rsync -avh LOGSEQ_Directory_Path
Backup_Directory_Path

Save this file with a name like backup_script.sh

We need to set this file as executable, so in the same working directory as the script, we need to run:

$ sudo chmod +x backup_script.sh

We can now test the backup_script.sh script by, in the same directory, executing it with:

$./backup_script.sh

You can then use the File Manager (or the Terminal) to check if the files have been copied to the newly created backup folder. Finally, we can automate this script using a cronjob. We can do this by editing a file called crontab. Type:

$ crontab -e

…in the terminal. If this is the first time you have done this, it might ask which editor you would like to use; select nano from the list. In the now-open crontab file, there is a long section of comments that give a great overview of how the cron system works (Figure 11). At the end of the file, we can add a line that has the following structure: minutes, hour, day of month, month, day of week, followed by the command. You can leave slots empty using an asterisk (*) so you can construct values for different automated periods. So, for example: 

46 10 * * 1 /home/concretedog/backup_script.sh

…would execute the backup_script.sh script every Monday at 10.46am. Note that if you wanted to run the script once a day, you’d simply leave the day of week value empty, such as: 

30 10 * * * /home/concretedog/backup_script.sh

Of course, you can extend and enhance the system; it would be great to back up the folders to an attached SSD, or you might like to explore how to add monitoring and logging to your cron system. Logseq and Syncthing are available for most desktop distributions, and they are also ported to smartphone operating systems, meaning you can carry your private, secure, and backed-up second brain with you always!

Figure 11: Setting up crontab to create a periodically repeating job

Find more Raspberry Pi projects in Raspberry Pi Official Magazine

This article appears in issue 169 of Raspberry Pi Official Magazine, which you can access online. You can also subscribe to the print version of our magazine. Not only do we deliver worldwide, but those who sign up to the six- or twelve-month print subscription will receive a FREE Raspberry Pi Pico 2 W!

You can find Raspberry Pi Official Magazine on FacebookXThreadsLinkedIn, and Mastodon. You can also contact the team via email: magazine@raspberrypi.com

The post Build a digitally sovereign second brain appeared first on Raspberry Pi.

Read the whole story
alvinashcraft
9 minutes ago
reply
Pennsylvania, USA
Share this story
Delete

Cutting tool output tokens in Microsoft Agent Framework with TOON

1 Share

If you've built anything with the Microsoft Agent Framework (MAF), you'll notice that your tool calls can fill up the context window quite fast. A function that returns a list of 50 orders as JSON easily costs you a few hundred tokens on braces, quotes and repeated field names alone. That cost hits you twice: once on the way into the model as tool output, and again on every subsequent turn where that history gets replayed.


That's where TOON (Token-Oriented Object Notation) comes in. Let's explore this.

What TOON actually is

TOON is a line-oriented, indentation-based encoding of the same data model JSON uses. It borrows YAML's indentation for nested objects and CSV's tabular layout for arrays of uniform objects. The trick is in that last part: if you have a list of objects that all share the same fields, TOON declares the field list once and then just streams the values, row by row, instead of repeating every key for every item.

Take a small object like this in JSON:

{
  "orders": [
    { "id": 1, "customer": "Acme", "status": "open" },
    { "id": 2, "customer": "Globex", "status": "shipped" },
    { "id": 3, "customer": "Initech", "status": "open" }
  ]
}

In TOON that becomes:

orders[3]{id,customer,status}:
  1,Acme,open
  2,Globex,shipped
  3,Initech,open

No repeated keys, no braces, no quotes around every string. The [3] tells the parser how many rows to expect and {id,customer,status} is the header, declared once. For uniform tabular data, this is where TOON earns its name: the published benchmarks from the TOON project show 30-60% fewer tokens compared to the equivalent JSON, depending on the tokenizer and how uniform your data is.

Remark: TOON is explicitly designed as an input format for LLMs, not a general-purpose replacement for JSON. You still want JSON (or your own DTOs) everywhere else in your application. TOON only earns its place at the boundary where structured data crosses into a prompt.

How to use this in a MAF agent

In the Microsoft Agent Framework, a tool is just a C# method wrapped with AIFunctionFactory.Create(). MAF takes care of generating the JSON schema for the parameters and, when the LLM calls the tool, feeding whatever your method returns straight back into the conversation as the tool result. By default that "whatever you return" gets serialized to JSON.

So the question becomes: what if the method just returns TOON instead of a plain object or a JSON string?

Turns out, that's easy to achieve. MAF doesn't care what your tool returns as long as it's a string (or something serializable). It doesn't force JSON on you at the tool-result boundary. You control the encoding yourself, inside the method body.

Putting it into action

I used the Toon.NET package here, but any of the TOON encoders on NuGet will do the same job. They all expose a similar Serialize() call that takes your object graph.

dotnet add package Toon.NET

A tool that looks up open orders for a customer, naively, looks like this:

[Description("Get the open orders for a given customer id")]
static List<Orders> GetOpenOrders(string customerId)
{
    return orderRepository.GetOpenOrders(customerId);
    // MAF will JSON-serialize this for the tool result
}

Swap the return type to TOON:

using Toon;

[Description("Get the open orders for a given customer id")]
static string GetOpenOrders(string customerId)
{
    var orders = orderRepository.GetOpenOrders(customerId);

    var serializer = new ToonSerializer();
    return serializer.Serialize(new { orders });
}

And wire it up on the agent as you normally would:

AIAgent orderAgent = chatClient.AsAIAgent(
    instructions: "You help customer service reps look up order status. " +
                  "Tool results are TOON-encoded: a compact, indented, comma-separated format.",
    name: "OrderAgent",
    tools: [AIFunctionFactory.Create(GetOpenOrders)]
);

Console.WriteLine(await orderAgent.RunAsync("Does Acme have any open orders?"));

That's it. The LLM sees a TOON block instead of a JSON blob for the tool result, and everything downstream — the reasoning, the follow-up questions, the final answer — works exactly the same, just cheaper.

Tip: Mention the format explicitly in your instructions, like in the example above. Modern models handle TOON fine without being told, since it reads close to YAML/CSV, but a one-line heads-up removes any ambiguity about how to interpret the header row.

Does the schema still work?

Yes! Nothing changes for the input side. AIFunctionFactory.Create() still inspects your method signature and generates a normal JSON Schema for the parameters, because that schema is metadata the model needs to know how to call the tool in the first place. TOON only replaces the output — the tool result that gets appended to the conversation after invocation. Two different concerns, two different formats, and that's exactly why you can mix them without MAF complaining.

When it's not worth it

TOON's savings come from tabular uniformity. If your tool returns a single object, or a small non-uniform blob, the TOON encoding barely differs from compact JSON, and you're adding a dependency for no real gain.

Remark: I'd only reach for this on tools that return lists; order histories, search results, log entries, anything array-of-objects shaped. For a single customer record or a boolean flag, plain JSON is fine and simpler to debug.

The other trade-off: TOON encoders on NuGet right now are mostly encode-only, or young libraries with evolving APIs (MAF itself is still in public preview, so this is a preview-on-preview combination). Pin your package versions and re-check the docs before you ship this into production.

That's it! A small change at the tool-result boundary, and every follow-up turn in the conversation carries a lighter payload.

More information

Read the whole story
alvinashcraft
9 minutes ago
reply
Pennsylvania, USA
Share this story
Delete
Next Page of Stories