summaryrefslogtreecommitdiff
path: root/scripts/E2EClient
diff options
context:
space:
mode:
Diffstat (limited to 'scripts/E2EClient')
-rw-r--r--scripts/E2EClient/E2EClient.csproj9
-rw-r--r--scripts/E2EClient/Program.cs67
2 files changed, 76 insertions, 0 deletions
diff --git a/scripts/E2EClient/E2EClient.csproj b/scripts/E2EClient/E2EClient.csproj
new file mode 100644
index 0000000..50fabca
--- /dev/null
+++ b/scripts/E2EClient/E2EClient.csproj
@@ -0,0 +1,9 @@
+<Project Sdk="Microsoft.NET.Sdk">
+ <PropertyGroup>
+ <OutputType>Exe</OutputType>
+ <TargetFramework>net10.0</TargetFramework>
+ </PropertyGroup>
+ <ItemGroup>
+ <PackageReference Include="ModelContextProtocol" />
+ </ItemGroup>
+</Project>
diff --git a/scripts/E2EClient/Program.cs b/scripts/E2EClient/Program.cs
new file mode 100644
index 0000000..ee7bb75
--- /dev/null
+++ b/scripts/E2EClient/Program.cs
@@ -0,0 +1,67 @@
+using ModelContextProtocol;
+using ModelContextProtocol.Client;
+using ModelContextProtocol.Protocol;
+
+if (args.Length < 4 || args[0] is not ("stdio" or "http"))
+{
+ Console.Error.WriteLine("Usage: E2EClient stdio <agent-exe> <config> <request> | http <endpoint> <unused> <request>");
+ return 2;
+}
+
+IClientTransport transport = args[0] == "stdio"
+ ? new StdioClientTransport(new StdioClientTransportOptions
+ {
+ Name = "agent-mcp-operational-e2e",
+ Command = Path.GetFullPath(args[1]),
+ Arguments = ["serve", "--config", Path.GetFullPath(args[2]), "--lang", "ja", "--transport", "stdio"],
+ WorkingDirectory = Path.GetDirectoryName(Path.GetFullPath(args[1]))!
+ })
+ : new HttpClientTransport(new HttpClientTransportOptions
+ {
+ Name = "agent-mcp-operational-e2e-http",
+ Endpoint = new Uri(args[1], UriKind.Absolute),
+ TransportMode = HttpTransportMode.StreamableHttp
+ });
+
+await using McpClient client = await McpClient.CreateAsync(transport);
+IList<McpClientTool> tools = await client.ListToolsAsync();
+if (!tools.Select(static tool => tool.Name).Order(StringComparer.Ordinal).SequenceEqual(["agent_run", "agent_status"]))
+ throw new InvalidOperationException("Unexpected MCP public tool surface.");
+
+_ = await ExecuteTask(client, args[3], null);
+if (args.Length >= 5) _ = await ExecuteTask(client, args[4], null);
+return 0;
+
+static async Task<(Guid TaskId, Guid SessionId)> ExecuteTask(McpClient client, string request, Guid? sessionId)
+{
+ var runArguments = new Dictionary<string, object?> { ["request"] = request, ["temperature"] = 0.0 };
+ if (sessionId is not null) runArguments["session_id"] = sessionId.Value;
+ CallToolResult run = await client.CallToolAsync("agent_run", runArguments);
+ if (run.IsError is true) throw new InvalidOperationException(Text(run));
+ string accepted = Text(run);
+ Guid taskId = ReadGuid(accepted, "task_id");
+ Guid acceptedSessionId = ReadGuid(accepted, "session_id");
+ Console.WriteLine(accepted);
+ for (int poll = 0; poll < 600; poll++)
+ {
+ await Task.Delay(500);
+ CallToolResult status = await client.CallToolAsync("agent_status", new Dictionary<string, object?> { ["task_id"] = taskId, ["session_id"] = acceptedSessionId });
+ string text = Text(status);
+ string state = ReadValue(text, "status");
+ if (state is "completed" or "failed" or "cancelled")
+ {
+ Console.WriteLine(text);
+ if (state != "completed") throw new InvalidOperationException($"Task {taskId} ended in state {state}.");
+ return (taskId, acceptedSessionId);
+ }
+ }
+ throw new TimeoutException($"Task {taskId} did not reach a terminal state.");
+}
+
+static string Text(CallToolResult result) => string.Join('\n', result.Content.OfType<TextContentBlock>().Select(static block => block.Text));
+static Guid ReadGuid(string text, string key) => Guid.Parse(ReadValue(text, key));
+static string ReadValue(string text, string key)
+{
+ string prefix = key + ":";
+ return text.Split('\n').First(line => line.StartsWith(prefix, StringComparison.Ordinal))[prefix.Length..].Trim();
+}