Add WithTools/Prompts overloads that accept McpServerTool/Prompts (#319)
To avoid folks needing to know how you should be registering tool instances into DI.
This commit is contained in:
parent
227f440ab9
commit
b0cd6541ce
4 changed files with 58 additions and 6 deletions
|
|
@ -57,7 +57,29 @@ public static partial class McpServerBuilderExtensions
|
||||||
|
|
||||||
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="toolTypes">Types with marked methods to add as tools to the server.</param>
|
/// <param name="tools">The <see cref="McpServerTool"/> instances to add to the server.</param>
|
||||||
|
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="tools"/> is <see langword="null"/>.</exception>
|
||||||
|
public static IMcpServerBuilder WithTools(this IMcpServerBuilder builder, IEnumerable<McpServerTool> tools)
|
||||||
|
{
|
||||||
|
Throw.IfNull(builder);
|
||||||
|
Throw.IfNull(tools);
|
||||||
|
|
||||||
|
foreach (var tool in tools)
|
||||||
|
{
|
||||||
|
if (tool is not null)
|
||||||
|
{
|
||||||
|
builder.Services.AddSingleton(tool);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds <see cref="McpServerTool"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
||||||
|
/// <param name="builder">The builder instance.</param>
|
||||||
|
/// <param name="toolTypes">Types with <see cref="McpServerToolAttribute"/>-attributed methods to add as tools to the server.</param>
|
||||||
/// <param name="serializerOptions">The serializer options governing tool parameter marshalling.</param>
|
/// <param name="serializerOptions">The serializer options governing tool parameter marshalling.</param>
|
||||||
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
|
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
|
||||||
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
|
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
|
||||||
|
|
@ -173,6 +195,28 @@ public static partial class McpServerBuilderExtensions
|
||||||
return builder;
|
return builder;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Adds <see cref="McpServerPrompt"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
||||||
|
/// <param name="builder">The builder instance.</param>
|
||||||
|
/// <param name="prompts">The <see cref="McpServerPrompt"/> instances to add to the server.</param>
|
||||||
|
/// <returns>The builder provided in <paramref name="builder"/>.</returns>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="builder"/> is <see langword="null"/>.</exception>
|
||||||
|
/// <exception cref="ArgumentNullException"><paramref name="prompts"/> is <see langword="null"/>.</exception>
|
||||||
|
public static IMcpServerBuilder WithPrompts(this IMcpServerBuilder builder, IEnumerable<McpServerPrompt> prompts)
|
||||||
|
{
|
||||||
|
Throw.IfNull(builder);
|
||||||
|
Throw.IfNull(prompts);
|
||||||
|
|
||||||
|
foreach (var prompt in prompts)
|
||||||
|
{
|
||||||
|
if (prompt is not null)
|
||||||
|
{
|
||||||
|
builder.Services.AddSingleton(prompt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Adds <see cref="McpServerPrompt"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
/// <summary>Adds <see cref="McpServerPrompt"/> instances to the service collection backing <paramref name="builder"/>.</summary>
|
||||||
/// <param name="builder">The builder instance.</param>
|
/// <param name="builder">The builder instance.</param>
|
||||||
/// <param name="promptTypes">Types with marked methods to add as prompts to the server.</param>
|
/// <param name="promptTypes">Types with marked methods to add as prompts to the server.</param>
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,10 @@ public class McpClientExtensionsTests : ClientServerTestBase
|
||||||
for (int f = 0; f < 10; f++)
|
for (int f = 0; f < 10; f++)
|
||||||
{
|
{
|
||||||
string name = $"Method{f}";
|
string name = $"Method{f}";
|
||||||
services.AddSingleton(McpServerTool.Create((int i) => $"{name} Result {i}", new() { Name = name }));
|
mcpServerBuilder.WithTools([McpServerTool.Create((int i) => $"{name} Result {i}", new() { Name = name })]);
|
||||||
}
|
}
|
||||||
services.AddSingleton(McpServerTool.Create([McpServerTool(Destructive = false, OpenWorld = true)] (string i) => $"{i} Result", new() { Name = "ValuesSetViaAttr" }));
|
mcpServerBuilder.WithTools([McpServerTool.Create([McpServerTool(Destructive = false, OpenWorld = true)] (string i) => $"{i} Result", new() { Name = "ValuesSetViaAttr" })]);
|
||||||
services.AddSingleton(McpServerTool.Create([McpServerTool(Destructive = false, OpenWorld = true)] (string i) => $"{i} Result", new() { Name = "ValuesSetViaOptions", Destructive = true, OpenWorld = false, ReadOnly = true }));
|
mcpServerBuilder.WithTools([McpServerTool.Create([McpServerTool(Destructive = false, OpenWorld = true)] (string i) => $"{i} Result", new() { Name = "ValuesSetViaOptions", Destructive = true, OpenWorld = false, ReadOnly = true })]);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Theory]
|
[Theory]
|
||||||
|
|
|
||||||
|
|
@ -202,6 +202,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
||||||
{
|
{
|
||||||
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>("prompts", () => builder.WithPrompts((IEnumerable<McpServerPrompt>)null!));
|
||||||
Assert.Throws<ArgumentNullException>("promptTypes", () => builder.WithPrompts((IEnumerable<Type>)null!));
|
Assert.Throws<ArgumentNullException>("promptTypes", () => builder.WithPrompts((IEnumerable<Type>)null!));
|
||||||
|
|
||||||
IMcpServerBuilder nullBuilder = null!;
|
IMcpServerBuilder nullBuilder = null!;
|
||||||
|
|
@ -215,6 +216,7 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
||||||
{
|
{
|
||||||
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
||||||
|
|
||||||
|
builder.WithPrompts(prompts: []); // no exception
|
||||||
builder.WithPrompts(promptTypes: []); // no exception
|
builder.WithPrompts(promptTypes: []); // no exception
|
||||||
builder.WithPrompts<object>(); // no exception even though no prompts exposed
|
builder.WithPrompts<object>(); // no exception even though no prompts exposed
|
||||||
builder.WithPromptsFromAssembly(typeof(AIFunction).Assembly); // no exception even though no prompts exposed
|
builder.WithPromptsFromAssembly(typeof(AIFunction).Assembly); // no exception even though no prompts exposed
|
||||||
|
|
@ -236,13 +238,15 @@ public partial class McpServerBuilderExtensionsPromptsTests : ClientServerTestBa
|
||||||
ServiceCollection sc = new();
|
ServiceCollection sc = new();
|
||||||
sc.AddMcpServer()
|
sc.AddMcpServer()
|
||||||
.WithPrompts<SimplePrompts>()
|
.WithPrompts<SimplePrompts>()
|
||||||
.WithPrompts<MorePrompts>(JsonContext4.Default.Options);
|
.WithPrompts<MorePrompts>(JsonContext4.Default.Options)
|
||||||
|
.WithPrompts([McpServerPrompt.Create(() => "42", new() { Name = "Returns42" })]);
|
||||||
IServiceProvider services = sc.BuildServiceProvider();
|
IServiceProvider services = sc.BuildServiceProvider();
|
||||||
|
|
||||||
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ReturnsChatMessages));
|
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ReturnsChatMessages));
|
||||||
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ThrowsException));
|
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ThrowsException));
|
||||||
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ReturnsString));
|
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(SimplePrompts.ReturnsString));
|
||||||
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(MorePrompts.AnotherPrompt));
|
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == nameof(MorePrompts.AnotherPrompt));
|
||||||
|
Assert.Contains(services.GetServices<McpServerPrompt>(), t => t.ProtocolPrompt.Name == "Returns42");
|
||||||
}
|
}
|
||||||
|
|
||||||
[McpServerPromptType]
|
[McpServerPromptType]
|
||||||
|
|
|
||||||
|
|
@ -408,6 +408,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
|
||||||
{
|
{
|
||||||
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
||||||
|
|
||||||
|
Assert.Throws<ArgumentNullException>("tools", () => builder.WithTools((IEnumerable<McpServerTool>)null!));
|
||||||
Assert.Throws<ArgumentNullException>("toolTypes", () => builder.WithTools((IEnumerable<Type>)null!));
|
Assert.Throws<ArgumentNullException>("toolTypes", () => builder.WithTools((IEnumerable<Type>)null!));
|
||||||
|
|
||||||
IMcpServerBuilder nullBuilder = null!;
|
IMcpServerBuilder nullBuilder = null!;
|
||||||
|
|
@ -421,6 +422,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
|
||||||
{
|
{
|
||||||
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
IMcpServerBuilder builder = new ServiceCollection().AddMcpServer();
|
||||||
|
|
||||||
|
builder.WithTools(tools: []); // no exception
|
||||||
builder.WithTools(toolTypes: []); // no exception
|
builder.WithTools(toolTypes: []); // no exception
|
||||||
builder.WithTools<object>(); // no exception even though no tools exposed
|
builder.WithTools<object>(); // no exception even though no tools exposed
|
||||||
builder.WithToolsFromAssembly(typeof(AIFunction).Assembly); // no exception even though no tools exposed
|
builder.WithToolsFromAssembly(typeof(AIFunction).Assembly); // no exception even though no tools exposed
|
||||||
|
|
@ -539,7 +541,8 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
|
||||||
sc.AddMcpServer()
|
sc.AddMcpServer()
|
||||||
.WithTools<EchoTool>(serializerOptions: BuilderToolsJsonContext.Default.Options)
|
.WithTools<EchoTool>(serializerOptions: BuilderToolsJsonContext.Default.Options)
|
||||||
.WithTools<AnotherToolType>(serializerOptions: BuilderToolsJsonContext.Default.Options)
|
.WithTools<AnotherToolType>(serializerOptions: BuilderToolsJsonContext.Default.Options)
|
||||||
.WithTools([typeof(ToolTypeWithNoAttribute)], BuilderToolsJsonContext.Default.Options);
|
.WithTools([typeof(ToolTypeWithNoAttribute)], BuilderToolsJsonContext.Default.Options)
|
||||||
|
.WithTools([McpServerTool.Create(() => "42", new() { Name = "Returns42" })]);
|
||||||
IServiceProvider services = sc.BuildServiceProvider();
|
IServiceProvider services = sc.BuildServiceProvider();
|
||||||
|
|
||||||
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "double_echo");
|
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "double_echo");
|
||||||
|
|
@ -547,6 +550,7 @@ public partial class McpServerBuilderExtensionsToolsTests : ClientServerTestBase
|
||||||
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodB");
|
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodB");
|
||||||
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodC");
|
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodC");
|
||||||
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodD");
|
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "MethodD");
|
||||||
|
Assert.Contains(services.GetServices<McpServerTool>(), t => t.ProtocolTool.Name == "Returns42");
|
||||||
}
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue