AXSG now exposes a unified MCP-oriented remote API across workspace tooling, runtime hot reload and hot design services, and preview orchestration. This guide explains which host to run, how it relates to dotnet watch, and how it fits with the VS Code extension.
| Host | How you run it | Best for | Live runtime state | Subscribe support |
|---|---|---|---|---|
| Workspace MCP host | axsg-mcp --workspace /path/to/repo |
workspace queries, preview project resolution, agent/tool integration outside the app process | no | no |
| Runtime MCP host | embed XamlSourceGenRuntimeMcpServer into the running Avalonia app |
hot reload, hot design, studio, watched app inspection | yes | yes |
| Preview MCP host | dotnet run --project src/XamlToCSharpGenerator.PreviewerHost -- --mcp |
preview lifecycle control for custom clients and test harnesses | preview-session state only | yes |
| VS Code extension | install the VSIX and use normal AXSG commands | packaged editing and preview experience | extension-owned | n/a |
The key distinction is process ownership:
Install the tool:
dotnet tool install --global XamlToCSharpGenerator.McpServer.Tool --version x.y.z
Run it against a workspace:
axsg-mcp --workspace /Users/wieslawsoltes/GitHub/XamlToCSharpGenerator
For repo development you can also run it directly from source:
dotnet run --project src/XamlToCSharpGenerator.McpServer -- --workspace /Users/wieslawsoltes/GitHub/XamlToCSharpGenerator
Use this host when you need:
axsg.preview.projectContextDo not use this host when you need live app state from a running dotnet watch session. It is query-oriented and does not attach to another process automatically.
The runtime MCP host is not a separate executable. You embed it into the Avalonia application that is already running AXSG hot reload or hot design.
Typical app bootstrap:
using Avalonia;
using XamlToCSharpGenerator.Runtime;
internal static class Program
{
public static AppBuilder BuildAvaloniaApp()
{
return AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace()
.UseAvaloniaSourceGeneratedXaml()
.UseAvaloniaSourceGeneratedRuntimeXamlCompilation(enable: true)
.UseAvaloniaSourceGeneratedXamlHotDesign(enable: true, configure: options =>
{
options.PersistChangesToSource = true;
options.WaitForHotReload = false;
})
.UseAvaloniaSourceGeneratedStudioFromEnvironment()
.UseAvaloniaSourceGeneratedXamlIdeHotReloadFallback(enable: true, pollingIntervalMs: 1000);
}
}
That setup gives the runtime host something meaningful to expose:
XamlSourceGenRuntimeMcpServer is stream-based:
new XamlSourceGenRuntimeMcpServer(Stream input, Stream output)
AXSG intentionally does not hard-code a transport policy for the live app host. That choice belongs to the embedding application.
Recommended transports:
For a normal dotnet watch desktop app, loopback TCP or a named pipe is the practical choice.
The example below uses a host-defined environment variable, AXSG_RUNTIME_MCP_PORT. That variable is not built into AXSG; it is just one simple convention for your app.
using System.Net;
using System.Net.Sockets;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using XamlToCSharpGenerator.Runtime;
internal sealed class AxsgRuntimeMcpTcpHost : IAsyncDisposable
{
private readonly TcpListener _listener;
private readonly CancellationTokenSource _cts = new();
private Task? _acceptLoop;
public AxsgRuntimeMcpTcpHost(int port)
{
_listener = new TcpListener(IPAddress.Loopback, port);
}
public void Start()
{
_listener.Start();
_acceptLoop = Task.Run(() => AcceptLoopAsync(_cts.Token));
}
private async Task AcceptLoopAsync(CancellationToken cancellationToken)
{
try
{
while (!cancellationToken.IsCancellationRequested)
{
TcpClient client = await _listener.AcceptTcpClientAsync(cancellationToken).ConfigureAwait(false);
_ = Task.Run(async () =>
{
await using var stream = client.GetStream();
using var server = new XamlSourceGenRuntimeMcpServer(stream, stream);
await server.RunAsync(cancellationToken).ConfigureAwait(false);
}, cancellationToken);
}
}
catch (OperationCanceledException)
{
}
catch (ObjectDisposedException)
{
}
}
public async ValueTask DisposeAsync()
{
_cts.Cancel();
_listener.Stop();
if (_acceptLoop is not null)
{
await _acceptLoop.ConfigureAwait(false);
}
_cts.Dispose();
}
}
internal static class Program
{
private static AxsgRuntimeMcpTcpHost? _runtimeMcpHost;
public static AppBuilder BuildAvaloniaApp()
{
var builder = AppBuilder.Configure<App>()
.UsePlatformDetect()
.LogToTrace()
.UseAvaloniaSourceGeneratedXaml()
.UseAvaloniaSourceGeneratedXamlHotDesign(enable: true)
.UseAvaloniaSourceGeneratedStudioFromEnvironment()
.UseAvaloniaSourceGeneratedXamlIdeHotReloadFallback(enable: true, pollingIntervalMs: 1000);
if (int.TryParse(Environment.GetEnvironmentVariable("AXSG_RUNTIME_MCP_PORT"), out int port) && port > 0)
{
builder = builder.AfterSetup(_ =>
{
_runtimeMcpHost = new AxsgRuntimeMcpTcpHost(port);
_runtimeMcpHost.Start();
if (Application.Current?.ApplicationLifetime is IControlledApplicationLifetime lifetime)
{
lifetime.Exit += async (_, _) =>
{
if (_runtimeMcpHost is not null)
{
await _runtimeMcpHost.DisposeAsync().ConfigureAwait(false);
}
};
}
});
}
return builder;
}
}
dotnet watchAXSG_RUNTIME_MCP_PORT=43110 \
dotnet watch run --project samples/SourceGenCrudSample/SourceGenCrudSample.csproj
Once the app is running, connect your MCP client to the loopback server you exposed.
The runtime MCP host is the one you use for:
The runtime host exposes the live runtime-facing resources:
axsg://runtime/hotreload/statusaxsg://runtime/hotreload/eventsaxsg://runtime/hotdesign/statusaxsg://runtime/hotdesign/documentsaxsg://runtime/hotdesign/eventsaxsg://runtime/studio/statusaxsg://runtime/studio/eventsThe matching tools are:
axsg.hotReload.statusaxsg.hotDesign.statusaxsg.hotDesign.documentsaxsg.hotDesign.workspaceaxsg.studio.statusRecommended client behavior:
tools/call for one-shot snapshotsresources/subscribe for status and event resources in a live appThe preview host also supports MCP directly:
dotnet run --project src/XamlToCSharpGenerator.PreviewerHost -- --mcp
This host exposes preview session tools:
axsg.preview.startaxsg.preview.hotReloadaxsg.preview.updateaxsg.preview.stopand preview lifecycle resources:
axsg://preview/session/statusaxsg://preview/session/eventsaxsg://preview/session/currentThe preview host is the right MCP surface when you are building:
It is not the same thing as the runtime MCP host. The runtime host mirrors the running application. The preview host owns a dedicated preview session.
Use this rule for preview mutations:
axsg.preview.hotReload: apply and wait for the in-process live-preview resultaxsg.preview.update: dispatch-only update when your client already handles completion asynchronouslyFor the full preview-host workflow and tool semantics, see Preview MCP Host and Live Preview.
For normal VS Code usage, you do not need to start any MCP host manually.
The extension already manages:
The current VS Code preview UI still uses the extension’s bundled helper transport for the product workflow. The preview host’s MCP mode exists for custom clients, testing, and future remote-integration work; it is not the primary extension transport today.
The workspace MCP host is still useful alongside VS Code when you want:
Use this rule:
More concretely:
axsg-mcp is stateless enough that tools/list, tools/call, and resources/read are the expected workflowXamlSourceGenRuntimeMcpServer should be treated as a live stream of status and event resourcesPreviewHostMcpServer supports dynamic tools and resources, so relist after notifications/tools/list_changed or notifications/resources/list_changedaxsg-mcp, but hot reload state is emptyThat is expected unless you embedded the runtime MCP host into the running app. The standalone tool does not attach to another process automatically.
dotnet watch plus MCP without editing the appThat is not the current architecture. Today, live runtime MCP is in-process by design. Add a small transport host to the app and let dotnet watch restart it normally.
Use the preview host MCP mode instead of the runtime host.
Use axsg.preview.hotReload instead of axsg.preview.update.
Use axsg-mcp --workspace ... and call axsg.preview.projectContext.