Add initial task extension conformance coverage (#1734)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: 7c99928c-a943-49f4-9a63-8b04a0e4d18b
This commit is contained in:
parent
f7c61fafdb
commit
83523b0a42
3 changed files with 463 additions and 43 deletions
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="../../src/ModelContextProtocol.AspNetCore/ModelContextProtocol.AspNetCore.csproj" />
|
||||
<ProjectReference Include="../../src/ModelContextProtocol.Extensions.Tasks/ModelContextProtocol.Extensions.Tasks.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ using ConformanceServer.Resources;
|
|||
using ConformanceServer.Tools;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
using ModelContextProtocol.Extensions.Tasks;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Text.Json;
|
||||
|
|
@ -52,7 +53,14 @@ public class Program
|
|||
.AddMcpServer()
|
||||
.WithHttpTransport(options => options.Stateless = stateless)
|
||||
.WithDistributedCacheEventStreamStore()
|
||||
.WithTasks(
|
||||
new InMemoryMcpTaskStore
|
||||
{
|
||||
DefaultPollIntervalMs = 50,
|
||||
DefaultTimeToLive = TimeSpan.FromMinutes(5),
|
||||
})
|
||||
.WithTools<ConformanceTools>()
|
||||
.WithTools<ConformanceTaskTools>()
|
||||
.WithTools<IncompleteResultTools>()
|
||||
.WithTools([ConformanceTools.CreateJsonSchema202012Tool()])
|
||||
.WithRequestFilters(filters => filters.AddCallToolFilter(next => async (request, cancellationToken) =>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,102 @@
|
|||
#pragma warning disable MCPEXP001 // MRTR (SEP-2322) is experimental.
|
||||
|
||||
using ModelContextProtocol;
|
||||
using ModelContextProtocol.Protocol;
|
||||
using ModelContextProtocol.Server;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace ConformanceServer.Tools;
|
||||
|
||||
[McpServerToolType]
|
||||
public sealed class ConformanceTaskTools
|
||||
{
|
||||
[McpServerTool(Name = "greet")]
|
||||
[Description("Returns a synchronous greeting.")]
|
||||
public static string Greet(string name) => $"Hello, {name}!";
|
||||
|
||||
[McpServerTool(Name = "slow_compute")]
|
||||
[Description("Completes after the requested number of seconds.")]
|
||||
public static async Task<string> SlowCompute(int seconds, string? label, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(seconds), cancellationToken);
|
||||
return $"Computed {label ?? "result"}";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "failing_job")]
|
||||
[Description("Produces a tool execution error.")]
|
||||
public static async Task<string> FailingJob(CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken);
|
||||
throw new Exception("The conformance task failed.");
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "protocol_error_job")]
|
||||
[Description("Produces a protocol-level error.")]
|
||||
public static string ProtocolErrorJob() =>
|
||||
throw new McpProtocolException("The conformance task encountered a protocol error.", McpErrorCode.InternalError);
|
||||
|
||||
[McpServerTool(Name = "confirm_delete")]
|
||||
[Description("Waits for elicitation before confirming deletion.")]
|
||||
public static async Task<string> ConfirmDelete(
|
||||
McpServer server,
|
||||
string filename,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var result = await server.ElicitAsync(CreateConfirmationRequest($"Delete {filename}?"), cancellationToken);
|
||||
return result.Action == "accept" ? $"Deleted {filename}" : $"Did not delete {filename}";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "multi_input")]
|
||||
[Description("Waits for two independent elicitation responses.")]
|
||||
public static async Task<string> MultiInput(McpServer server, CancellationToken cancellationToken)
|
||||
{
|
||||
await Task.WhenAll(
|
||||
server.ElicitAsync(CreateConfirmationRequest("Confirm the first operation."), cancellationToken).AsTask(),
|
||||
server.ElicitAsync(CreateConfirmationRequest("Confirm the second operation."), cancellationToken).AsTask());
|
||||
return "Both inputs received.";
|
||||
}
|
||||
|
||||
[McpServerTool(Name = "test_tool_with_task")]
|
||||
[Description("Collects input synchronously, then completes through a task.")]
|
||||
public static string ToolWithTask(RequestContext<CallToolRequestParams> context)
|
||||
{
|
||||
if (context.Params!.InputResponses is { } responses &&
|
||||
responses.TryGetValue("user_name", out var response))
|
||||
{
|
||||
var elicitation = response.Deserialize(InputResponse.ElicitResultJsonTypeInfo);
|
||||
var name = elicitation?.Content?["name"].GetString() ?? "world";
|
||||
return $"Hello, {name}!";
|
||||
}
|
||||
|
||||
throw new InputRequiredException(
|
||||
new Dictionary<string, InputRequest>
|
||||
{
|
||||
["user_name"] = InputRequest.ForElicitation(new ElicitRequestParams
|
||||
{
|
||||
Message = "What is your name?",
|
||||
RequestedSchema = new ElicitRequestParams.RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["name"] = new ElicitRequestParams.StringSchema(),
|
||||
},
|
||||
Required = ["name"],
|
||||
},
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private static ElicitRequestParams CreateConfirmationRequest(string message) =>
|
||||
new()
|
||||
{
|
||||
Message = message,
|
||||
RequestedSchema = new ElicitRequestParams.RequestSchema
|
||||
{
|
||||
Properties =
|
||||
{
|
||||
["confirm"] = new ElicitRequestParams.BooleanSchema(),
|
||||
},
|
||||
Required = ["confirm"],
|
||||
},
|
||||
};
|
||||
}
|
||||
Loading…
Reference in a new issue