/* * 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 */ /** * Tool-related data types for MCP protocol. * * This namespace contains all tool-related types defined by the MCP specification, * including tool definitions, execution results, and related structures. */ namespace Mcp.Tools.Types { /** * Tool definition as defined by MCP protocol. */ public class ToolDefinition : GLib.Object { /** * Name of the tool. */ public string name { get; set; } /** * Optional title of the tool. */ public string? title { get; set; } /** * Optional description of the tool. */ public string? description { get; set; } /** * JSON schema for tool input. */ public Variant input_schema { get; set; } /** * Optional JSON schema for tool output. */ public Variant? output_schema { get; set; } /** * Optional execution settings. */ public ToolExecution? execution { get; set; } /** * Optional annotations for the tool. */ public ToolAnnotations? annotations { get; set; } /** * Creates a new ToolDefinition. * * @param name Name of the tool * @param input_schema JSON schema for input */ public ToolDefinition (string name, Variant input_schema) { this.name = name; this.input_schema = input_schema; } /** * Serializes ToolDefinition to GLib.Variant. * * @return A GLib.Variant representing this ToolDefinition */ public Variant to_variant () { var builder = Mcp.Types.VariantUtils.new_dict_builder (); builder.add ("{sv}", "name", new Variant.string (name)); Mcp.Types.VariantUtils.add_string_if_not_null (builder, "title", title); Mcp.Types.VariantUtils.add_string_if_not_null (builder, "description", description); builder.add ("{sv}", "inputSchema", input_schema); Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "outputSchema", output_schema); Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "execution", execution != null ? execution.to_variant () : null); Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "annotations", annotations != null ? annotations.to_variant () : null); return builder.end (); } /** * Creates a ToolDefinition from GLib.Variant. * * @param variant The GLib.Variant to deserialize * @return The deserialized ToolDefinition * @throws Error If deserialization fails */ public ToolDefinition.from_variant (Variant variant) throws Error { if (!variant.is_of_type (VariantType.VARDICT)) { throw new Mcp.Core.McpError.PARSE_ERROR ("ToolDefinition must be a dictionary"); } if (variant.lookup_value ("name", null) == null) { throw new Mcp.Core.McpError.INVALID_REQUEST ("Missing name in ToolDefinition"); } if (variant.lookup_value ("inputSchema", null) == null) { throw new Mcp.Core.McpError.INVALID_REQUEST ("Missing inputSchema in ToolDefinition"); } this.name = variant.lookup_value ("name", VariantType.STRING).get_string (); if (variant.lookup_value ("title", null) != null) { this.title = variant.lookup_value ("title", VariantType.STRING).get_string (); } if (variant.lookup_value ("description", null) != null) { this.description = variant.lookup_value ("description", VariantType.STRING).get_string (); } this.input_schema = variant.lookup_value ("inputSchema", VariantType.VARDICT); if (variant.lookup_value ("outputSchema", null) != null) { this.output_schema = variant.lookup_value ("outputSchema", VariantType.VARDICT); } if (variant.lookup_value ("execution", null) != null) { this.execution = new ToolExecution.from_variant (variant.lookup_value ("execution", VariantType.VARDICT)); } if (variant.lookup_value ("annotations", null) != null) { this.annotations = new ToolAnnotations.from_variant (variant.lookup_value ("annotations", VariantType.VARDICT)); } } } /** * Tool execution settings with enhanced context and state management. */ public class ToolExecution : GLib.Object { /** * Task support level. */ public string task_support { get; set; default = "forbidden"; } /** * Whether the tool supports progress reporting. */ public bool supports_progress { get; set; default = false; } /** * Whether the tool supports cancellation. */ public bool supports_cancellation { get; set; default = false; } /** * Maximum execution time in seconds (0 for no limit). */ public int timeout_seconds { get; set; default = 30; } /** * Creates a new ToolExecution. * * @param task_support Task support level * @param supports_progress Whether progress reporting is supported * @param supports_cancellation Whether cancellation is supported * @param timeout_seconds Maximum execution time in seconds */ public ToolExecution (string task_support = "forbidden", bool supports_progress = false, bool supports_cancellation = false, int timeout_seconds = 30) { this.task_support = task_support; this.supports_progress = supports_progress; this.supports_cancellation = supports_cancellation; this.timeout_seconds = timeout_seconds; } /** * Serializes ToolExecution to GLib.Variant. * * @return A GLib.Variant representing this ToolExecution */ public Variant to_variant () { var builder = Mcp.Types.VariantUtils.new_dict_builder (); builder.add ("{sv}", "taskSupport", new Variant.string (task_support)); if (supports_progress) { builder.add ("{sv}", "supportsProgress", new Variant.boolean (supports_progress)); } if (supports_cancellation) { builder.add ("{sv}", "supportsCancellation", new Variant.boolean (supports_cancellation)); } if (timeout_seconds > 0) { builder.add ("{sv}", "timeoutSeconds", new Variant.int32 (timeout_seconds)); } return builder.end (); } /** * Creates a ToolExecution from GLib.Variant. * * @param variant The GLib.Variant to deserialize * @return The deserialized ToolExecution * @throws Error If deserialization fails */ public ToolExecution.from_variant (Variant variant) throws Error { if (!variant.is_of_type (VariantType.VARDICT)) { throw new Mcp.Core.McpError.PARSE_ERROR ("ToolExecution must be a dictionary"); } if (variant.lookup_value ("taskSupport", null) != null) { this.task_support = variant.lookup_value ("taskSupport", VariantType.STRING).get_string (); } if (variant.lookup_value ("supportsProgress", null) != null) { this.supports_progress = variant.lookup_value ("supportsProgress", VariantType.BOOLEAN).get_boolean (); } if (variant.lookup_value ("supportsCancellation", null) != null) { this.supports_cancellation = variant.lookup_value ("supportsCancellation", VariantType.BOOLEAN).get_boolean (); } if (variant.lookup_value ("timeoutSeconds", null) != null) { this.timeout_seconds = variant.lookup_value ("timeoutSeconds", VariantType.INT32).get_int32 (); } } } /** * Tool execution context for preserving state during execution. */ public class ToolExecutionContext : GLib.Object { /** * Unique identifier for this execution context. */ public string execution_id { get; set; } /** * The tool name being executed. */ public string tool_name { get; set; } /** * The arguments passed to the tool. */ public Variant arguments { get; set; } /** * Progress token for reporting progress. */ public string? progress_token { get; set; } /** * Current execution state. */ public ExecutionState state { get; set; default = ExecutionState.INITIALIZED; } /** * Timestamp when execution started. */ public DateTime? start_time { get; set; } /** * Timestamp when execution ended. */ public DateTime? end_time { get; set; } /** * Whether execution has been cancelled. */ public bool is_cancelled { get; set; default = false; } /** * Cancellation source for async operations. */ private GLib.Cancellable cancellable; /** * Creates a new ToolExecutionContext. * * @param execution_id Unique identifier for the execution * @param tool_name The tool name being executed * @param arguments The arguments passed to the tool * @param progress_token Optional progress token */ public ToolExecutionContext (string execution_id, string tool_name, Variant arguments, string? progress_token = null) { this.execution_id = execution_id; this.tool_name = tool_name; this.arguments = arguments; this.progress_token = progress_token; this.cancellable = new GLib.Cancellable (); } /** * Gets the cancellable for this execution context. * * @return The cancellable object */ public GLib.Cancellable get_cancellable () { return cancellable; } /** * Cancels the execution. */ public void cancel () { is_cancelled = true; state = ExecutionState.CANCELLED; cancellable.cancel (); } /** * Marks the execution as started. */ public void mark_started () { start_time = new DateTime.now_utc (); state = ExecutionState.RUNNING; } /** * Marks the execution as completed. */ public void mark_completed () { end_time = new DateTime.now_utc (); if (state != ExecutionState.CANCELLED) { state = ExecutionState.COMPLETED; } } /** * Marks the execution as failed. */ public void mark_failed () { end_time = new DateTime.now_utc (); state = ExecutionState.FAILED; } /** * Gets the execution duration in milliseconds. * * @return The duration in milliseconds, or 0 if not available */ public int64 get_duration_ms () { if (start_time == null || end_time == null) { return 0; } return (int64) (end_time.difference (start_time) / 1000); } } /** * Execution states for tools. */ public enum ExecutionState { /** * Execution has been initialized but not started. */ INITIALIZED, /** * Execution is currently running. */ RUNNING, /** * Execution completed successfully. */ COMPLETED, /** * Execution failed. */ FAILED, /** * Execution was cancelled. */ CANCELLED } /** * Progress information for tool execution. */ public class ToolProgress : GLib.Object { /** * Progress token identifying this progress update. */ public string progress_token { get; set; } /** * Current progress value (0-100). */ public double progress { get; set; } /** * Optional progress message. */ public string? message { get; set; } /** * Creates a new ToolProgress. * * @param progress_token Progress token * @param progress Current progress value (0-100) * @param message Optional progress message */ public ToolProgress (string progress_token, double progress, string? message = null) { this.progress_token = progress_token; this.progress = progress; this.message = message; } /** * Serializes ToolProgress to GLib.Variant. * * @return A GLib.Variant representing this ToolProgress */ public Variant to_variant () { var builder = Mcp.Types.VariantUtils.new_dict_builder (); builder.add ("{sv}", "progressToken", new Variant.string (progress_token)); builder.add ("{sv}", "progress", new Variant.double (progress)); Mcp.Types.VariantUtils.add_string_if_not_null (builder, "message", message); return builder.end (); } } /** * Tool annotations. */ public class ToolAnnotations : GLib.Object { /** * Optional audience for this tool. */ public string? audience { get; set; } /** * Optional priority of this tool. */ public double? priority { get; set; } /** * Creates a new ToolAnnotations. */ public ToolAnnotations () { } /** * Serializes ToolAnnotations to GLib.Variant. * * @return A GLib.Variant representing this ToolAnnotations */ public Variant to_variant () { var builder = Mcp.Types.VariantUtils.new_dict_builder (); Mcp.Types.VariantUtils.add_string_if_not_null (builder, "audience", audience); if (priority != null) { builder.add ("{sv}", "priority", new Variant.double (priority)); } return builder.end (); } /** * Creates ToolAnnotations from GLib.Variant. * * @param variant The GLib.Variant to deserialize * @return The deserialized ToolAnnotations * @throws Error If deserialization fails */ public ToolAnnotations.from_variant (Variant variant) throws Error { if (!variant.is_of_type (VariantType.VARDICT)) { throw new Mcp.Core.McpError.PARSE_ERROR ("ToolAnnotations must be a dictionary"); } if (variant.lookup_value ("audience", null) != null) { this.audience = variant.lookup_value ("audience", VariantType.STRING).get_string (); } if (variant.lookup_value ("priority", null) != null) { this.priority = variant.lookup_value ("priority", VariantType.DOUBLE).get_double (); } } } /** * Result of calling a tool. */ public class CallToolResult : GLib.Object { /** * List of content blocks in the result. */ public Gee.ArrayList content { get; set; } /** * Optional structured content data. * This field is used for structured data that doesn't fit into content blocks, * such as JSON objects, arrays, or other structured data formats. */ public Variant? structured_content { get; set; } /** * Whether this result represents an error. */ public bool is_error { get; set; default = false; } /** * Optional metadata about the result. */ public Variant? metadata { get; set; } /** * Optional pagination cursor for results that support pagination. */ public string? next_cursor { get; set; } /** * Creates a new CallToolResult. */ public CallToolResult () { content = new Gee.ArrayList (); } /** * Creates a new CallToolResult with content. * * @param content_blocks Initial content blocks * @param is_error Whether this result represents an error */ public CallToolResult.with_content (Gee.ArrayList content_blocks, bool is_error = false) { this.content = content_blocks; this.is_error = is_error; } /** * Creates a new CallToolResult with structured content. * * @param structured_data The structured content data * @param text_description Optional text description * @param is_error Whether this result represents an error */ public CallToolResult.with_structured (Variant structured_data, string? text_description = null, bool is_error = false) { this.content = new Gee.ArrayList (); this.structured_content = structured_data; this.is_error = is_error; if (text_description != null) { var text_content = new Mcp.Types.Common.TextContent (text_description); this.content.add (text_content); } } /** * Serializes CallToolResult to GLib.Variant. * * @return A GLib.Variant representing this CallToolResult */ public Variant to_variant () { var builder = Mcp.Types.VariantUtils.new_dict_builder (); // Serialize content array var content_builder = Mcp.Types.VariantUtils.new_dict_array_builder (); foreach (var content_block in content) { content_builder.add_value (content_block.to_variant ()); } builder.add ("{sv}", "content", content_builder.end ()); // Add structured content if present Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "structuredContent", structured_content); // Add error flag builder.add ("{sv}", "isError", new Variant.boolean (is_error)); // Add metadata if present Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "metadata", metadata); // Add pagination cursor if present Mcp.Types.VariantUtils.add_string_if_not_null (builder, "nextCursor", next_cursor); return builder.end (); } /** * Creates a CallToolResult from GLib.Variant. * * @param variant The GLib.Variant to deserialize * @return The deserialized CallToolResult * @throws Error If deserialization fails */ public CallToolResult.from_variant (Variant variant) throws Error { if (!variant.is_of_type (VariantType.VARDICT)) { throw new Mcp.Core.McpError.PARSE_ERROR ("CallToolResult must be a dictionary"); } if (variant.lookup_value ("content", null) == null) { throw new Mcp.Core.McpError.INVALID_REQUEST ("Missing content in CallToolResult"); } this.content = new Gee.ArrayList (); var content = variant.lookup_value ("content", new VariantType ("aa{sv}")); for (size_t i = 0; i < content.n_children (); i++) { var element = content.get_child_value (i); if (element.is_of_type (VariantType.VARDICT)) { var content_block = deserialize_content_block (element); if (content_block != null) { this.content.add (content_block); } } } // Parse structured content if present if (variant.lookup_value ("structuredContent", null) != null) { this.structured_content = variant.lookup_value ("structuredContent", null); } // Parse error flag if present if (variant.lookup_value ("isError", null) != null) { this.is_error = variant.lookup_value ("isError", VariantType.BOOLEAN).get_boolean (); } // Parse metadata if present if (variant.lookup_value ("metadata", null) != null) { this.metadata = variant.lookup_value ("metadata", null); } // Parse pagination cursor if present if (variant.lookup_value ("nextCursor", null) != null) { this.next_cursor = variant.lookup_value ("nextCursor", VariantType.STRING).get_string (); } } /** * Deserializes a content block from a Variant. * * @param variant The Variant to deserialize * @return The deserialized ContentBlock or null if type is unknown * @throws Error If deserialization fails */ private Mcp.Types.Common.ContentBlock? deserialize_content_block (Variant variant) throws Error { if (!variant.is_of_type (VariantType.VARDICT)) { return null; } if (variant.lookup_value ("type", null) == null) { throw new Mcp.Core.McpError.INVALID_REQUEST ("Content block missing type field"); } string type = variant.lookup_value ("type", VariantType.STRING).get_string (); switch (type) { case "text": return new Mcp.Types.Common.TextContent.from_variant (variant); case "image": return new Mcp.Types.Common.ImageContent.from_variant (variant); case "audio": return new Mcp.Types.Common.AudioContent.from_variant (variant); case "resource_link": return new Mcp.Types.Common.ResourceLink.from_variant (variant); case "resource": return new Mcp.Types.Common.EmbeddedResource.from_variant (variant); default: // Unknown content type, create a generic text content with warning return new Mcp.Types.Common.TextContent ( "Warning: Unknown content type '%s'".printf (type) ); } } /** * Adds a text content block to result. * * @param text The text content to add */ public void add_text (string text) { var text_content = new Mcp.Types.Common.TextContent (text); content.add (text_content); } /** * Adds an image content block to result. * * @param data Base64 encoded image data * @param mime_type MIME type of image */ public void add_image (string data, string mime_type) { var image_content = new Mcp.Types.Common.ImageContent (data, mime_type); content.add (image_content); } /** * Adds a resource link to result. * * @param uri URI of resource * @param name Name of resource */ public void add_resource_link (string uri, string name) { var resource_link = new Mcp.Types.Common.ResourceLink (uri, name); content.add (resource_link); } /** * Sets structured content and optionally adds a text description. * * @param structured_data The structured content data * @param description Optional text description */ public void update_structured_content (Variant structured_data, string? description = null) { this.structured_content = structured_data; if (description != null) { add_text (description); } } /** * Sets metadata for result. * * @param metadata The metadata to set */ public void update_metadata (Variant metadata) { this.metadata = metadata; } /** * Sets pagination cursor for result. * * @param cursor The pagination cursor */ public void update_next_cursor (string cursor) { this.next_cursor = cursor; } /** * Creates a successful result with text content. * * @param text The text content * @return A successful CallToolResult */ public static CallToolResult success (string text) { var result = new CallToolResult (); result.add_text (text); return result; } /** * Creates an error result with text content. * * @param error_message The error message * @return An error CallToolResult */ public static CallToolResult error (string error_message) { var result = new CallToolResult (); result.add_text (error_message); result.is_error = true; return result; } /** * Creates a successful result with structured content. * * @param structured_data The structured content data * @param description Optional text description * @return A successful CallToolResult with structured content */ public static CallToolResult success_structured (Variant structured_data, string? description = null) { var result = new CallToolResult (); result.update_structured_content (structured_data, description); return result; } } }