Skip to content

Commit 36498c7

Browse files
authored
Merge pull request #1270 from dotnet/copilot/fix-d5166ca3-64b9-4823-8fdd-cb13b2aefa91
Add support for stamping version on server.json for MCP servers with 0.0.0-placeholder replacement
1 parent 1d65c8d commit 36498c7

File tree

3 files changed

+182
-0
lines changed

3 files changed

+182
-0
lines changed
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
2+
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
3+
4+
using Microsoft.Build.Framework;
5+
6+
namespace Nerdbank.GitVersioning.Tasks;
7+
8+
/// <summary>
9+
/// MSBuild task that stamps version information into an MCP server.json file.
10+
/// </summary>
11+
public class StampMcpServerJson : Microsoft.Build.Utilities.Task
12+
{
13+
/// <summary>
14+
/// Gets or sets the path to the source server.json file.
15+
/// </summary>
16+
[Required]
17+
public string SourceServerJson { get; set; }
18+
19+
/// <summary>
20+
/// Gets or sets the path where the stamped server.json file should be written.
21+
/// </summary>
22+
[Required]
23+
public string OutputServerJson { get; set; }
24+
25+
/// <summary>
26+
/// Gets or sets the version to stamp into the server.json file.
27+
/// </summary>
28+
[Required]
29+
public string Version { get; set; }
30+
31+
/// <summary>
32+
/// Executes the task to stamp version information into the MCP server.json file.
33+
/// </summary>
34+
/// <returns><see langword="true"/> if the task succeeded; <see langword="false"/> otherwise.</returns>
35+
public override bool Execute()
36+
{
37+
try
38+
{
39+
if (string.IsNullOrEmpty(this.SourceServerJson) || string.IsNullOrEmpty(this.OutputServerJson) || string.IsNullOrEmpty(this.Version))
40+
{
41+
this.Log.LogError("SourceServerJson, OutputServerJson, and Version are required parameters.");
42+
return !this.Log.HasLoggedErrors;
43+
}
44+
45+
if (!File.Exists(this.SourceServerJson))
46+
{
47+
this.Log.LogError($"Source server.json file not found: {this.SourceServerJson}");
48+
return !this.Log.HasLoggedErrors;
49+
}
50+
51+
// Ensure output directory exists
52+
string outputDir = Path.GetDirectoryName(this.OutputServerJson);
53+
if (!string.IsNullOrEmpty(outputDir))
54+
{
55+
Directory.CreateDirectory(outputDir);
56+
}
57+
58+
// Read the server.json file and replace version placeholders
59+
string jsonContent = File.ReadAllText(this.SourceServerJson);
60+
jsonContent = jsonContent.Replace("\"0.0.0-placeholder\"", $"\"{this.Version}\"");
61+
62+
File.WriteAllText(this.OutputServerJson, jsonContent);
63+
this.Log.LogMessage(MessageImportance.Low, $"Stamped version '{this.Version}' into server.json: {this.OutputServerJson}");
64+
}
65+
catch (Exception ex)
66+
{
67+
this.Log.LogErrorFromException(ex);
68+
}
69+
70+
return !this.Log.HasLoggedErrors;
71+
}
72+
}

src/Nerdbank.GitVersioning.Tasks/build/Nerdbank.GitVersioning.targets

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
<UsingTask AssemblyFile="$(NerdbankGitVersioningTasksPath)Nerdbank.GitVersioning.Tasks.dll" TaskName="Nerdbank.GitVersioning.Tasks.NativeVersionInfo"/>
3737
<UsingTask AssemblyFile="$(NerdbankGitVersioningTasksPath)Nerdbank.GitVersioning.Tasks.dll" TaskName="Nerdbank.GitVersioning.Tasks.SetCloudBuildVariables"/>
3838
<UsingTask AssemblyFile="$(NerdbankGitVersioningTasksPath)Nerdbank.GitVersioning.Tasks.dll" TaskName="Nerdbank.GitVersioning.Tasks.CompareFiles"/>
39+
<UsingTask AssemblyFile="$(NerdbankGitVersioningTasksPath)Nerdbank.GitVersioning.Tasks.dll" TaskName="Nerdbank.GitVersioning.Tasks.StampMcpServerJson"/>
3940

4041
<Target Name="NBGV_SetDefaults">
4142
<!-- Workarounds for https://github.com/dotnet/Nerdbank.GitVersioning/issues/404 -->
@@ -310,6 +311,34 @@
310311
</PropertyGroup>
311312
</Target>
312313

314+
<!-- Support for MCP servers: stamp version in server.json -->
315+
<Target Name="NBGV_StampMcpServerJson"
316+
Condition="'$(PackageType)' == 'McpServer'"
317+
BeforeTargets="GenerateNuspec;_GetPackageFiles"
318+
DependsOnTargets="GetBuildVersion">
319+
<ItemGroup>
320+
<_NBGV_OriginalServerJson Include="$(MSBuildProjectDirectory)\server.json" Condition="Exists('$(MSBuildProjectDirectory)\server.json')" />
321+
</ItemGroup>
322+
323+
<PropertyGroup>
324+
<_NBGV_StampedServerJsonPath>$(IntermediateOutputPath)server.json</_NBGV_StampedServerJsonPath>
325+
</PropertyGroup>
326+
327+
<!-- Transform server.json with versioned content -->
328+
<Nerdbank.GitVersioning.Tasks.StampMcpServerJson
329+
Condition="'@(_NBGV_OriginalServerJson)' != ''"
330+
SourceServerJson="%(_NBGV_OriginalServerJson.Identity)"
331+
OutputServerJson="$(_NBGV_StampedServerJsonPath)"
332+
Version="$(Version)" />
333+
334+
<!-- Remove original server.json from packaging and add stamped version -->
335+
<ItemGroup Condition="'$(_NBGV_StampedServerJsonPath)' != ''">
336+
<Content Remove="server.json" />
337+
<None Remove="server.json" />
338+
<Content Include="$(_NBGV_StampedServerJsonPath)" PackagePath="server.json" Pack="true" />
339+
</ItemGroup>
340+
</Target>
341+
313342
<!-- Workaround till https://github.com/NuGet/NuGet.Client/issues/1064 is merged and used. -->
314343
<Target Name="_NBGV_CalculateNuSpecVersionHelper"
315344
BeforeTargets="GenerateNuspec"

test/Nerdbank.GitVersioning.Tests/BuildIntegrationManagedTests.cs

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
// Copyright (c) .NET Foundation and Contributors. All rights reserved.
22
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
33

4+
using System.Text.Json.Nodes;
5+
using Microsoft.Build.Construction;
6+
using Microsoft.Build.Framework;
47
using Nerdbank.GitVersioning;
58
using Xunit;
69

@@ -15,6 +18,84 @@ public BuildIntegrationManagedTests(ITestOutputHelper logger)
1518
{
1619
}
1720

21+
/// <summary>
22+
/// Verifies that MCP server.json files get version stamping when PackageType=McpServer.
23+
/// </summary>
24+
[Fact]
25+
public async Task McpServerJson_VersionStamping()
26+
{
27+
// Create a sample server.json file based on the real MCP server template
28+
string serverJsonContent = /* lang=c#-test */ """
29+
{
30+
"$schema": "https://modelcontextprotocol.io/schemas/draft/2025-07-09/server.json",
31+
"description": "Test .NET MCP Server",
32+
"name": "io.github.test/testmcpserver",
33+
"version": "0.0.0-placeholder",
34+
"packages": [
35+
{
36+
"registry_type": "nuget",
37+
"identifier": "Test.McpServer",
38+
"version": "0.0.0-placeholder",
39+
"transport": {
40+
"type": "stdio"
41+
},
42+
"package_arguments": [],
43+
"environment_variables": []
44+
}
45+
],
46+
"repository": {
47+
"url": "https://github.com/test/testmcpserver",
48+
"source": "github"
49+
}
50+
}
51+
""";
52+
53+
string serverJsonPath = Path.Combine(this.projectDirectory, "server.json");
54+
File.WriteAllText(serverJsonPath, serverJsonContent);
55+
56+
// Set PackageType to McpServer
57+
ProjectPropertyGroupElement propertyGroup = this.testProject.CreatePropertyGroupElement();
58+
this.testProject.AppendChild(propertyGroup);
59+
propertyGroup.AddProperty("PackageType", "McpServer");
60+
61+
this.WriteVersionFile();
62+
BuildResults result = await this.BuildAsync("NBGV_StampMcpServerJson", logVerbosity: LoggerVerbosity.Detailed);
63+
64+
// Verify the build succeeded
65+
Assert.Empty(result.LoggedEvents.OfType<BuildErrorEventArgs>());
66+
67+
// Verify the stamped server.json was created
68+
string stampedServerJsonPath = Path.Combine(this.projectDirectory, result.BuildResult.ProjectStateAfterBuild.GetPropertyValue("IntermediateOutputPath"), "server.json");
69+
Assert.True(File.Exists(stampedServerJsonPath), $"Expected stamped server.json at: {stampedServerJsonPath}");
70+
71+
// Verify the version was correctly stamped
72+
string stampedContent = File.ReadAllText(stampedServerJsonPath);
73+
var stampedJson = JsonNode.Parse(stampedContent) as JsonObject;
74+
Assert.NotNull(stampedJson);
75+
76+
string expectedVersion = result.BuildResult.ProjectStateAfterBuild.GetPropertyValue("Version");
77+
78+
// Verify root version was stamped
79+
Assert.Equal(expectedVersion, stampedJson["version"]?.ToString());
80+
81+
// Verify package version was also stamped
82+
JsonArray packages = stampedJson["packages"]?.AsArray();
83+
Assert.NotNull(packages);
84+
Assert.Single(packages);
85+
86+
JsonObject package = packages[0]?.AsObject();
87+
Assert.NotNull(package);
88+
Assert.Equal(expectedVersion, package["version"]?.ToString());
89+
90+
// Verify other properties were preserved
91+
Assert.Equal("io.github.test/testmcpserver", stampedJson["name"]?.ToString());
92+
Assert.Equal("Test .NET MCP Server", stampedJson["description"]?.ToString());
93+
Assert.Equal("Test.McpServer", package["identifier"]?.ToString());
94+
95+
// Verify that no placeholder remain in the entire JSON
96+
Assert.DoesNotContain("0.0.0-placeholder", stampedContent);
97+
}
98+
1899
protected override GitContext CreateGitContext(string path, string committish = null)
19100
=> GitContext.Create(path, committish, GitContext.Engine.ReadOnly);
20101

0 commit comments

Comments
 (0)