Implement SEP-973: Icons and metadata support for Implementations, Resources, Tools, and Prompts (#802)

* Initial plan

* Initial plan for SEP-973 implementation

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Implement SEP-973: Add Icon class and icon support to Implementation, Resource, Tool, and Prompt

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Complete SEP-973 implementation with documentation and fix property consistency

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Address code review feedback: improve tests, remove docs, split test files

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Fix tests to use S.T.J. source generator

* Styling fixes

* Simplify Icons property documentation as suggested in code review

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Rename Icon.Src property to Icon.Source for more .NET-y naming

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Add icon support to McpServerTool infrastructure and attributes

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Cleanup

* Refactor icon tests to use Delegate overload instead of MethodInfo pattern

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Change Icon.Sizes property from string to IList<string> per spec update

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>

* Add icon support to Prompts and Resources (CreateOptions and Attributes)

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Add client-server integration tests for Icons and WebsiteUrl

Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>

* Fix nullability warnings

* Revert "Add client-server integration tests for Icons and WebsiteUrl"

This reverts commit f37de15569e54e86a0709121c9bdf80c5045511c.

* More nullability fixes

* Add more tests

* Address code review feedback: add Theme property, fix docs, improve test patterns

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>

* Update Icon tests to exercise Theme property

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>

* Add Theme property assertions to client-server integration tests

Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>

* Fix nullability

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: MackinnonBuck <10456961+MackinnonBuck@users.noreply.github.com>
Co-authored-by: Mackinnon Buck <mackinnon.buck@gmail.com>
Co-authored-by: jozkee <16040868+jozkee@users.noreply.github.com>
Co-authored-by: David Cantú <dacantu@microsoft.com>
This commit is contained in:
Copilot 2025-10-08 12:23:01 -07:00 • committed by GitHub
commit f28639119b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -678,6 +678,71 @@ public partial class McpServerToolTests
record Person(string Name, int Age);
[Fact]
public void SupportsIconsInCreateOptions()
{
var icons = new List<Icon>
{
new() { Source = "https://example.com/icon.png", MimeType = "image/png", Sizes = new List<string> { "48x48" } },
new() { Source = "https://example.com/icon.svg", MimeType = "image/svg+xml", Sizes = new List<string> { "any" } }
};
McpServerTool tool = McpServerTool.Create(() => "test", new McpServerToolCreateOptions
{
Icons = icons
});
Assert.NotNull(tool.ProtocolTool.Icons);
Assert.Equal(2, tool.ProtocolTool.Icons.Count);
Assert.Equal("https://example.com/icon.png", tool.ProtocolTool.Icons[0].Source);
Assert.Equal("image/png", tool.ProtocolTool.Icons[0].MimeType);
Assert.NotNull(tool.ProtocolTool.Icons[0].Sizes);
Assert.Single(tool.ProtocolTool.Icons[0].Sizes!);
Assert.Equal("48x48", tool.ProtocolTool.Icons[0].Sizes![0]);
Assert.Equal("https://example.com/icon.svg", tool.ProtocolTool.Icons[1].Source);
Assert.Equal("image/svg+xml", tool.ProtocolTool.Icons[1].MimeType);
Assert.NotNull(tool.ProtocolTool.Icons[1].Sizes);
Assert.Single(tool.ProtocolTool.Icons[1].Sizes!);
Assert.Equal("any", tool.ProtocolTool.Icons[1].Sizes![0]);
}
[Fact]
public void SupportsIconSourceInAttribute()
{
McpServerTool tool = McpServerTool.Create([McpServerTool(IconSource = "https://example.com/tool-icon.png")] () => "result");
var icon = Assert.Single(tool.ProtocolTool.Icons!);
Assert.Equal("https://example.com/tool-icon.png", icon.Source);
Assert.Null(icon.MimeType);
Assert.Null(icon.Sizes);
}
[Fact]
public void CreateOptionsIconsOverrideAttributeIconSource()
{
var optionsIcons = new List<Icon>
{
new() { Source = "https://example.com/override-icon.svg", MimeType = "image/svg+xml" }
};
McpServerTool tool = McpServerTool.Create([McpServerTool(IconSource = "https://example.com/tool-icon.png")] () => "result", new McpServerToolCreateOptions
{
Icons = optionsIcons
});
var icon = Assert.Single(tool.ProtocolTool.Icons!);
Assert.Equal("https://example.com/override-icon.svg", icon.Source);
Assert.Equal("image/svg+xml", icon.MimeType);
}
[Fact]
public void SupportsToolWithoutIcons()
{
McpServerTool tool = McpServerTool.Create([McpServerTool] () => "result");
Assert.Null(tool.ProtocolTool.Icons);
}
[JsonSourceGenerationOptions(PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase)]
[JsonSerializable(typeof(DisposableToolType))]
[JsonSerializable(typeof(AsyncDisposableToolType))]