Merge branch 'master' into master
This commit is contained in:
commit
382e9b1f02
|
|
@ -111,9 +111,10 @@
|
|||
<PackageVersion Include="MSTest.TestAdapter" Version="3.1.1" />
|
||||
<PackageVersion Include="MSTest.TestFramework" Version="3.1.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="ModelContextProtocol" Version="0.1.0-preview.2" />
|
||||
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="8.0.0" />
|
||||
<PackageVersion Include="Shouldly" Version="4.3.0" />
|
||||
<PackageVersion Include="ModelContextProtocol" Version="0.1.0-preview.5" />
|
||||
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.1.0-preview.5" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageVersion Include="BotSharp.Core" Version="$(BotSharpVersion)" />
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@ public class McpServerConfigModel
|
|||
/// </summary>
|
||||
public string? Location { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Arguments (if any) to pass to the executable.
|
||||
/// </summary>
|
||||
public string[]? Arguments { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Additional transport-specific configuration.
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -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
|
|||
/// <returns></returns>
|
||||
public static IServiceCollection AddBotSharpMCP(this IServiceCollection services, IConfiguration config)
|
||||
{
|
||||
services.AddScoped<IMcpService, McpService>();
|
||||
var settings = config.GetSection("MCP").Get<McpSettings>();
|
||||
services.AddScoped(provider => { return settings; });
|
||||
|
||||
|
|
|
|||
|
|
@ -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<IMcpService, McpService>();
|
||||
}
|
||||
|
||||
public bool AttachMenu(List<PluginMenuDef> menu)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -26,7 +26,6 @@ public class McpService : IMcpService
|
|||
Name = x.Name,
|
||||
TransportType = x.TransportType,
|
||||
TransportOptions = x.TransportOptions,
|
||||
Arguments = x.Arguments,
|
||||
Location = x.Location
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
using ModelContextProtocol.Client;
|
||||
using ModelContextProtocol.Configuration;
|
||||
using ModelContextProtocol;
|
||||
|
||||
namespace BotSharp.Core.MCP.Settings;
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
<PackageReference Include="Microsoft.Extensions.Hosting" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" />
|
||||
<PackageReference Include="ModelContextProtocol" />
|
||||
<PackageReference Include="ModelContextProtocol.AspNetCore" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" />
|
||||
<PackageReference Include="Serilog.Sinks.File" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -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<ILoggerFactory>();
|
||||
var mcpServerOptions = endpoints.ServiceProvider.GetRequiredService<IOptions<McpServerOptions>>();
|
||||
|
||||
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<IJsonRpcMessage>(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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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<string> _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<string> 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<string, JsonSchemaProperty>()
|
||||
// {
|
||||
// ["order_number"] = new JsonSchemaProperty() { Type = "string", Description = "order number." },
|
||||
// ["total_amount"] = new JsonSchemaProperty() { Type = "string", Description = "total amount." },
|
||||
// },
|
||||
// Required = new List<string>() { "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<string, JsonSchemaProperty>()
|
||||
// {
|
||||
// ["pizza_type"] = new JsonSchemaProperty() { Type = "string", Description = "The pizza type." },
|
||||
// ["quantity"] = new JsonSchemaProperty() { Type = "string", Description = "quantity of pizza." },
|
||||
|
||||
// },
|
||||
// Required = new List<string>(){ "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<string, JsonSchemaProperty>()
|
||||
// {
|
||||
// ["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<string>(){"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();
|
||||
|
|
@ -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<IJsonRpcMessage> _incomingChannel = CreateSingleItemChannel<IJsonRpcMessage>();
|
||||
private readonly Channel<SseItem<IJsonRpcMessage?>> _outgoingSseChannel = CreateSingleItemChannel<SseItem<IJsonRpcMessage?>>();
|
||||
|
||||
private Task? _sseWriteTask;
|
||||
private Utf8JsonWriter? _jsonWriter;
|
||||
|
||||
public bool IsConnected => _sseWriteTask?.IsCompleted == false;
|
||||
|
||||
public Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
void WriteJsonRpcMessageToBuffer(SseItem<IJsonRpcMessage?> item, IBufferWriter<byte> 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<IJsonRpcMessage?>(null, "endpoint"));
|
||||
|
||||
var sseItems = _outgoingSseChannel.Reader.ReadAllAsync(cancellationToken);
|
||||
return _sseWriteTask = SseFormatter.WriteAsync(sseItems, sseResponseStream, WriteJsonRpcMessageToBuffer, cancellationToken);
|
||||
}
|
||||
|
||||
public ChannelReader<IJsonRpcMessage> 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<IJsonRpcMessage?>(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<T> CreateSingleItemChannel<T>() =>
|
||||
Channel.CreateBounded<T>(new BoundedChannelOptions(1)
|
||||
{
|
||||
SingleReader = true,
|
||||
SingleWriter = false,
|
||||
});
|
||||
|
||||
private Utf8JsonWriter GetUtf8JsonWriter(IBufferWriter<byte> writer)
|
||||
{
|
||||
if (_jsonWriter is null)
|
||||
{
|
||||
_jsonWriter = new Utf8JsonWriter(writer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_jsonWriter.Reset(writer);
|
||||
}
|
||||
|
||||
return _jsonWriter;
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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'");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue