/* * 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 list_resources (string? cursor) throws Error { var resources = new Gee.ArrayList (); // 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 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 (); 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 resources; private uint32 next_id; /** * Creates a new MemoryResourceProvider. */ public MemoryResourceProvider () { Object (); resources = new HashTable (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 list_resources (string? cursor) throws Error { var result = new Gee.ArrayList (); 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 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 (); 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 generate_messages (GLib.Variant arguments) { var messages = new Gee.ArrayList (); 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 generate_messages (GLib.Variant arguments) { var messages = new Gee.ArrayList (); 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 generate_messages (GLib.Variant arguments) { var messages = new Gee.ArrayList (); 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"} */