summaryrefslogtreecommitdiff
path: root/tests/Agent.Mcp.TestHost/Program.cs
blob: 3447beb0a1985aa76e515010c64c469f201b3977 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
using Agent.Core.Localization;
using Agent.Core.Ollama;
using Agent.Core.Runtime;
using Agent.Mcp;
using ModelContextProtocol.Server;
using ModelContextProtocol.Protocol;
using System.Reflection;
using Microsoft.Extensions.DependencyInjection;
using ModelContextProtocol;

namespace Agent.Mcp.TestHost;

public static class Marker;

internal static class Program
{
    public static async Task Main(string[] args)
    {
        if (args.Length is not (2 or 4)) throw new ArgumentException("Expected <locale> <task-state> or http <port> <locale> <task-state>.");
        bool http = args.Length == 4;
        int offset = http ? 2 : 0;
        LocaleCatalog locale = LocaleCatalog.Load(args[offset], "en");
        var ollama = new OllamaSettings(new Uri("http://127.0.0.1:1"), "unused", false, "0", new Dictionary<string, System.Text.Json.JsonElement>());
        var store = new McpTaskStoreSettings(args[offset + 1], TimeSpan.FromHours(24), 1000);
        var runtime = new AgentRuntimeSettings("", "en", [], "sandbox");
        McpServerTool[] fixtures =
        [
            McpServerTool.Create((string name) => $"Hello, {name}!", new McpServerToolCreateOptions { Name = "greet" }),
            McpServerTool.Create(async (int seconds, CancellationToken cancellationToken) =>
            {
                await Task.Delay(TimeSpan.FromSeconds(seconds), cancellationToken).ConfigureAwait(false);
                return $"Computed after {seconds} seconds";
            }, new McpServerToolCreateOptions { Name = "slow_compute" }),
            McpServerTool.Create(async () =>
            {
                await Task.Delay(1000).ConfigureAwait(false);
                return new CallToolResult { IsError = true, Content = [new TextContentBlock { Text = "intentional tool error" }] };
            }, new McpServerToolCreateOptions { Name = "failing_job" }),
            McpServerTool.Create((Func<string>)(() => throw new InvalidOperationException("intentional protocol error")),
                new McpServerToolCreateOptions { Name = "protocol_error_job" }),
            .. typeof(ConformanceTools).GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)
                .Where(static method => method.GetCustomAttribute<McpServerToolAttribute>() is not null)
                .Select(method => McpServerTool.Create(method, new ConformanceTools()))
        ];
        var taskTools = new HashSet<string>(["agent_run", "slow_compute", "confirm_delete", "multi_input", "protocol_error_job"], StringComparer.Ordinal);
        var requiredTaskTools = new HashSet<string>(["failing_job", "test_tool_with_task"], StringComparer.Ordinal);
        if (http)
            await McpHost.RunHttpAsync(locale, ollama, runtime, new VisionLimits(), "127.0.0.1", int.Parse(args[1], System.Globalization.CultureInfo.InvariantCulture), CancellationToken.None,
                taskStoreSettings: store, additionalTools: fixtures, taskCapableTools: taskTools, requiredTaskTools: requiredTaskTools,
                configureBuilder: ConfigureConformanceFilters).ConfigureAwait(false);
        else
            await McpHost.RunStdioAsync(locale, ollama, runtime, new VisionLimits(), CancellationToken.None, taskStoreSettings: store).ConfigureAwait(false);
    }

    private static void ConfigureConformanceFilters(IMcpServerBuilder builder)
    {
        builder.WithRequestFilters(filters => filters.AddCallToolFilter(next => async (context, cancellationToken) =>
        {
            if (context.Params?.Name == "test_missing_capability")
            {
#pragma warning disable MCP9005 // Frozen alpha conformance probes the legacy sampling capability shape.
                throw new MissingRequiredClientCapabilityException(
                    new ClientCapabilities { Sampling = new SamplingCapability() },
                    "The conformance diagnostic requires the sampling capability.");
#pragma warning restore MCP9005
            }
            if (context.Params?.Name == "protocol_error_job")
                throw new McpProtocolException("intentional protocol error", McpErrorCode.InternalError);
            return await next(context, cancellationToken).ConfigureAwait(false);
        }));
    }
}