-
Notifications
You must be signed in to change notification settings - Fork 546
Everything server #151
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
halter73
merged 51 commits into
modelcontextprotocol:main
from
aaronpowell:dotnet-everything
Apr 4, 2025
Merged
Everything server #151
Changes from 45 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
618e1df
wip of server-everything
aaronpowell ef9ca63
Sharing the tiny image
aaronpowell fb83e20
Cleanup from bad merge
aaronpowell 1862fe4
Adding a second call tool handler
aaronpowell 393c671
Fixing broken props file
aaronpowell 42dae94
Merge branch 'main' into dotnet-everything
aaronpowell c393bdd
Merge branch 'main' into dotnet-everything
aaronpowell 1ef2688
Updating to latest API design
aaronpowell 646d441
Merge branch 'main' into dotnet-everything
aaronpowell 4893d70
WIP
aaronpowell 5bbf1c2
Adding support for returning collections from tools
aaronpowell 933f9f6
Merge branch 'more-tool-return-support' into dotnet-everything
aaronpowell b75b6bc
Updating
aaronpowell 3203b80
Merge branch 'main' into dotnet-everything
aaronpowell b9dfc51
Completing the tools as best as possible
aaronpowell e3f5fa6
Working on improvements to resources
aaronpowell e5227b2
WIP
aaronpowell ca59b30
Supporting resource templates with read resource callback
aaronpowell c53abef
Aligning the resource data structures with the 2024-11-05 schema
aaronpowell d08062c
Apply suggestions from code review
aaronpowell 3ed3b99
Putting files back where they belong
aaronpowell 7c9bcc3
Merge branch 'main' into improving-resources
aaronpowell a49c560
Adding an internal constructor to prevent overloads
aaronpowell 25d54a6
Addressing feedback
aaronpowell 54c7829
Apply suggestions from code review
aaronpowell f24dd18
Update src/ModelContextProtocol/Protocol/Types/ResourceContents.cs
stephentoub d753183
Addressing feedback
aaronpowell f055547
Merge branch 'improving-resources' of https://github.com/aaronpowell/…
aaronpowell 59dadaa
Merge branch 'main' into improving-resources
aaronpowell 07dcebb
Merge branch 'improving-resources' into dotnet-everything
aaronpowell 0358a34
Updating from merge
aaronpowell 061316b
Handling complex prompt properly
aaronpowell 6889563
Adding subscriptions
aaronpowell ddf39ee
Adding completion handler
aaronpowell bcb195e
Merge branch 'main' into dotnet-everything
aaronpowell cab54dd
Moving to using the PromptType attribute
aaronpowell 8f318f5
Implementing annotated tool call
aaronpowell 20ab476
Improving how to setup logging and implementing logging handler
aaronpowell e841b95
Merge branch 'main' into dotnet-everything
aaronpowell aadf1c7
Apply suggestions from code review
aaronpowell aa27fba
Merge branch 'main' into dotnet-everything
aaronpowell 6ba94be
Fixing build errors
aaronpowell dd1ffe5
Converting to proper background services
aaronpowell 7519f90
Adding tests for setlogginghandler
aaronpowell 1f25fe3
Fixing primary constructor usgae
aaronpowell 387e8fe
Apply suggestions from code review
aaronpowell 6428259
Apply suggestions from code review
aaronpowell f61784a
Fixing compiler error
aaronpowell 3be1849
Merge branch 'main' into dotnet-everything
aaronpowell 7352efb
Merge branch 'main' into dotnet-everything
stephentoub 7fece16
Fix McpServerException rename
stephentoub File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| <Project Sdk="Microsoft.NET.Sdk"> | ||
|
|
||
| <PropertyGroup> | ||
| <TargetFramework>net9.0</TargetFramework> | ||
| <Nullable>enable</Nullable> | ||
| <ImplicitUsings>enable</ImplicitUsings> | ||
| <OutputType>Exe</OutputType> | ||
| </PropertyGroup> | ||
|
|
||
| <ItemGroup> | ||
| <PackageReference Include="Microsoft.Extensions.Hosting" /> | ||
| </ItemGroup> | ||
|
|
||
| <ItemGroup> | ||
| <ProjectReference Include="..\..\src\ModelContextProtocol\ModelContextProtocol.csproj" /> | ||
| </ItemGroup> | ||
|
|
||
| </Project> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
| using ModelContextProtocol; | ||
| using ModelContextProtocol.Protocol.Types; | ||
| using ModelContextProtocol.Server; | ||
|
|
||
| namespace EverythingServer; | ||
|
|
||
| public class LoggingUpdateMessageSender(IMcpServer server, Func<LoggingLevel> currentLevel) : BackgroundService | ||
| { | ||
| readonly Dictionary<LoggingLevel, string> _loggingLevelMap = new() | ||
| { | ||
| { LoggingLevel.Debug, "Debug-level message" }, | ||
| { LoggingLevel.Info, "Info-level message" }, | ||
| { LoggingLevel.Notice, "Notice-level message" }, | ||
| { LoggingLevel.Warning, "Warning-level message" }, | ||
| { LoggingLevel.Error, "Error-level message" }, | ||
| { LoggingLevel.Critical, "Critical-level message" }, | ||
| { LoggingLevel.Alert, "Alert-level message" }, | ||
| { LoggingLevel.Emergency, "Emergency-level message" } | ||
| }; | ||
|
|
||
| protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||
| { | ||
| while (!stoppingToken.IsCancellationRequested) | ||
| { | ||
| var newLevel = (LoggingLevel)Random.Shared.Next(_loggingLevelMap.Count); | ||
|
|
||
| var message = new | ||
| { | ||
| Level = newLevel.ToString().ToLower(), | ||
| Data = _loggingLevelMap[newLevel], | ||
| }; | ||
|
|
||
| if (newLevel > currentLevel()) | ||
aaronpowell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| { | ||
| await server.SendNotificationAsync("notifications/message", message, cancellationToken: stoppingToken); | ||
| } | ||
|
|
||
| await Task.Delay(15000, stoppingToken); | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,195 @@ | ||
| using EverythingServer; | ||
| using EverythingServer.Prompts; | ||
| using EverythingServer.Tools; | ||
| using Microsoft.Extensions.AI; | ||
| using Microsoft.Extensions.DependencyInjection; | ||
| using Microsoft.Extensions.Hosting; | ||
| using Microsoft.Extensions.Logging; | ||
| using ModelContextProtocol; | ||
| using ModelContextProtocol.Protocol.Types; | ||
| using ModelContextProtocol.Server; | ||
|
|
||
| var builder = Host.CreateApplicationBuilder(args); | ||
| builder.Logging.AddConsole(consoleLogOptions => | ||
| { | ||
| // Configure all logs to go to stderr | ||
| consoleLogOptions.LogToStandardErrorThreshold = LogLevel.Trace; | ||
| }); | ||
|
|
||
| HashSet<string> subscriptions = []; | ||
| var _minimumLoggingLevel = LoggingLevel.Debug; | ||
|
|
||
| builder.Services | ||
| .AddMcpServer() | ||
| .WithStdioServerTransport() | ||
| .WithTools<AddTool>() | ||
| .WithTools<AnnotatedMessageTool>() | ||
| .WithTools<EchoTool>() | ||
| .WithTools<LongRunningTool>() | ||
| .WithTools<PrintEnvTool>() | ||
| .WithTools<SampleLlmTool>() | ||
| .WithTools<TinyImageTool>() | ||
| .WithPrompts<ComplexPromptType>() | ||
| .WithPrompts<SimplePromptType>() | ||
| .WithListResourceTemplatesHandler((ctx, ct) => | ||
| { | ||
| return Task.FromResult(new ListResourceTemplatesResult | ||
| { | ||
| ResourceTemplates = | ||
| [ | ||
| new ResourceTemplate { Name = "Static Resource", Description = "A static resource with a numeric ID", UriTemplate = "test://static/resource/{id}" } | ||
| ] | ||
| }); | ||
| }) | ||
| .WithReadResourceHandler((ctx, ct) => | ||
| { | ||
| var uri = ctx.Params?.Uri; | ||
|
|
||
| if (uri is null || !uri.StartsWith("test://static/resource/")) | ||
| { | ||
| throw new NotSupportedException($"Unknown resource: {uri}"); | ||
| } | ||
|
|
||
| int index = int.Parse(uri["test://static/resource/".Length..]) - 1; | ||
|
|
||
| if (index < 0 || index >= ResourceGenerator.Resources.Count) | ||
| { | ||
| throw new NotSupportedException($"Unknown resource: {uri}"); | ||
| } | ||
|
|
||
| var resource = ResourceGenerator.Resources[index]; | ||
|
|
||
| if (resource.MimeType == "text/plain") | ||
| { | ||
| return Task.FromResult(new ReadResourceResult | ||
| { | ||
| Contents = [new TextResourceContents | ||
| { | ||
| Text = resource.Description!, | ||
| MimeType = resource.MimeType, | ||
| Uri = resource.Uri, | ||
| }] | ||
| }); | ||
| } | ||
| else | ||
| { | ||
| return Task.FromResult(new ReadResourceResult | ||
| { | ||
| Contents = [new BlobResourceContents | ||
| { | ||
| Blob = resource.Description!, | ||
| MimeType = resource.MimeType, | ||
| Uri = resource.Uri, | ||
| }] | ||
| }); | ||
| } | ||
| }) | ||
| .WithSubscribeToResourcesHandler(async (ctx, ct) => | ||
| { | ||
| var uri = ctx.Params?.Uri; | ||
|
|
||
| if (uri is not null) | ||
| { | ||
| subscriptions.Add(uri); | ||
|
|
||
| await ctx.Server.RequestSamplingAsync([ | ||
| new ChatMessage(ChatRole.System, "You are a helpful test server"), | ||
| new ChatMessage(ChatRole.User, $"Resource {uri}, context: A new subscription was started"), | ||
| ], | ||
| options: new ChatOptions | ||
| { | ||
| MaxOutputTokens = 100, | ||
| Temperature = 0.7f, | ||
| }, | ||
| cancellationToken: ct); | ||
| } | ||
|
|
||
| return new EmptyResult(); | ||
| }) | ||
| .WithUnsubscribeFromResourcesHandler((ctx, ct) => | ||
| { | ||
| var uri = ctx.Params?.Uri; | ||
| if (uri is not null) | ||
| { | ||
| subscriptions.Remove(uri); | ||
| } | ||
| return Task.FromResult(new EmptyResult()); | ||
| }) | ||
| .WithGetCompletionHandler((ctx, ct) => | ||
| { | ||
| var exampleCompletions = new Dictionary<string, IEnumerable<string>> | ||
| { | ||
| { "style", ["casual", "formal", "technical", "friendly"] }, | ||
| { "temperature", ["0", "0.5", "0.7", "1.0"] }, | ||
| { "resourceId", ["1", "2", "3", "4", "5"] } | ||
| }; | ||
|
|
||
| var @ref = ctx.Params?.Ref; | ||
|
|
||
| if (@ref is null) | ||
| { | ||
| throw new NotSupportedException($"Reference is required."); | ||
| } | ||
|
|
||
| var argument = ctx.Params!.Argument; | ||
aaronpowell marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if (@ref.Type == "ref/resource") | ||
| { | ||
| var resourceId = @ref.Uri?.Split("/").Last(); | ||
|
|
||
| if (resourceId is null) | ||
| { | ||
| return Task.FromResult(new CompleteResult()); | ||
| } | ||
|
|
||
| var values = exampleCompletions["resourceId"].Where(id => id.StartsWith(argument.Value)); | ||
|
|
||
| return Task.FromResult(new CompleteResult | ||
| { | ||
| Completion = new Completion { Values = [..values], HasMore = false, Total = values.Count() } | ||
| }); | ||
| } | ||
|
|
||
| if (@ref.Type == "ref/prompt") | ||
| { | ||
| if (!exampleCompletions.TryGetValue(argument.Name, out IEnumerable<string>? value)) | ||
| { | ||
| throw new NotSupportedException($"Unknown argument name: {argument.Name}"); | ||
| } | ||
|
|
||
| var values = value.Where(value => value.StartsWith(argument.Value)); | ||
| return Task.FromResult(new CompleteResult | ||
| { | ||
| Completion = new Completion { Values = [..values], HasMore = false, Total = values.Count() } | ||
| }); | ||
| } | ||
|
|
||
| throw new NotSupportedException($"Unknown reference type: {@ref.Type}"); | ||
| }) | ||
| .WithSetLoggingLevelHandler(async (ctx, ct) => | ||
| { | ||
| if (ctx.Params?.Level is null) | ||
| { | ||
| throw new McpServerException("Missing required argument 'level'"); | ||
| } | ||
|
|
||
| _minimumLoggingLevel = ctx.Params.Level; | ||
|
|
||
| await ctx.Server.SendNotificationAsync("notifications/message", new | ||
| { | ||
| Level = "debug", | ||
| Logger = "test-server", | ||
| Data = $"Logging level set to {_minimumLoggingLevel}", | ||
| }, cancellationToken: ct); | ||
|
|
||
| return new EmptyResult(); | ||
| }) | ||
| ; | ||
|
|
||
| builder.Services.AddSingleton(subscriptions); | ||
| builder.Services.AddHostedService<SubscriptionMessageSender>(); | ||
| builder.Services.AddHostedService<LoggingUpdateMessageSender>(); | ||
|
|
||
| builder.Services.AddSingleton<Func<LoggingLevel>>(_ => () => _minimumLoggingLevel); | ||
|
|
||
| await builder.Build().RunAsync(); | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,22 @@ | ||
| using EverythingServer.Tools; | ||
| using Microsoft.Extensions.AI; | ||
| using ModelContextProtocol.Server; | ||
| using System.ComponentModel; | ||
|
|
||
| namespace EverythingServer.Prompts; | ||
|
|
||
| [McpServerPromptType] | ||
| public class ComplexPromptType | ||
| { | ||
| [McpServerPrompt(Name = "complex_prompt"), Description("A prompt with arguments")] | ||
| public static IEnumerable<ChatMessage> ComplexPrompt( | ||
| [Description("Temperature setting")] int temperature, | ||
| [Description("Output style")] string? style = null) | ||
| { | ||
| return [ | ||
| new ChatMessage(ChatRole.User,$"This is a complex prompt with arguments: temperature={temperature}, style={style}"), | ||
| new ChatMessage(ChatRole.Assistant, "I understand. You've provided a complex prompt with temperature and style arguments. How would you like me to proceed?"), | ||
| new ChatMessage(ChatRole.User, [new DataContent(TinyImageTool.MCP_TINY_IMAGE)]) | ||
| ]; | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using ModelContextProtocol.Server; | ||
| using System.ComponentModel; | ||
|
|
||
| namespace EverythingServer.Prompts; | ||
|
|
||
| [McpServerPromptType] | ||
| public class SimplePromptType | ||
| { | ||
| [McpServerPrompt(Name = "simple_prompt"), Description("A prompt without arguments")] | ||
| public static string SimplePrompt() => "This is a simple prompt without arguments"; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| using ModelContextProtocol.Protocol.Types; | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Linq; | ||
|
|
||
| namespace EverythingServer; | ||
|
|
||
| static class ResourceGenerator | ||
| { | ||
| private static readonly List<Resource> _resources = Enumerable.Range(1, 100).Select(i => | ||
| { | ||
| var uri = $"test://static/resource/{i}"; | ||
| if (i % 2 != 0) | ||
| { | ||
| return new Resource | ||
| { | ||
| Uri = uri, | ||
| Name = $"Resource {i}", | ||
| MimeType = "text/plain", | ||
| Description = $"Resource {i}: This is a plaintext resource" | ||
| }; | ||
| } | ||
| else | ||
| { | ||
| var buffer = System.Text.Encoding.UTF8.GetBytes($"Resource {i}: This is a base64 blob"); | ||
| return new Resource | ||
| { | ||
| Uri = uri, | ||
| Name = $"Resource {i}", | ||
| MimeType = "application/octet-stream", | ||
| Description = Convert.ToBase64String(buffer) | ||
| }; | ||
| } | ||
| }).ToList(); | ||
|
|
||
| public static IReadOnlyList<Resource> Resources => _resources; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| using Microsoft.Extensions.Hosting; | ||
| using ModelContextProtocol; | ||
| using ModelContextProtocol.Server; | ||
|
|
||
| internal class SubscriptionMessageSender(IMcpServer server, HashSet<string> subscriptions) : BackgroundService | ||
| { | ||
| protected override async Task ExecuteAsync(CancellationToken stoppingToken) | ||
| { | ||
| while (!stoppingToken.IsCancellationRequested) | ||
| { | ||
| foreach (var uri in subscriptions) | ||
| { | ||
| await server.SendNotificationAsync("notifications/resource/updated", | ||
| new | ||
| { | ||
| Uri = uri, | ||
| }, cancellationToken: stoppingToken); | ||
| } | ||
|
|
||
| await Task.Delay(5000, stoppingToken); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,11 @@ | ||
| using ModelContextProtocol.Server; | ||
| using System.ComponentModel; | ||
|
|
||
| namespace EverythingServer.Tools; | ||
|
|
||
| [McpServerToolType] | ||
| public class AddTool | ||
| { | ||
| [McpServerTool(Name = "add"), Description("Adds two numbers.")] | ||
| public static string Add(int a, int b) => $"The sum of {a} and {b} is {a + b}"; | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.