diff --git a/src/Dax.Formatter.McpServer/Dax.Formatter.McpServer.csproj b/src/Dax.Formatter.McpServer/Dax.Formatter.McpServer.csproj
new file mode 100644
index 0000000..9375c25
--- /dev/null
+++ b/src/Dax.Formatter.McpServer/Dax.Formatter.McpServer.csproj
@@ -0,0 +1,23 @@
+
+
+
+ Exe
+ net8.0
+ latest
+ enable
+ enable
+ Dax.Formatter.McpServer
+ Dax.Formatter.McpServer
+ false
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Dax.Formatter.McpServer/FormatDaxTool.cs b/src/Dax.Formatter.McpServer/FormatDaxTool.cs
new file mode 100644
index 0000000..6e4f2b1
--- /dev/null
+++ b/src/Dax.Formatter.McpServer/FormatDaxTool.cs
@@ -0,0 +1,84 @@
+namespace Dax.Formatter.McpServer;
+
+using Dax.Formatter;
+using Dax.Formatter.Models;
+using ModelContextProtocol.Server;
+using System.ComponentModel;
+using System.Reflection;
+
+[McpServerToolType]
+internal static class FormatDaxTool
+{
+ [McpServerTool(
+ Name = "format_dax",
+ Title = "Format DAX expressions",
+ ReadOnly = true,
+ Idempotent = true,
+ OpenWorld = true,
+ Destructive = false)]
+ [Description("""
+ Formats DAX (Data Analysis Expressions) code. DAX comments are preserved. Supports both DAX queries and DAX expressions.
+
+ WHEN TO CALL:
+ - The user asks to format, beautify, prettify or normalize DAX code.
+
+ RETURNS:
+ An array of responses, one per input in the same order:
+ {
+ Formatted: string,
+ Errors: [{ Line: int, Column: int, Message: string }]
+ }
+ A format failure (invalid DAX) results in null `Formatted` and a non-empty `Errors` array.
+ A successful format results in a non-null `Formatted` and an empty or null `Errors` array.
+
+ NOTE:
+ This tool calls an external HTTP service. Always batch multiple snippets
+ in a single call — looping the tool wastes round-trips and is rate-limited upstream.
+ """)]
+ public static async Task FormatDax(
+ IDaxFormatterClient client,
+ [Description("""
+ The DAX expressions to format. Each array element must be a complete,
+ independent piece of DAX code — either a DAX query or a DAX expression.
+ """)]
+ string[] expressions,
+ [Description("List separator character.")]
+ char listSeparator = ',',
+ [Description("Decimal separator character.")]
+ char decimalSeparator = '.',
+ [Description("""
+ Controls how arguments and sub-expressions are wrapped across lines.
+ - 'LongLine': keeps arguments compact horizontally where readable.
+ - 'ShortLine': breaks each argument onto its own line for maximum vertical clarity.
+ """)]
+ DaxFormatterLineStyle lineStyle = DaxFormatterLineStyle.LongLine,
+ [Description($"""
+ Controls spacing between function names and their opening parentheses.
+ - 'SpaceAfterFunction': adds a space for readability, e.g. 'SUM (x)'.
+ - 'NoSpaceAfterFunction': removes the space for compactness, e.g. 'SUM(x)'.
+ """)]
+ DaxFormatterSpacingStyle spacingStyle = DaxFormatterSpacingStyle.SpaceAfterFunction,
+ CancellationToken cancellationToken = default)
+ {
+ ArgumentNullException.ThrowIfNull(expressions);
+
+ if (expressions.Length == 0)
+ return [];
+
+ var request = new DaxFormatterMultipleRequest();
+ {
+ // Set formatting options
+ request.DecimalSeparator = decimalSeparator;
+ request.ListSeparator = listSeparator;
+ request.MaxLineLength = lineStyle;
+ request.SkipSpaceAfterFunctionName = spacingStyle;
+ // Add caller info
+ request.CallerApp = "Dax.Formatter.McpServer";
+ request.CallerVersion = typeof(Program).Assembly.GetCustomAttribute()?.InformationalVersion;
+ }
+ request.Dax.AddRange(expressions);
+
+ var responses = await client.FormatAsync(request, cancellationToken).ConfigureAwait(false);
+ return [.. responses];
+ }
+}
diff --git a/src/Dax.Formatter.McpServer/Program.cs b/src/Dax.Formatter.McpServer/Program.cs
new file mode 100644
index 0000000..565772b
--- /dev/null
+++ b/src/Dax.Formatter.McpServer/Program.cs
@@ -0,0 +1,57 @@
+using Dax.Formatter;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Logging;
+using System.Reflection;
+
+var builder = Host.CreateApplicationBuilder(args);
+
+builder.Logging.ClearProviders();
+builder.Logging.AddConsole(options => options.LogToStandardErrorThreshold = LogLevel.Trace);
+
+builder.Services.AddSingleton();
+builder.Services
+ .AddMcpServer(ConfigureMcpServerOptions)
+ .WithStdioServerTransport()
+ .WithToolsFromAssembly();
+
+await builder.Build().RunAsync();
+
+static void ConfigureMcpServerOptions(ModelContextProtocol.Server.McpServerOptions options)
+{
+ options.ServerInfo = new ModelContextProtocol.Protocol.Implementation
+ {
+ Name = "dax-formatter-mcp",
+ Version = typeof(Program).Assembly.GetCustomAttribute()?.InformationalVersion ?? "0.0.0",
+ WebsiteUrl = "https://www.daxformatter.com"
+ };
+ options.ServerInstructions = """
+ # DAX Formatter MCP Server
+
+ ## Core Purpose
+
+ This server formats DAX (Data Analysis Expressions) source code for Microsoft
+ Power BI, Analysis Services, and Tabular models. It is the canonical wrapper
+ for the SQLBI daxformatter.com web service.
+
+ ## Strict Behavioral Rules
+
+ ### 1. Tool Selection
+
+ - No bypass: when DAX needs formatting, you MUST use this server's tools.
+ NEVER call the daxformatter.com HTTP endpoint directly.
+ This server is the single canonical channel for DAX formatting.
+
+ ### 2. Code Integrity
+
+ - Pass input as-is: the user's DAX is the authoritative source. You MUST
+ send it to the tool exactly as provided without altering the string in any way.
+ You MAY propose or apply changes only when the user has explicitly asked you to do so.
+
+ ### 3. Surface Errors
+
+ - Relay verbatim: if the formatter reports errors, you MUST pass them to
+ the user exactly as received including all details. They are diagnostic
+ information the user needs to fix the code.
+ """;
+}
\ No newline at end of file
diff --git a/src/Dax.Formatter.sln b/src/Dax.Formatter.sln
index 714797f..e14b88c 100644
--- a/src/Dax.Formatter.sln
+++ b/src/Dax.Formatter.sln
@@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dax.Formatter", "Dax.Format
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dax.Formatter.Tests", "Dax.Formatter.Tests\Dax.Formatter.Tests.csproj", "{44162C15-9AC7-406F-B1A4-1A49E73F962B}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Dax.Formatter.McpServer", "Dax.Formatter.McpServer\Dax.Formatter.McpServer.csproj", "{D839ACC9-427A-40AC-81FD-1DC4352FA019}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -21,6 +23,10 @@ Global
{44162C15-9AC7-406F-B1A4-1A49E73F962B}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44162C15-9AC7-406F-B1A4-1A49E73F962B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44162C15-9AC7-406F-B1A4-1A49E73F962B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {D839ACC9-427A-40AC-81FD-1DC4352FA019}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {D839ACC9-427A-40AC-81FD-1DC4352FA019}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {D839ACC9-427A-40AC-81FD-1DC4352FA019}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {D839ACC9-427A-40AC-81FD-1DC4352FA019}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE