Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Contributing to SQL Performance Studio
# Contributing to Performance Studio

Thank you for your interest in contributing to SQL Performance Studio! This guide will help you get started.
Thank you for your interest in contributing to Performance Studio! This guide will help you get started.

## Reporting Issues

Expand Down
57 changes: 50 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# SQL Performance Studio
# Performance Studio

A cross-platform SQL Server execution plan analyzer. Parses `.sqlplan` XML, identifies performance problems, suggests missing indexes, and provides actionable warnings — from the command line or a desktop GUI.
A cross-platform SQL Server execution plan analyzer with built-in MCP server for AI-assisted analysis. Parses `.sqlplan` XML, identifies performance problems, suggests missing indexes, and provides actionable warnings — from the command line or a desktop GUI.

Built for developers and DBAs who want fast, automated plan analysis without clicking through SSMS.

Expand Down Expand Up @@ -139,7 +139,7 @@ planview analyze ./queries/ --server sql2022 --database StackOverflow2013 \
```

Batch mode produces three files per query:
- `query_name.sqlplan` — the raw execution plan XML (openable in SSMS or the SQL Performance Studio GUI)
- `query_name.sqlplan` — the raw execution plan XML (openable in SSMS or the Performance Studio GUI)
- `query_name.analysis.json` — structured analysis with warnings, missing indexes, and operator tree
- `query_name.analysis.txt` — human-readable text report

Expand Down Expand Up @@ -240,6 +240,7 @@ Features:
- **Copy Repro Script** — extracts parameters, SET options, and query text into a runnable `sp_executesql` script
- **Get Actual Plan** — connect to a server and re-execute the query to capture runtime stats
- **Query Store Analysis** — connect to a server and analyze top queries by CPU, duration, or reads
- **MCP Server** — built-in Model Context Protocol server for AI-assisted plan analysis (opt-in)
- Dark theme

```bash
Expand All @@ -248,14 +249,14 @@ dotnet run --project src/PlanViewer.App

## SSMS Extension

A VSIX extension that adds **"Open in SQL Performance Studio"** to the execution plan right-click context menu in SSMS 18-22.
A VSIX extension that adds **"Open in Performance Studio"** to the execution plan right-click context menu in SSMS 18-22.

### How it works

1. Right-click on any execution plan in SSMS
2. Click "Open in SQL Performance Studio"
2. Click "Open in Performance Studio"
3. The extension extracts the plan XML via reflection and saves it to a temp file
4. SQL Performance Studio opens with the plan loaded
4. Performance Studio opens with the plan loaded

### Installation

Expand All @@ -267,13 +268,55 @@ A VSIX extension that adds **"Open in SQL Performance Studio"** to the execution

### First run

On first use, if SQL Performance Studio isn't found automatically, the extension will prompt you to locate `PlanViewer.App.exe`. The path is saved to the registry (`HKCU\SOFTWARE\DarlingData\SQLPerformanceStudio\InstallPath`) so you only need to do this once.
On first use, if Performance Studio isn't found automatically, the extension will prompt you to locate `PlanViewer.App.exe`. The path is saved to the registry (`HKCU\SOFTWARE\DarlingData\SQLPerformanceStudio\InstallPath`) so you only need to do this once.

The extension searches for the app in this order:
1. Registry key (set automatically after first browse)
2. System PATH
3. Common install locations (`%LOCALAPPDATA%\Programs\SQLPerformanceStudio\`, `Program Files`, etc.)

## MCP Server (LLM Integration)

The desktop GUI includes an embedded [Model Context Protocol](https://modelcontextprotocol.io) server that exposes loaded execution plans and Query Store data to LLM clients like Claude Code and Cursor.

### Setup

1. Enable the MCP server in `~/.planview/settings.json`:

```json
{
"mcp_enabled": true,
"mcp_port": 5152
}
```

2. Register with Claude Code:

```
claude mcp add --transport streamable-http --scope user performance-studio http://localhost:5152/
```

3. Open a new Claude Code session and ask questions like:
- "What plans are loaded in the application?"
- "Analyze the execution plan and tell me what's wrong"
- "Are there any missing index suggestions?"
- "Compare these two plans — which is better?"
- "Fetch the top 10 queries by CPU from Query Store"

### Available Tools

13 tools for plan analysis and Query Store data:

| Category | Tools |
|---|---|
| Discovery | `list_plans`, `get_connections` |
| Plan Analysis | `analyze_plan`, `get_plan_summary`, `get_plan_warnings`, `get_missing_indexes`, `get_plan_parameters`, `get_expensive_operators`, `get_plan_xml`, `compare_plans`, `get_repro_script` |
| Query Store | `check_query_store`, `get_query_store_top` |

Plan analysis tools work on plans loaded in the app (via file open, paste, query execution, or Query Store fetch). Query Store tools use a built-in read-only DMV query — no arbitrary SQL can be executed.

The MCP server binds to `localhost` only and does not accept remote connections. Disabled by default.

## Project Structure

```
Expand Down
4 changes: 2 additions & 2 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Reporting a Vulnerability

If you discover a security vulnerability in SQL Performance Studio, please report it responsibly.
If you discover a security vulnerability in Performance Studio, please report it responsibly.

**Do not open a public GitHub issue for security vulnerabilities.**

Expand All @@ -26,7 +26,7 @@ This policy applies to:

## Security Best Practices

When using SQL Performance Studio:
When using Performance Studio:

- Use Windows Authentication where possible when connecting to SQL Server
- Use dedicated accounts with minimal required permissions
Expand Down
26 changes: 21 additions & 5 deletions src/PlanViewer.App/AboutWindow.axaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PlanViewer.App.AboutWindow"
Title="About SQL Performance Studio"
Width="450" Height="340"
Title="About Performance Studio"
Width="450" Height="420"
CanResize="False"
WindowStartupLocation="CenterOwner"
Icon="avares://PlanViewer.App/EDD.ico"
Expand All @@ -11,7 +11,7 @@
<Grid Margin="24" RowDefinitions="Auto,Auto,Auto,Auto,*,Auto">

<!-- Title -->
<TextBlock Grid.Row="0" Text="SQL Performance Studio" FontWeight="Bold" FontSize="20"
<TextBlock Grid.Row="0" Text="Performance Studio" FontWeight="Bold" FontSize="20"
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,4"/>
<TextBlock Grid.Row="1" x:Name="VersionText" Text="Version 0.3.0" FontSize="12"
Foreground="{DynamicResource ForegroundMutedBrush}" Margin="0,0,0,4"/>
Expand Down Expand Up @@ -46,8 +46,24 @@
TextDecorations="Underline"/>
</StackPanel>

<!-- Spacer -->
<Panel Grid.Row="4"/>
<!-- MCP Settings -->
<StackPanel Grid.Row="4" Margin="0,0,0,12">
<TextBlock Text="Settings" FontWeight="SemiBold" FontSize="13"
Foreground="{DynamicResource ForegroundBrush}" Margin="0,0,0,8"/>
<StackPanel Orientation="Horizontal" Spacing="12">
<CheckBox x:Name="McpEnabledCheckBox" Content="Enable MCP Server"
FontSize="12" VerticalContentAlignment="Center"
Foreground="{DynamicResource ForegroundBrush}"/>
<TextBlock Text="Port:" FontSize="12" VerticalAlignment="Center"
Foreground="{DynamicResource ForegroundBrush}"/>
<TextBox x:Name="McpPortInput" Width="70" Height="28"
Text="5152" FontSize="12" Padding="6,2"
VerticalContentAlignment="Center"/>
</StackPanel>
<TextBlock Text="Restart the application after changing MCP settings."
FontSize="11" Foreground="{DynamicResource ForegroundMutedBrush}"
Margin="0,4,0,0"/>
</StackPanel>

<!-- Close -->
<Button Grid.Row="5" Content="Close" Click="CloseButton_Click"
Expand Down
31 changes: 30 additions & 1 deletion src/PlanViewer.App/AboutWindow.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
/*
* SQL Performance Studio — SQL Server Execution Plan Analyzer
* Performance Studio — SQL Server Execution Plan Analyzer
* Copyright (c) 2026 Erik Darling, Darling Data LLC
* Licensed under the MIT License - see LICENSE file for details
*/

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text.Json;
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Interactivity;
using PlanViewer.App.Mcp;

namespace PlanViewer.App;

Expand All @@ -25,6 +29,31 @@ public AboutWindow()
var version = Assembly.GetExecutingAssembly().GetName().Version;
if (version != null)
VersionText.Text = $"Version {version.Major}.{version.Minor}.{version.Build}";

// Load current MCP settings
var settings = McpSettings.Load();
McpEnabledCheckBox.IsChecked = settings.Enabled;
McpPortInput.Text = settings.Port.ToString();

// Save on change
McpEnabledCheckBox.IsCheckedChanged += (_, _) => SaveMcpSettings();
McpPortInput.LostFocus += (_, _) => SaveMcpSettings();
}

private void SaveMcpSettings()
{
var settingsDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".planview");
var settingsFile = Path.Combine(settingsDir, "settings.json");

var json = JsonSerializer.Serialize(new
{
mcp_enabled = McpEnabledCheckBox.IsChecked == true,
mcp_port = int.TryParse(McpPortInput.Text, out var p) && p >= 1024 && p <= 65535 ? p : 5152
}, new JsonSerializerOptions { WriteIndented = true });

Directory.CreateDirectory(settingsDir);
File.WriteAllText(settingsFile, json);
}

private void GitHubLink_Click(object? sender, PointerPressedEventArgs e) => OpenUrl(GitHubUrl);
Expand Down
4 changes: 2 additions & 2 deletions src/PlanViewer.App/App.axaml
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="PlanViewer.App.App"
Name="SQL Performance Studio"
Name="Performance Studio"
RequestedThemeVariant="Dark">

<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="About SQL Performance Studio" Click="OnAboutClicked" />
<NativeMenuItem Header="About Performance Studio" Click="OnAboutClicked" />
</NativeMenu>
</NativeMenu.Menu>

Expand Down
4 changes: 2 additions & 2 deletions src/PlanViewer.App/Controls/QuerySessionControl.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -607,7 +607,7 @@ private void ShowAdviceWindow(string title, string content)

var window = new Window
{
Title = $"SQL Performance Studio — {title}",
Title = $"Performance Studio — {title}",
Width = 700,
Height = 600,
MinWidth = 400,
Expand Down Expand Up @@ -1119,7 +1119,7 @@ otherwise fall back to the currently selected database */
database,
planXml,
isolationLevel: null,
source: "SQL Performance Studio",
source: "Performance Studio",
isAzureSqlDb: IsAzureConnection);

try
Expand Down
4 changes: 2 additions & 2 deletions src/PlanViewer.App/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
<plist version="1.0">
<dict>
<key>CFBundleName</key>
<string>SQL Performance Studio</string>
<string>Performance Studio</string>
<key>CFBundleDisplayName</key>
<string>SQL Performance Studio</string>
<string>Performance Studio</string>
<key>CFBundleIdentifier</key>
<string>com.darlingdata.sqlperformancestudio</string>
<key>CFBundleVersion</key>
Expand Down
6 changes: 3 additions & 3 deletions src/PlanViewer.App/MainWindow.axaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:PlanViewer.App.Controls"
x:Class="PlanViewer.App.MainWindow"
Title="SQL Performance Studio — SQL Server Execution Plan Analyzer"
Title="Performance Studio — SQL Server Execution Plan Analyzer"
Width="1280" Height="800"
MinWidth="640" MinHeight="480"
WindowState="Maximized"
Expand All @@ -25,7 +25,7 @@
InputGesture="Alt+F4"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Header="_About SQL Performance Studio" Click="About_Click"/>
<MenuItem Header="_About Performance Studio" Click="About_Click"/>
<Separator/>
<MenuItem x:Name="McpStatusMenuItem" Header="MCP Server: Off" IsEnabled="False"/>
</MenuItem>
Expand All @@ -40,7 +40,7 @@
<Border x:Name="EmptyOverlay" Background="{DynamicResource BackgroundBrush}"
IsVisible="True" IsHitTestVisible="False">
<StackPanel VerticalAlignment="Center" HorizontalAlignment="Center" Spacing="8">
<TextBlock Text="SQL Performance Studio" FontSize="28" FontWeight="Bold"
<TextBlock Text="Performance Studio" FontSize="28" FontWeight="Bold"
Foreground="{DynamicResource ForegroundBrush}"
HorizontalAlignment="Center"/>
<TextBlock Text="Ctrl+N to open a new query session"
Expand Down
6 changes: 3 additions & 3 deletions src/PlanViewer.App/MainWindow.axaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -496,7 +496,7 @@ private DockPanel CreatePlanTabContent(PlanViewerControl viewer)

var reproScript = ReproScriptBuilder.BuildReproScript(
queryText, database, planXml,
isolationLevel: null, source: "SQL Performance Studio");
isolationLevel: null, source: "Performance Studio");

var clipboard = this.Clipboard;
if (clipboard != null)
Expand Down Expand Up @@ -597,7 +597,7 @@ private void ShowAdviceWindow(string title, string content)

var window = new Window
{
Title = $"SQL Performance Studio — {title}",
Title = $"Performance Studio — {title}",
Width = 700,
Height = 600,
MinWidth = 400,
Expand Down Expand Up @@ -1059,7 +1059,7 @@ private void ShowError(string message)
{
var dialog = new Window
{
Title = "SQL Performance Studio",
Title = "Performance Studio",
Width = 450,
Height = 200,
WindowStartupLocation = WindowStartupLocation.CenterOwner,
Expand Down
2 changes: 1 addition & 1 deletion src/PlanViewer.App/Mcp/McpHostService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
options.ServerInfo = new()
{
Name = "SQLPerformanceStudio",
Name = "PerformanceStudio",
Version = "0.7.0"
};
options.ServerInstructions = McpInstructions.Text;
Expand Down
6 changes: 3 additions & 3 deletions src/PlanViewer.App/Mcp/McpInstructions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ namespace PlanViewer.App.Mcp;
internal static class McpInstructions
{
public const string Text = """
You are connected to SQL Performance Studio, a SQL Server execution plan analyzer.
You are connected to Performance Studio, a SQL Server execution plan analyzer.

## CRITICAL: Read-Only Access

Expand Down Expand Up @@ -104,9 +104,9 @@ 4. Use plan analysis tools above with the returned session_ids
```json
{
"mcpServers": {
"sql-performance-studio": {
"performance-studio": {
"type": "streamable-http",
"url": "http://localhost:5152/mcp"
"url": "http://localhost:5152/"
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/PlanViewer.App/PlanViewer.App.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
<Version>0.7.0</Version>
<Authors>Erik Darling</Authors>
<Company>Darling Data LLC</Company>
<Product>SQL Performance Studio</Product>
<Product>Performance Studio</Product>
<Copyright>Copyright (c) 2026 Erik Darling, Darling Data LLC</Copyright>
</PropertyGroup>

Expand Down