diff --git a/Directory.Packages.props b/Directory.Packages.props
index bf5d41c9..e3e4b982 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -111,7 +111,8 @@
-
+
+
diff --git a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs
index b50f2366..86f2d924 100644
--- a/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/Browsing/Models/ElementLocatingArgs.cs
@@ -36,4 +36,6 @@ public class ElementLocatingArgs
///
public bool Highlight { get; set; }
public string HighlightColor { get; set; } = "red";
+ [JsonPropertyName("is_read_content")]
+ public bool IsReadContent { get;set; }
}
diff --git a/src/Infrastructure/BotSharp.Abstraction/MCP/Models/McpServerConfigModel.cs b/src/Infrastructure/BotSharp.Abstraction/MCP/Models/McpServerConfigModel.cs
index a18230c0..2e0967b9 100644
--- a/src/Infrastructure/BotSharp.Abstraction/MCP/Models/McpServerConfigModel.cs
+++ b/src/Infrastructure/BotSharp.Abstraction/MCP/Models/McpServerConfigModel.cs
@@ -24,11 +24,6 @@ public class McpServerConfigModel
///
public string? Location { get; set; }
- ///
- /// Arguments (if any) to pass to the executable.
- ///
- public string[]? Arguments { get; set; }
-
///
/// Additional transport-specific configuration.
///
diff --git a/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs b/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs
index 1223c4c0..ee065459 100644
--- a/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/BotSharpMCPExtensions.cs
@@ -1,10 +1,11 @@
using BotSharp.Core.MCP.Functions;
-using BotSharp.Core.MCP.Settings;
using BotSharp.Core.MCP.Hooks;
-using Microsoft.Extensions.Configuration;
-using ModelContextProtocol.Configuration;
-using ModelContextProtocol.Client;
using BotSharp.Core.MCP.Managers;
+using BotSharp.Core.MCP.Services;
+using BotSharp.Core.MCP.Settings;
+using Microsoft.Extensions.Configuration;
+using ModelContextProtocol;
+using ModelContextProtocol.Client;
namespace BotSharp.Core.MCP;
@@ -18,6 +19,7 @@ public static class BotSharpMcpExtensions
///
public static IServiceCollection AddBotSharpMCP(this IServiceCollection services, IConfiguration config)
{
+ services.AddScoped();
var settings = config.GetSection("MCP").Get();
services.AddScoped(provider => { return settings; });
diff --git a/src/Infrastructure/BotSharp.Core.MCP/McpPlugin.cs b/src/Infrastructure/BotSharp.Core.MCP/McpPlugin.cs
deleted file mode 100644
index 57d2669b..00000000
--- a/src/Infrastructure/BotSharp.Core.MCP/McpPlugin.cs
+++ /dev/null
@@ -1,31 +0,0 @@
-using Microsoft.Extensions.Configuration;
-using BotSharp.Abstraction.Plugins.Models;
-using BotSharp.Abstraction.Plugins;
-using BotSharp.Abstraction.Settings;
-using BotSharp.Core.MCP.Settings;
-using BotSharp.Core.MCP.Services;
-
-namespace BotSharp.Core.MCP;
-
-public class McpPlugin : IBotSharpPlugin
-{
- public string Id => "0cfb486a-229e-4470-a4c6-d2d4a5fdc727";
- public string Name => "Model context protocol";
- public string Description => "Model context protocol";
-
- public SettingsMeta Settings =>
- new SettingsMeta("MCP");
-
- public object GetNewSettingsInstance() =>
- new McpSettings();
-
- public void RegisterDI(IServiceCollection services, IConfiguration config)
- {
- services.AddScoped();
- }
-
- public bool AttachMenu(List menu)
- {
- return true;
- }
-}
diff --git a/src/Infrastructure/BotSharp.Core.MCP/Services/McpService.cs b/src/Infrastructure/BotSharp.Core.MCP/Services/McpService.cs
index 4cae7503..786f0857 100644
--- a/src/Infrastructure/BotSharp.Core.MCP/Services/McpService.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/Services/McpService.cs
@@ -26,7 +26,6 @@ public class McpService : IMcpService
Name = x.Name,
TransportType = x.TransportType,
TransportOptions = x.TransportOptions,
- Arguments = x.Arguments,
Location = x.Location
});
}
diff --git a/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs b/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs
index 364b0a91..cd4bfd0f 100644
--- a/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs
+++ b/src/Infrastructure/BotSharp.Core.MCP/Settings/MCPSettings.cs
@@ -1,5 +1,5 @@
using ModelContextProtocol.Client;
-using ModelContextProtocol.Configuration;
+using ModelContextProtocol;
namespace BotSharp.Core.MCP.Settings;
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs b/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
index 3d45ecfd..efa7d38c 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Hooks/RealtimeConversationHook.cs
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Utilities;
+using BotSharp.Core.Infrastructures;
namespace BotSharp.Core.Realtime.Hooks;
@@ -29,6 +30,10 @@ public class RealtimeConversationHook : ConversationHookBase, IConversationHook
{
return;
}
+
+ // Clear cache to force to rebuild the agent instruction
+ Utilities.ClearCache();
+
var routing = _services.GetRequiredService();
message.Role = AgentRole.Function;
diff --git a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
index bf813034..497730c0 100644
--- a/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
+++ b/src/Infrastructure/BotSharp.Core.Realtime/Services/RealtimeHub.cs
@@ -49,6 +49,9 @@ public class RealtimeHub : IRealtimeHub
{
await _completer.AppenAudioBuffer(_conn.Data);
}
+ else if (_conn.Event == "user_dtmf_receiving")
+ {
+ }
else if (_conn.Event == "user_dtmf_received")
{
await HandleUserDtmfReceived();
diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
index ba2dc522..d40abe89 100644
--- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
+++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs
@@ -144,13 +144,28 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
Action onUserInterrupted)
{
var buffer = new byte[1024 * 32];
- WebSocketReceiveResult result;
+ // Model response timeout
+ var timeout = 30;
+ WebSocketReceiveResult? result = default;
do
{
Array.Clear(buffer, 0, buffer.Length);
- result = await _webSocket.ReceiveAsync(
- new ArraySegment(buffer), CancellationToken.None);
+
+ var taskWorker = _webSocket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None);
+ var taskTimer = Task.Delay(1000 * timeout);
+ var completedTask = await Task.WhenAny(taskWorker, taskTimer);
+
+ if (completedTask == taskWorker)
+ {
+ result = taskWorker.Result;
+ }
+ else
+ {
+ _logger.LogWarning($"Timeout {timeout} seconds waiting for Model response.");
+ await TriggerModelInference("Response user immediately");
+ continue;
+ }
// Convert received data to text/audio (Twilio sends Base64-encoded audio)
string receivedText = Encoding.UTF8.GetString(buffer, 0, result.Count);
@@ -164,6 +179,11 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
if (response.Type == "error")
{
_logger.LogError($"{response.Type}: {receivedText}");
+ var error = JsonSerializer.Deserialize(receivedText);
+ if (error?.Body.Type == "server_error")
+ {
+ break;
+ }
}
else if (response.Type == "session.created")
{
@@ -182,7 +202,6 @@ public class RealTimeCompletionProvider : IRealTimeCompletion
{
_logger.LogInformation($"{response.Type}: {receivedText}");
var data = JsonSerializer.Deserialize(receivedText);
- await Task.Delay(1000);
onModelAudioTranscriptDone(data.Transcript);
}
else if (response.Type == "response.audio.delta")
diff --git a/src/Plugins/BotSharp.Plugin.Twilio/TwilioStreamMiddleware.cs b/src/Plugins/BotSharp.Plugin.Twilio/TwilioStreamMiddleware.cs
index 7ff5ab98..5726bf6e 100644
--- a/src/Plugins/BotSharp.Plugin.Twilio/TwilioStreamMiddleware.cs
+++ b/src/Plugins/BotSharp.Plugin.Twilio/TwilioStreamMiddleware.cs
@@ -1,5 +1,4 @@
using BotSharp.Abstraction.Realtime;
-using BotSharp.Abstraction.Realtime.Models;
using BotSharp.Plugin.Twilio.Interfaces;
using BotSharp.Plugin.Twilio.Models.Stream;
using Microsoft.AspNetCore.Http;
@@ -100,6 +99,7 @@ public class TwilioStreamMiddleware
}
else
{
+ conn.Event = "user_dtmf_receiving";
conn.KeypadInputBuffer += dtmfResponse.Body.Digit;
}
break;
@@ -115,6 +115,7 @@ public class TwilioStreamMiddleware
streamSid = response.StreamSid,
media = new { payload = message }
};
+
conn.OnModelAudioResponseDone = () =>
new
{
@@ -122,12 +123,21 @@ public class TwilioStreamMiddleware
streamSid = response.StreamSid,
mark = new { name = "responsePart" }
};
+
conn.OnModelUserInterrupted = () =>
new
{
@event = "clear",
streamSid = response.StreamSid
};
+
+ /*if (response.Event == "dtmf")
+ {
+ // Send a Stop command to Twilio
+ string stopPlaybackCommand = "{ \"action\": \"stop_playback\" }";
+ var stopBytes = Encoding.UTF8.GetBytes(stopPlaybackCommand);
+ webSocket.SendAsync(new ArraySegment(stopBytes), WebSocketMessageType.Text, true, CancellationToken.None);
+ }*/
});
}
}
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs
index c40f1be0..d30048c8 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/UtilFunctions/UtilWebLocateElementFn.cs
@@ -32,7 +32,11 @@ public class UtilWebLocateElementFn : IFunctionCallback
};
var result = await browser.LocateElement(msg, locatorArgs);
- message.Content = $"Locating element {(result.IsSuccess ? "success" : "failed")}";
+ message.Content = $"Locating element {(result.IsSuccess ? "success" : "failed")}. ";
+ if (locatorArgs.IsReadContent && result.IsSuccess && !string.IsNullOrWhiteSpace(result.Body))
+ {
+ message.Content += $"Content is: \n{result.Body}";
+ }
var path = webDriverService.GetScreenshotFilePath(message.MessageId);
diff --git a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-locate_element.json b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-locate_element.json
index dd59d10f..a0313564 100644
--- a/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-locate_element.json
+++ b/src/Plugins/BotSharp.Plugin.WebDriver/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-web-locate_element.json
@@ -7,6 +7,10 @@
"selector": {
"type": "string",
"description": "element selector in XPath, use syntax of Playwright in .NET"
+ },
+ "is_read_content": {
+ "type": "boolean",
+ "description": "read the content"
}
},
"required": [ "selector" ]
diff --git a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
index 319f10a1..0e7f08a7 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
+++ b/tests/BotSharp.PizzaBot.MCPServer/BotSharp.PizzaBot.MCPServer.csproj
@@ -10,6 +10,7 @@
+
diff --git a/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs b/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs
deleted file mode 100644
index f2138444..00000000
--- a/tests/BotSharp.PizzaBot.MCPServer/McpEndpointRouteBuilderExtensions.cs
+++ /dev/null
@@ -1,62 +0,0 @@
-using Microsoft.Extensions.Options;
-using ModelContextProtocol.Protocol.Messages;
-using ModelContextProtocol.Server;
-using ModelContextProtocol.Utils.Json;
-
-namespace BotSharp.PizzaBot.MCPServer;
-
-public static class McpEndpointRouteBuilderExtensions
-{
- public static IEndpointConventionBuilder MapMcpSse(this IEndpointRouteBuilder endpoints)
- {
- IMcpServer? server = null;
- SseServerStreamTransport? transport = null;
- var loggerFactory = endpoints.ServiceProvider.GetRequiredService();
- var mcpServerOptions = endpoints.ServiceProvider.GetRequiredService>();
-
- var routeGroup = endpoints.MapGroup("");
-
- routeGroup.MapGet("/sse", async (HttpResponse response, CancellationToken requestAborted) =>
- {
- await using var localTransport = transport = new SseServerStreamTransport(response.Body);
- await using var localServer = server = McpServerFactory.Create(transport, mcpServerOptions.Value, loggerFactory, endpoints.ServiceProvider);
-
- await localServer.StartAsync(requestAborted);
-
- response.Headers.ContentType = "text/event-stream";
- response.Headers.CacheControl = "no-cache";
-
- try
- {
- await transport.RunAsync(requestAborted);
- }
- catch (OperationCanceledException) when (requestAborted.IsCancellationRequested)
- {
- // RequestAborted always triggers when the client disconnects before a complete response body is written,
- // but this is how SSE connections are typically closed.
- }
- });
-
- routeGroup.MapPost("/message", async (HttpContext context) =>
- {
- if (transport is null)
- {
- await Results.BadRequest("Connect to the /sse endpoint before sending messages.").ExecuteAsync(context);
- return;
- }
-
- var message = await context.Request.ReadFromJsonAsync(McpJsonUtilities.DefaultOptions, context.RequestAborted);
- if (message is null)
- {
- await Results.BadRequest("No message in request body.").ExecuteAsync(context);
- return;
- }
-
- await transport.OnMessageReceivedAsync(message, context.RequestAborted);
- context.Response.StatusCode = StatusCodes.Status202Accepted;
- await context.Response.WriteAsync("Accepted");
- });
-
- return routeGroup;
- }
-}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Program.cs b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
index d2cf912e..2a77ff61 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Program.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Program.cs
@@ -1,237 +1,9 @@
-using BotSharp.PizzaBot.MCPServer;
-using ModelContextProtocol;
-
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMcpServer()
.WithToolsFromAssembly();
var app = builder.Build();
app.MapGet("/", () => "This is a test server with only stub functionality!");
-app.MapMcpSse();
+app.MapMcp();
-app.Run();
-
-
-//namespace BotSharp.PizzaBot.MCPServer
-//{
-// internal class Program
-// {
-// private static HashSet _subscribedResources = new();
-// private static readonly object _subscribedResourcesLock = new();
-
-// private static async Task Main(string[] args)
-// {
-// Console.WriteLine("Starting server...");
-
-// McpServerOptions options = new McpServerOptions()
-// {
-// ServerInfo = new Implementation() { Name = "PizzaServer", Version = "1.0.0" },
-// Capabilities = new ServerCapabilities()
-// {
-// Tools = ConfigureTools(),
-// },
-// ProtocolVersion = "2024-11-05",
-// ServerInstructions = "This is a test server with only stub functionality"
-// };
-// var loggerFactory = CreateLoggerFactory();
-// await using IMcpServer server = McpServerFactory.Create(new StdioServerTransport("TestServer", loggerFactory), options, loggerFactory);
-// Log.Logger.Information("Server initialized.");
-
-// await server.StartAsync();
-
-// Log.Logger.Information("Server started.");
-
-// // Run until process is stopped by the client (parent process)
-// while (true)
-// {
-// await Task.Delay(5000);
-
-// // Snapshot the subscribed resources, rather than locking while sending notifications
-// List resources;
-// lock (_subscribedResourcesLock)
-// {
-// resources = _subscribedResources.ToList();
-// }
-
-// foreach (var resource in resources)
-// {
-// ResourceUpdatedNotificationParams notificationParams = new() { Uri = resource };
-// await server.SendMessageAsync(new JsonRpcNotification()
-// {
-// Method = NotificationMethods.ResourceUpdatedNotification,
-// Params = notificationParams
-// });
-// }
-// }
-// }
-
-// private static ToolsCapability ConfigureTools()
-// {
-// return new()
-// {
-// ListToolsHandler = (request, cancellationToken) =>
-// {
-// return Task.FromResult(new ListToolsResult()
-// {
-// Tools = [
-// new Tool()
-// {
-// Name = "make_payment",
-// Description = "call this function to make payment",
-// InputSchema = new JsonSchema()
-// {
-// Type = "object",
-// Properties = new Dictionary()
-// {
-// ["order_number"] = new JsonSchemaProperty() { Type = "string", Description = "order number." },
-// ["total_amount"] = new JsonSchemaProperty() { Type = "string", Description = "total amount." },
-// },
-// Required = new List() { "order_number", "total_amount" }
-// },
-// },
-// new Tool()
-// {
-// Name = "get_pizza_prices",
-// Description = "call this function to get pizza unit price",
-// InputSchema = new JsonSchema()
-// {
-// Type = "object",
-// Properties = new Dictionary()
-// {
-// ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
-// ["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
-
-// },
-// Required = new List(){ "pizza_type", "quantity" }
-// }
-// },
-// new Tool()
-// {
-// Name = "place_an_order",
-// Description = "Place an order when user has confirmed the pizza type and quantity.",
-// InputSchema = new JsonSchema()
-// {
-// Type = "object",
-// Properties = new Dictionary()
-// {
-// ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
-// ["quantity"] = new JsonSchemaProperty() { Type = "number", Description = "quantity of pizza." },
-// ["unit_price"] = new JsonSchemaProperty() { Type = "number", Description = "pizza unit price" },
-
-// },
-// Required = new List(){"pizza_type", "quantity", "unit_price" }
-// }
-// }
-// ]
-// });
-// },
-
-// CallToolHandler = async (request, cancellationToken) =>
-// {
-// if (request.Params.Name == "make_payment")
-// {
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("order_number", out var order_number))
-// {
-// throw new McpServerException("Missing required argument 'order_number'");
-// }
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("total_amount", out var total_amount))
-// {
-// throw new McpServerException("Missing required argument 'total_amount'");
-// }
-// //dynamic message = new ExpandoObject();
-// //message.Transaction = Guid.NewGuid().ToString();
-// //message.Status = "Success";
-
-// //// Serialize the message to JSON
-// //var jso = new JsonSerializerOptions() { WriteIndented = true };
-// //var jsonMessage = JsonSerializer.Serialize(message, jso);
-
-// return new CallToolResponse()
-// {
-// Content = [new Content() { Text = "Payment proceed successfully. Thank you for your business. Have a great day!", Type = "text" }]
-// };
-// }
-// else if (request.Params.Name == "get_pizza_prices")
-// {
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("pizza_type", out var pizza_type))
-// {
-// throw new McpServerException("Missing required argument 'pizza_type'");
-// }
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity))
-// {
-// throw new McpServerException("Missing required argument 'quantity'");
-// }
-// double unit_price = 0;
-// if(pizza_type.ToString() == "Pepperoni Pizza")
-// {
-// unit_price = 3.2 * (int)quantity;
-// }
-// else if(pizza_type.ToString() == "Cheese Pizza")
-// {
-// unit_price = 3.5 * (int)quantity; ;
-// }
-// else if(pizza_type.ToString() == "Margherita Pizza")
-// {
-// unit_price = 3.8 * (int)quantity; ;
-// }
-// dynamic message = new ExpandoObject();
-// message.unit_price = unit_price;
-// var jso = new JsonSerializerOptions() { WriteIndented = true };
-// var jsonMessage = JsonSerializer.Serialize(message, jso);
-// return new CallToolResponse()
-// {
-// Content = [new Content() { Text = jsonMessage, Type = "text" }]
-// };
-// }
-// else if (request.Params.Name == "place_an_order")
-// {
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("pizza_type", out var pizza_type))
-// {
-// throw new McpServerException("Missing required argument 'pizza_type'");
-// }
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("quantity", out var quantity))
-// {
-// throw new McpServerException("Missing required argument 'quantity'");
-// }
-// if (request.Params.Arguments is null || !request.Params.Arguments.TryGetValue("unit_price", out var unit_price))
-// {
-// throw new McpServerException("Missing required argument 'unit_price'");
-// }
-// //dynamic message = new ExpandoObject();
-// //message.order_number = "P123-01";
-// //message.Content = "The order number is P123-01";
-// //// Serialize the message to JSON
-// //var jso = new JsonSerializerOptions() { WriteIndented = true };
-// //var jsonMessage = JsonSerializer.Serialize(message, jso);
-// return new CallToolResponse()
-// {
-// Content = [new Content() { Text = "The order number is P123-01: {order_number = \"P123-01\" }", Type = "text" }]
-// };
-// }
-// else
-// {
-// throw new McpServerException($"Unknown tool: {request.Params.Name}");
-// }
-// }
-// };
-// }
-
-
-// private static ILoggerFactory CreateLoggerFactory()
-// {
-// // Use serilog
-// Log.Logger = new LoggerConfiguration()
-// .MinimumLevel.Verbose() // Capture all log levels
-// .WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "TestServer_.log"),
-// rollingInterval: RollingInterval.Day,
-// outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}")
-// .CreateLogger();
-
-// var logsPath = Path.Combine(AppContext.BaseDirectory, "testserver.log");
-// return LoggerFactory.Create(builder =>
-// {
-// builder.AddSerilog();
-// });
-// }
-// }
-//}
+app.Run();
\ No newline at end of file
diff --git a/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs b/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs
deleted file mode 100644
index 7a7ce51a..00000000
--- a/tests/BotSharp.PizzaBot.MCPServer/SseServerStreamTransport.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-using ModelContextProtocol.Protocol.Messages;
-using ModelContextProtocol.Protocol.Transport;
-using ModelContextProtocol.Utils.Json;
-using System.Buffers;
-using System.Net.ServerSentEvents;
-using System.Text.Json;
-using System.Threading.Channels;
-
-namespace BotSharp.PizzaBot.MCPServer;
-
-public class SseServerStreamTransport(Stream sseResponseStream) : ITransport
-{
- private readonly Channel _incomingChannel = CreateSingleItemChannel();
- private readonly Channel> _outgoingSseChannel = CreateSingleItemChannel>();
-
- private Task? _sseWriteTask;
- private Utf8JsonWriter? _jsonWriter;
-
- public bool IsConnected => _sseWriteTask?.IsCompleted == false;
-
- public Task RunAsync(CancellationToken cancellationToken)
- {
- void WriteJsonRpcMessageToBuffer(SseItem item, IBufferWriter writer)
- {
- if (item.EventType == "endpoint")
- {
- writer.Write("/message"u8);
- return;
- }
-
- JsonSerializer.Serialize(GetUtf8JsonWriter(writer), item.Data, McpJsonUtilities.DefaultOptions);
- }
-
- // The very first SSE event isn't really an IJsonRpcMessage, but there's no API to write a single item of a different type,
- // so we fib and special-case the "endpoint" event type in the formatter.
- _outgoingSseChannel.Writer.TryWrite(new SseItem(null, "endpoint"));
-
- var sseItems = _outgoingSseChannel.Reader.ReadAllAsync(cancellationToken);
- return _sseWriteTask = SseFormatter.WriteAsync(sseItems, sseResponseStream, WriteJsonRpcMessageToBuffer, cancellationToken);
- }
-
- public ChannelReader MessageReader => _incomingChannel.Reader;
-
- public ValueTask DisposeAsync()
- {
- _incomingChannel.Writer.TryComplete();
- _outgoingSseChannel.Writer.TryComplete();
- return new ValueTask(_sseWriteTask ?? Task.CompletedTask);
- }
-
- public Task SendMessageAsync(IJsonRpcMessage message, CancellationToken cancellationToken = default) =>
- _outgoingSseChannel.Writer.WriteAsync(new SseItem(message), cancellationToken).AsTask();
-
- public Task OnMessageReceivedAsync(IJsonRpcMessage message, CancellationToken cancellationToken)
- {
- if (!IsConnected)
- {
- throw new McpTransportException("Transport is not connected");
- }
-
- return _incomingChannel.Writer.WriteAsync(message, cancellationToken).AsTask();
- }
-
- private static Channel CreateSingleItemChannel() =>
- Channel.CreateBounded(new BoundedChannelOptions(1)
- {
- SingleReader = true,
- SingleWriter = false,
- });
-
- private Utf8JsonWriter GetUtf8JsonWriter(IBufferWriter writer)
- {
- if (_jsonWriter is null)
- {
- _jsonWriter = new Utf8JsonWriter(writer);
- }
- else
- {
- _jsonWriter.Reset(writer);
- }
-
- return _jsonWriter;
- }
-}
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
index da198c47..5016f5bb 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/MakePayment.cs
@@ -7,7 +7,7 @@ namespace BotSharp.PizzaBot.MCPServer.Tools;
[McpServerToolType]
public static class MakePayment
{
- [McpServerTool(name: "make_payment"), Description("call this function to make payment.")]
+ [McpServerTool(Name = "make_payment"), Description("call this function to make payment.")]
public static string Make_Payment(
[Description("order number"),Required] string order_number,
[Description("total amount"),Required] int total_amount)
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
index 762d67db..e8aa677c 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PizzaPrices.cs
@@ -9,7 +9,7 @@ namespace BotSharp.PizzaBot.MCPServer.Tools;
[McpServerToolType]
public static class PizzaPrices
{
- [McpServerTool(name: "get_pizza_prices"), Description("call this function to get pizza unit price.")]
+ [McpServerTool(Name = "get_pizza_prices"), Description("call this function to get pizza unit price.")]
public static string GetPizzaPrices(
[Description("The pizza type."), Required] string pizza_type,
[Description("quantity of pizza"), Required] int quantity)
diff --git a/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
index 73245751..d6b82bd7 100644
--- a/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
+++ b/tests/BotSharp.PizzaBot.MCPServer/Tools/PlaceOrder.cs
@@ -7,7 +7,7 @@ namespace BotSharp.PizzaBot.MCPServer.Tools;
[McpServerToolType]
public static class PlaceOrder
{
- [McpServerTool(name: "place_an_order"), Description("Place an order when user has confirmed the pizza type and quantity.")]
+ [McpServerTool(Name = "place_an_order"), Description("Place an order when user has confirmed the pizza type and quantity.")]
public static string PlaceAnOrder(
[Description("The pizza type."), Required] string pizza_type,
[Description("quantity of pizza"), Required] int quantity,
@@ -21,7 +21,7 @@ public static class PlaceOrder
{
throw new McpServerException("Missing required argument 'quantity'");
}
- if (unit_price <= 0)
+ if (unit_price < 0)
{
throw new McpServerException("Missing required argument 'unit_price'");
}