Skip to content

Commit e5bb09f

Browse files
committed
Refactor format_dax tool
1 parent 03d95dc commit e5bb09f

4 files changed

Lines changed: 128 additions & 128 deletions

File tree

src/Dax.Formatter.McpServer/Dax.Formatter.McpServer.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<OutputType>Exe</OutputType>
@@ -12,8 +12,8 @@
1212
</PropertyGroup>
1313

1414
<ItemGroup>
15-
<PackageReference Include="ModelContextProtocol" Version="0.3.0-preview.4" />
16-
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.1" />
15+
<PackageReference Include="ModelContextProtocol" Version="1.2.0" />
16+
<PackageReference Include="Microsoft.Extensions.Hosting" Version="10.0.7" />
1717
</ItemGroup>
1818

1919
<ItemGroup>

src/Dax.Formatter.McpServer/DaxFormatterTools.cs

Lines changed: 0 additions & 123 deletions
This file was deleted.
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
namespace Dax.Formatter.McpServer;
2+
3+
using Dax.Formatter;
4+
using Dax.Formatter.Models;
5+
using ModelContextProtocol.Server;
6+
using System.ComponentModel;
7+
using System.Reflection;
8+
9+
[McpServerToolType]
10+
internal static class FormatDaxTool
11+
{
12+
[McpServerTool(
13+
Name = "format_dax",
14+
Title = "Format DAX expressions",
15+
ReadOnly = true,
16+
Idempotent = true,
17+
OpenWorld = true,
18+
Destructive = false)]
19+
[Description("""
20+
Formats DAX (Data Analysis Expressions) code. DAX comments are preserved. Supports both DAX queries and DAX expressions.
21+
22+
WHEN TO CALL:
23+
- The user asks to format, beautify, prettify or normalize DAX code.
24+
25+
RETURNS:
26+
An array of responses, one per input in the same order:
27+
{
28+
Formatted: string,
29+
Errors: [{ Line: int, Column: int, Message: string }]
30+
}
31+
A format failure (invalid DAX) results in null `Formatted` and a non-empty `Errors` array.
32+
A successful format results in a non-null `Formatted` and an empty or null `Errors` array.
33+
34+
NOTE:
35+
This tool calls an external HTTP service. Always batch multiple snippets
36+
in a single call — looping the tool wastes round-trips and is rate-limited upstream.
37+
""")]
38+
public static async Task<DaxFormatterResponse[]> FormatDax(
39+
IDaxFormatterClient client,
40+
[Description("""
41+
The DAX expressions to format. Each array element must be a complete,
42+
independent piece of DAX code — either a DAX query or a DAX expression.
43+
""")]
44+
string[] expressions,
45+
[Description("List separator character.")]
46+
char listSeparator = ',',
47+
[Description("Decimal separator character.")]
48+
char decimalSeparator = '.',
49+
[Description("""
50+
Controls how arguments and sub-expressions are wrapped across lines.
51+
- 'LongLine': keeps arguments compact horizontally where readable.
52+
- 'ShortLine': breaks each argument onto its own line for maximum vertical clarity.
53+
""")]
54+
DaxFormatterLineStyle lineStyle = DaxFormatterLineStyle.LongLine,
55+
[Description($"""
56+
Controls spacing between function names and their opening parentheses.
57+
- 'SpaceAfterFunction': adds a space for readability, e.g. 'SUM (x)'.
58+
- 'NoSpaceAfterFunction': removes the space for compactness, e.g. 'SUM(x)'.
59+
""")]
60+
DaxFormatterSpacingStyle spacingStyle = DaxFormatterSpacingStyle.SpaceAfterFunction,
61+
CancellationToken cancellationToken = default)
62+
{
63+
ArgumentNullException.ThrowIfNull(expressions);
64+
65+
if (expressions.Length == 0)
66+
return [];
67+
68+
var request = new DaxFormatterMultipleRequest();
69+
{
70+
// Set formatting options
71+
request.DecimalSeparator = decimalSeparator;
72+
request.ListSeparator = listSeparator;
73+
request.MaxLineLength = lineStyle;
74+
request.SkipSpaceAfterFunctionName = spacingStyle;
75+
// Add caller info
76+
request.CallerApp = "Dax.Formatter.McpServer";
77+
request.CallerVersion = typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion;
78+
}
79+
request.Dax.AddRange(expressions);
80+
81+
var responses = await client.FormatAsync(request, cancellationToken).ConfigureAwait(false);
82+
return [.. responses];
83+
}
84+
}

src/Dax.Formatter.McpServer/Program.cs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,56 @@
22
using Microsoft.Extensions.DependencyInjection;
33
using Microsoft.Extensions.Hosting;
44
using Microsoft.Extensions.Logging;
5+
using System.Reflection;
56

67
var builder = Host.CreateApplicationBuilder(args);
78

89
builder.Logging.ClearProviders();
910
builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
1011

1112
builder.Services.AddSingleton<IDaxFormatterClient, DaxFormatterClient>();
12-
1313
builder.Services
14-
.AddMcpServer()
14+
.AddMcpServer(ConfigureMcpServerOptions)
1515
.WithStdioServerTransport()
1616
.WithToolsFromAssembly();
1717

1818
await builder.Build().RunAsync();
19+
20+
static void ConfigureMcpServerOptions(ModelContextProtocol.Server.McpServerOptions options)
21+
{
22+
options.ServerInfo = new ModelContextProtocol.Protocol.Implementation
23+
{
24+
Name = "dax-formatter-mcp",
25+
Version = typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>()?.InformationalVersion ?? "0.0.0",
26+
WebsiteUrl = "https://www.daxformatter.com"
27+
};
28+
options.ServerInstructions = """
29+
# DAX Formatter MCP Server
30+
31+
## Core Purpose
32+
33+
This server formats DAX (Data Analysis Expressions) source code for Microsoft
34+
Power BI, Analysis Services, and Tabular models. It is the canonical wrapper
35+
for the SQLBI daxformatter.com web service.
36+
37+
## Strict Behavioral Rules
38+
39+
### 1. Tool Selection
40+
41+
- No bypass: when DAX needs formatting, you MUST use this server's tools.
42+
NEVER call the daxformatter.com HTTP endpoint directly.
43+
This server is the single canonical channel for DAX formatting.
44+
45+
### 2. Code Integrity
46+
47+
- Pass input as-is: the user's DAX is the authoritative source. You MUST
48+
send it to the tool exactly as provided without altering the string in any way.
49+
You MAY propose or apply changes only when the user has explicitly asked you to do so.
50+
51+
### 3. Surface Errors
52+
53+
- Relay verbatim: if the formatter reports errors, you MUST pass them to
54+
the user exactly as received including all details. They are diagnostic
55+
information the user needs to fix the code.
56+
""";
57+
}

0 commit comments

Comments
 (0)