Add request options bag to high level requests and include Meta (#970)
Co-authored-by: Stephen Toub <stoub@microsoft.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
627de76ca6
commit
c447973972
12 changed files with 882 additions and 164 deletions
141
tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs
Normal file
141
tests/ModelContextProtocol.Tests/Client/McpClientMetaTests.cs
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
using Microsoft.Extensions.DependencyInjection;
|
||||
using ModelContextProtocol.Client;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace ModelContextProtocol.Tests.Client;
|
||||
|
||||
public class McpClientMetaTests : ClientServerTestBase
|
||||
{
|
||||
public McpClientMetaTests(ITestOutputHelper outputHelper)
|
||||
: base(outputHelper)
|
||||
{
|
||||
}
|
||||
|
||||
protected override void ConfigureServices(ServiceCollection services, IMcpServerBuilder mcpServerBuilder)
|
||||
{
|
||||
services.Configure<McpServerOptions>(o =>
|
||||
{
|
||||
o.ServerInfo = new Implementation
|
||||
{
|
||||
Name = "test-server",
|
||||
Version = "1.0.0",
|
||||
Description = "A test server for unit testing",
|
||||
WebsiteUrl = "https://example.com",
|
||||
};
|
||||
o.ToolCollection = new ();
|
||||
o.ResourceCollection = new ();
|
||||
o.PromptCollection = new ();
|
||||
});
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ToolCallWithMetaFields()
|
||||
{
|
||||
Server.ServerOptions.ToolCollection?.Add(McpServerTool.Create(
|
||||
async (RequestContext<CallToolRequestParams> context) =>
|
||||
{
|
||||
// Access the foo property of _meta field from the request parameters
|
||||
var metaFoo = context.Params?.Meta?["foo"]?.ToString();
|
||||
|
||||
// Assert that the meta foo is correctly passed
|
||||
Assert.NotNull(metaFoo);
|
||||
|
||||
return $"Meta foo is {metaFoo}";
|
||||
},
|
||||
new () { Name = "meta_tool" }));
|
||||
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var requestOptions = new RequestOptions()
|
||||
{
|
||||
Meta = new JsonObject()
|
||||
{
|
||||
{ "foo", "bar baz" }
|
||||
}
|
||||
};
|
||||
|
||||
var result = await client.CallToolAsync("meta_tool", options: requestOptions, cancellationToken: TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Null(result.IsError);
|
||||
|
||||
var textContent = result.Content.OfType<TextContentBlock>().FirstOrDefault();
|
||||
Assert.NotNull(textContent);
|
||||
Assert.Contains("bar baz", textContent.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ResourceReadWithMetaFields()
|
||||
{
|
||||
Server.ServerOptions.ResourceCollection?.Add(McpServerResource.Create(
|
||||
(RequestContext<ReadResourceRequestParams> context) =>
|
||||
{
|
||||
// Access the foo property of _meta field from the request parameters
|
||||
var metaFoo = context.Params?.Meta?["foo"]?.ToString();
|
||||
|
||||
// Assert that the meta foo is correctly passed
|
||||
Assert.NotNull(metaFoo);
|
||||
|
||||
return $"Resource with Meta foo is {metaFoo}";
|
||||
},
|
||||
new () { UriTemplate = "test://meta_resource" }));
|
||||
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var requestOptions = new RequestOptions()
|
||||
{
|
||||
Meta = new JsonObject()
|
||||
{
|
||||
{ "foo", "bar baz" }
|
||||
}
|
||||
};
|
||||
|
||||
var result = await client.ReadResourceAsync("test://meta_resource", options: requestOptions, cancellationToken: TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(result);
|
||||
|
||||
var textContent = result.Contents.OfType<TextResourceContents>().FirstOrDefault();
|
||||
Assert.NotNull(textContent);
|
||||
Assert.Contains("bar baz", textContent.Text);
|
||||
}
|
||||
|
||||
|
||||
[Fact]
|
||||
public async Task PromptGetWithMetaFields()
|
||||
{
|
||||
Server.ServerOptions.PromptCollection?.Add(McpServerPrompt.Create(
|
||||
(RequestContext<GetPromptRequestParams> context) =>
|
||||
{
|
||||
// Access the foo property of _meta field from the request parameters
|
||||
var metaFoo = context.Params?.Meta?["foo"]?.ToString();
|
||||
|
||||
// Assert that the meta foo is correctly passed
|
||||
Assert.NotNull(metaFoo);
|
||||
|
||||
return $"Prompt with Meta foo is {metaFoo}";
|
||||
},
|
||||
new () { Name = "meta_prompt" }));
|
||||
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var requestOptions = new RequestOptions()
|
||||
{
|
||||
Meta = new JsonObject()
|
||||
{
|
||||
{ "foo", "bar baz" }
|
||||
}
|
||||
};
|
||||
|
||||
var result = await client.GetPromptAsync("meta_prompt", options: requestOptions, cancellationToken: TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.NotEmpty(result.Messages);
|
||||
var message = result.Messages.First();
|
||||
Assert.NotNull(message.Content);
|
||||
var textContent = message.Content as TextContentBlock;
|
||||
Assert.NotNull(textContent);
|
||||
Assert.Contains("bar baz", textContent.Text);
|
||||
}
|
||||
}
|
||||
|
|
@ -75,7 +75,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var result = await client.ReadResourceAsync(uriTemplate, variables, TestContext.Current.CancellationToken);
|
||||
var result = await client.ReadResourceAsync(uriTemplate, variables, null, TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(result);
|
||||
var actualUri = Assert.IsType<TextResourceContents>(Assert.Single(result.Contents)).Text;
|
||||
|
||||
|
|
@ -110,13 +110,13 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
// The JSON from the test case files has been extracted below.
|
||||
|
||||
// Copyright 2011- The Authors
|
||||
//
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
|
@ -255,7 +255,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["X{.var:3}", "X.val"],
|
||||
["X{.list}", "X.red,green,blue"],
|
||||
["X{.list*}", "X.red.green.blue"],
|
||||
["X{.keys}", [
|
||||
["X{.keys}", [
|
||||
"X.comma,%2C,dot,.,semi,%3B",
|
||||
"X.comma,%2C,semi,%3B,dot,.",
|
||||
"X.dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -275,7 +275,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"/semi,%3B,comma,%2C,dot,.",
|
||||
"/semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{/keys*}", [
|
||||
["{/keys*}", [
|
||||
"/comma=%2C/dot=./semi=%3B",
|
||||
"/comma=%2C/semi=%3B/dot=.",
|
||||
"/dot=./comma=%2C/semi=%3B",
|
||||
|
|
@ -286,7 +286,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{;hello:5}", ";hello=Hello"],
|
||||
["{;list}", ";list=red,green,blue"],
|
||||
["{;list*}", ";list=red;list=green;list=blue"],
|
||||
["{;keys}", [
|
||||
["{;keys}", [
|
||||
";keys=comma,%2C,dot,.,semi,%3B",
|
||||
";keys=comma,%2C,semi,%3B,dot,.",
|
||||
";keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -294,7 +294,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
";keys=semi,%3B,comma,%2C,dot,.",
|
||||
";keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{;keys*}", [
|
||||
["{;keys*}", [
|
||||
";comma=%2C;dot=.;semi=%3B",
|
||||
";comma=%2C;semi=%3B;dot=.",
|
||||
";dot=.;comma=%2C;semi=%3B",
|
||||
|
|
@ -305,7 +305,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{?var:3}", "?var=val"],
|
||||
["{?list}", "?list=red,green,blue"],
|
||||
["{?list*}", "?list=red&list=green&list=blue"],
|
||||
["{?keys}", [
|
||||
["{?keys}", [
|
||||
"?keys=comma,%2C,dot,.,semi,%3B",
|
||||
"?keys=comma,%2C,semi,%3B,dot,.",
|
||||
"?keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -313,7 +313,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"?keys=semi,%3B,comma,%2C,dot,.",
|
||||
"?keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{?keys*}", [
|
||||
["{?keys*}", [
|
||||
"?comma=%2C&dot=.&semi=%3B",
|
||||
"?comma=%2C&semi=%3B&dot=.",
|
||||
"?dot=.&comma=%2C&semi=%3B",
|
||||
|
|
@ -324,7 +324,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{&var:3}", "&var=val"],
|
||||
["{&list}", "&list=red,green,blue"],
|
||||
["{&list*}", "&list=red&list=green&list=blue"],
|
||||
["{&keys}", [
|
||||
["{&keys}", [
|
||||
"&keys=comma,%2C,dot,.,semi,%3B",
|
||||
"&keys=comma,%2C,semi,%3B,dot,.",
|
||||
"&keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -332,7 +332,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"&keys=semi,%3B,comma,%2C,dot,.",
|
||||
"&keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{&keys*}", [
|
||||
["{&keys*}", [
|
||||
"&comma=%2C&dot=.&semi=%3B",
|
||||
"&comma=%2C&semi=%3B&dot=.",
|
||||
"&dot=.&comma=%2C&semi=%3B",
|
||||
|
|
@ -636,7 +636,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"/semi,%3B,comma,%2C,dot,.",
|
||||
"/semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{/keys*}", [
|
||||
["{/keys*}", [
|
||||
"/comma=%2C/dot=./semi=%3B",
|
||||
"/comma=%2C/semi=%3B/dot=.",
|
||||
"/dot=./comma=%2C/semi=%3B",
|
||||
|
|
@ -679,7 +679,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{;x,y,undef}", ";x=1024;y=768"],
|
||||
["{;list}", ";list=red,green,blue"],
|
||||
["{;list*}", ";list=red;list=green;list=blue"],
|
||||
["{;keys}", [
|
||||
["{;keys}", [
|
||||
";keys=comma,%2C,dot,.,semi,%3B",
|
||||
";keys=comma,%2C,semi,%3B,dot,.",
|
||||
";keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -687,7 +687,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
";keys=semi,%3B,comma,%2C,dot,.",
|
||||
";keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{;keys*}", [
|
||||
["{;keys*}", [
|
||||
";comma=%2C;dot=.;semi=%3B",
|
||||
";comma=%2C;semi=%3B;dot=.",
|
||||
";dot=.;comma=%2C;semi=%3B",
|
||||
|
|
@ -727,7 +727,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{?var:3}", "?var=val"],
|
||||
["{?list}", "?list=red,green,blue"],
|
||||
["{?list*}", "?list=red&list=green&list=blue"],
|
||||
["{?keys}", [
|
||||
["{?keys}", [
|
||||
"?keys=comma,%2C,dot,.,semi,%3B",
|
||||
"?keys=comma,%2C,semi,%3B,dot,.",
|
||||
"?keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -735,7 +735,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"?keys=semi,%3B,comma,%2C,dot,.",
|
||||
"?keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{?keys*}", [
|
||||
["{?keys*}", [
|
||||
"?comma=%2C&dot=.&semi=%3B",
|
||||
"?comma=%2C&semi=%3B&dot=.",
|
||||
"?dot=.&comma=%2C&semi=%3B",
|
||||
|
|
@ -775,7 +775,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
["{&x,y,undef}", "&x=1024&y=768"],
|
||||
["{&list}", "&list=red,green,blue"],
|
||||
["{&list*}", "&list=red&list=green&list=blue"],
|
||||
["{&keys}", [
|
||||
["{&keys}", [
|
||||
"&keys=comma,%2C,dot,.,semi,%3B",
|
||||
"&keys=comma,%2C,semi,%3B,dot,.",
|
||||
"&keys=dot,.,comma,%2C,semi,%3B",
|
||||
|
|
@ -783,7 +783,7 @@ public partial class McpClientResourceTemplateTests : ClientServerTestBase
|
|||
"&keys=semi,%3B,comma,%2C,dot,.",
|
||||
"&keys=semi,%3B,dot,.,comma,%2C"
|
||||
]],
|
||||
["{&keys*}", [
|
||||
["{&keys*}", [
|
||||
"&comma=%2C&dot=.&semi=%3B",
|
||||
"&comma=%2C&semi=%3B&dot=.",
|
||||
"&dot=.&comma=%2C&semi=%3B",
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ public class McpClientTests : ClientServerTestBase
|
|||
JsonSerializerOptions emptyOptions = new() { TypeInfoResolver = JsonTypeInfoResolver.Combine() };
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
await Assert.ThrowsAsync<NotSupportedException>(async () => await client.GetPromptAsync("Prompt", new Dictionary<string, object?> { ["i"] = 42 }, emptyOptions, cancellationToken: TestContext.Current.CancellationToken));
|
||||
await Assert.ThrowsAsync<NotSupportedException>(async () => await client.GetPromptAsync("Prompt", new Dictionary<string, object?> { ["i"] = 42 }, new RequestOptions { JsonSerializerOptions = emptyOptions }, cancellationToken: TestContext.Current.CancellationToken));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
|
@ -319,7 +319,7 @@ public class McpClientTests : ClientServerTestBase
|
|||
JsonSerializerOptions options = new(JsonSerializerOptions.Default);
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).First();
|
||||
var tool = (await client.ListToolsAsync(new RequestOptions { JsonSerializerOptions = options }, TestContext.Current.CancellationToken)).First();
|
||||
var originalName = tool.Name;
|
||||
var renamedTool = tool.WithName("RenamedTool");
|
||||
|
||||
|
|
@ -333,7 +333,7 @@ public class McpClientTests : ClientServerTestBase
|
|||
{
|
||||
JsonSerializerOptions options = new(JsonSerializerOptions.Default);
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
var tool = (await client.ListToolsAsync(options, TestContext.Current.CancellationToken)).FirstOrDefault();
|
||||
var tool = (await client.ListToolsAsync(new RequestOptions { JsonSerializerOptions = options }, TestContext.Current.CancellationToken)).FirstOrDefault();
|
||||
var originalDescription = tool?.Description;
|
||||
var redescribedTool = tool?.WithDescription("ToolWithNewDescription");
|
||||
Assert.NotNull(redescribedTool);
|
||||
|
|
@ -407,7 +407,7 @@ public class McpClientTests : ClientServerTestBase
|
|||
Assert.False(logger.IsEnabled(LogLevel.Error));
|
||||
Assert.False(logger.IsEnabled(LogLevel.Critical));
|
||||
|
||||
await client.SetLoggingLevel(LoggingLevel.Info, TestContext.Current.CancellationToken);
|
||||
await client.SetLoggingLevel(LoggingLevel.Info, options: null, TestContext.Current.CancellationToken);
|
||||
|
||||
DateTime start = DateTime.UtcNow;
|
||||
while (Server.LoggingLevel is null)
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
|
||||
// Act
|
||||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
await client.PingAsync(TestContext.Current.CancellationToken);
|
||||
await client.PingAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(client);
|
||||
|
|
@ -141,7 +141,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
|
||||
// act
|
||||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
var prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
var prompts = await client.ListPromptsAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
// assert
|
||||
Assert.NotEmpty(prompts);
|
||||
|
|
@ -193,7 +193,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
|
||||
// act
|
||||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () =>
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () =>
|
||||
await client.GetPromptAsync("non_existent_prompt", null, cancellationToken: TestContext.Current.CancellationToken));
|
||||
}
|
||||
|
||||
|
|
@ -206,7 +206,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
// act
|
||||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
|
||||
IList<McpClientResourceTemplate> allResourceTemplates = await client.ListResourceTemplatesAsync(TestContext.Current.CancellationToken);
|
||||
IList<McpClientResourceTemplate> allResourceTemplates = await client.ListResourceTemplatesAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
// The server provides a single test resource template
|
||||
Assert.Single(allResourceTemplates);
|
||||
|
|
@ -221,7 +221,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
// act
|
||||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
|
||||
IList<McpClientResource> allResources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
IList<McpClientResource> allResources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
|
||||
// The server provides 100 test resources
|
||||
Assert.Equal(100, allResources.Count);
|
||||
|
|
@ -237,7 +237,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
// Odd numbered resources are text in the everything server (despite the docs saying otherwise)
|
||||
// 1 is index 0, which is "even" in the 0-based index
|
||||
var result = await client.ReadResourceAsync("test://static/resource/1", TestContext.Current.CancellationToken);
|
||||
var result = await client.ReadResourceAsync("test://static/resource/1", null, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result.Contents);
|
||||
|
|
@ -256,7 +256,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
await using var client = await _fixture.CreateClientAsync(clientId);
|
||||
// Even numbered resources are binary in the everything server (despite the docs saying otherwise)
|
||||
// 2 is index 1, which is "odd" in the 0-based index
|
||||
var result = await client.ReadResourceAsync("test://static/resource/2", TestContext.Current.CancellationToken);
|
||||
var result = await client.ReadResourceAsync("test://static/resource/2", null, TestContext.Current.CancellationToken);
|
||||
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result.Contents);
|
||||
|
|
@ -290,7 +290,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
}
|
||||
});
|
||||
|
||||
await client.SubscribeToResourceAsync("test://static/resource/1", TestContext.Current.CancellationToken);
|
||||
await client.SubscribeToResourceAsync("test://static/resource/1", null, TestContext.Current.CancellationToken);
|
||||
|
||||
await tcs.Task;
|
||||
}
|
||||
|
|
@ -319,13 +319,13 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
]
|
||||
}
|
||||
});
|
||||
await client.SubscribeToResourceAsync("test://static/resource/1", TestContext.Current.CancellationToken);
|
||||
await client.SubscribeToResourceAsync("test://static/resource/1", null, TestContext.Current.CancellationToken);
|
||||
|
||||
// wait until we received a notification
|
||||
await receivedNotification.Task;
|
||||
|
||||
// unsubscribe
|
||||
await client.UnsubscribeFromResourceAsync("test://static/resource/1", TestContext.Current.CancellationToken);
|
||||
await client.UnsubscribeFromResourceAsync("test://static/resource/1", null, TestContext.Current.CancellationToken);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
|
|
@ -408,7 +408,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
//[Theory]
|
||||
//[MemberData(nameof(GetClients))]
|
||||
//public async Task Roots_Stdio_EverythingServer(string clientId)
|
||||
//{
|
||||
//{
|
||||
// var rootsHandlerCalls = 0;
|
||||
// var testRoots = new List<Root>
|
||||
// {
|
||||
|
|
@ -573,7 +573,7 @@ public partial class ClientIntegrationTests : LoggedTest, IClassFixture<ClientIn
|
|||
});
|
||||
|
||||
// act
|
||||
await client.SetLoggingLevel(LoggingLevel.Debug, TestContext.Current.CancellationToken);
|
||||
await client.SetLoggingLevel(LoggingLevel.Debug, options: null, TestContext.Current.CancellationToken);
|
||||
|
||||
// assert
|
||||
await receivedNotification.Task;
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
var prompts = await client.ListPromptsAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(6, prompts.Count);
|
||||
|
||||
var prompt = prompts.First(t => t.Name == "returns_chat_messages");
|
||||
|
|
@ -129,7 +129,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
var prompts = await client.ListPromptsAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(6, prompts.Count);
|
||||
|
||||
Channel<JsonRpcNotification> listChanged = Channel.CreateUnbounded<JsonRpcNotification>();
|
||||
|
|
@ -150,7 +150,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
|||
serverPrompts.Add(newPrompt);
|
||||
await notificationRead;
|
||||
|
||||
prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
prompts = await client.ListPromptsAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(7, prompts.Count);
|
||||
Assert.Contains(prompts, t => t.Name == "NewPrompt");
|
||||
|
||||
|
|
@ -160,7 +160,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
|||
await notificationRead;
|
||||
}
|
||||
|
||||
prompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
prompts = await client.ListPromptsAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(6, prompts.Count);
|
||||
Assert.DoesNotContain(prompts, t => t.Name == "NewPrompt");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
|
|||
|
||||
Assert.NotNull(client.ServerCapabilities.Resources);
|
||||
|
||||
var resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(5, resources.Count);
|
||||
|
||||
var resource = resources.First(t => t.Name == "some_neat_direct_resource");
|
||||
|
|
@ -146,7 +146,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var resources = await client.ListResourceTemplatesAsync(TestContext.Current.CancellationToken);
|
||||
var resources = await client.ListResourceTemplatesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(3, resources.Count);
|
||||
|
||||
var resource = resources.First(t => t.Name == "some_neat_templated_resource");
|
||||
|
|
@ -163,7 +163,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(5, resources.Count);
|
||||
|
||||
Channel<JsonRpcNotification> listChanged = Channel.CreateUnbounded<JsonRpcNotification>();
|
||||
|
|
@ -184,7 +184,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
|
|||
serverResources.Add(newResource);
|
||||
await notificationRead;
|
||||
|
||||
resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(6, resources.Count);
|
||||
Assert.Contains(resources, t => t.Name == "NewResource");
|
||||
|
||||
|
|
@ -194,7 +194,7 @@ public partial class McpServerBuilderExtensionsResourcesTests : ClientServerTest
|
|||
await notificationRead;
|
||||
}
|
||||
|
||||
resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal(5, resources.Count);
|
||||
Assert.DoesNotContain(resources, t => t.Name == "NewResource");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ using System.Collections.Concurrent;
|
|||
using System.ComponentModel;
|
||||
using System.IO.Pipelines;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Channels;
|
||||
|
|
@ -686,7 +687,11 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
|
|||
new CallToolRequestParams
|
||||
{
|
||||
Name = progressTool.ProtocolTool.Name,
|
||||
ProgressToken = new("abc123"),
|
||||
// Set the progress token in Meta
|
||||
Meta = new JsonObject
|
||||
{
|
||||
["progressToken"] = "abc123",
|
||||
}
|
||||
},
|
||||
cancellationToken: TestContext.Current.CancellationToken);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class McpServerResourceCapabilityIntegrationTests : ClientServerTestBase
|
|||
Assert.True(client.ServerCapabilities.Resources.Subscribe, "Server should advertise Subscribe capability when manually set");
|
||||
|
||||
// The resources should be exposed and listable
|
||||
var resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.NotEmpty(resources);
|
||||
Assert.Contains(resources, r => r.Name == "test_resource");
|
||||
}
|
||||
|
|
@ -56,7 +56,7 @@ public class McpServerResourceCapabilityIntegrationTests : ClientServerTestBase
|
|||
Assert.NotNull(client.ServerCapabilities.Resources);
|
||||
|
||||
// The resources should be exposed and listable
|
||||
var resources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var resources = await client.ListResourcesAsync(null, TestContext.Current.CancellationToken);
|
||||
Assert.NotEmpty(resources);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,19 +23,19 @@ public sealed class McpServerResourceRoutingTests(ITestOutputHelper testOutputHe
|
|||
// Regression test for https://github.com/modelcontextprotocol/csharp-sdk/issues/821.
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var nonTemplatedResult = await client.ReadResourceAsync("test://resource/non-templated", TestContext.Current.CancellationToken);
|
||||
var nonTemplatedResult = await client.ReadResourceAsync("test://resource/non-templated", null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal("static", ((TextResourceContents)nonTemplatedResult.Contents[0]).Text);
|
||||
|
||||
var templatedResult = await client.ReadResourceAsync("test://resource/12345", TestContext.Current.CancellationToken);
|
||||
var templatedResult = await client.ReadResourceAsync("test://resource/12345", null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal("template: 12345", ((TextResourceContents)templatedResult.Contents[0]).Text);
|
||||
|
||||
var exactTemplatedResult = await client.ReadResourceAsync("test://resource/{id}", TestContext.Current.CancellationToken);
|
||||
var exactTemplatedResult = await client.ReadResourceAsync("test://resource/{id}", null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal("template: {id}", ((TextResourceContents)exactTemplatedResult.Contents[0]).Text);
|
||||
|
||||
var paramsResult = await client.ReadResourceAsync("test://params?a1=a&a2=b&a3=c", TestContext.Current.CancellationToken);
|
||||
var paramsResult = await client.ReadResourceAsync("test://params?a1=a&a2=b&a3=c", null, TestContext.Current.CancellationToken);
|
||||
Assert.Equal("params: a, b, c", ((TextResourceContents)paramsResult.Contents[0]).Text);
|
||||
|
||||
var mcpEx = await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ReadResourceAsync("test://params{?a1,a2,a3}", TestContext.Current.CancellationToken));
|
||||
var mcpEx = await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ReadResourceAsync("test://params{?a1,a2,a3}", null, TestContext.Current.CancellationToken));
|
||||
Assert.Equal(McpErrorCode.InvalidParams, mcpEx.ErrorCode);
|
||||
Assert.Equal("Request failed (remote): Unknown resource URI: 'test://params{?a1,a2,a3}'", mcpEx.Message);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public partial class McpServerScopedTests : ClientServerTestBase
|
|||
{
|
||||
await using McpClient client = await CreateMcpClientForServer();
|
||||
|
||||
var tools = await client.ListToolsAsync(McpServerScopedTestsJsonContext.Default.Options, TestContext.Current.CancellationToken);
|
||||
var tools = await client.ListToolsAsync(new RequestOptions { JsonSerializerOptions = McpServerScopedTestsJsonContext.Default.Options }, TestContext.Current.CancellationToken);
|
||||
var tool = tools.First(t => t.Name == "echo_complex");
|
||||
Assert.DoesNotContain("\"complex\"", JsonSerializer.Serialize(tool.JsonSchema, McpJsonUtilities.DefaultOptions));
|
||||
|
||||
|
|
|
|||
448
tests/ModelContextProtocol.Tests/RequestOptionsTests.cs
Normal file
448
tests/ModelContextProtocol.Tests/RequestOptionsTests.cs
Normal file
|
|
@ -0,0 +1,448 @@
|
|||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using ModelContextProtocol.Protocol;
|
||||
|
||||
namespace ModelContextProtocol.Tests;
|
||||
|
||||
public static class RequestOptionsTests
|
||||
{
|
||||
[Fact]
|
||||
public static void RequestOptions_DefaultConstructor()
|
||||
{
|
||||
// Arrange & Act
|
||||
var options = new RequestOptions();
|
||||
|
||||
// Assert
|
||||
// ProgressToken and JsonSerializerOptions should be null by default
|
||||
Assert.Null(options.ProgressToken);
|
||||
Assert.Null(options.JsonSerializerOptions);
|
||||
// Meta should return a new (empty) JsonObject when accessed
|
||||
Assert.NotNull(options.Meta);
|
||||
Assert.Empty(options.Meta);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaSetter_SetsValue()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var metaValue = new JsonObject
|
||||
{
|
||||
["key"] = "value"
|
||||
};
|
||||
|
||||
// Act
|
||||
options.Meta = metaValue;
|
||||
|
||||
// Assert
|
||||
Assert.Same(metaValue, options.Meta);
|
||||
Assert.Equal("value", options.Meta["key"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_Meta_SetsSingleField()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
|
||||
// Act
|
||||
options.Meta!["key"] = "value";
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value", options.Meta!["key"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaSetter_NullValue_RemovesMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["key"] = "value" }
|
||||
};
|
||||
|
||||
// Act
|
||||
options.Meta = null;
|
||||
|
||||
// Assert - accessing Meta will create a new empty JsonObject
|
||||
Assert.NotNull(options.Meta);
|
||||
Assert.Empty(options.Meta);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaSetter_WithProgressToken_InMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var newMeta = new JsonObject
|
||||
{
|
||||
["custom"] = "data",
|
||||
["progressToken"] = "token"
|
||||
};
|
||||
|
||||
// Act
|
||||
options.Meta = newMeta;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("token", options.ProgressToken?.ToString());
|
||||
Assert.Equal("data", options.Meta["custom"]?.ToString());
|
||||
Assert.True(options.Meta.ContainsKey("progressToken"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenGetter_ReturnsNull_WhenNotSet()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["other"] = "value" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var token = options.ProgressToken;
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenSetter_StringToken_SetsInMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var token = new ProgressToken("my-token");
|
||||
|
||||
// Act
|
||||
options.ProgressToken = token;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("my-token", options.ProgressToken?.ToString());
|
||||
Assert.NotNull(options.Meta);
|
||||
Assert.Equal("my-token", options.Meta["progressToken"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenSetter_LongToken_SetsInMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var token = new ProgressToken(42L);
|
||||
|
||||
// Act
|
||||
options.ProgressToken = token;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("42", options.ProgressToken?.ToString());
|
||||
Assert.NotNull(options.Meta);
|
||||
Assert.Equal(42L, options.Meta["progressToken"]?.AsValue().GetValue<long>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenSetter_Null_RemovesFromMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
ProgressToken = new ProgressToken("token-to-remove")
|
||||
};
|
||||
|
||||
// Act
|
||||
options.ProgressToken = null;
|
||||
|
||||
// Assert
|
||||
Assert.Null(options.ProgressToken);
|
||||
Assert.False(options.Meta!.ContainsKey("progressToken"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenSetter_Null_WhenNoProgressToken_DoesNothing()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
|
||||
// Act
|
||||
options.ProgressToken = null;
|
||||
|
||||
// Assert
|
||||
Assert.Null(options.ProgressToken);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenGetter_StringValue_ReturnsCorrectToken()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["progressToken"] = "test-token" }
|
||||
};
|
||||
|
||||
// Act
|
||||
var token = options.ProgressToken;
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(token);
|
||||
Assert.Equal("test-token", token.Value.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenGetter_LongValue_ReturnsCorrectToken()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["progressToken"] = 123L }
|
||||
};
|
||||
|
||||
// Act
|
||||
var token = options.ProgressToken;
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(token);
|
||||
Assert.Equal("123", token.Value.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenGetter_InvalidValue_ReturnsNull()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["progressToken"] = new JsonObject() }
|
||||
};
|
||||
|
||||
// Act
|
||||
var token = options.ProgressToken;
|
||||
|
||||
// Assert
|
||||
Assert.Null(token);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_JsonSerializerOptions_GetSet()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var serializerOptions = new JsonSerializerOptions { WriteIndented = true };
|
||||
|
||||
// Act
|
||||
options.JsonSerializerOptions = serializerOptions;
|
||||
|
||||
// Assert
|
||||
Assert.Same(serializerOptions, options.JsonSerializerOptions);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaAndProgressToken_WorkTogether()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
|
||||
// Act - set progress token first
|
||||
options.ProgressToken = new ProgressToken("token1");
|
||||
options.Meta!["custom"] = "value1";
|
||||
|
||||
// Assert
|
||||
Assert.Equal("token1", options.ProgressToken?.ToString());
|
||||
Assert.Equal("value1", options.Meta["custom"]?.ToString());
|
||||
Assert.True(options.Meta.ContainsKey("progressToken"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressToken_OverwritesPreviousValue()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
ProgressToken = new ProgressToken("old-token")
|
||||
};
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken("new-token");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("new-token", options.ProgressToken?.ToString());
|
||||
Assert.Equal("new-token", options.Meta!["progressToken"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressToken_StringToLong_ChangesType()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
ProgressToken = new ProgressToken("string-token")
|
||||
};
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken(999L);
|
||||
|
||||
// Assert
|
||||
Assert.Equal("999", options.ProgressToken?.ToString());
|
||||
Assert.Equal(999L, options.Meta!["progressToken"]?.AsValue().GetValue<long>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_Meta_MultipleProperties_Preserved()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject
|
||||
{
|
||||
["prop1"] = "value1",
|
||||
["prop2"] = 42,
|
||||
["prop3"] = true
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken("my-token");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value1", options.Meta["prop1"]?.ToString());
|
||||
Assert.Equal(42, options.Meta["prop2"]?.AsValue().GetValue<int>());
|
||||
Assert.True(options.Meta["prop3"]?.AsValue().GetValue<bool>());
|
||||
Assert.Equal("my-token", options.Meta["progressToken"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaSetter_ReplacesExistingMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject
|
||||
{
|
||||
["old1"] = "value1",
|
||||
["old2"] = "value2"
|
||||
}
|
||||
};
|
||||
|
||||
var newMeta = new JsonObject
|
||||
{
|
||||
["new1"] = "newValue1",
|
||||
["new2"] = "newValue2"
|
||||
};
|
||||
|
||||
// Act
|
||||
options.Meta = newMeta;
|
||||
|
||||
// Assert
|
||||
Assert.False(options.Meta.ContainsKey("old1"));
|
||||
Assert.False(options.Meta.ContainsKey("old2"));
|
||||
Assert.Equal("newValue1", options.Meta["new1"]?.ToString());
|
||||
Assert.Equal("newValue2", options.Meta["new2"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_MetaSetter_NullWithNoProgressToken_ClearsMeta()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject { ["key"] = "value" }
|
||||
};
|
||||
|
||||
// Act
|
||||
options.Meta = null;
|
||||
|
||||
// Assert - getter creates new empty object
|
||||
Assert.NotNull(options.Meta);
|
||||
Assert.Empty(options.Meta);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ProgressTokenSetter_CreatesMetaIfNeeded()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
Assert.Null(options.ProgressToken);
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken("create-meta");
|
||||
|
||||
// Assert
|
||||
Assert.NotNull(options.ProgressToken);
|
||||
Assert.Equal("create-meta", options.ProgressToken?.ToString());
|
||||
Assert.True(options.Meta!.ContainsKey("progressToken"));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ComplexScenario_SetMetaThenProgressToken()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions
|
||||
{
|
||||
Meta = new JsonObject
|
||||
{
|
||||
["custom1"] = "value1",
|
||||
["custom2"] = 123
|
||||
}
|
||||
};
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken("scenario-token");
|
||||
|
||||
// Assert
|
||||
Assert.Equal("value1", options.Meta["custom1"]?.ToString());
|
||||
Assert.Equal(123, options.Meta["custom2"]?.AsValue().GetValue<int>());
|
||||
Assert.Equal("scenario-token", options.ProgressToken?.ToString());
|
||||
Assert.Equal(3, options.Meta.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ComplexScenario_SetMetaWithProgressToken()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
|
||||
// Act
|
||||
var newMeta = new JsonObject
|
||||
{
|
||||
["data1"] = "info1",
|
||||
["data2"] = false,
|
||||
["progressToken"] = 456L
|
||||
};
|
||||
options.Meta = newMeta;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("info1", options.Meta["data1"]?.ToString());
|
||||
Assert.False(options.Meta["data2"]?.AsValue().GetValue<bool>());
|
||||
Assert.Equal(456L, options.ProgressToken?.Token as long?);
|
||||
Assert.Equal(3, options.Meta.Count);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_ComplexScenario_SetMetaWithProgressUpdatesProgress()
|
||||
{
|
||||
// Arrange
|
||||
RequestOptions options = new();
|
||||
|
||||
// Act
|
||||
options.ProgressToken = new ProgressToken("token1");
|
||||
JsonObject meta = new() { ["progressToken"] = "token2" };
|
||||
options.Meta = meta;
|
||||
|
||||
// Assert
|
||||
Assert.Equal("token2", options.ProgressToken?.Token as string);
|
||||
Assert.Equal("token2", options.Meta["progressToken"]?.ToString());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public static void RequestOptions_AllProperties_CanBeSetIndependently()
|
||||
{
|
||||
// Arrange
|
||||
var options = new RequestOptions();
|
||||
var customOptions = new JsonSerializerOptions { WriteIndented = true };
|
||||
|
||||
// Act
|
||||
options.JsonSerializerOptions = customOptions;
|
||||
options.ProgressToken = new ProgressToken("independent");
|
||||
options.Meta!["field"] = "value";
|
||||
|
||||
// Assert
|
||||
Assert.Same(customOptions, options.JsonSerializerOptions);
|
||||
Assert.Equal("independent", options.ProgressToken?.ToString());
|
||||
Assert.Equal("value", options.Meta["field"]?.ToString());
|
||||
}
|
||||
}
|
||||
|
|
@ -31,7 +31,7 @@ public class EmptyCollectionTests : ClientServerTestBase
|
|||
var client = await CreateMcpClientForServer();
|
||||
|
||||
// Initially, the resource collection is empty
|
||||
var initialResources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var initialResources = await client.ListResourcesAsync(options: null, TestContext.Current.CancellationToken);
|
||||
Assert.Empty(initialResources);
|
||||
|
||||
// Add a resource dynamically
|
||||
|
|
@ -40,7 +40,7 @@ public class EmptyCollectionTests : ClientServerTestBase
|
|||
new() { UriTemplate = "test://resource/1" }));
|
||||
|
||||
// The resource should now be listed
|
||||
var updatedResources = await client.ListResourcesAsync(TestContext.Current.CancellationToken);
|
||||
var updatedResources = await client.ListResourcesAsync(options: null, TestContext.Current.CancellationToken);
|
||||
Assert.Single(updatedResources);
|
||||
Assert.Equal("test://resource/1", updatedResources[0].Uri);
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ public class EmptyCollectionTests : ClientServerTestBase
|
|||
var client = await CreateMcpClientForServer();
|
||||
|
||||
// Initially, the prompt collection is empty
|
||||
var initialPrompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
var initialPrompts = await client.ListPromptsAsync(options: null, TestContext.Current.CancellationToken);
|
||||
Assert.Empty(initialPrompts);
|
||||
|
||||
// Add a prompt dynamically
|
||||
|
|
@ -80,7 +80,7 @@ public class EmptyCollectionTests : ClientServerTestBase
|
|||
new() { Name = "test_prompt", Description = "A test prompt" }));
|
||||
|
||||
// The prompt should now be listed
|
||||
var updatedPrompts = await client.ListPromptsAsync(TestContext.Current.CancellationToken);
|
||||
var updatedPrompts = await client.ListPromptsAsync(options: null, TestContext.Current.CancellationToken);
|
||||
Assert.Single(updatedPrompts);
|
||||
Assert.Equal("test_prompt", updatedPrompts[0].Name);
|
||||
}
|
||||
|
|
@ -96,7 +96,7 @@ public class EmptyCollectionTests : ClientServerTestBase
|
|||
new() { UriTemplate = "test://resource/dynamic" }));
|
||||
|
||||
// Read the resource
|
||||
var result = await client.ReadResourceAsync("test://resource/dynamic", TestContext.Current.CancellationToken);
|
||||
var result = await client.ReadResourceAsync("test://resource/dynamic", options: null, TestContext.Current.CancellationToken);
|
||||
Assert.NotNull(result);
|
||||
Assert.Single(result.Contents);
|
||||
Assert.IsType<TextResourceContents>(result.Contents[0]);
|
||||
|
|
@ -159,7 +159,7 @@ public class NullCollectionTests : ClientServerTestBase
|
|||
var client = await CreateMcpClientForServer();
|
||||
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ListToolsAsync(cancellationToken: TestContext.Current.CancellationToken));
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ListPromptsAsync(TestContext.Current.CancellationToken));
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ListResourcesAsync(TestContext.Current.CancellationToken));
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ListPromptsAsync(cancellationToken: TestContext.Current.CancellationToken));
|
||||
await Assert.ThrowsAsync<McpProtocolException>(async () => await client.ListResourcesAsync(cancellationToken: TestContext.Current.CancellationToken));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue