Skip to content

Commit f3698c7

Browse files
halter73Copilot
andcommitted
Tighten conformance gates with HasMatchingDraftWireVersion guard
The previous gates activated draft-only scenarios as soon as the conformance package version reached 0.2.0. That works for conformance >= 0.2.0-alpha.3 (or a local build of main) where the bundled DRAFT_PROTOCOL_VERSION constant matches this SDK's value, but breaks under 0.2.0-alpha.2 because alpha.2 still ships the placeholder 'DRAFT-2026-v1' wire string while this SDK only accepts the ratified '2026-07-28'. Add HasMatchingDraftWireVersion() that greps the bundled node_modules/@modelcontextprotocol/conformance/dist/index.js for this SDK's McpHttpHeaders.DraftProtocolVersion (the bundle is minified so we can't grep the constant name, but the literal version string survives bundling and is specific enough to be reliable). AND it into the three gates: HasSep2243Scenarios(), HasCachingScenario(), HasMrtrScenarios(). Also unify HasMrtrScenarios() with HasSep2243Scenarios()/HasCachingScenario(): read the installed version from node_modules instead of the pinned version from package.json. This lets a local 'npm install --no-save <path-to-conformance>' activate MRTR scenarios the same way it already activates SEP-2243/caching. Under 0.2.0-alpha.2 (this PR's pin), the 14 gated draft scenarios all SKIP cleanly instead of failing on wire-string mismatch. Once 0.2.0-alpha.3 publishes (or a local main build is installed), the gates auto-activate. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent d539e7f commit f3698c7

1 file changed

Lines changed: 57 additions & 35 deletions

File tree

tests/Common/Utils/NodeHelpers.cs

Lines changed: 57 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Runtime.InteropServices;
33
using System.Text;
44
using System.Text.RegularExpressions;
5+
using ModelContextProtocol.Protocol;
56

67
namespace ModelContextProtocol.Tests.Utils;
78

@@ -187,8 +188,12 @@ public static bool IsNodeInstalled()
187188
/// the pinned version in package.json) means this also returns <see langword="true"/>
188189
/// when a newer private build has been installed locally via
189190
/// <c>npm install --no-save &lt;path-to-conformance&gt;</c>.
191+
/// Additionally requires that the installed conformance package emits the draft wire
192+
/// version this SDK speaks — see <see cref="HasMatchingDraftWireVersion"/>.
190193
/// </summary>
191-
public static bool HasSep2243Scenarios() => HasInstalledConformanceVersionAtLeast(new Version(0, 2, 0));
194+
public static bool HasSep2243Scenarios()
195+
=> HasInstalledConformanceVersionAtLeast(new Version(0, 2, 0))
196+
&& HasMatchingDraftWireVersion();
192197

193198
/// <summary>
194199
/// Checks whether the SEP-2549 "caching" conformance scenario (added in conformance
@@ -197,8 +202,47 @@ public static bool IsNodeInstalled()
197202
/// Reading the installed version (rather than the pinned version in package.json) means
198203
/// this also returns <see langword="true"/> when a newer private build has been installed
199204
/// locally via <c>npm install --no-save &lt;path-to-conformance&gt;</c>.
205+
/// Additionally requires that the installed conformance package emits the draft wire
206+
/// version this SDK speaks — see <see cref="HasMatchingDraftWireVersion"/>.
200207
/// </summary>
201-
public static bool HasCachingScenario() => HasInstalledConformanceVersionAtLeast(new Version(0, 2, 0));
208+
public static bool HasCachingScenario()
209+
=> HasInstalledConformanceVersionAtLeast(new Version(0, 2, 0))
210+
&& HasMatchingDraftWireVersion();
211+
212+
/// <summary>
213+
/// Returns <see langword="true"/> when the installed conformance package's bundled
214+
/// dist emits the same draft protocol version string as this SDK
215+
/// (<see cref="McpHttpHeaders.DraftProtocolVersion"/>). Used to suppress draft-only
216+
/// conformance scenarios when the published conformance binary is still pinned to a
217+
/// stale wire string (for example, conformance 0.2.0-alpha.2 ships
218+
/// <c>"DRAFT-2026-v1"</c> while this SDK speaks <c>"2026-07-28"</c>).
219+
/// </summary>
220+
/// <remarks>
221+
/// This check is a pragmatic alternative to inspecting the conformance package's
222+
/// internal constants: the bundled <c>dist/index.js</c> is minified so we can't grep
223+
/// the constant name, but the literal version string survives bundling and is unique
224+
/// enough to be a reliable signal.
225+
/// </remarks>
226+
public static bool HasMatchingDraftWireVersion()
227+
{
228+
try
229+
{
230+
var repoRoot = FindRepoRoot();
231+
var distPath = Path.Combine(
232+
repoRoot, "node_modules", "@modelcontextprotocol", "conformance", "dist", "index.js");
233+
if (!File.Exists(distPath))
234+
{
235+
return false;
236+
}
237+
238+
var bundled = File.ReadAllText(distPath);
239+
return bundled.Contains(McpHttpHeaders.DraftProtocolVersion, StringComparison.Ordinal);
240+
}
241+
catch
242+
{
243+
return false;
244+
}
245+
}
202246

203247
/// <summary>
204248
/// Returns <see langword="true"/> when the conformance package installed in node_modules
@@ -373,42 +417,20 @@ private static bool ConformanceOutputIndicatesSuccess(string output)
373417
}
374418

375419
/// <summary>
376-
/// Checks whether the SEP-2322 (Multi Round-Trip Requests / IncompleteResult)
377-
/// conformance scenarios are available by reading the conformance package version
378-
/// from the repo's package.json. MRTR scenarios require a conformance package version
379-
/// that includes SEP-2322 support (see
420+
/// Checks whether the SEP-2322 (Multi Round-Trip Requests / InputRequiredResult)
421+
/// conformance scenarios are available, by reading the <em>installed</em> conformance
422+
/// package version from node_modules. The <c>incomplete-result-*</c> scenarios were
423+
/// introduced in conformance package 0.2.0 (see
380424
/// https://github.com/modelcontextprotocol/conformance/pull/188).
425+
/// Reading the installed version (rather than the pinned version in package.json) means
426+
/// this also returns <see langword="true"/> when a newer private build has been installed
427+
/// locally via <c>npm install --no-save &lt;path-to-conformance&gt;</c>.
428+
/// Additionally requires that the installed conformance package emits the draft wire
429+
/// version this SDK speaks — see <see cref="HasMatchingDraftWireVersion"/>.
381430
/// </summary>
382431
public static bool HasMrtrScenarios()
383-
{
384-
try
385-
{
386-
var repoRoot = FindRepoRoot();
387-
var packageJsonPath = Path.Combine(repoRoot, "package.json");
388-
if (!File.Exists(packageJsonPath))
389-
{
390-
return false;
391-
}
392-
393-
var json = System.Text.Json.JsonDocument.Parse(File.ReadAllText(packageJsonPath));
394-
if (json.RootElement.TryGetProperty("dependencies", out var deps) &&
395-
deps.TryGetProperty("@modelcontextprotocol/conformance", out var versionElement))
396-
{
397-
var versionStr = versionElement.GetString();
398-
if (versionStr is not null && Version.TryParse(versionStr, out var version))
399-
{
400-
// SEP-2322 scenarios are expected in conformance package >= 0.2.0
401-
return version >= new Version(0, 2, 0);
402-
}
403-
}
404-
405-
return false;
406-
}
407-
catch
408-
{
409-
return false;
410-
}
411-
}
432+
=> HasInstalledConformanceVersionAtLeast(new Version(0, 2, 0))
433+
&& HasMatchingDraftWireVersion();
412434

413435
private static ProcessStartInfo NpmStartInfo(string arguments, string workingDirectory)
414436
{

0 commit comments

Comments
 (0)