Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -89,8 +89,8 @@
<PackageVersion Include="Microsoft.DataFactory.MCP.Core" Version="0.21.0-beta" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.2" />
<PackageVersion Include="ModelContextProtocol" Version="2.0.0-preview.1" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="2.0.0-preview.1" />
<PackageVersion Include="ModelContextProtocol" Version="2.0.0-preview.3" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="2.0.0-preview.3" />
<PackageVersion Include="MySqlConnector" Version="2.4.0" />
<PackageVersion Include="Npgsql" Version="10.0.1" />
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,9 @@ private static McpServerOptions CreateOptions(McpServerHandlers? serverHandlers
{
return new McpServerOptions
{
ProtocolVersion = "2024",
// Use a supported protocol version. preview.3 tightened validation and
// rejects bare '2024'; supported versions: 2024-11-05, 2025-11-25, 2026-07-28.
ProtocolVersion = "2025-11-25",
InitializationTimeout = TimeSpan.FromSeconds(30),
Handlers = serverHandlers ?? new(),
ServerInfo = new Implementation { Name = "Azure MCP", Version = "1.0.0-beta" }
Expand Down Expand Up @@ -50,7 +52,7 @@ await Invoke_Request_To_Server(
Assert.NotNull(result);
Assert.Equal("Azure MCP", result.ServerInfo.Name);
Assert.Equal("1.0.0-beta", result.ServerInfo.Version);
Assert.Equal("2024", result.ProtocolVersion);
Assert.Equal("2025-11-25", result.ProtocolVersion);
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,13 @@ public async Task Should_Error_When_Resources_Unsubscribe_Not_Supported()
[Fact]
public async Task Should_Not_Hang_On_Logging_SetLevel_Not_Supported()
{
await Client.SetLoggingLevelAsync(LoggingLevel.Info, cancellationToken: TestContext.Current.CancellationToken);
// logging/setLevel was removed in MCP 2026-07-28 (SDK 2.0.0-preview.3).
// The method is no longer supported; per-request log level is now set via
// _meta/io.modelcontextprotocol/logLevel. The call should throw rather than hang.
var ex = await Assert.ThrowsAsync<McpProtocolException>(
async () => await Client.SetLoggingLevelAsync(LoggingLevel.Info,
cancellationToken: TestContext.Current.CancellationToken));
Assert.Contains("logging/setLevel", ex.Message, StringComparison.OrdinalIgnoreCase);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
changes:
- section: "Other Changes"
description: "Updated ModelContextProtocol packages to 2.0.0-preview.3."
subsection: "Dependency Updates"
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,51 @@
// Licensed under the MIT License.

using System.Net;
using System.Text.Json.Nodes;
using Xunit;

namespace Azure.Mcp.Server.Tests.Infrastructure;

public class ConsolidatedModeTests
{
private const string StatelessProtocolVersion = "2026-07-28";

/// <summary>
/// Builds a <see cref="StringContent"/> for a 2026-07-28 stateless JSON-RPC request
/// containing the required <c>_meta</c> envelope fields. Optional extra <c>_meta</c>
/// entries (e.g. W3C trace context) can be merged in via <paramref name="extraMeta"/>.
/// </summary>
private static StringContent CreateStateless2026RequestContent(
string method,
int id = 1,
JsonObject? extraMeta = null)
{
var meta = new JsonObject
{
["io.modelcontextprotocol/protocolVersion"] = StatelessProtocolVersion,
["io.modelcontextprotocol/clientInfo"] = new JsonObject { ["name"] = "test-client", ["version"] = "1.0" },
["io.modelcontextprotocol/clientCapabilities"] = new JsonObject()
};

if (extraMeta != null)
{
foreach (var (key, value) in extraMeta)
{
meta[key] = value?.DeepClone();
}
}

var body = new JsonObject
{
["jsonrpc"] = "2.0",
["id"] = id,
["method"] = method,
["params"] = new JsonObject { ["_meta"] = meta }
};

return new StringContent(body.ToJsonString(), System.Text.Encoding.UTF8, "application/json");
}

[Fact]
public async Task ConsolidatedMode_HttpTransport_Should_List_Tools_On_Root_Endpoint()
{
Expand Down Expand Up @@ -37,10 +76,10 @@ public async Task ConsolidatedMode_HttpTransport_Should_List_Tools_On_Root_Endpo
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent("{" + "\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}" + "}", System.Text.Encoding.UTF8, "application/json")
Content = CreateStateless2026RequestContent("tools/list")
};
Comment thread
g2vinay marked this conversation as resolved.
request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand Down Expand Up @@ -93,16 +132,17 @@ public async Task ConsolidatedMode_HttpTransport_Should_Accept_Meta_In_Request_P
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent(
"{" +
"\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{\"_meta\":{\"traceparent\":\"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01\",\"clientId\":\"phase2-hardening-test\"}}" +
"}",
System.Text.Encoding.UTF8,
"application/json")
Content = CreateStateless2026RequestContent(
"tools/list",
extraMeta: new JsonObject
{
["traceparent"] = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
["clientId"] = "phase2-hardening-test"
})
};

request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand Down Expand Up @@ -152,16 +192,11 @@ public async Task ConsolidatedMode_HttpTransport_Should_Support_ServerDiscover_W
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent(
"{" +
"\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"server/discover\",\"params\":{}" +
"}",
System.Text.Encoding.UTF8,
"application/json")
Content = CreateStateless2026RequestContent("server/discover")
};

request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "server/discover");
request.Headers.TryAddWithoutValidation("Mcp-Name", "server/discover");

Expand Down Expand Up @@ -213,10 +248,10 @@ public async Task ConsolidatedMode_HttpTransport_Should_Handle_Repeated_ToolsLis

using var request1 = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}", System.Text.Encoding.UTF8, "application/json")
Content = CreateStateless2026RequestContent("tools/list", id: 1)
};
request1.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request1.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request1.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request1.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request1.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand All @@ -225,10 +260,10 @@ public async Task ConsolidatedMode_HttpTransport_Should_Handle_Repeated_ToolsLis

using var request2 = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent("{\"jsonrpc\":\"2.0\",\"id\":2,\"method\":\"tools/list\",\"params\":{}}", System.Text.Encoding.UTF8, "application/json")
Content = CreateStateless2026RequestContent("tools/list", id: 2)
};
request2.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request2.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request2.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request2.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request2.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand Down Expand Up @@ -341,22 +376,18 @@ public async Task ConsolidatedMode_HttpTransport_Should_Propagate_W3C_Trace_Cont
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent(
"{" +
"\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{" +
"\"_meta\":{" +
"\"traceparent\":\"00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01\"," +
"\"tracestate\":\"vendor1=opaquevalue1,vendor2=opaquevalue2\"," +
"\"baggage\":\"userId=alice,serverNode=DF:28,isProduction=false\"" +
"}" +
"}" +
"}",
System.Text.Encoding.UTF8,
"application/json")
Content = CreateStateless2026RequestContent(
"tools/list",
extraMeta: new JsonObject
{
["traceparent"] = "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
["tracestate"] = "vendor1=opaquevalue1,vendor2=opaquevalue2",
["baggage"] = "userId=alice,serverNode=DF:28,isProduction=false"
})
};

request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand Down Expand Up @@ -634,10 +665,10 @@ public async Task ConsolidatedMode_HttpTransport_Should_Accept_NonNamedMethod_Wi
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent("{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}", System.Text.Encoding.UTF8, "application/json")
Content = CreateStateless2026RequestContent("tools/list")
};
request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
// Intentionally omit Mcp-Name — valid for a non-named method

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ public async Task NamespaceMode_Should_List_Tools_Without_Initialize()
public async Task AllMode_Should_List_Tools_Without_Initialize()
{
// "all" mode exposes every individual Azure tool directly.
// Uses a longer timeout because registering all tools takes more time on slow CI runners.
Assert.True(File.Exists(AzmcpPath), $"Executable not found at {AzmcpPath}. Please build the Azure.Mcp.Server project first.");

var processStartInfo = new System.Diagnostics.ProcessStartInfo
Expand All @@ -141,7 +142,8 @@ public async Task AllMode_Should_List_Tools_Without_Initialize()
await process.StandardInput.WriteLineAsync(listToolsRequest);
await process.StandardInput.FlushAsync(TestContext.Current.CancellationToken);

var response = await ReadResponseAsync(process.StandardOutput);
// Allow up to 60 s: "all" mode registers every tool and is slower on macOS CI runners.
var response = await ReadResponseAsync(process.StandardOutput, TimeSpan.FromSeconds(60));

Assert.NotNull(response);
Assert.Contains("\"result\"", response, StringComparison.OrdinalIgnoreCase);
Expand All @@ -156,9 +158,9 @@ public async Task AllMode_Should_List_Tools_Without_Initialize()
}
}

private static async Task<string?> ReadResponseAsync(StreamReader reader)
private static async Task<string?> ReadResponseAsync(StreamReader reader, TimeSpan? timeout = null)
{
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));
using var cts = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(15));
try
{
return await reader.ReadLineAsync(cts.Token);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,39 @@
// Licensed under the MIT License.

using System.Net;
using System.Text.Json.Nodes;
using Xunit;

namespace Fabric.Mcp.Server.Tests.Infrastructure;

public class ServerStartupTests
{
private const string StatelessProtocolVersion = "2026-07-28";

/// <summary>
/// Builds a <see cref="StringContent"/> for a 2026-07-28 stateless JSON-RPC request
/// containing the required <c>_meta</c> envelope fields.
/// </summary>
private static StringContent CreateStateless2026RequestContent(string method, int id = 1)
{
var body = new JsonObject
{
["jsonrpc"] = "2.0",
["id"] = id,
["method"] = method,
["params"] = new JsonObject
{
["_meta"] = new JsonObject
{
["io.modelcontextprotocol/protocolVersion"] = StatelessProtocolVersion,
["io.modelcontextprotocol/clientInfo"] = new JsonObject { ["name"] = "test-client", ["version"] = "1.0" },
["io.modelcontextprotocol/clientCapabilities"] = new JsonObject()
}
}
};
return new StringContent(body.ToJsonString(), System.Text.Encoding.UTF8, "application/json");
}

[Fact]
public async Task Server_Should_List_Tools_Over_Http_Root_Endpoint()
{
Expand Down Expand Up @@ -47,10 +74,10 @@ public async Task Server_Should_List_Tools_Over_Http_Root_Endpoint()
using var client = new HttpClient();
using var request = new HttpRequestMessage(HttpMethod.Post, $"http://127.0.0.1:{port}/")
{
Content = new StringContent("{" + "\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}" + "}", System.Text.Encoding.UTF8, "application/json")
Content = CreateStateless2026RequestContent("tools/list")
};
Comment thread
g2vinay marked this conversation as resolved.
request.Headers.TryAddWithoutValidation("Accept", "application/json, text/event-stream");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", "2026-07-28");
request.Headers.TryAddWithoutValidation("MCP-Protocol-Version", StatelessProtocolVersion);
request.Headers.TryAddWithoutValidation("Mcp-Method", "tools/list");
request.Headers.TryAddWithoutValidation("Mcp-Name", "tools/list");

Expand Down
Loading