|
@@ -0,0 +1,1028 @@
|
|
|
|
|
+/*
|
|
|
|
|
+ * Copyright (C) 2025 Mcp-Vala Project
|
|
|
|
|
+ *
|
|
|
|
|
+ * This library is free software; you can redistribute it and/or
|
|
|
|
|
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
|
|
+ * License as published by the Free Software Foundation; either
|
|
|
|
|
+ * version 2.1 of the License, or (at your option) any later version.
|
|
|
|
|
+ *
|
|
|
|
|
+ * This library is distributed in the hope that it will be useful,
|
|
|
|
|
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
+ * Lesser General Public License for more details.
|
|
|
|
|
+ *
|
|
|
|
|
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
|
|
+ * License along with this library; if not, write to the Free Software
|
|
|
|
|
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
|
|
|
+ *
|
|
|
|
|
+ * Author: Mcp-Vala Project
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+using Mcp;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Simple MCP server example.
|
|
|
|
|
+ *
|
|
|
|
|
+ * This example demonstrates basic usage of the MCP Vala library
|
|
|
|
|
+ * with a simple echo tool and basic resource provider.
|
|
|
|
|
+ */
|
|
|
|
|
+public class SimpleServer : GLib.Object {
|
|
|
|
|
+ private Mcp.Core.Server server;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new SimpleServer.
|
|
|
|
|
+ */
|
|
|
|
|
+ public SimpleServer () {
|
|
|
|
|
+ // Create server info
|
|
|
|
|
+ var server_info = new Mcp.Types.Protocol.ServerInfo (
|
|
|
|
|
+ "simple-server",
|
|
|
|
|
+ "1.0.0"
|
|
|
|
|
+ );
|
|
|
|
|
+ server_info.description = "A simple MCP server example";
|
|
|
|
|
+
|
|
|
|
|
+ // Create server capabilities
|
|
|
|
|
+ var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
|
|
|
|
|
+ capabilities.logging = true;
|
|
|
|
|
+
|
|
|
|
|
+ // Create server
|
|
|
|
|
+ server = new Mcp.Core.Server (server_info, capabilities);
|
|
|
|
|
+
|
|
|
|
|
+ // Register tools with tool manager
|
|
|
|
|
+ var echo_tool = new EchoTool ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.tool_manager.register_executor ("echo", echo_tool);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var calculator_tool = new CalculatorTool ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.tool_manager.register_executor ("calculator", calculator_tool);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var file_tool = new FileOperationTool ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.tool_manager.register_executor ("file_ops", file_tool);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Register resource providers with the resource manager
|
|
|
|
|
+ var file_provider = new FileResourceProvider ();
|
|
|
|
|
+ server.resource_manager.register_provider ("file", file_provider);
|
|
|
|
|
+
|
|
|
|
|
+ var memory_provider = new MemoryResourceProvider ();
|
|
|
|
|
+ server.resource_manager.register_provider ("memory", memory_provider);
|
|
|
|
|
+
|
|
|
|
|
+ // Add a resource template
|
|
|
|
|
+ var template = new Mcp.Resources.Types.Resource.ResourceTemplate (
|
|
|
|
|
+ "note",
|
|
|
|
|
+ "Create a new note"
|
|
|
|
|
+ );
|
|
|
|
|
+ template.description = "Creates a new text note with specified content";
|
|
|
|
|
+ server.resource_manager.register_template ("note", template);
|
|
|
|
|
+
|
|
|
|
|
+ // Register prompt templates with the prompt manager
|
|
|
|
|
+ var code_review_prompt = new CodeReviewPromptTemplate ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.prompt_manager.register_template ("code_review", code_review_prompt);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var summary_prompt = new SummaryPromptTemplate ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.prompt_manager.register_template ("summary", summary_prompt);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var explain_prompt = new ExplainPromptTemplate ();
|
|
|
|
|
+ try {
|
|
|
|
|
+ server.prompt_manager.register_template ("explain", explain_prompt);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ // Error handling
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Runs the server.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return Exit code
|
|
|
|
|
+ */
|
|
|
|
|
+ public async int run () {
|
|
|
|
|
+ try {
|
|
|
|
|
+ // Start the server
|
|
|
|
|
+ bool started = yield server.start ();
|
|
|
|
|
+ if (!started) {
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Server started. Waiting for connections...
|
|
|
|
|
+
|
|
|
|
|
+ // Start a demo timer to show resource updates
|
|
|
|
|
+ Timeout.add_seconds (5, () => {
|
|
|
|
|
+ demo_resource_updates ();
|
|
|
|
|
+ return true; // Continue timer
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Run the main loop
|
|
|
|
|
+ var main_loop = new MainLoop ();
|
|
|
|
|
+
|
|
|
|
|
+ // Connect shutdown signal
|
|
|
|
|
+ server.shutdown.connect (() => {
|
|
|
|
|
+ main_loop.quit ();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ main_loop.run ();
|
|
|
|
|
+
|
|
|
|
|
+ return 0;
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ return 1;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Demonstrates resource updates and notifications.
|
|
|
|
|
+ */
|
|
|
|
|
+ private void demo_resource_updates () {
|
|
|
|
|
+ // Get the memory provider
|
|
|
|
|
+ // Get the memory provider from the resource manager's providers
|
|
|
|
|
+ var providers = server.resource_manager.get_providers ();
|
|
|
|
|
+ MemoryResourceProvider? provider = null;
|
|
|
|
|
+ foreach (var key in providers.get_keys ()) {
|
|
|
|
|
+ if (key == "memory") {
|
|
|
|
|
+ provider = providers.get (key) as MemoryResourceProvider;
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (provider != null) {
|
|
|
|
|
+ // Increment the counter
|
|
|
|
|
+ int new_value = provider.increment_counter ();
|
|
|
|
|
+
|
|
|
|
|
+ // Create a new note every 30 seconds (6 * 5 second intervals)
|
|
|
|
|
+ uint note_counter = 0;
|
|
|
|
|
+ note_counter++;
|
|
|
|
|
+ if (note_counter % 6 == 0) {
|
|
|
|
|
+ string uri = provider.create_note (
|
|
|
|
|
+ "Demo Note %u".printf (note_counter / 6),
|
|
|
|
|
+ "This is an auto-generated note created at %s".printf (new DateTime.now_utc ().to_string ())
|
|
|
|
|
+ );
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Simple echo tool implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class EchoTool : Mcp.Tools.BaseExecutor {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new EchoTool.
|
|
|
|
|
+ */
|
|
|
|
|
+ public EchoTool () {
|
|
|
|
|
+ // Create tool definition using Variant
|
|
|
|
|
+ var builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Add schema properties
|
|
|
|
|
+ builder.add ("{sv}", "type", new Variant.string ("object"));
|
|
|
|
|
+ builder.add ("{sv}", "description", new Variant.string ("Text to echo back"));
|
|
|
|
|
+
|
|
|
|
|
+ var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ var text_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ text_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ text_prop_builder.add ("{sv}", "description", new Variant.string ("The text to echo back"));
|
|
|
|
|
+ properties_builder.add ("{sv}", "text", text_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ builder.add ("{sv}", "properties", properties_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var required_array_builder = new VariantBuilder (new VariantType ("as"));
|
|
|
|
|
+ required_array_builder.add ("s", "text");
|
|
|
|
|
+ builder.add ("{sv}", "required", required_array_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var input_schema = builder.end ();
|
|
|
|
|
+
|
|
|
|
|
+ var definition = new Mcp.Tools.Types.ToolDefinition ("echo", input_schema);
|
|
|
|
|
+ definition.title = "Echo Tool";
|
|
|
|
|
+ definition.description = "Echoes back the provided text";
|
|
|
|
|
+
|
|
|
|
|
+ base (definition);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
|
|
|
|
|
+ string text = get_string_arg (arguments, "text");
|
|
|
|
|
+
|
|
|
|
|
+ return create_text_result ("Echo: %s".printf (text));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Calculator tool implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class CalculatorTool : Mcp.Tools.BaseExecutor {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new CalculatorTool.
|
|
|
|
|
+ */
|
|
|
|
|
+ public CalculatorTool () {
|
|
|
|
|
+ // Create tool definition using Variant
|
|
|
|
|
+ var builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Add schema properties
|
|
|
|
|
+ builder.add ("{sv}", "type", new Variant.string ("object"));
|
|
|
|
|
+ builder.add ("{sv}", "description", new Variant.string ("Perform mathematical calculations"));
|
|
|
|
|
+
|
|
|
|
|
+ var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Expression property
|
|
|
|
|
+ var expr_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ expr_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ expr_prop_builder.add ("{sv}", "description", new Variant.string ("Mathematical expression to evaluate (e.g., '2 + 3 * 4')"));
|
|
|
|
|
+ properties_builder.add ("{sv}", "expression", expr_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ // Operation property
|
|
|
|
|
+ var op_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "description", new Variant.string ("Operation: add, subtract, multiply, divide"));
|
|
|
|
|
+
|
|
|
|
|
+ var enum_array_builder = new VariantBuilder (new VariantType ("as"));
|
|
|
|
|
+ enum_array_builder.add ("s", "add");
|
|
|
|
|
+ enum_array_builder.add ("s", "subtract");
|
|
|
|
|
+ enum_array_builder.add ("s", "multiply");
|
|
|
|
|
+ enum_array_builder.add ("s", "divide");
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "enum", enum_array_builder.end ());
|
|
|
|
|
+ properties_builder.add ("{sv}", "operation", op_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ // Operands property
|
|
|
|
|
+ var operands_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ operands_prop_builder.add ("{sv}", "type", new Variant.string ("array"));
|
|
|
|
|
+ operands_prop_builder.add ("{sv}", "description", new Variant.string ("Array of numbers for the operation"));
|
|
|
|
|
+
|
|
|
|
|
+ var items_schema_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ items_schema_builder.add ("{sv}", "type", new Variant.string ("number"));
|
|
|
|
|
+ operands_prop_builder.add ("{sv}", "items", items_schema_builder.end ());
|
|
|
|
|
+ properties_builder.add ("{sv}", "operands", operands_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ builder.add ("{sv}", "properties", properties_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var required_array_builder = new VariantBuilder (new VariantType ("as"));
|
|
|
|
|
+ required_array_builder.add ("s", "expression");
|
|
|
|
|
+ builder.add ("{sv}", "required", required_array_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var input_schema = builder.end ();
|
|
|
|
|
+
|
|
|
|
|
+ var definition = new Mcp.Tools.Types.ToolDefinition ("calculator", input_schema);
|
|
|
|
|
+ definition.title = "Calculator";
|
|
|
|
|
+ definition.description = "Perform mathematical calculations";
|
|
|
|
|
+
|
|
|
|
|
+ base (definition);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
|
|
|
|
|
+ string expression = get_string_arg (arguments, "expression");
|
|
|
|
|
+ string operation = get_string_arg (arguments, "operation", "");
|
|
|
|
|
+
|
|
|
|
|
+ // Parse operands if provided
|
|
|
|
|
+ double[] operands = {};
|
|
|
|
|
+ if (arguments.lookup_value ("operands", null) != null) {
|
|
|
|
|
+ var operands_variant = arguments.lookup_value ("operands", new VariantType ("ad"));
|
|
|
|
|
+ if (operands_variant != null) {
|
|
|
|
|
+ // Get the array from variant using proper GLib.Variant API
|
|
|
|
|
+ size_t n_elements;
|
|
|
|
|
+ double* elements = (double*) operands_variant.get_data ();
|
|
|
|
|
+ n_elements = operands_variant.n_children ();
|
|
|
|
|
+
|
|
|
|
|
+ operands = new double[n_elements];
|
|
|
|
|
+ for (size_t i = 0; i < n_elements; i++) {
|
|
|
|
|
+ operands[i] = operands_variant.get_child_value (i).get_double ();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ double result = 0.0;
|
|
|
|
|
+
|
|
|
|
|
+ if (expression != "") {
|
|
|
|
|
+ // Simple expression evaluation (in real implementation, use proper parser)
|
|
|
|
|
+ try {
|
|
|
|
|
+ result = evaluate_simple_expression (expression);
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid expression: %s".printf (e.message));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (operands.length > 0) {
|
|
|
|
|
+ // Use operation with operands
|
|
|
|
|
+ if (operands.length < 2) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Operation requires at least 2 operands");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ switch (operation) {
|
|
|
|
|
+ case "add":
|
|
|
|
|
+ result = operands[0] + operands[1];
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "subtract":
|
|
|
|
|
+ result = operands[0] - operands[1];
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "multiply":
|
|
|
|
|
+ result = operands[0] * operands[1];
|
|
|
|
|
+ break;
|
|
|
|
|
+ case "divide":
|
|
|
|
|
+ if (operands[1] == 0) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Division by zero");
|
|
|
|
|
+ }
|
|
|
|
|
+ result = operands[0] / operands[1];
|
|
|
|
|
+ break;
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Unknown operation: %s".printf (operation));
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Either expression or operands must be provided");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Create structured result using Variant
|
|
|
|
|
+ var structured_data_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ structured_data_builder.add ("{sv}", "result", new Variant.double (result));
|
|
|
|
|
+ structured_data_builder.add ("{sv}", "expression", new Variant.string (expression != "" ? expression : operation));
|
|
|
|
|
+
|
|
|
|
|
+ var structured_data = structured_data_builder.end ();
|
|
|
|
|
+
|
|
|
|
|
+ return create_structured_result ("Calculation result: %g".printf (result), structured_data);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Evaluates a simple mathematical expression.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param expression The expression to evaluate
|
|
|
|
|
+ * @return The result
|
|
|
|
|
+ * @throws Error If evaluation fails
|
|
|
|
|
+ */
|
|
|
|
|
+ private double evaluate_simple_expression (string expression) throws Error {
|
|
|
|
|
+ // Very simple expression evaluator for demonstration
|
|
|
|
|
+ // In production, use a proper math expression parser
|
|
|
|
|
+
|
|
|
|
|
+ // Handle basic operations
|
|
|
|
|
+ if (expression.contains ("+")) {
|
|
|
|
|
+ var parts = expression.split ("+");
|
|
|
|
|
+ if (parts.length == 2) {
|
|
|
|
|
+ double a = double.parse (parts[0].strip ());
|
|
|
|
|
+ double b = double.parse (parts[1].strip ());
|
|
|
|
|
+ return a + b;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (expression.contains ("-")) {
|
|
|
|
|
+ var parts = expression.split ("-");
|
|
|
|
|
+ if (parts.length == 2) {
|
|
|
|
|
+ double a = double.parse (parts[0].strip ());
|
|
|
|
|
+ double b = double.parse (parts[1].strip ());
|
|
|
|
|
+ return a - b;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (expression.contains ("*")) {
|
|
|
|
|
+ var parts = expression.split ("*");
|
|
|
|
|
+ if (parts.length == 2) {
|
|
|
|
|
+ double a = double.parse (parts[0].strip ());
|
|
|
|
|
+ double b = double.parse (parts[1].strip ());
|
|
|
|
|
+ return a * b;
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (expression.contains ("/")) {
|
|
|
|
|
+ var parts = expression.split ("/");
|
|
|
|
|
+ if (parts.length == 2) {
|
|
|
|
|
+ double a = double.parse (parts[0].strip ());
|
|
|
|
|
+ double b = double.parse (parts[1].strip ());
|
|
|
|
|
+ if (b == 0) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Division by zero");
|
|
|
|
|
+ }
|
|
|
|
|
+ return a / b;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Try to parse as single number
|
|
|
|
|
+ return double.parse (expression);
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * File operations tool implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class FileOperationTool : Mcp.Tools.BaseExecutor {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new FileOperationTool.
|
|
|
|
|
+ */
|
|
|
|
|
+ public FileOperationTool () {
|
|
|
|
|
+ // Create tool definition using Variant
|
|
|
|
|
+ var builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Add schema properties
|
|
|
|
|
+ builder.add ("{sv}", "type", new Variant.string ("object"));
|
|
|
|
|
+ builder.add ("{sv}", "description", new Variant.string ("Perform file operations"));
|
|
|
|
|
+
|
|
|
|
|
+ var properties_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Operation property
|
|
|
|
|
+ var op_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "description", new Variant.string ("Operation: read, write, list, delete"));
|
|
|
|
|
+
|
|
|
|
|
+ var enum_array_builder = new VariantBuilder (new VariantType ("as"));
|
|
|
|
|
+ enum_array_builder.add ("s", "read");
|
|
|
|
|
+ enum_array_builder.add ("s", "write");
|
|
|
|
|
+ enum_array_builder.add ("s", "list");
|
|
|
|
|
+ enum_array_builder.add ("s", "delete");
|
|
|
|
|
+ op_prop_builder.add ("{sv}", "enum", enum_array_builder.end ());
|
|
|
|
|
+ properties_builder.add ("{sv}", "operation", op_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ // Path property
|
|
|
|
|
+ var path_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ path_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ path_prop_builder.add ("{sv}", "description", new Variant.string ("File path"));
|
|
|
|
|
+ properties_builder.add ("{sv}", "path", path_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ // Content property (for write operation)
|
|
|
|
|
+ var content_prop_builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+ content_prop_builder.add ("{sv}", "type", new Variant.string ("string"));
|
|
|
|
|
+ content_prop_builder.add ("{sv}", "description", new Variant.string ("Content to write (for write operation)"));
|
|
|
|
|
+ properties_builder.add ("{sv}", "content", content_prop_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ builder.add ("{sv}", "properties", properties_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var required_array_builder = new VariantBuilder (new VariantType ("as"));
|
|
|
|
|
+ required_array_builder.add ("s", "operation");
|
|
|
|
|
+ required_array_builder.add ("s", "path");
|
|
|
|
|
+ builder.add ("{sv}", "required", required_array_builder.end ());
|
|
|
|
|
+
|
|
|
|
|
+ var input_schema = builder.end ();
|
|
|
|
|
+
|
|
|
|
|
+ var definition = new Mcp.Tools.Types.ToolDefinition ("file_ops", input_schema);
|
|
|
|
|
+ definition.title = "File Operations";
|
|
|
|
|
+ definition.description = "Perform basic file operations";
|
|
|
|
|
+
|
|
|
|
|
+ base (definition);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override async Mcp.Tools.Types.CallToolResult do_execute (GLib.Variant arguments) throws Error {
|
|
|
|
|
+ string operation = get_string_arg (arguments, "operation");
|
|
|
|
|
+ string path = get_string_arg (arguments, "path");
|
|
|
|
|
+ string content = get_string_arg (arguments, "content", "");
|
|
|
|
|
+
|
|
|
|
|
+ switch (operation) {
|
|
|
|
|
+ case "read":
|
|
|
|
|
+ return yield handle_read (path);
|
|
|
|
|
+ case "write":
|
|
|
|
|
+ return yield handle_write (path, content);
|
|
|
|
|
+ case "list":
|
|
|
|
|
+ return yield handle_list (path);
|
|
|
|
|
+ case "delete":
|
|
|
|
|
+ return yield handle_delete (path);
|
|
|
|
|
+ default:
|
|
|
|
|
+ throw new Mcp.Core.McpError.INVALID_PARAMS ("Unknown operation: %s".printf (operation));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles file read operation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param path The file path
|
|
|
|
|
+ * @return The operation result
|
|
|
|
|
+ * @throws Error If reading fails
|
|
|
|
|
+ */
|
|
|
|
|
+ private async Mcp.Tools.Types.CallToolResult handle_read (string path) throws Error {
|
|
|
|
|
+ var file = File.new_for_path (path);
|
|
|
|
|
+ if (!file.query_exists ()) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (path));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint8[] contents;
|
|
|
|
|
+ string etag;
|
|
|
|
|
+ yield file.load_contents_async (null, out contents, out etag);
|
|
|
|
|
+ string text = (string) contents;
|
|
|
|
|
+
|
|
|
|
|
+ return create_text_result ("File content:\n%s".printf (text));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles file write operation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param path The file path
|
|
|
|
|
+ * @param content The content to write
|
|
|
|
|
+ * @return The operation result
|
|
|
|
|
+ * @throws Error If writing fails
|
|
|
|
|
+ */
|
|
|
|
|
+ private async Mcp.Tools.Types.CallToolResult handle_write (string path, string content) throws Error {
|
|
|
|
|
+ var file = File.new_for_path (path);
|
|
|
|
|
+
|
|
|
|
|
+ // Create parent directories if needed
|
|
|
|
|
+ var parent = file.get_parent ();
|
|
|
|
|
+ if (parent != null && !parent.query_exists ()) {
|
|
|
|
|
+ try {
|
|
|
|
|
+ parent.make_directory_with_parents ();
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INTERNAL_ERROR ("Failed to create parent directory: %s".printf (e.message));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint8[] data = content.data;
|
|
|
|
|
+ string etag_out;
|
|
|
|
|
+ try {
|
|
|
|
|
+ yield file.replace_contents_async (
|
|
|
|
|
+ data,
|
|
|
|
|
+ null,
|
|
|
|
|
+ false,
|
|
|
|
|
+ FileCreateFlags.REPLACE_DESTINATION,
|
|
|
|
|
+ null,
|
|
|
|
|
+ out etag_out
|
|
|
|
|
+ );
|
|
|
|
|
+ } catch (Error e) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.INTERNAL_ERROR ("Failed to write file: %s".printf (e.message));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return create_text_result ("Wrote %u bytes to %s".printf (data.length, path));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles file list operation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param path The directory path
|
|
|
|
|
+ * @return The operation result
|
|
|
|
|
+ * @throws Error If listing fails
|
|
|
|
|
+ */
|
|
|
|
|
+ private async Mcp.Tools.Types.CallToolResult handle_list (string path) throws Error {
|
|
|
|
|
+ var dir = File.new_for_path (path);
|
|
|
|
|
+ if (!dir.query_exists ()) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Directory not found: %s".printf (path));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var enumerator = yield dir.enumerate_children_async (
|
|
|
|
|
+ "standard::name,standard::type,standard::size",
|
|
|
|
|
+ FileQueryInfoFlags.NONE,
|
|
|
|
|
+ Priority.DEFAULT,
|
|
|
|
|
+ null
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ string[] entries = {};
|
|
|
|
|
+ FileInfo? file_info = null;
|
|
|
|
|
+ while ((file_info = enumerator.next_file ()) != null) {
|
|
|
|
|
+ string entry = "%s (%s)".printf (
|
|
|
|
|
+ file_info.get_name (),
|
|
|
|
|
+ file_info.get_file_type () == FileType.DIRECTORY ? "dir" : "file"
|
|
|
|
|
+ );
|
|
|
|
|
+ entries += entry;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return create_text_result ("Directory listing:\n%s".printf (string.joinv ("\n", entries)));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Handles file delete operation.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param path The file path
|
|
|
|
|
+ * @return The operation result
|
|
|
|
|
+ * @throws Error If deletion fails
|
|
|
|
|
+ */
|
|
|
|
|
+ private async Mcp.Tools.Types.CallToolResult handle_delete (string path) throws Error {
|
|
|
|
|
+ var file = File.new_for_path (path);
|
|
|
|
|
+ if (!file.query_exists ()) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (path));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ yield file.delete_async ();
|
|
|
|
|
+
|
|
|
|
|
+ return create_text_result ("Deleted: %s".printf (path));
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Simple file resource provider implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class FileResourceProvider : Mcp.Resources.BaseProvider {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new FileResourceProvider.
|
|
|
|
|
+ */
|
|
|
|
|
+ public FileResourceProvider () {
|
|
|
|
|
+ Object ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public override async Gee.ArrayList<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
|
|
|
|
|
+ var resources = new Gee.ArrayList<Mcp.Resources.Types.Resource> ();
|
|
|
|
|
+
|
|
|
|
|
+ // List files in current directory
|
|
|
|
|
+ var dir = File.new_for_path (".");
|
|
|
|
|
+ var enumerator = yield dir.enumerate_children_async (
|
|
|
|
|
+ "standard::name,standard::type,standard::size",
|
|
|
|
|
+ FileQueryInfoFlags.NONE,
|
|
|
|
|
+ Priority.DEFAULT,
|
|
|
|
|
+ null
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ FileInfo? file_info = null;
|
|
|
|
|
+ while ((file_info = enumerator.next_file ()) != null) {
|
|
|
|
|
+ if (file_info.get_file_type () == FileType.REGULAR) {
|
|
|
|
|
+ var file = File.new_for_path (file_info.get_name ());
|
|
|
|
|
+ var resource = new Mcp.Resources.Types.Resource (
|
|
|
|
|
+ file.get_uri (),
|
|
|
|
|
+ file_info.get_name ()
|
|
|
|
|
+ );
|
|
|
|
|
+ resource.mime_type = guess_mime_type (file_info.get_name ());
|
|
|
|
|
+ resource.size = file_info.get_size ();
|
|
|
|
|
+
|
|
|
|
|
+ resources.add (resource);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return resources;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public override async Gee.ArrayList<Mcp.Types.Common.ResourceContents> read_resource (string uri) throws Error {
|
|
|
|
|
+ var file = File.new_for_uri (uri);
|
|
|
|
|
+ if (!file.query_exists ()) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (uri));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ uint8[] contents;
|
|
|
|
|
+ string etag;
|
|
|
|
|
+ yield file.load_contents_async (null, out contents, out etag);
|
|
|
|
|
+
|
|
|
|
|
+ string text = (string) contents;
|
|
|
|
|
+
|
|
|
|
|
+ var result = new Gee.ArrayList<Mcp.Types.Common.ResourceContents> ();
|
|
|
|
|
+ result.add (new Mcp.Types.Common.TextResourceContents (uri, text));
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Guesses MIME type from filename.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param filename The filename
|
|
|
|
|
+ * @return The guessed MIME type
|
|
|
|
|
+ */
|
|
|
|
|
+ private new string guess_mime_type (string filename) {
|
|
|
|
|
+ // Simple MIME type guessing based on extension
|
|
|
|
|
+ string extension = filename.down ();
|
|
|
|
|
+
|
|
|
|
|
+ if (extension == "txt") {
|
|
|
|
|
+ return "text/plain";
|
|
|
|
|
+ } else if (extension == "json") {
|
|
|
|
|
+ return "application/json";
|
|
|
|
|
+ } else if (extension == "xml") {
|
|
|
|
|
+ return "application/xml";
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return "application/octet-stream";
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * In-memory resource provider with subscription support.
|
|
|
|
|
+ */
|
|
|
|
|
+public class MemoryResourceProvider : Mcp.Resources.BaseProvider {
|
|
|
|
|
+ private HashTable<string, string> resources;
|
|
|
|
|
+ private uint32 next_id;
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new MemoryResourceProvider.
|
|
|
|
|
+ */
|
|
|
|
|
+ public MemoryResourceProvider () {
|
|
|
|
|
+ Object ();
|
|
|
|
|
+ resources = new HashTable<string, string> (str_hash, str_equal);
|
|
|
|
|
+ next_id = 1;
|
|
|
|
|
+
|
|
|
|
|
+ // Add some initial resources
|
|
|
|
|
+ resources.insert ("memory://note/1", "This is a sample note in memory.");
|
|
|
|
|
+ resources.insert ("memory://note/2", "Another note with some content.");
|
|
|
|
|
+ resources.insert ("memory://counter", "0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public override async Gee.ArrayList<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
|
|
|
|
|
+ var result = new Gee.ArrayList<Mcp.Resources.Types.Resource> ();
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var uri in resources.get_keys ()) {
|
|
|
|
|
+ var resource = new Mcp.Resources.Types.Resource (uri, get_name_from_uri (uri));
|
|
|
|
|
+ resource.mime_type = "text/plain";
|
|
|
|
|
+ resource.size = resources[uri].length;
|
|
|
|
|
+
|
|
|
|
|
+ result.add (resource);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public override async Gee.ArrayList<Mcp.Types.Common.ResourceContents> read_resource (string uri) throws Error {
|
|
|
|
|
+ if (!resources.contains (uri)) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found: %s".printf (uri));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ var result = new Gee.ArrayList<Mcp.Types.Common.ResourceContents> ();
|
|
|
|
|
+ result.add (new Mcp.Types.Common.TextResourceContents (uri, resources[uri]));
|
|
|
|
|
+
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ public override async void subscribe (string uri) throws Error {
|
|
|
|
|
+ if (!resources.contains (uri)) {
|
|
|
|
|
+ throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found: %s".printf (uri));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // BaseProvider will handle subscription tracking
|
|
|
|
|
+ yield base.subscribe (uri);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Updates a resource and notifies subscribers.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param uri The URI of the resource to update
|
|
|
|
|
+ * @param content The new content
|
|
|
|
|
+ */
|
|
|
|
|
+ public void update_resource (string uri, string content) {
|
|
|
|
|
+ if (!resources.contains (uri)) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ resources[uri] = content;
|
|
|
|
|
+ notify_resource_updated (uri);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new note resource.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param title The title of the note
|
|
|
|
|
+ * @param content The content of the note
|
|
|
|
|
+ * @return The URI of the new resource
|
|
|
|
|
+ */
|
|
|
|
|
+ public string create_note (string title, string content) {
|
|
|
|
|
+ string uri = "memory://note/%u".printf (next_id++);
|
|
|
|
|
+ resources.insert (uri, "Title: %s\n\n%s".printf (title, content));
|
|
|
|
|
+
|
|
|
|
|
+ // The ResourceManager will handle list_changed notifications
|
|
|
|
|
+ // when resources are listed
|
|
|
|
|
+
|
|
|
|
|
+ return uri;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Increments the counter resource.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @return The new counter value
|
|
|
|
|
+ */
|
|
|
|
|
+ public int increment_counter () {
|
|
|
|
|
+ string uri = "memory://counter";
|
|
|
|
|
+ if (!resources.contains (uri)) {
|
|
|
|
|
+ resources.insert (uri, "0");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int value = int.parse (resources[uri]);
|
|
|
|
|
+ value++;
|
|
|
|
|
+ resources[uri] = value.to_string ();
|
|
|
|
|
+
|
|
|
|
|
+ // Notify subscribers
|
|
|
|
|
+ notify_resource_updated (uri);
|
|
|
|
|
+
|
|
|
|
|
+ return value;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Extracts name from URI.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param uri The URI
|
|
|
|
|
+ * @return The name
|
|
|
|
|
+ */
|
|
|
|
|
+ private string get_name_from_uri (string uri) {
|
|
|
|
|
+ var parts = uri.split ("/");
|
|
|
|
|
+ return parts[parts.length - 1];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Code review prompt template implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class CodeReviewPromptTemplate : Mcp.Prompts.BaseTemplate {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new CodeReviewPromptTemplate.
|
|
|
|
|
+ */
|
|
|
|
|
+ public CodeReviewPromptTemplate () {
|
|
|
|
|
+ base ("code_review", "Code Review", "Generates a code review prompt for analyzing code quality and best practices");
|
|
|
|
|
+
|
|
|
|
|
+ // Add arguments
|
|
|
|
|
+ add_argument ("code", "Code", "The code to review", true);
|
|
|
|
|
+ add_argument ("language", "Language", "Programming language of the code", false);
|
|
|
|
|
+ add_argument ("focus", "Focus Area", "Specific areas to focus on (e.g., security, performance, readability)", false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
|
|
|
|
|
+ var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
|
|
|
|
|
+
|
|
|
|
|
+ string language = "";
|
|
|
|
|
+ if (arguments.lookup_value ("language", null) != null) {
|
|
|
|
|
+ language = arguments.lookup_value ("language", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string focus = "";
|
|
|
|
|
+ if (arguments.lookup_value ("focus", null) != null) {
|
|
|
|
|
+ focus = arguments.lookup_value ("focus", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string template = """Please review the following {{language}} code:
|
|
|
|
|
+
|
|
|
|
|
+```{{language}}
|
|
|
|
|
+{{code}}
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+{{#focus}}
|
|
|
|
|
+Please focus your review on: {{focus}}
|
|
|
|
|
+{{/focus}}
|
|
|
|
|
+
|
|
|
|
|
+Please analyze:
|
|
|
|
|
+1. Code quality and readability
|
|
|
|
|
+2. Potential bugs or issues
|
|
|
|
|
+3. Performance considerations
|
|
|
|
|
+4. Security vulnerabilities
|
|
|
|
|
+5. Adherence to best practices
|
|
|
|
|
+6. Suggestions for improvement
|
|
|
|
|
+
|
|
|
|
|
+Provide specific, actionable feedback.""";
|
|
|
|
|
+
|
|
|
|
|
+ messages.add (create_user_message (template, arguments));
|
|
|
|
|
+ return messages;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Summary prompt template implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class SummaryPromptTemplate : Mcp.Prompts.BaseTemplate {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new SummaryPromptTemplate.
|
|
|
|
|
+ */
|
|
|
|
|
+ public SummaryPromptTemplate () {
|
|
|
|
|
+ base ("summary", "Text Summary", "Generates a summary of the provided text");
|
|
|
|
|
+
|
|
|
|
|
+ // Add arguments
|
|
|
|
|
+ add_argument ("text", "Text", "The text to summarize", true);
|
|
|
|
|
+ add_argument ("length", "Summary Length", "Desired length of the summary (short, medium, long)", false);
|
|
|
|
|
+ add_argument ("style", "Summary Style", "Style of the summary (bullet_points, paragraph, executive)", false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
|
|
|
|
|
+ var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
|
|
|
|
|
+
|
|
|
|
|
+ string length = "medium";
|
|
|
|
|
+ if (arguments.lookup_value ("length", null) != null) {
|
|
|
|
|
+ length = arguments.lookup_value ("length", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string style = "paragraph";
|
|
|
|
|
+ if (arguments.lookup_value ("style", null) != null) {
|
|
|
|
|
+ style = arguments.lookup_value ("style", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string template = """Please provide a {{length}} summary of the following text:
|
|
|
|
|
+
|
|
|
|
|
+{{text}}
|
|
|
|
|
+
|
|
|
|
|
+Please format the summary as {{style}}.
|
|
|
|
|
+
|
|
|
|
|
+{{#style}}
|
|
|
|
|
+{{#bullet_points}}
|
|
|
|
|
+Use bullet points for key points.
|
|
|
|
|
+{{/bullet_points}}
|
|
|
|
|
+{{#paragraph}}
|
|
|
|
|
+Write in a clear, concise paragraph.
|
|
|
|
|
+{{/paragraph}}
|
|
|
|
|
+{{#executive}}
|
|
|
|
|
+Provide an executive summary with key takeaways.
|
|
|
|
|
+{{/executive}}
|
|
|
|
|
+{{/style}}""";
|
|
|
|
|
+
|
|
|
|
|
+ messages.add (create_user_message (template, arguments));
|
|
|
|
|
+ return messages;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Explain prompt template implementation.
|
|
|
|
|
+ */
|
|
|
|
|
+public class ExplainPromptTemplate : Mcp.Prompts.BaseTemplate {
|
|
|
|
|
+ /**
|
|
|
|
|
+ * Creates a new ExplainPromptTemplate.
|
|
|
|
|
+ */
|
|
|
|
|
+ public ExplainPromptTemplate () {
|
|
|
|
|
+ base ("explain", "Explain Concept", "Generates an explanation of a concept or topic");
|
|
|
|
|
+
|
|
|
|
|
+ // Add arguments
|
|
|
|
|
+ add_argument ("topic", "Topic", "The concept or topic to explain", true);
|
|
|
|
|
+ add_argument ("audience", "Audience", "Target audience level (beginner, intermediate, advanced)", false);
|
|
|
|
|
+ add_argument ("depth", "Depth", "Depth of explanation (brief, detailed, comprehensive)", false);
|
|
|
|
|
+ add_argument ("examples", "Include Examples", "Whether to include examples", false);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * {@inheritDoc}
|
|
|
|
|
+ */
|
|
|
|
|
+ protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (GLib.Variant arguments) {
|
|
|
|
|
+ var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
|
|
|
|
|
+
|
|
|
|
|
+ string audience = "intermediate";
|
|
|
|
|
+ if (arguments.lookup_value ("audience", null) != null) {
|
|
|
|
|
+ audience = arguments.lookup_value ("audience", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string depth = "detailed";
|
|
|
|
|
+ if (arguments.lookup_value ("depth", null) != null) {
|
|
|
|
|
+ depth = arguments.lookup_value ("depth", new VariantType ("s")).get_string ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ bool include_examples = false;
|
|
|
|
|
+ if (arguments.lookup_value ("examples", null) != null) {
|
|
|
|
|
+ include_examples = arguments.lookup_value ("examples", new VariantType ("b")).get_boolean ();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ string template = """Please explain the concept of "{{topic}}" for a {{audience}} audience.
|
|
|
|
|
+
|
|
|
|
|
+Provide a {{depth}} explanation that covers:
|
|
|
|
|
+1. Definition and core concepts
|
|
|
|
|
+2. Key principles and mechanisms
|
|
|
|
|
+3. Practical applications and use cases
|
|
|
|
|
+4. Common challenges and considerations
|
|
|
|
|
+
|
|
|
|
|
+{{#examples}}
|
|
|
|
|
+Please include relevant examples to illustrate the concept.
|
|
|
|
|
+{{/examples}}
|
|
|
|
|
+
|
|
|
|
|
+Tailor your explanation to the specified audience level and ensure clarity and accuracy.""";
|
|
|
|
|
+
|
|
|
|
|
+ // Create a modified arguments variant for examples
|
|
|
|
|
+ var builder = new VariantBuilder (new VariantType ("a{sv}"));
|
|
|
|
|
+
|
|
|
|
|
+ // Copy all existing arguments
|
|
|
|
|
+ var iter = arguments.iterator ();
|
|
|
|
|
+ string key;
|
|
|
|
|
+ Variant value;
|
|
|
|
|
+ while (iter.next ("{sv}", out key, out value)) {
|
|
|
|
|
+ builder.add ("{sv}", key, value);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Add/override the examples flag
|
|
|
|
|
+ builder.add ("{sv}", "examples", new Variant.boolean (include_examples));
|
|
|
|
|
+
|
|
|
|
|
+ var modified_args = builder.end ();
|
|
|
|
|
+
|
|
|
|
|
+ messages.add (create_user_message (template, modified_args));
|
|
|
|
|
+ return messages;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * Main function.
|
|
|
|
|
+ *
|
|
|
|
|
+ * @param args Command line arguments
|
|
|
|
|
+ * @return Exit code
|
|
|
|
|
+ */
|
|
|
|
|
+public static int main (string[] args) {
|
|
|
|
|
+ var server = new SimpleServer ();
|
|
|
|
|
+
|
|
|
|
|
+ // Create a main loop
|
|
|
|
|
+ var loop = new MainLoop ();
|
|
|
|
|
+
|
|
|
|
|
+ server.run.begin ((obj, res) => {
|
|
|
|
|
+ loop.quit ();
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ loop.run ();
|
|
|
|
|
+ return 0;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+Content-Length: 54
|
|
|
|
|
+
|
|
|
|
|
+{"jsonrpc":"2.0","id":1,"method":"initialize"}
|
|
|
|
|
+*/
|