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

Visualizing Claude Code MCP Requests with Coding Agent Explorer

1 Share

Model Context Protocol (MCP) servers are becoming a key part of how Claude Code extends its capabilities. They give the agent access to documentation, code search, external APIs, and much more. But when Claude Code talks to an MCP server, what exactly is it saying? And what is the server sending back?

Until now, that communication has been invisible. The MCP Observer changes that. It is a new feature in the Coding Agent Explorer that intercepts all traffic between Claude Code and any MCP server and displays it in a real-time dashboard, with both a raw JSON view and a readable, formatted view that makes sense of the data.

Here is what you will learn in this post:

  • What MCP is and why it matters for coding agents
  • What the MCP Observer does and how it works
  • How to configure it and connect it to any MCP server
  • What you can see in the dashboard
  • A few MCP services to try straight away

This is a multi-part series on the Coding Agent Explorer, an open-source .NET tool for inspecting what AI coding agents do under the hood. 

What Is Model Context Protocol (MCP)?

The Model Context Protocol is an open standard that lets AI agents connect to external tools and data sources. An MCP server exposes a set of tools that the agent can discover and call, just like a REST API but designed specifically for AI agent communication.

Claude Code has built-in support for MCP. You can register any MCP server, and Claude Code will automatically discover the tools it provides and use them whenever they are relevant to the task at hand.

The protocol is built on JSON-RPC 2.0. When Claude Code connects to an MCP server, it sends messages like initialize, tools/list, and tools/call. The server responds with its capabilities and tool results. This is the conversation the MCP Observer lets you see.

HTTP vs. STDIO MCP Servers

MCP servers can communicate over two transports: HTTP and STDIO. Claude Code supports both.

MCP HTTP vs STDIO Services

Here is the difference:

  • HTTP servers
    Expose a URL that Claude Code connects to over the network. The traffic is standard HTTP, which means a proxy can sit in the middle and capture it transparently. This is what the MCP Observer uses.
  • STDIO servers
    Run as a local process. Claude Code launches the process and communicates with it over stdin and stdout. The MCP Observer cannot intercept this communication, so STDIO-based servers are not visible in the observer.

That said, Claude Code’s PreToolUse and PostToolUse hooks fire for MCP tool calls regardless of transport. They do not show the raw JSON-RPC protocol, but they do show which MCP tool was called and what parameters were passed. That is enough to understand what the agent is doing, even without the protocol detail. If you have not read the previous post on hooks, it is a good companion to this one.

What Is the MCP Observer?

The MCP Observer is a transparent proxy that runs on port 9999 (HTTP). Claude Code thinks it is talking to the real MCP server. In reality, it is talking to the observer, which forwards every request to the real server, captures the traffic, and displays it on the dashboard.

Here is how the data flows:

Claude Code gets the real responses without any modification. The observer is completely transparent. You can add it to any MCP workflow without changing how Claude Code or the MCP server behaves.

Why HTTP Only on Port 9999?

The proxy between Claude Code and the observer runs over plain HTTP, not HTTPS. This is intentional. The connection is local-only: both Claude Code and the observer run on the same machine, so the traffic never leaves your computer and encryption adds no security benefit. Using HTTP also avoids the need to set up a local certificate, which would make the setup much more involved.

The outbound connection from the observer to the real MCP server is a separate matter. The observer forwards requests to whatever URL you configure, and that connection uses whatever scheme the server requires. In practice, all public MCP servers use HTTPS, so the traffic between the observer and the real server is always encrypted.

How to Set It Up the MCP Observer

Setting up the MCP Observer takes about two minutes.

Step 1: Start the Coding Agent Explorer

First, we run:

dotnet run

This starts the MCP proxy on port 9999 and the existing proxy on port 8888, as well as the dashboard on ports 5000 and 5001.

For details on how to install, build, and run the Coding Agent Explorer, see the first post in this series: Introducing the Coding Agent Explorer.

Step 2: Create a working directory

Create a fresh folder where you will run claude. Then open a terminal in that folder before running the next step.

Step 3: Register the proxy with Claude Code

Run this command in your terminal from inside your working directory:

claude mcp add --transport http mcp_proxy http://localhost:9999

By default this uses local scope: the registration is stored in ~/.claude.json under your project path and is only active when Claude Code is started from this directory.

The URL http://localhost:9999 is the fixed address of the MCP Observer proxy and never changes. Which real MCP server the traffic is forwarded to is controlled separately inside the MCP Observer dashboard. You only need to run this command once.

To remove the registration later:

claude mcp remove mcp_proxy

For full details on scopes and other options, see the Claude Code MCP documentation.

Step 4: Verify the MCP configuration

Start Claude Code and run the /mcp command:

/mcp

This lists all registered MCP servers and their connection status. You should see mcp_proxy listed as connected. If the status shows an error, check that the Coding Agent Explorer is running and that a destination URL has been set in the MCP Observer dashboard.

Step 5: Open the MCP Observer dashboard

Open the dashboard at https://localhost:5001 and click MCP Observer in the navigation bar.

Coding Agent Explorer main menu with MCP Observer

Step 6: Set the destination URL

The destination URL field is pre-filled with  https://gitmcp.io/tndata/CloudDebugger as a ready-to-use default. This is the MCP endpoint for Cloud Debugger, another open-source project by me. It is a good starting point if you just want to try the observer without setting up your own MCP server.

GitMCP is a free, open-source service that turns any GitHub repository into a remote MCP server, giving AI tools access to up-to-date documentation and code directly from the source.

Leave the default URL in place and click Set to activate it. The status line updates to confirm the proxy is active and forwarding traffic.

When you click Set, the Coding Agent Explorer updates its internal destination and reconfigures the YARP reverse proxy on port 9999 to forward traffic to the new URL. No restart is needed. Only one destination can be active at a time. If you change it, the request history is cleared so that the table always reflects a single server.

Important!

If you change the destination URL while Claude Code is already running, restart Claude Code before continuing. Claude Code caches tool information from the MCP server when it connects, and it will not pick up the new server’s tools until it reconnects.

Step 7: Try your first prompt

The Cloud Debugger is an open-source .NET tool for debugging live Azure applications. It can capture and display HTTP requests, exceptions, service bus messages, and more, without attaching a debugger or redeploying.

With the MCP Observer active and the Cloud Debugger endpoint set, start Claude Code from your working directory and try one of these prompts:

  • What is the Cloud Debugger and what can it do?
  • Does the Cloud Debugger include any Python code?
  • Which Azure services does the Cloud Debugger support?

Watch the MCP Observer dashboard as Claude Code connects: you will see the initialize and tools/list calls appear first, followed by one or more tools/call requests as Claude Code fetches the information it needs to answer your question.

What You Can See

The MCP Observer captures every request and response and displays them in a table, sorted oldest to newest. Each row shows the time, HTTP method, path, JSON-RPC method, response status, and duration.

MCP Observer Request List Example

For tools/call requests, the JSON-RPC method column shows the name of the tool that was called in parentheses, so you can see at a glance which tools Claude Code is using. For example: tools/call (search_docs) or tools/call (fetch_page).

Click any row to open the detail panel at the bottom, which shows the request body on the left and the response on the right.

Two Ways to View the Response

The response panel has two view modes: Pretty and Raw.

Pretty 

This is the default. It renders the response in a readable format that depends on the type of request:

  • tools/list responses are shown as a card for each tool, with the tool name, description, and input parameters clearly laid out.
  • initialize responses show the protocol version, server name and version, and capabilities as a simple key-value list.
  • tools/call responses show the returned content directly, so you can read the actual text the MCP server returned.
  • All other responses fall back to formatted JSON.

Raw

Shows the full JSON, pretty-printed, so you can inspect every field.

MCP Observer raw view

Sample MCP Services to Try

Here are two public MCP services that work well as starting points.

Microsoft Learn

The Microsoft Learn MCP server gives Claude Code access to the full suite of Microsoft’s documentation, including Azure, .NET, and the rest of Microsoft’s product ecosystem.

Destination URL:

https://learn.microsoft.com/api/mcp

Once registered, try asking Claude Code:

How do I create an Azure Container App using the az CLI?

You will see Claude Code call tools/list to discover the available tools,and then it will call one or more of them to fetch the relevant documentation, and finally it uses the results to answer your question. The MCP Observer shows each of those calls as they happen.

Context7

Context7 provides up-to-date documentation for popular libraries and frameworks. It is particularly useful for questions about rapidly changing ecosystems like Next.js, where training data can be out of date.

Destination URL:

https://mcp.context7.com/mcp 

Once registered, try asking Claude Code:

How do I set up middleware in Next.js 15? Use context7

The Use context7 hint tells Claude Code to prioritise the Context7 MCP server for this query. Watch the MCP Observer to see how it resolves the library ID, fetches the relevant documentation, and uses it to answer your question.

A Practical Example: Watching a tools/call in Action

To make this concrete, here is what happens when you ask Claude Code the Azure Container App question above.

You type your prompt. Claude Code connects to the MCP server and the observer captures the following sequence:

Clicking the tools/list row in Pretty view shows all the tools the Microsoft Learn MCP server exposes: their names, descriptions, and accepted parameters. Clicking the tools/call row shows exactly what Claude Code asked for and what the server returned.

MCP Requests and Hooks

The MCP Observer shows you the protocol-level conversation between Claude Code and the MCP server. But there is a complementary view available in the Conversation View if you have hooks configured.

Claude Code fires PreToolUse and PostToolUse hook events for every MCP tool call, just as it does for built-in tools like Read or Bash

The MCP Observer and the Conversation View complement each other. The observer shows you the raw protocol detail. The Conversation View with hooks shows you the full picture of what the agent was doing and why.

Here is an example of a PreToolUse call:

MCP pretool use example in Claude code

If you have set up HookAgent as described in the previous post in this series, those events appear in the Conversation View alongside the LLM API calls. This lets you see MCP tool usage in context: you can watch the LLM call that triggered the tool call, the hook events that fired around it, and the next LLM call that consumed the result, all on the same timeline.

Why This Matters

Most developers who start using MCP servers treat them as a black box. You register a server, Claude Code uses it, and something useful happens. But what tools does the server expose? What does Claude Code actually ask it? What does the server send back?

Those are exactly the questions the MCP Observer answers. Once you can see the traffic, you can evaluate whether a server is giving Claude Code good information, whether the tool descriptions are clear enough for the model to use them correctly, and whether the responses are fast enough to be practical.

For anyone building their own MCP server, the observer is invaluable. You can see exactly how Claude Code interacts with your server during development, without adding any logging to the server itself.

What's Next

The Coding Agent Explorer now covers three layers of Claude Code’s operation:

  • LLM API (HTTP Inspector and Conversation View)
  • Lifecycle events (hooks)
  • MCP servers (MCP Observer).

Together they give you a complete picture of what the agent is doing at every level.

The full project is open-source and available on GitHub: github.com/tndata/CodingAgentExplorer

I Want Your Feedback!

If you try the MCP Observer and find a bug, have a feature request, or want to share your experience, please create an issue on GitHub. Contributions are welcome. The codebase is intentionally simple to make it easy to jump in.

Want to Learn Agentic Development?

I am currently working on a workshop called Agentic Development with Claude Code where we use the Coding Agent Explorer to explore how coding agents work under the hood. The MCP Observer is one of the tools I use to show participants exactly how Claude Code discovers and uses external tools.

You can read more about my workshops here: tn-data.se/courses.

I also give a presentation called How Does a Coding Agent Work? for companies and conferences. Contact me if you are interested.

Frequently Asked Questions

Does the MCP Observer affect Claude Code or the MCP server in any way?

No. The observer forwards requests and responses without modification. Both Claude Code and the MCP server behave exactly as they would without the proxy.

Can I use the MCP Observer with any MCP server?

Yes, as long as the server uses the streamable HTTP transport (which all modern MCP servers do). Enter the server’s URL in the destination field and click Set.

Can I watch multiple MCP servers at the same time?

Not currently. The observer is configured for one destination at a time. To switch servers, enter the new URL and click Set. The request history is cleared when you change the destination. After changing the URL you must also restart Claude Code; as it caches tool information from the MCP server at startup and will not pick up the new server’s tools until it reconnects.

Why does changing the destination clear the request history?

Mixing requests from different servers in the same list would make the timeline confusing and hard to read. Clearing on change keeps the view focused on the server you are currently observing.

Do I need to restart Claude Code after registering the proxy?

Always start Claude Code after the Coding Agent Explorer is already running and after you have set the destination URL in the MCP Observer dashboard. Claude Code connects to the MCP server during startup to discover its tools. If the proxy is not yet active when Claude Code starts, it will not find any tools.

Can I observe STDIO-based MCP servers?

Not with the MCP Observer, which only works with HTTP transport. However, Claude Code fires `PreToolUse` and `PostToolUse` hook events for every MCP tool call regardless of transport. If you have hooks configured, those events appear in the Conversation View and show you which tool was called and what parameters were passed. It is a higher-level view than the raw JSON-RPC protocol, but it gives you meaningful visibility into what the agent is doing. See the previous post in this series for details on setting up hooks.

About the Author

Tore Nestenius is a Microsoft MVP in .NET and a senior .NET consultant, instructor, and software architect with over 25 years of experience in software development. He specializes in .NET, ASP.NET Core, Azure, identity architecture, and application security, helping development teams design secure, scalable, and maintainable systems.

Tore delivers .NET workshops, Azure training, and technical presentations for companies and development teams across Europe. His focus is on practical, hands-on learning that helps developers understand modern tooling, cloud architecture, and AI-assisted development.

Learn more on his .NET blog at nestenius.se or explore his workshops and training at tn-data.se.

Add Your Heading Text Here​

The post Visualizing Claude Code MCP Requests with Coding Agent Explorer appeared first on Personal Blog of Tore Nestenius | Insights on .NET, C#, and Software Development.

Read the whole story
alvinashcraft
12 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Announcing Rust 1.95.0

1 Share

The Rust team is happy to announce a new version of Rust, 1.95.0. Rust is a programming language empowering everyone to build reliable and efficient software.

If you have a previous version of Rust installed via rustup, you can get 1.95.0 with:

$ rustup update stable

If you don't have it already, you can get rustup from the appropriate page on our website, and check out the detailed release notes for 1.95.0.

If you'd like to help us out by testing future releases, you might consider updating locally to use the beta channel (rustup default beta) or the nightly channel (rustup default nightly). Please report any bugs you might come across!

What's in 1.95.0 stable

cfg_select!

Rust 1.95 introduces a cfg_select! macro that acts roughly similar to a compile-time match on cfgs. This fulfills the same purpose as the popular cfg-if crate, although with a different syntax. cfg_select! expands to the right-hand side of the first arm whose configuration predicate evaluates to true. Some examples:

cfg_select! {
    unix => {
        fn foo() { /* unix specific functionality */ }
    }
    target_pointer_width = "32" => {
        fn foo() { /* non-unix, 32-bit functionality */ }
    }
    _ => {
        fn foo() { /* fallback implementation */ }
    }
}

let is_windows_str = cfg_select! {
    windows => "windows",
    _ => "not windows",
};

if-let guards in matches

Rust 1.88 stabilized let chains. Rust 1.95 brings that capability into match expressions, allowing for conditionals based on pattern matching.

match value {
    Some(x) if let Ok(y) = compute(x) => {
        // Both `x` and `y` are available here
        println!("{}, {}", x, y);
    }
    _ => {}
}

Note that the compiler will not currently consider the patterns matched in if let guards as part of the exhaustiveness evaluation of the overall match, just like if guards.

Stabilized APIs

These previously stable APIs are now stable in const contexts:

Destabilized JSON target specs

Rust 1.95 removes support on stable for passing a custom target specification to rustc. This should not affect any Rust users using a fully stable toolchain, as building the standard library (including just core) already required using nightly-only features.

We're also gathering use cases for custom targets on the tracking issue as we consider whether some form of this feature should eventually be stabilized.

Other changes

Check out everything that changed in Rust, Cargo, and Clippy.

Contributors to 1.95.0

Many people came together to create Rust 1.95.0. We couldn't have done it without all of you. Thanks!

Read the whole story
alvinashcraft
19 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

How to Implement Command Pattern in C#: Step-by-Step Guide

1 Share

Learn how to implement command pattern in C# with a step-by-step guide covering command interfaces, concrete commands, invokers, and undo/redo support.

Read the whole story
alvinashcraft
27 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Platform as a Product: Delivering Value While Balancing Competing Priorities

1 Share

Software platforms must be treated as products. Success requires balancing engineering, design, usability, security, and value for internal customers and the organisation, Abby Bangser mentioned in her talk Platform as a Product. A product mindset, clear ownership, and continuous investment prevent bottlenecks, platform decay, and wasted effort, enabling scalable, sustainable value over time.

By Ben Linders
Read the whole story
alvinashcraft
35 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

.NET MAUI ListView vs CollectionView: How Syncfusion ListView Performs Better

1 Share

.NET MAUI ListView vs CollectionView How Syncfusion ListView Performs Better

TL;DR: Many .NET MAUI lists falter under real-world demands. This comparison reveals how Syncfusion .NET MAUI ListView confidently manages grouping, gestures, infinite scroll, and large data, delivering smoother scrolling, fewer allocations, and less glue code than CollectionView.

When CollectionView hits its limits, here’s the ListView built for real apps

If you’ve built more than a demo app with .NET MAUI, you’ve probably hit this point:

“The list worked great—until we added more features.”

The .NET MAUI CollectionView is a solid starting point, but it starts to show strain as an app grows.

Add grouped data, sticky headers, swipe actions, drag‑and‑drop, or infinite scrolling, and CollectionView often turns into a web of event handlers, state management, and performance trade‑offs. The result is familiar: more code to maintain, tougher debugging, and a list of experiences that feel sluggish on real devices.

Syncfusion® .NET MAUI ListView takes a different approach.

Instead of making you piece together common list behaviors, it delivers them out of the box using built‑in properties, MVVM‑friendly commands, and a virtualization engine designed for large datasets. The payoff is immediate: cleaner code, smoother scrolling, and significantly lower memory usage.

This post compares both controls using the same real‑world scenario and shows where Syncfusion .NET MAUI ListView saves time, code, and memory.

How Syncfusion .NET MAUI ListView outshines Collection View

When comparing these two controls, the Syncfusion .NET MAUI ListView stands out with built‑in features that reduce extra code and deliver smoother performance:

  1. Grouping with sticky headers: Pins the current group header at the top while scrolling.
  2. Swipe actions: Reveals quick actions by swiping left or right on an item.
  3. Drag-and-drop reorder: Users can reorder items by dragging them.
  4. Incremental loading: Loads the page on demand for faster, lighter lists.
  5. Layout choices: Switches between list and grid presentations to fit the content.
  6. Item sizing and virtualization: Uses fixed or measured row heights to keep scrolling smooth.

Syncfusion .NET MAUI ListView vs .NET MAUI CollectionView: Real feature comparison

To keep this comparison honest, we are going to test both controls using the same MVVM book list, no shortcuts, no artificial demos. Each section answers the kinds of questions developers actually run into when building production apps:

  • How much effort does sticky grouping really take?
  • How quickly does swipe support get messy?
  • What breaks when you add drag‑and‑drop reordering?
  • And what happens to memory usage as the list grows?

Along the way, you’ll see where Syncfusion ListView handles these scenarios with built‑in properties and templates, and where Collection View requires extra event wiring, state tracking, and custom logic.

Instead of theory or feature checklists, this walkthrough shows real code, real UI behavior, and real performance numbers, so you can decide which control fits your feature needs and performance goals based on facts rather than assumptions.

1. Grouping with sticky headers

To see the difference clearly, let’s compare how each control handles this feature:

a) Syncfusion .NET MAUI ListView

Sticky headers are a built-in feature in Syncfusion .NET MAUI ListView. As you scroll, the current group title stays pinned at the top, so users always know where they are. A single setting enables it, and grouping by the first letter feels effortless.

Here’s the code you need:

<listView:SfListView x:Name="listView"
                      ItemsSource="{Binding BookInfo}"
                      IsStickyGroupHeader="True">

    <listView:SfListView.GroupHeaderTemplate>
        <DataTemplate>
            <Grid BackgroundColor="#F2F2F2" Padding="8,4">
                <Label Text="{Binding Key}" VerticalTextAlignment="Center" FontAttributes="Bold" />
            </Grid>
        </DataTemplate>
    </listView:SfListView.GroupHeaderTemplate>
</listView:SfListView>
// Group items by the first character of BookName (uppercase).
listView.DataSource.GroupDescriptors.Add(new GroupDescriptor()
{
    PropertyName = "BookName",
    KeySelector = (object obj1) =>
    {
        var item = (obj1 as BookInfo);
        return item.BookName[0].ToString();
    }
});

b) .NET MAUI CollectionView

You need to manage sticky headers manually. On every scroll, you recalculate which group is visible and update the header. It works but requires extra housekeeping.

Refer to the following code.

<Grid RowDefinitions="Auto,*">
    <Border BackgroundColor="#F2F2F2" Padding="8,4">
        <Label Text="{Binding CurrentGroupName}" FontAttributes="Bold"/>
    </Border>

    <CollectionView x:Name="List"
                    Grid.Row="1"
                    ItemsSource="{Binding BookGroups}"
                    IsGrouped="True"
                    SelectionMode="Multiple"
                    ItemSizingStrategy="MeasureFirstItem"
                    Scrolled="OnCollectionViewScrolled">
        <CollectionView.GroupHeaderTemplate>
            <DataTemplate>
                <Grid BackgroundColor="#f2f2f2" Padding="12">
                    <Label Text="{Binding Name}" FontAttributes="Bold" />
                </Grid>
            </DataTemplate>
        </CollectionView.GroupHeaderTemplate>
    </CollectionView>
</Grid>
Loaded += OnLoaded;

private void OnLoaded(object sender, EventArgs e)
{
    UpdateCurrentGroupHeader();
}

private void OnCollectionViewScrolled(object sender, ItemsViewScrolledEventArgs e)
{
    UpdateCurrentGroupHeader(e.FirstVisibleItemIndex);
}

private void UpdateCurrentGroupHeader(int firstVisibleIndex = 0)
{
    if (Vm == null || Vm.BookGroups == null || Vm.BookGroups.Count == 0)
        return;

    var index = firstVisibleIndex < 0 ? 0 : firstVisibleIndex;

    // Map to group by flattening groups until we cover firstVisibleIndex
    int cursor = 0;
    foreach (var group in Vm.BookGroups)
    {
        int groupCount = group.Count;
        if (index < cursor + groupCount)
        {
            Vm.CurrentGroupName = group.Name;
            return;
        }
        cursor += groupCount;
    }

    // Fallback
    Vm.CurrentGroupName = Vm.BookGroups[0].Name;
}

2. Swipe actions

Both controls support swipe gestures, but the way they implement and simplify quick actions differs significantly:

a) Syncfusion .NET MAUI ListView

Swipe gestures reveal sleek, ready-to-use actions like Favorite and Delete. Templates keep the UI consistent, and commands bind directly to your ViewModel. The experience feels native across platforms.

Refer to the following code example.

<listView:SfListView x:Name="listView"
                      ItemsSource="{Binding BookInfo}"
                      AllowSwiping="True">

    <listView:SfListView.StartSwipeTemplate>
        <DataTemplate>
            <Grid BackgroundColor="#E8F5E9" Padding="12">
                <Button Text="Favourite"
                        FontSize="12"
                        Padding="10"
                        TextColor="White"
                        BackgroundColor="#2E7032"/>
            </Grid>
        </DataTemplate>
    </listView:SfListView.StartSwipeTemplate>

    <listView:SfListView.EndSwipeTemplate>
        <DataTemplate>
            <Grid BackgroundColor="#FFEBEE" Padding="12">
                <Button Text="Delete"
                        TextColor="White"
                        BackgroundColor="#C62828"
                        Command="{Binding BindingContext.DeleteCommand, Source={x:Reference listView}}"
                        CommandParameter="{Binding .}"/>
            </Grid>
        </DataTemplate>
    </listView:SfListView.EndSwipeTemplate>
</listView:SfListView>

b) .NET MAUI CollectionView

You wrap each item in a Swipe View and define left/right actions yourself. Flexible, but repetitive, more template code for the same result.

Here’s how to implement this feature:

<CollectionView ItemsSource="{Binding BookInfo}">
  <CollectionView.ItemTemplate>
    <DataTemplate>
      <SwipeView>
        <SwipeView.LeftItems>
          <SwipeItems Mode="Reveal">
            <SwipeItem Text="Fav" BackgroundColor="#FFE08A"
                       Command="{Binding FavoriteCommand}" CommandParameter="{Binding .}"/>
          </SwipeItems>
        </SwipeView.LeftItems>
        <SwipeView.RightItems>
          <SwipeItems Mode="Reveal">
            <SwipeItem Text="Delete" BackgroundColor="#FFCDD2"
                       Command="{Binding DeleteCommand}" CommandParameter="{Binding .}"/>
          </SwipeItems>
        </SwipeView.RightItems>
        <Grid Padding="12,8"><Label Text="{Binding BookName}"/></Grid>
      </SwipeView>
    </DataTemplate>
  </CollectionView.ItemTemplate>
</CollectionView>

3. Drag-and-drop reordering

This feature shows a clear difference in how each control handles item movement and user interaction:

a) Syncfusion .NET MAUI ListView

Reordering is built in. You can press, drag, and release; items move exactly where expected without juggling gestures or indexes, as shown in the code below.

<listView:SfListView x:Name="listView"
                      ItemsSource="{Binding BookInfo}"
                      DragStartMode="OnHold">

b) .NET MAUI CollectionView

You wire up drag/drop events, manage indices, and refresh grouping after reordering. Powerful, but more moving parts.

Here’s how you can do it in code:

<CollectionView x:Name="List"
                Grid.Row="1"
                ItemsSource="{Binding BookGroups}"
                IsGrouped="True"
                SelectionMode="Multiple"
                ItemSizingStrategy="MeasureFirstItem"
                Scrolled="OnCollectionViewScrolled">

    <CollectionView.ItemTemplate>
        <DataTemplate>
            <SwipeView>
                <Grid Padding="12" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,*" HeightRequest="70">
                    <Grid.GestureRecognizers>
                        <DropGestureRecognizer AllowDrop="True" DragOver="OnDragOver" Drop="OnDrop" />
                    </Grid.GestureRecognizers>

                    <!-- Drag handle icon -->
                    <Label Grid.RowSpan="2" Grid.Column="0" VerticalOptions="Center" Margin="0,0,8,0" Text="≡≡" FontSize="18" Opacity="0.6">
                        <Label.GestureRecognizers>
                            <DragGestureRecognizer CanDrag="True" DragStarting="OnDragStarting" DropCompleted="OnDropCompleted" />
                        </Label.GestureRecognizers>
                    </Label>

                    <Label Text="{Binding BookName}" Grid.Row="0" Grid.Column="1" FontAttributes="Bold" />
                    <Label Text="{Binding BookDescription}" Grid.Row="1" Grid.Column="1" Opacity="0.7" />
                </Grid>
            </SwipeView>
        </DataTemplate>
    </CollectionView.ItemTemplate>
</CollectionView>
private void OnDragStarting(object sender, DragStartingEventArgs e)
{
    if (sender is Element element && element.BindingContext is BookInfo item)
    {
        e.Data.Properties["item"] = item;
    }
}

private void OnDrop(object sender, DropEventArgs e)
{
    if (Vm == null) return;
    if (!e.Data.Properties.TryGetValue("item", out var payload) || payload is not BookInfo source)
        return;

    if (sender is Element element && element.BindingContext is BookInfo target && !ReferenceEquals(source, target))
    {
        var list = Vm.BookInfo;
        var sourceIndex = list.IndexOf(source);
        var targetIndex = list.IndexOf(target);
        if (sourceIndex >= 0 && targetIndex >= 0)
        {
            // Normalize target index when removing earlier item affects index
            if (sourceIndex < targetIndex)
                targetIndex--;

            list.RemoveAt(sourceIndex);
            list.Insert(targetIndex, source);
            // Update stable order indices and rebuild grouping to reflect new order in the UI
            Vm.ReindexOrders();
            Vm.RefreshGroups();
        }
    }
}

4. Incremental loading

Handling large datasets highlights how each control approaches infinite scrolling and data fetch efficiency:

a) Syncfusion .NET MAUI ListView

Infinite scrolling is simple. The control fetches more items as you near the end, with a built-in busy indicator. A single command controls when and how much to load.

Refer to the following code examples for feature implementation.

<listView:SfListView x:Name="listView"
                      ItemsSource="{Binding BookInfo}"
                      LoadMoreOption="Manual"
                      LoadMorePosition="End"
                      LoadMoreCommand="{Binding LoadMoreItemsCommand}"
                      LoadMoreCommandParameter="{Binding Source={x:Reference listView}}">
public ICommand LoadMoreItemsCommand { get; }

LoadMoreItemsCommand =
    new Command<object>(LoadMoreItems, CanLoadMoreItems);

private bool CanLoadMore() => BookInfo.Count < totalItems;

private bool CanLoadMoreItems(object obj) => CanLoadMore() && !IsLoading;

private async void LoadMoreItems(object obj)
{
    if (!CanLoadMore() || IsLoading)
    {
        return;
    }
    else
    {
        listView.IsLazyLoading = true;
        await Task.Delay(2000);
        listView.IsLazyLoading = false;
    }

    AddBooks(currentIndex, PageSize);
    currentIndex += PageSize;
}

b) .NET MAUI CollectionView

You implement a “Load More” footer and spinner, track remaining items, and manage progress state. Clear and explicit, but heavier on logic.

Here’s the code you need for quick implementation:

<CollectionView x:Name="List"
                 ItemsSource="{Binding BookGroups}">

    <!-- Manual Load More footer to mirror List View's LoadMoreOption=Manual at End -->
    <CollectionView.Footer>
        <Grid Padding="12" BackgroundColor="Transparent" HorizontalOptions="Center" RowDefinitions="Auto,Auto" ColumnDefinitions="Auto,Auto">
            <Button Text="Load More"
                    Grid.Row="0" Grid.ColumnSpan="2"
                    HorizontalOptions="Center"
                    IsEnabled="{Binding HasMoreItems}"
                    Command="{Binding LoadMoreItemsCommand}"
                    CommandParameter="{Binding Source={x:Reference List}}" />
            <ActivityIndicator Grid.Row="1" Grid.ColumnSpan="2"
                               IsVisible="{Binding IsLoading}"
                               IsRunning="{Binding IsLoading}" />
        </Grid>
    </CollectionView.Footer>
</CollectionView>
public ICommand LoadMoreItemsCommand { get; }

LoadMoreItemsCommand =
    new Command<object>(LoadMoreItems, CanLoadMoreItems);

private bool CanLoadMore() => BookInfo.Count < totalItems;

private bool CanLoadMoreItems(object obj) => CanLoadMore() && !IsLoading;

private async void LoadMoreItems(object obj)
{
    if (!CanLoadMore() || IsLoading)
    {
        return;
    }
    else
    {
        // CollectionView path uses the footer ActivityIndicator bound to IsLoading
        IsLoading = true;
        await Task.Delay(2000);
        IsLoading = false;
    }

    AddBooks(currentIndex, PageSize);
    currentIndex += PageSize;
    RebuildGroups();
}

5. Layout choices

Flexibility in presentation is another area where the two controls differ, especially when switching between list and grid views:

a) Syncfusion .NET MAUI ListView

Switch to a two-column grid with one setting, same control, same smooth virtualization.

Here’s the code you need:

<listView:SfListView.ItemsLayout>
    <listView:GridLayout SpanCount="2"/>
</listView:SfListView.ItemsLayout>

b) .NET MAUI CollectionView

Grid layouts are easy too, set the span and tweak spacing. Flexible, but needs more fine-tuning.

See the code snippet to achieve this:

<CollectionView.ItemsLayout>
    <GridItemsLayout Orientation="Vertical" Span="2"/>
</CollectionView.ItemsLayout>

6. Item sizing and virtualization

Efficient scrolling and memory use depend on how each control manages row heights and virtualization:

a) Syncfusion .NET MAUI ListView

Fixed row heights make long lists scroll effortlessly. Virtualization remains fast because sizes are known in advance, making it ideal for uniform feeds.

Code example for quick integration:

<listView:SfListView ItemsSource="{Binding BookInfo}" ItemSize="70">
  <listView:SfListView.ItemTemplate>
    <DataTemplate>
      <Grid HeightRequest="70" Padding="12,0" VerticalOptions="Center">
        <Label Text="{Binding BookName}" VerticalOptions="Center"/>
      </Grid>
    </DataTemplate>
  </listView:SfListView.ItemTemplate>
</listView:SfListView>

b) .NET MAUI CollectionView

Auto-measuring adapts well to mixed sizes but adds layout overhead as lists grow. Best for varied item heights when performance allows.

Here’s the complete code block:

<CollectionView ItemsSource="{Binding BookInfo}" ItemSizingStrategy="MeasureFirstItem">
  <CollectionView.ItemTemplate>
    <DataTemplate>
      <Grid HeightRequest="70" Padding="12,0" VerticalOptions="Center">
        <Label Text="{Binding BookName}" VerticalOptions="Center"/>
      </Grid>
    </DataTemplate>
  </CollectionView.ItemTemplate>
</CollectionView>

Watch these outputs to see how the above-mentioned features work in action:

Output for .NET MAUI CollectionView
Output for .NET MAUI CollectionView
Output for Syncfusion .NET MAUI ListView
Output for Syncfusion .NET MAUI ListView

Memory & performance: The silent differentiator

Features are easy to spot. Memory behavior isn’t, but users feel it.

To understand efficiency beyond features, this section compares how each control consumes memory under identical conditions using Visual Studio Diagnostics.

  • With 50 identical items and templates, Syncfusion .NET MAUI ListView reports peak process memory usage, allocation churn, and managed heap growth.
Profiler metric Syncfusion .NET MAUI ListView .NET MAUI CollectionView
Peak process memory  85 MB  119 MB
Allocations between snapshots  +23,761 objects  +1,139,714 objects
Managed heap growth between snapshots  +1,613 KB  +7,005 KB
Initial snapshot heap delta after warm-up  2,202 KB  2,212 KB

Refer to the following images.

Profiler memory usage in Syncfusion .NET MAUI List View
Profiler memory usage in Syncfusion .NET MAUI ListView
Profiler memory usage in .NET MAUI Collection View
Profiler memory usage in .NET MAUI Collection View

Usage summary

  • Peak memory: Syncfusion ListView uses ~34 MB less (~28% reduction).
  • Allocation churn: Syncfusion ListView allocates ~98% fewer objects (23,761 vs 1,139,714).
  • Managed heap growth while scrolling: Syncfusion ListView is ~77% lower (1,613 KB vs 7,005 KB).
  • Warm-up retained heap: Similar for both (~2.2 MB).

Syncfusion .NET MAUI List View is significantly more memory-efficient, especially in allocation churn and heap growth.

Note: Values are approximate and may vary by environment. These values are recorded on Windows 11 Enterprise 24H2 (build 26100.7462), .NET 10 SDK (10.0.100), .NET MAUI 10.0.1, Visual Studio 2026; hardware: AMD Ryzen 5 7530U, 16 GB RAM.

For a quick reference, visit:

GitHub reference

For more details, refer to how Syncfusion .NET MAUI ListView outshines .NET MAUI CollectionView GitHub demo.

Frequently Asked Questions

How do I implement incremental loading?

Implement incremental loading by using the LoadMoreOption and LoadMoreCommand methods in the Syncfusion .NET MAUI ListView. Display the built-in busy indicator while fetching items.

When should I use a fixed ItemSize?

Set ItemSize to a fixed value to achieve uniform row heights, reduce measurement costs, and improve scrolling performance with large datasets.

Can I switch between list and grid layouts without changing controls?

Yes. Present items in a grid by applying GridLayout with SpanCount in the same Syncfusion .NET MAUI ListView.

Is it hard to migrate from CollectionView to Syncfusion .NET MAUI ListView?

No. Move your ItemTemplate and bindings to the new ListView. Replace custom event wiring, such as swipe, drag, sticky headers, and load more, with the built-in properties and commands of the Syncfusion control. Preserve your MVVM structure.

Does Syncfusion .NET MAUI ListView support multiple selection?

Yes. Set SelectionMode to Multiple. Bind SelectedItems or SelectedItem as required.

How do I show an empty state when there is no data?

Use EmptyView or EmptyViewTemplate to show a placeholder when displaying an empty state.

Can I add headers and footers to the list?

Yes. Define HeaderTemplate and FooterTemplate for static or bound content as needed.

Supercharge your cross-platform apps with Syncfusion's robust .NET MAUI controls.

Collection View helps you start. Syncfusion .NET MAUI List View helps you scale.

Thank you for reading! In this post, we’ve compared .NET MAUI CollectionView with Syncfusion .NET MAUI ListView, focusing on how each handles real‑world needs. Through side‑by‑side examples, we’ve seen that CollectionView gets you started while ListView delivers the advanced features with built‑in properties, smooth virtualization, and MVVM‑friendly commands.

If your app depends on lists that are fast, interactive, and maintainable, Syncfusion .NET MAUI ListView helps you ship with confidence, saving time on wiring behaviors and letting you focus on the parts of your app that users notice most.

If you’re a Syncfusion user, you can download the latest setup from the license and downloads page. Otherwise, you can download a free 30-day trial.

You can contact us through our support forumsupport portal, or feedback portal for queries. We are always happy to assist you!

Read the whole story
alvinashcraft
41 seconds ago
reply
Pennsylvania, USA
Share this story
Delete

Free SQL Server Performance Monitoring: Lite Dashboard

1 Share

Free SQL Server Performance Monitoring: Lite Dashboard


Summary

In this video, I dive into the Lite dashboard of my free open-source monitoring tool, which has garnered significant attention with over 10,000 installs based on GitHub repo stats. I highlight its user-friendly nature, especially for consultants or those who can’t install software on client servers, as it allows you to collect a wide range of performance metrics without the need for a separate database. I also showcase how DuckDB, an embedded analytics database, powers the Lite dashboard, ensuring fast query performance and efficient storage through Parquet file compression, making it an ideal solution for monitoring Azure SQL databases and other environments.

Chapters

Full Transcript

ErikMonitoringToolMogulDarling here. I suppose my rates are reasonable for this free open source monitoring tool as well. So, you know, that’s nice. But I want to talk about the Lite dashboard a little bit more. This is far and away the most popular download based on GitHub repo stats. I do keep an eye on those because I care and I want to make sure that people are using this, getting stuff. I want to say that we’re very close to 10,000 installs, or at least that’s what it would seem like based on the numbers that I’ve seen over the past week or so. So that’s pretty exciting. Big monitoring. I’m coming for you. Coming for your customers. I’m going to eat you alive. Anyway, the Lite dashboard is fun because you don’t have to install anything. You just open it up, point it at a SQL Server, even Azure SQL database, and it’ll just start chewing away collecting data and making your performance tuning life easier. It’s got a lot of the same tabs and collectors that the full dashboard does, but it’s also got some kind of neat stuff internally.

And the thing that I really love about it is, you know, I’m a big fan of DuckDB. I think it’s one of the coolest things that’s happened in databases in a long time. And so what I wanted to, I wanted, when I was first working on this, you know, doing this sort of design spec, you know, almost anything where it’s like embedded database, you’re like, oh, SQLite. The thing is, I’m not doing SQLite work here. All right. I’m not doing tiny little transactional things. I’m like inserting bunches of rows and stuff. And then I need to like, you know, run reporting type queries off them. And DuckDB is a no brainer for that. So I have DuckDB embedded in this thing. And what I do is I load up DuckDB until it has about 512 megs in the tables. And then when it, as soon as I hit 512 megs, I archive all that stuff out to Parquet files, which have insane compression on them. And that helps to keep the, helps to control the size of the database over time. The Parquet files are very, very well compressed. It’s a very cool little thing that goes on in there.

So like I said, it’s got just about all the same tabs and whatnot that you would expect to see in the full dashboard. I tried to replicate as much of it as possible. But there are a few things that I’m just not quite keen to do, like run SP health parser to look at the system health extended event. And what’s the other one, I don’t set up a custom trace in this one. So there are a couple things that I left out of light just to make it a little bit lighter. But hopefully, the set of things that I collect in here is useful. enough for you that it sort of makes up for the few things that are missing. So like I said, there’s no system events tab. That’s SP health parse or looking at the system health event. I don’t look too much at the default trace stuff. But there are some very cool things that I do have in here that I think make this a unique sort of proposition. One is Azure SQL database support. The full dashboard obviously doesn’t work there because it creates a database and store procedures and all the other stuff. So it just can’t function with Azure SQL database. Whereas the light one absolutely can. But they both have the MCP servers built in those are opt in only so if you if you want to have one of your robot friends talk to you your performance data and just your performance data, you can have a chat away with the light database and that I think that’s even kind of in a way better than the full dashboard because it is confined to what has been collected in DuckDB in the parquet files and it doesn’t have to go like run any queries against the database where the data lives. Right. So like with the full dashboard, you have the performance monitor database and that’s collecting stuff. And the MCP servers talk to just that. But this is just talking to DuckDB wherever it lives. So that’s kind of neat. It has the same alerts and email notifications system tray stuff like that.

CSV export dark theme all the things that I have in there. My dear contributor Claudio added some light theme support recently. I haven’t used light themes yet because you know I’m a dark mode kind of guy but it’s cool that they’re in there just in case you want them. So DuckDB, I chose DuckDB because it’s an embedded analytics database. I don’t need a separate server process. It lives inside its own executable. There’s no external dependencies and it is by default columnar storage. So it’s a So all of the stuff that I put in there, all the queries are very fast against it. And like when they go out to parquet, there’s like almost no hit on query performance.

When it, when my date, when the, when the, my, my DuckDB, when your DuckDB database gets to be about 512 megs, I bump everything out to parquet files and like just sort of empty the tables out and start over again. Uh, and the compression on those parquet files is amazing. Uh, the footprint on them is very, very small. Um, it’s all very portable. Uh, you can, you know, take it wherever you want. And, uh, there’s no SQL Server dependency for storing historical data. It’s all captured within the executable.

So, uh, I’m going to show you a few things poking around the, um, the, uh, the light dashboard. Uh, this is primarily aimed at, um, you know, just anyone. I mean, it says consultants, but if you can, if you’re not allowed to install stuff on client servers, um, or like, you know, your servers at work and you don’t want to like have a database sitting on there collecting stuff. This is really perfect. Just a way to like spin it up, collect, collect a whole bunch of great performance metrics.

And especially if you’re on Azure SQL database, and this is the one that supports it. Uh, but you know, you can literally run it from anywhere. Uh, if you’re in a locked down environment, we really needs minimal permissions for stuff. So it makes life a lot easier. Um, uh, that’s not what I wanted. All right. Let’s go to the light dashboard. There we go. All right. So this is connected to SQL Server 2022.

Uh, like I said, it has just about all the stuff that you would want a dashboard to collect. Uh, you start off on the wait stats tab. Um, over here, we’ve got the list of servers and their sort of general, um, you know, presence in the world. Uh, we’ve got an overview tab showing all the usual stuff where, uh, you know, what CPU is, you know, if there’s any blocking or deadlocking, what memory’s up to, uh, if there are any alerts that have been fired off.

I don’t think I’ve had any alerts recently. Um, I’m working on this FinOps tab because, uh, a lot of people have been like sort of behind the scenes asking me about this because apparently a lot of managers and bosses really care about this. So I’m trying to get this stuff going, uh, so that, um, I can sort of make, uh, on top of it being a free monitoring tool, uh, it can be something that points you to where you can save money, especially with Azure SQL database cloud stuff. Um, if things are, you know, over provisioned, under provisioned, all that.

So, uh, I’m working on this, uh, it’s a work in progress. Uh, it’ll be out soon, but it’s, you know, gonna be a few versions before I think it really finds its footing, but I got to start somewhere so I can get some feedback and start getting some, uh, real sort of real, um, you know, scenarios from it other than what’s going on in my local test servers. But just, just to make sure it all works, we get this stuff first, but anyway, we get weight stats.

Uh, again, it’s all the weights that, uh, we, that we look at in the same way as the full dashboard. So like your top 10 weights, plus the sort of usual suspects, plus the poison weights. So if anything terrible is happening, you can see it pretty quickly.

Uh, under the queries tabs, again, I’m such a developer. I don’t have any stored procedures running, but, uh, you know, you sort of get a look at query duration from, uh, the plan cache, from query store, and then, uh, executions is what’s behind me.

Um, you can get an active queries tab. This is not run SP who is active. This just runs something. This just runs a simple script from, uh, SP pressure detector, uh, to get a whole bunch of information about what’s currently running on the server.

Uh, there’s a top queries by duration. And, you know, just like with everything else, um, you know, you can grab the execution plans, you can see the queries, you can open them in the plan viewer. Um, you know, everything that you would come to expect from other, other, other parts of the tool.

And then there’s the same thing for query store. Um, the plan viewer, I’ve talked about that in a few videos. Um, we just say view plan that pops up in plan viewer and you get, you know, sort of, you get the, this is just the estimated execution plan for this, which is not terribly interesting, but you know, that’s okay.

Uh, estimated plans are not meant for real, real use anyway. Uh, we have a CPU tab that will show, uh, CPU usage, um, sort of, you know, over time, just, you know, same thing as in full. Uh, we have a memory usage tab up here, uh, where we have the overview.

We have memory clerks. We have memory grants, uh, file IO, uh, broken down, uh, first by latency, right? So we have read latency and write latency, and then we have throughput numbers.

So, uh, latency is time and throughput is like data. So these, these are all measured in megabytes and you can see like over time, just sort of, you know, when things spiked up and when you might want to take a look at things. Uh, we have a temp DB tab, of course, uh, we have blocking and deadlocking and all the other stuff.

Uh, I don’t think there’s really been any on this system lately. We have some lock weights, but nothing has really been getting jammed up. Uh, so this section is not going to be terribly interesting right here, but, you know, on your systems, it might be much more interesting.

Uh, we collect perfmon counters, just like in the full dashboard. Uh, we collect information about running jobs. Uh, I tell you about your server, database, server and database configuration, database scope configurations, and the active trace flags.

Uh, sort of a daily summary of what’s been going on on the server. For this server, it’s been pretty quiet today because I’ve been working on other stuff and, you know, trying to get ready to record these videos. And then finally, there is a collection health tab and the collection health tab is nice because this will tell you, um, if, uh, anything has been going wrong with, um, your servers.

When thing, when, when the collector has been running now, you’ll notice that there are a bunch of errors in here. And that’s because I had a wifi blip earlier and everything went weird for a minute. So the, but the wifi blip has since been, uh, since been fixed.

So you can just ignore these errors for now. Uh, there’s also a collection log that has sort of like, um, more specific breakdown of, um, like when things failed and all that other stuff. And then, uh, a duration trends tab, which will tell you, uh, which collectors, uh, sort of take the longest.

And if you look at this one here, apparently our query store query could use a bit of tuning. Cause at times it takes 1.5 seconds to grab that query store data. So I’ll be having a look at that when I’m done recording this video.

Anyway, this is just a short tour of the light dashboard. Uh, we’re going to go more in depth on, uh, other parts of this over the next couple of videos. But, uh, anyway, um, again, this is all at code.erikdarling.com.

Let me get to a slide where that actually shows up. There we go. That’s wonderful. Uh, code.erikdarling.com. Uh, it’s totally free open source.

Uh, it doesn’t do anything weird or funky. Um, you know, no requirements on email, no phone home. No telemetry, nothing like that. Uh, just wanted to put something kind of cool out there in the world to help you find folks, uh, with your SQL Server performance tuning efforts.

Anyway, thank you for watching and I will see you over in tomorrow’s video. All right. Goodbye.

Going Further


If this is the kind of SQL Server stuff you love learning about, you’ll love my training. Blog readers get 25% off the Everything Bundle — over 100 hours of performance tuning content. Need hands-on help? I offer consulting engagements from targeted investigations to ongoing retainers. Want a quick sanity check before committing to a full engagement? Schedule a call — no commitment required.

The post Free SQL Server Performance Monitoring: Lite Dashboard appeared first on Darling Data.

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