소스 검색

Fix tools

clanker 1 개월 전
부모
커밋
91cdf5a2c9
9개의 변경된 파일2471개의 추가작업 그리고 58개의 파일을 삭제
  1. 1 0
      meson.build
  2. 233 2
      src/core/error.vala
  3. 2 1
      src/meson.build
  4. 83 0
      src/tools/executor.vala
  5. 1053 43
      src/tools/manager.vala
  6. 457 10
      src/tools/types.vala
  7. 159 2
      src/types/common.vala
  8. 240 0
      test_calculator_comprehensive.py
  9. 243 0
      test_calculator_debug.py

+ 1 - 0
meson.build

@@ -12,6 +12,7 @@ json_glib_dep = dependency('json-glib-1.0', version: '>= 1.6')
 jsonrpc_glib_dep = dependency('jsonrpc-glib-1.0', version: '>= 3.34')
 gee_dep = dependency('gee-0.8', version: '>= 0.20')
 posix_dep = meson.get_compiler('vala').find_library('posix')
+math_dep = meson.get_compiler('c').find_library('m', required : false)
 
 # Subdirectories
 subdir('src')

+ 233 - 2
src/core/error.vala

@@ -77,6 +77,30 @@ namespace Mcp.Core {
          */
         TOOL_EXECUTION_FAILED,
         
+        /**
+         * Tool execution timed out.
+         * MCP-specific error code.
+         */
+        TOOL_EXECUTION_TIMEOUT,
+        
+        /**
+         * Tool execution was cancelled.
+         * MCP-specific error code.
+         */
+        TOOL_EXECUTION_CANCELLED,
+        
+        /**
+         * Invalid progress token.
+         * MCP-specific error code.
+         */
+        INVALID_PROGRESS_TOKEN,
+        
+        /**
+         * Tool context error.
+         * MCP-specific error code.
+         */
+        TOOL_CONTEXT_ERROR,
+        
         /**
          * Prompt not found.
          * MCP-specific error code.
@@ -96,18 +120,92 @@ namespace Mcp.Core {
         PROTOCOL_ERROR
     }
     
+    /**
+     * Tool execution error context.
+     */
+    public class ToolErrorContext : GLib.Object {
+        /**
+         * The tool name that caused the error.
+         */
+        public string tool_name { get; set; }
+        
+        /**
+         * The execution ID of the failed operation.
+         */
+        public string? execution_id { get; set; }
+        
+        /**
+         * The progress token if available.
+         */
+        public string? progress_token { get; set; }
+        
+        /**
+         * The arguments that were being processed.
+         */
+        public Variant? arguments { get; set; }
+        
+        /**
+         * The stage of execution where the error occurred.
+         */
+        public string? execution_stage { get; set; }
+        
+        /**
+         * Additional error data.
+         */
+        public Variant? error_data { get; set; }
+        
+        /**
+         * Creates a new ToolErrorContext.
+         *
+         * @param tool_name The tool name
+         */
+        public ToolErrorContext (string tool_name) {
+            this.tool_name = tool_name;
+        }
+        
+        /**
+         * Serializes ToolErrorContext to GLib.Variant.
+         *
+         * @return A GLib.Variant representing this ToolErrorContext
+         */
+        public Variant to_variant () {
+            var builder = Mcp.Types.VariantUtils.new_dict_builder ();
+            builder.add ("{sv}", "toolName", new Variant.string (tool_name));
+            
+            Mcp.Types.VariantUtils.add_string_if_not_null (builder, "executionId", execution_id);
+            Mcp.Types.VariantUtils.add_string_if_not_null (builder, "progressToken", progress_token);
+            Mcp.Types.VariantUtils.add_string_if_not_null (builder, "executionStage", execution_stage);
+            Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "arguments", arguments);
+            Mcp.Types.VariantUtils.add_variant_if_not_null (builder, "errorData", error_data);
+            
+            return builder.end ();
+        }
+    }
+    
     /**
      * Utility class for creating and handling MCP errors.
      */
     public class ErrorHandler : GLib.Object {
         /**
          * Creates a JSON-RPC error response node.
-         * 
+         *
          * @param id The request ID
          * @param error The McpError to convert
          * @return A JSON node representing the error response
          */
         public static Json.Node create_error_response (string id, McpError error) {
+            return create_error_response_with_context (id, error, null);
+        }
+        
+        /**
+         * Creates a JSON-RPC error response node with context.
+         *
+         * @param id The request ID
+         * @param error The McpError to convert
+         * @param context Optional error context
+         * @return A JSON node representing the error response
+         */
+        public static Json.Node create_error_response_with_context (string id, McpError error, ToolErrorContext? context = null) {
             var object = new Json.Object ();
             
             object.set_string_member ("jsonrpc", "2.0");
@@ -117,6 +215,38 @@ namespace Mcp.Core {
             error_obj.set_int_member ("code", get_json_rpc_code (error));
             error_obj.set_string_member ("message", error.message);
             
+            // Add context if available
+            if (context != null) {
+                var context_obj = new Json.Object ();
+                context_obj.set_string_member ("toolName", context.tool_name);
+                
+                if (context.execution_id != null) {
+                    context_obj.set_string_member ("executionId", context.execution_id);
+                }
+                
+                if (context.progress_token != null) {
+                    context_obj.set_string_member ("progressToken", context.progress_token);
+                }
+                
+                if (context.execution_stage != null) {
+                    context_obj.set_string_member ("executionStage", context.execution_stage);
+                }
+                
+                // Add error data if available
+                if (context.error_data != null) {
+                    // Convert Variant to JSON (simplified for now)
+                    if (context.error_data.is_of_type (VariantType.VARDICT)) {
+                        var data_obj = new Json.Object ();
+                        // This is a simplified conversion - a full implementation would
+                        // need to recursively convert all Variant types to JSON
+                        data_obj.set_string_member ("type", "variant_data");
+                        context_obj.set_object_member ("errorData", data_obj);
+                    }
+                }
+                
+                error_obj.set_object_member ("context", context_obj);
+            }
+            
             object.set_object_member ("error", error_obj);
             
             var node = new Json.Node (Json.NodeType.OBJECT);
@@ -127,11 +257,22 @@ namespace Mcp.Core {
         
         /**
          * Creates a JSON-RPC error notification node.
-         * 
+         *
          * @param error The McpError to convert
          * @return A JSON node representing the error notification
          */
         public static Json.Node create_error_notification (McpError error) {
+            return create_error_notification_with_context (error, null);
+        }
+        
+        /**
+         * Creates a JSON-RPC error notification node with context.
+         *
+         * @param error The McpError to convert
+         * @param context Optional error context
+         * @return A JSON node representing the error notification
+         */
+        public static Json.Node create_error_notification_with_context (McpError error, ToolErrorContext? context = null) {
             var object = new Json.Object ();
             
             object.set_string_member ("jsonrpc", "2.0");
@@ -141,6 +282,26 @@ namespace Mcp.Core {
             params_obj.set_int_member ("code", get_json_rpc_code (error));
             params_obj.set_string_member ("message", error.message);
             
+            // Add context if available
+            if (context != null) {
+                var context_obj = new Json.Object ();
+                context_obj.set_string_member ("toolName", context.tool_name);
+                
+                if (context.execution_id != null) {
+                    context_obj.set_string_member ("executionId", context.execution_id);
+                }
+                
+                if (context.progress_token != null) {
+                    context_obj.set_string_member ("progressToken", context.progress_token);
+                }
+                
+                if (context.execution_stage != null) {
+                    context_obj.set_string_member ("executionStage", context.execution_stage);
+                }
+                
+                params_obj.set_object_member ("context", context_obj);
+            }
+            
             object.set_object_member ("params", params_obj);
             
             var node = new Json.Node (Json.NodeType.OBJECT);
@@ -149,6 +310,60 @@ namespace Mcp.Core {
             return node;
         }
         
+        /**
+         * Creates a tool execution error with context.
+         *
+         * @param tool_name The tool name
+         * @param message The error message
+         * @param execution_stage The execution stage where error occurred
+         * @param execution_id The execution ID
+         * @param progress_token The progress token
+         * @return A McpError with context
+         */
+        public static McpError create_tool_error (string tool_name, string message,
+                                               string? execution_stage = null,
+                                               string? execution_id = null,
+                                               string? progress_token = null) {
+            var context = new ToolErrorContext (tool_name);
+            context.execution_stage = execution_stage;
+            context.execution_id = execution_id;
+            context.progress_token = progress_token;
+            
+            // Store context in the error (this is a simplified approach)
+            // In a full implementation, we might extend the Error class
+            return new McpError.TOOL_EXECUTION_FAILED ("%s (tool: %s)".printf (message, tool_name));
+        }
+        
+        /**
+         * Creates a tool timeout error with context.
+         *
+         * @param tool_name The tool name
+         * @param timeout_seconds The timeout duration
+         * @param execution_id The execution ID
+         * @param progress_token The progress token
+         * @return A McpError with context
+         */
+        public static McpError create_tool_timeout_error (string tool_name, int timeout_seconds,
+                                                        string? execution_id = null,
+                                                        string? progress_token = null) {
+            var message = "Tool execution timed out after %d seconds".printf (timeout_seconds);
+            return create_tool_error (tool_name, message, "timeout", execution_id, progress_token);
+        }
+        
+        /**
+         * Creates a tool cancellation error with context.
+         *
+         * @param tool_name The tool name
+         * @param execution_id The execution ID
+         * @param progress_token The progress token
+         * @return A McpError with context
+         */
+        public static McpError create_tool_cancellation_error (string tool_name,
+                                                            string? execution_id = null,
+                                                            string? progress_token = null) {
+            return create_tool_error (tool_name, "Tool execution was cancelled", "cancelled", execution_id, progress_token);
+        }
+        
         /**
          * Converts an McpError to a JSON-RPC error code.
          * 
@@ -171,6 +386,14 @@ namespace Mcp.Core {
                     return -32001;
                 case McpError.TOOL_EXECUTION_FAILED:
                     return -32002;
+                case McpError.TOOL_EXECUTION_TIMEOUT:
+                    return -32006;
+                case McpError.TOOL_EXECUTION_CANCELLED:
+                    return -32007;
+                case McpError.INVALID_PROGRESS_TOKEN:
+                    return -32008;
+                case McpError.TOOL_CONTEXT_ERROR:
+                    return -32009;
                 case McpError.PROMPT_NOT_FOUND:
                     return -32003;
                 case McpError.TRANSPORT_ERROR:
@@ -205,6 +428,14 @@ namespace Mcp.Core {
                     return new McpError.RESOURCE_NOT_FOUND (message);
                 case -32002:
                     return new McpError.TOOL_EXECUTION_FAILED (message);
+                case -32006:
+                    return new McpError.TOOL_EXECUTION_TIMEOUT (message);
+                case -32007:
+                    return new McpError.TOOL_EXECUTION_CANCELLED (message);
+                case -32008:
+                    return new McpError.INVALID_PROGRESS_TOKEN (message);
+                case -32009:
+                    return new McpError.TOOL_CONTEXT_ERROR (message);
                 case -32003:
                     return new McpError.PROMPT_NOT_FOUND (message);
                 case -32004:

+ 2 - 1
src/meson.build

@@ -42,7 +42,8 @@ mcp_lib = shared_library(
     json_glib_dep,
     jsonrpc_glib_dep,
     gee_dep,
-    posix_dep
+    posix_dep,
+    math_dep
   ],
   vala_args: mcp_vala_args,
   install: true,

+ 83 - 0
src/tools/executor.vala

@@ -63,6 +63,24 @@ namespace Mcp.Tools {
          * @throws Error If execution fails
          */
         public abstract async Mcp.Tools.Types.CallToolResult execute (Variant arguments) throws Error;
+        
+        /**
+         * Executes the tool with given arguments and cancellation support.
+         *
+         * This method performs the actual tool execution with the provided
+         * arguments and supports cancellation. Arguments have already been
+         * validated against the tool's input schema by the ToolManager.
+         *
+         * @param arguments The validated tool arguments
+         * @param cancellable Cancellable object for cancellation support
+         * @return The execution result with content blocks
+         * @throws Error If execution fails or is cancelled
+         */
+        public virtual async Mcp.Tools.Types.CallToolResult execute_with_context (Variant arguments, Cancellable? cancellable = null) throws Error {
+            // Default implementation delegates to the original execute method
+            // Subclasses can override for proper cancellation support
+            return yield execute (arguments);
+        }
     }
     
     /**
@@ -112,6 +130,27 @@ namespace Mcp.Tools {
             }
         }
         
+        /**
+         * Executes the tool with argument validation, error handling, and cancellation support.
+         *
+         * This method validates the arguments against the tool's schema
+         * before calling the abstract do_execute_with_context method.
+         *
+         * {@inheritDoc}
+         */
+        public async Mcp.Tools.Types.CallToolResult execute_with_context (Variant arguments, Cancellable? cancellable = null) throws Error {
+            // Validate arguments
+            validate_arguments (arguments);
+            
+            try {
+                // Call the concrete implementation with cancellation support
+                return yield do_execute_with_context (arguments, cancellable);
+            } catch (Error e) {
+                // Create error result
+                return create_error_result (e);
+            }
+        }
+        
         /**
          * Performs the actual tool execution.
          *
@@ -124,6 +163,24 @@ namespace Mcp.Tools {
          */
         protected abstract async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error;
         
+        /**
+         * Performs the actual tool execution with cancellation support.
+         *
+         * This method must be implemented by concrete tool executors that
+         * support cancellation. Arguments are already validated when this
+         * method is called. The default implementation delegates to do_execute.
+         *
+         * @param arguments The validated tool arguments
+         * @param cancellable Cancellable object for cancellation support
+         * @return The execution result
+         * @throws Error If execution fails or is cancelled
+         */
+        protected virtual async Mcp.Tools.Types.CallToolResult do_execute_with_context (Variant arguments, Cancellable? cancellable = null) throws Error {
+            // Default implementation delegates to the non-cancellable version
+            // Subclasses should override for proper cancellation support
+            return yield do_execute (arguments);
+        }
+        
         /**
          * Validates arguments against the tool's input schema.
          *
@@ -268,10 +325,36 @@ namespace Mcp.Tools {
          * @return The argument value or default
          */
         protected double get_double_arg (Variant arguments, string name, double default_value = 0.0) {
+            // Try DOUBLE first, then try integer types
             var value = arguments.lookup_value (name, VariantType.DOUBLE);
             if (value != null) {
                 return value.get_double ();
             }
+            
+            // If not found as DOUBLE, try INT32
+            value = arguments.lookup_value (name, VariantType.INT32);
+            if (value != null) {
+                return (double) value.get_int32 ();
+            }
+            
+            // If not found as INT32, try INT64
+            value = arguments.lookup_value (name, VariantType.INT64);
+            if (value != null) {
+                return (double) value.get_int64 ();
+            }
+            
+            // If not found as INT64, try UINT32
+            value = arguments.lookup_value (name, VariantType.UINT32);
+            if (value != null) {
+                return (double) value.get_uint32 ();
+            }
+            
+            // If not found as UINT32, try UINT64
+            value = arguments.lookup_value (name, VariantType.UINT64);
+            if (value != null) {
+                return (double) value.get_uint64 ();
+            }
+            
             return default_value;
         }
     }

+ 1053 - 43
src/tools/manager.vala

@@ -40,17 +40,26 @@ namespace Mcp.Tools {
     public class Manager : GLib.Object {
         private HashTable<string, Executor> executors;
         private Variant? validation_schema;
+        private HashTable<string, Mcp.Tools.Types.ToolExecutionContext> active_executions;
+        private HashTable<string, Cancellable> progress_tokens;
         
         /**
          * Signal emitted when the tool list changes.
          */
         public signal void list_changed ();
         
+        /**
+         * Signal emitted when tool execution progress is updated.
+         */
+        public signal void progress_updated (string progress_token, double progress, string? message = null);
+        
         /**
          * Creates a new Manager.
          */
         public Manager () {
             executors = new HashTable<string, Executor> (str_hash, str_equal);
+            active_executions = new HashTable<string, Mcp.Tools.Types.ToolExecutionContext> (str_hash, str_equal);
+            progress_tokens = new HashTable<string, Cancellable> (str_hash, str_equal);
             
             // Initialize basic validation schema
             setup_validation_schema ();
@@ -64,6 +73,9 @@ namespace Mcp.Tools {
          * @throws Error If registration fails (e.g., duplicate name)
          */
         public void register_executor (string name, Executor executor) throws Error {
+            // Validate tool name according to MCP specification
+            validate_tool_name (name);
+            
             if (executors.contains (name)) {
                 throw new Mcp.Core.McpError.INVALID_PARAMS ("Tool already registered: %s".printf (name));
             }
@@ -75,6 +87,9 @@ namespace Mcp.Tools {
                     throw new Mcp.Core.McpError.INVALID_PARAMS ("Tool definition name mismatch: %s != %s".printf (definition.name, name));
                 }
                 
+                // Validate tool definition name as well
+                validate_tool_name (definition.name);
+                
                 // Validate input schema
                 validate_tool_schema (definition.input_schema);
                 
@@ -141,49 +156,108 @@ namespace Mcp.Tools {
         }
         
         /**
-         * Handles the tools/list JSON-RPC method.
+         * Handles the tools/list JSON-RPC method with complete pagination support.
          *
-         * This method returns a list of all available tools with their definitions.
+         * This method returns a paginated list of all available tools with their definitions.
          * Each tool includes its name, description, and input schema.
          *
          * @param params The method parameters (may include cursor for pagination)
-         * @return The response result containing tools array
+         * @return The response result containing tools array and pagination info
          * @throws Error If handling fails
          */
         public async Variant handle_list (Variant @params) throws Error {
-            var result = new Gee.ArrayList<Mcp.Tools.Types.ToolDefinition> ();
+            // Extract cursor parameter if present
+            string? cursor = null;
+            if (@params.lookup_value ("cursor", null) != null) {
+                cursor = @params.lookup_value ("cursor", VariantType.STRING).get_string ();
+            }
             
-            // Get tool definitions from all executors
+            // Get all tool definitions from all executors
+            var all_tools = new Gee.ArrayList<Mcp.Tools.Types.ToolDefinition> ();
             foreach (var name in executors.get_keys ()) {
                 var executor = executors.lookup (name);
                 
                 try {
                     var definition = executor.get_definition ();
-                    result.add (definition);
+                    all_tools.add (definition);
                 } catch (Error e) {
                     // Continue with other executors
                 }
             }
             
+            // Sort tools by name for consistent pagination
+            all_tools.sort ((a, b) => {
+                return strcmp (a.name, b.name);
+            });
+            
+            // Pagination settings
+            const int PAGE_SIZE = 20; // Tools per page
+            int start_index = 0;
+            
+            // Parse cursor to determine starting position
+            if (cursor != null) {
+                // Validate cursor format - should be a base64-encoded position
+                try {
+                    // Decode base64 cursor to get position
+                    uint8[] cursor_bytes = Base64.decode (cursor);
+                    if (cursor_bytes.length == 4) {
+                        // Convert 4 bytes to int (little-endian)
+                        start_index = cursor_bytes[0] |
+                                      (cursor_bytes[1] << 8) |
+                                      (cursor_bytes[2] << 16) |
+                                      (cursor_bytes[3] << 24);
+                    } else {
+                        // Fallback to simple integer parsing for backward compatibility
+                        start_index = int.parse (cursor);
+                    }
+                } catch (Error e) {
+                    // Invalid cursor, start from beginning
+                    start_index = 0;
+                }
+                
+                // Validate start_index bounds
+                if (start_index < 0) {
+                    start_index = 0;
+                } else if (start_index >= all_tools.size) {
+                    // Cursor beyond available tools, return empty result
+                    var result_builder = Mcp.Types.VariantUtils.new_dict_builder ();
+                    var tools_builder = Mcp.Types.VariantUtils.new_dict_array_builder ();
+                    result_builder.add ("{sv}", "tools", tools_builder.end ());
+                    return result_builder.end ();
+                }
+            }
+            
+            // Calculate the end index for this page
+            int end_index = int.min (start_index + PAGE_SIZE, all_tools.size);
+            
+            // Get the tools for this page
+            var page_tools = new Gee.ArrayList<Mcp.Tools.Types.ToolDefinition> ();
+            for (int i = start_index; i < end_index; i++) {
+                page_tools.add (all_tools[i]);
+            }
+            
             // Build response as Variant using utility functions
             var result_builder = Mcp.Types.VariantUtils.new_dict_builder ();
             
             // Serialize tools array
             var tools_builder = Mcp.Types.VariantUtils.new_dict_array_builder ();
-            foreach (var tool in result) {
+            foreach (var tool in page_tools) {
                 tools_builder.add_value (tool.to_variant ());
             }
             result_builder.add ("{sv}", "tools", tools_builder.end ());
             
-            // Add pagination cursor if supported (for future implementation)
-            // Only include nextCursor if it's not null
-            if (@params.lookup_value ("cursor", null) != null) {
-                string? next_cursor = null;
-                // In a real implementation, this would calculate the actual next cursor
-                // For now, we'll just not include it in the response
-                if (next_cursor != null) {
-                    result_builder.add ("{sv}", "nextCursor", new Variant.string (next_cursor));
-                }
+            // Add nextCursor if there are more tools
+            if (end_index < all_tools.size) {
+                // Encode next position as base64 for proper cursor implementation
+                uint8[] cursor_bytes = new uint8[4];
+                int next_position = end_index;
+                cursor_bytes[0] = (uint8) (next_position & 0xFF);
+                cursor_bytes[1] = (uint8) ((next_position >> 8) & 0xFF);
+                cursor_bytes[2] = (uint8) ((next_position >> 16) & 0xFF);
+                cursor_bytes[3] = (uint8) ((next_position >> 24) & 0xFF);
+                
+                string next_cursor = Base64.encode (cursor_bytes);
+                result_builder.add ("{sv}", "nextCursor", new Variant.string (next_cursor));
             }
             
             return result_builder.end ();
@@ -193,7 +267,8 @@ namespace Mcp.Tools {
          * Handles the tools/call JSON-RPC method.
          *
          * This method executes a tool with the provided arguments after validation.
-         * It supports both synchronous and asynchronous tool execution.
+         * It supports both synchronous and asynchronous tool execution with progress
+         * reporting and cancellation support.
          *
          * @param params The method parameters containing tool name and arguments
          * @return The response result containing tool execution output
@@ -214,6 +289,15 @@ namespace Mcp.Tools {
                 arguments = Mcp.Types.VariantUtils.new_empty_dict ();
             }
             
+            // Check for progress token
+            string? progress_token = null;
+            if (@params.lookup_value ("_meta", null) != null) {
+                var meta = @params.lookup_value ("_meta", VariantType.VARDICT);
+                if (meta != null && meta.lookup_value ("progressToken", null) != null) {
+                    progress_token = meta.lookup_value ("progressToken", VariantType.STRING).get_string ();
+                }
+            }
+            
             Executor? executor = executors.lookup (name);
             if (executor == null) {
                 throw new Mcp.Core.McpError.TOOL_EXECUTION_FAILED ("Tool not found: %s".printf (name));
@@ -222,11 +306,45 @@ namespace Mcp.Tools {
             // Validate arguments against tool schema
             validate_arguments (name, arguments);
             
+            // Create execution context
+            string execution_id = generate_execution_id ();
+            var context = new Mcp.Tools.Types.ToolExecutionContext (
+                execution_id, name, arguments, progress_token
+            );
+            active_executions.insert (execution_id, context);
+            
+            // Register progress token if provided
+            if (progress_token != null) {
+                progress_tokens.insert (progress_token, context.get_cancellable ());
+            }
+            
             try {
-                // Execute tool with timeout support
-                var result = yield execute_with_timeout (executor, arguments);
+                // Mark execution as started
+                context.mark_started ();
+                
+                // Execute tool with context support
+                var result = yield execute_with_context (executor, context);
+                
+                // Mark execution as completed
+                context.mark_completed ();
+                
+                // Clean up
+                active_executions.remove (execution_id);
+                if (progress_token != null) {
+                    progress_tokens.remove (progress_token);
+                }
+                
                 return result.to_variant ();
             } catch (Error e) {
+                // Mark execution as failed
+                context.mark_failed ();
+                
+                // Clean up
+                active_executions.remove (execution_id);
+                if (progress_token != null) {
+                    progress_tokens.remove (progress_token);
+                }
+                
                 // Create error result
                 var error_content = new Mcp.Types.Common.TextContent (
                     "Tool execution failed: %s".printf (e.message)
@@ -241,38 +359,168 @@ namespace Mcp.Tools {
         }
         
         /**
-         * Executes a tool with timeout support.
+         * Executes a tool with context and timeout support.
          *
          * @param executor The tool executor
-         * @param arguments The tool arguments
+         * @param context The execution context
          * @return The execution result
          * @throws Error If execution fails or times out
          */
-        private async Mcp.Tools.Types.CallToolResult execute_with_timeout (Executor executor, Variant arguments) throws Error {
-            // Default timeout of 30 seconds for tool execution
-            const int TIMEOUT_SECONDS = 30;
+        private async Mcp.Tools.Types.CallToolResult execute_with_context (Executor executor, Mcp.Tools.Types.ToolExecutionContext context) throws Error {
+            // Get timeout from tool definition or use default
+            int timeout_seconds = 30;
+            var definition = executor.get_definition ();
+            if (definition.execution != null && definition.execution.timeout_seconds > 0) {
+                timeout_seconds = definition.execution.timeout_seconds;
+            }
             
-            var source = new TimeoutSource (TIMEOUT_SECONDS * 1000);
+            // Create timeout source
+            var source = new TimeoutSource (timeout_seconds * 1000);
             bool timed_out = false;
             source.set_callback (() => {
                 timed_out = true;
+                context.cancel ();
                 return false; // Remove timeout source
             });
             source.attach (null);
             
             try {
-                var result = yield executor.execute (arguments);
+                // Execute tool with cancellable support
+                var result = yield executor.execute_with_context (context.arguments, context.get_cancellable ());
+                
                 source.destroy ();
+                
                 if (timed_out) {
-                    throw new Mcp.Core.McpError.TOOL_EXECUTION_FAILED ("Tool execution timed out");
+                    throw new Mcp.Core.McpError.TOOL_EXECUTION_FAILED ("Tool execution timed out after %d seconds".printf (timeout_seconds));
+                }
+                
+                if (context.is_cancelled) {
+                    throw new Mcp.Core.McpError.TOOL_EXECUTION_FAILED ("Tool execution was cancelled");
                 }
+                
                 return result;
             } catch (Error e) {
                 source.destroy ();
+                
+                // Check if error is due to cancellation
+                if (e is IOError.CANCELLED || context.is_cancelled) {
+                    throw new Mcp.Core.McpError.TOOL_EXECUTION_FAILED ("Tool execution was cancelled");
+                }
+                
                 throw e;
             }
         }
         
+        /**
+         * Generates a unique execution ID.
+         *
+         * @return A unique execution ID
+         */
+        private string generate_execution_id () {
+            var timestamp = new DateTime.now_utc ().to_unix ();
+            var random = GLib.Random.next_int ();
+            return "exec_%lld_%u".printf (timestamp, random);
+        }
+        
+        /**
+         * Reports progress for a tool execution.
+         *
+         * @param progress_token The progress token
+         * @param progress Current progress value (0-100)
+         * @param message Optional progress message
+         * @throws Error If the progress token is not found
+         */
+        public void report_progress (string progress_token, double progress, string? message = null) throws Error {
+            if (progress_tokens.contains (progress_token)) {
+                progress_updated (progress_token, progress, message);
+            } else {
+                throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid progress token: %s".printf (progress_token));
+            }
+        }
+        
+        /**
+         * Cancels a tool execution using its progress token.
+         *
+         * @param progress_token The progress token of the execution to cancel
+         * @throws Error If the progress token is not found
+         */
+        public void cancel_execution (string progress_token) throws Error {
+            if (progress_tokens.contains (progress_token)) {
+                var cancellable = progress_tokens.lookup (progress_token);
+                cancellable.cancel ();
+            } else {
+                throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid progress token: %s".printf (progress_token));
+            }
+        }
+        
+        /**
+         * Gets the execution context for a given execution ID.
+         *
+         * @param execution_id The execution ID
+         * @return The execution context or null if not found
+         */
+        public Mcp.Tools.Types.ToolExecutionContext? get_execution_context (string execution_id) {
+            return active_executions.lookup (execution_id);
+        }
+        
+        /**
+         * Gets all active execution contexts.
+         *
+         * @return Array of active execution contexts
+         */
+        public Mcp.Tools.Types.ToolExecutionContext[] get_active_executions () {
+            var contexts = new Mcp.Tools.Types.ToolExecutionContext[active_executions.size ()];
+            int i = 0;
+            foreach (var context in active_executions.get_values ()) {
+                contexts[i++] = context;
+            }
+            return contexts;
+        }
+        
+        /**
+         * Validates a tool name according to MCP specification.
+         *
+         * @param name The tool name to validate
+         * @throws Error If name is invalid
+         */
+        private void validate_tool_name (string name) throws Error {
+            // Check if name is null or empty
+            if (name == null || name.strip () == "") {
+                throw new Mcp.Core.McpError.INVALID_PARAMS ("Tool name cannot be null or empty");
+            }
+            
+            // Check length (1-128 characters)
+            if (name.length < 1 || name.length > 128) {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Tool name must be between 1 and 128 characters long, got %d characters".printf (name.length)
+                );
+            }
+            
+            // Check for allowed characters: ASCII letters, digits, underscore, hyphen, and dot
+            // Regex pattern: ^[a-zA-Z0-9_.-]+$
+            try {
+                Regex name_regex = new Regex ("^[a-zA-Z0-9_.-]+$");
+                if (!name_regex.match (name)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Tool name contains invalid characters. Only ASCII letters, digits, underscore, hyphen, and dot are allowed"
+                    );
+                }
+            } catch (Error e) {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Failed to validate tool name: %s".printf (e.message)
+                );
+            }
+            
+            // Check for spaces, commas, or other special characters (additional safety check)
+            foreach (char c in name.to_utf8 ()) {
+                if (c == ' ' || c == ',' || c == ';' || c == ':' || c == '/' || c == '\\') {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Tool name contains invalid character '%c'. Spaces, commas, and other special characters are not allowed".printf (c)
+                    );
+                }
+            }
+        }
+        
         /**
          * Sets up the basic JSON schema validation.
          */
@@ -282,7 +530,7 @@ namespace Mcp.Tools {
         }
         
         /**
-         * Validates a tool's JSON schema.
+         * Validates a tool's JSON schema with comprehensive checks.
          *
          * @param schema The schema to validate
          * @throws Error If the schema is invalid
@@ -296,41 +544,803 @@ namespace Mcp.Tools {
                 throw new Mcp.Core.McpError.INVALID_PARAMS ("Tool schema must have a 'type' property");
             }
             
-            if (schema.lookup_value ("type", VariantType.STRING).get_string () != "object") {
+            string schema_type = schema.lookup_value ("type", VariantType.STRING).get_string ();
+            if (schema_type != "object") {
                 throw new Mcp.Core.McpError.INVALID_PARAMS ("Tool schema type must be 'object'");
             }
+            
+            // Validate properties if present
+            if (schema.lookup_value ("properties", null) != null) {
+                var properties = schema.lookup_value ("properties", VariantType.VARDICT);
+                if (!properties.is_of_type (VariantType.VARDICT)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS ("Schema properties must be a dictionary");
+                }
+                
+                // Validate each property schema
+                validate_property_schemas (properties);
+            }
+            
+            // Validate required array if present
+            if (schema.lookup_value ("required", null) != null) {
+                var required = schema.lookup_value ("required", new VariantType ("as"));
+                if (!required.is_of_type (new VariantType ("as"))) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS ("Schema required must be an array of strings");
+                }
+            }
+        }
+        
+        /**
+         * Validates property schemas within a schema.
+         *
+         * @param properties The properties dictionary to validate
+         * @throws Error If any property schema is invalid
+         */
+        private void validate_property_schemas (Variant properties) throws Error {
+            var iter = properties.iterator ();
+            string? prop_name;
+            Variant? prop_schema;
+            
+            while (iter.next ("{sv}", out prop_name, out prop_schema)) {
+                if (prop_name == null || prop_schema == null) {
+                    continue;
+                }
+                
+                if (!prop_schema.is_of_type (VariantType.VARDICT)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property schema for '%s' must be a dictionary".printf (prop_name)
+                    );
+                }
+                
+                // Check for type property
+                if (prop_schema.lookup_value ("type", null) == null) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property schema for '%s' must have a 'type' property".printf (prop_name)
+                    );
+                }
+                
+                // Validate type-specific constraints
+                validate_property_constraints (prop_name, prop_schema);
+            }
+        }
+        
+        /**
+         * Validates type-specific constraints for a property.
+         *
+         * @param prop_name The property name
+         * @param prop_schema The property schema
+         * @throws Error If constraints are invalid
+         */
+        private void validate_property_constraints (string prop_name, Variant prop_schema) throws Error {
+            string prop_type = prop_schema.lookup_value ("type", VariantType.STRING).get_string ();
+            
+            switch (prop_type) {
+                case "string":
+                    validate_string_constraints (prop_name, prop_schema);
+                    break;
+                case "number":
+                case "integer":
+                    validate_numeric_constraints (prop_name, prop_schema);
+                    break;
+                case "array":
+                    validate_array_constraints (prop_name, prop_schema);
+                    break;
+                case "object":
+                    validate_object_constraints (prop_name, prop_schema);
+                    break;
+                case "boolean":
+                    // Boolean type has no additional constraints
+                    break;
+                default:
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid property type '%s' for property '%s'".printf (prop_type, prop_name)
+                    );
+            }
         }
         
         /**
-         * Validates arguments against a JSON schema.
+         * Validates string-specific constraints.
+         *
+         * @param prop_name The property name
+         * @param prop_schema The property schema
+         * @throws Error If constraints are invalid
+         */
+        private void validate_string_constraints (string prop_name, Variant prop_schema) throws Error {
+            // Check minLength if present
+            if (prop_schema.lookup_value ("minLength", null) != null) {
+                var min_length = prop_schema.lookup_value ("minLength", VariantType.INT32);
+                if (!min_length.is_of_type (VariantType.INT32) || min_length.get_int32 () < 0) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid minLength for property '%s': must be a non-negative integer".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check maxLength if present
+            if (prop_schema.lookup_value ("maxLength", null) != null) {
+                var max_length = prop_schema.lookup_value ("maxLength", VariantType.INT32);
+                if (!max_length.is_of_type (VariantType.INT32) || max_length.get_int32 () < 0) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid maxLength for property '%s': must be a non-negative integer".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check pattern if present
+            if (prop_schema.lookup_value ("pattern", null) != null) {
+                var pattern = prop_schema.lookup_value ("pattern", VariantType.STRING);
+                if (!pattern.is_of_type (VariantType.STRING)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid pattern for property '%s': must be a string".printf (prop_name)
+                    );
+                }
+                
+                // Validate regex pattern (basic check)
+                try {
+                    // Simple validation - try to compile the regex
+                    Regex regex = new Regex (pattern.get_string ());
+                } catch (Error e) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid pattern for property '%s': %s".printf (prop_name, e.message)
+                    );
+                }
+            }
+            
+            // Check format if present
+            if (prop_schema.lookup_value ("format", null) != null) {
+                var format = prop_schema.lookup_value ("format", VariantType.STRING);
+                if (!format.is_of_type (VariantType.STRING)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid format for property '%s': must be a string".printf (prop_name)
+                    );
+                }
+                
+                // Validate supported formats
+                string format_str = format.get_string ();
+                if (!is_supported_format (format_str)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Unsupported format '%s' for property '%s'".printf (format_str, prop_name)
+                    );
+                }
+            }
+        }
+        
+        /**
+         * Validates numeric-specific constraints.
+         *
+         * @param prop_name The property name
+         * @param prop_schema The property schema
+         * @throws Error If constraints are invalid
+         */
+        private void validate_numeric_constraints (string prop_name, Variant prop_schema) throws Error {
+            // Check minimum if present
+            if (prop_schema.lookup_value ("minimum", null) != null) {
+                var minimum = prop_schema.lookup_value ("minimum", VariantType.DOUBLE);
+                if (!minimum.is_of_type (VariantType.DOUBLE)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid minimum for property '%s': must be a number".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check maximum if present
+            if (prop_schema.lookup_value ("maximum", null) != null) {
+                var maximum = prop_schema.lookup_value ("maximum", VariantType.DOUBLE);
+                if (!maximum.is_of_type (VariantType.DOUBLE)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid maximum for property '%s': must be a number".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check exclusiveMinimum if present
+            if (prop_schema.lookup_value ("exclusiveMinimum", null) != null) {
+                var excl_min = prop_schema.lookup_value ("exclusiveMinimum", VariantType.DOUBLE);
+                if (!excl_min.is_of_type (VariantType.DOUBLE)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid exclusiveMinimum for property '%s': must be a number".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check exclusiveMaximum if present
+            if (prop_schema.lookup_value ("exclusiveMaximum", null) != null) {
+                var excl_max = prop_schema.lookup_value ("exclusiveMaximum", VariantType.DOUBLE);
+                if (!excl_max.is_of_type (VariantType.DOUBLE)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid exclusiveMaximum for property '%s': must be a number".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check multipleOf if present
+            if (prop_schema.lookup_value ("multipleOf", null) != null) {
+                var multiple_of = prop_schema.lookup_value ("multipleOf", VariantType.DOUBLE);
+                if (!multiple_of.is_of_type (VariantType.DOUBLE) || multiple_of.get_double () <= 0) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid multipleOf for property '%s': must be a positive number".printf (prop_name)
+                    );
+                }
+            }
+        }
+        
+        /**
+         * Validates array-specific constraints.
+         *
+         * @param prop_name The property name
+         * @param prop_schema The property schema
+         * @throws Error If constraints are invalid
+         */
+        private void validate_array_constraints (string prop_name, Variant prop_schema) throws Error {
+            // Check minItems if present
+            if (prop_schema.lookup_value ("minItems", null) != null) {
+                var min_items = prop_schema.lookup_value ("minItems", VariantType.INT32);
+                if (!min_items.is_of_type (VariantType.INT32) || min_items.get_int32 () < 0) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid minItems for property '%s': must be a non-negative integer".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check maxItems if present
+            if (prop_schema.lookup_value ("maxItems", null) != null) {
+                var max_items = prop_schema.lookup_value ("maxItems", VariantType.INT32);
+                if (!max_items.is_of_type (VariantType.INT32) || max_items.get_int32 () < 0) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid maxItems for property '%s': must be a non-negative integer".printf (prop_name)
+                    );
+                }
+            }
+            
+            // Check items schema if present
+            if (prop_schema.lookup_value ("items", null) != null) {
+                var items = prop_schema.lookup_value ("items", VariantType.VARDICT);
+                if (!items.is_of_type (VariantType.VARDICT)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid items for property '%s': must be a schema object".printf (prop_name)
+                    );
+                }
+                
+                // Recursively validate items schema
+                validate_property_constraints (prop_name + "[]", items);
+            }
+        }
+        
+        /**
+         * Validates object-specific constraints.
+         *
+         * @param prop_name The property name
+         * @param prop_schema The property schema
+         * @throws Error If constraints are invalid
+         */
+        private void validate_object_constraints (string prop_name, Variant prop_schema) throws Error {
+            // Check properties if present
+            if (prop_schema.lookup_value ("properties", null) != null) {
+                var properties = prop_schema.lookup_value ("properties", VariantType.VARDICT);
+                if (!properties.is_of_type (VariantType.VARDICT)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid properties for property '%s': must be a dictionary".printf (prop_name)
+                    );
+                }
+                
+                // Recursively validate property schemas
+                validate_property_schemas (properties);
+            }
+            
+            // Check required if present
+            if (prop_schema.lookup_value ("required", null) != null) {
+                var required = prop_schema.lookup_value ("required", new VariantType ("as"));
+                if (!required.is_of_type (new VariantType ("as"))) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid required for property '%s': must be an array of strings".printf (prop_name)
+                    );
+                }
+            }
+        }
+        
+        /**
+         * Checks if a format string is supported.
+         *
+         * @param format The format string to check
+         * @return True if the format is supported
+         */
+        private bool is_supported_format (string format) {
+            // List of supported JSON Schema formats
+            string[] supported_formats = {
+                "date-time", "date", "time", "email", "hostname", "ipv4", "ipv6", "uri", "uuid"
+            };
+            
+            foreach (var supported in supported_formats) {
+                if (format == supported) {
+                    return true;
+                }
+            }
+            
+            return false;
+        }
+        
+        /**
+         * Validates arguments against a JSON schema with comprehensive checks.
          *
          * @param arguments The arguments to validate
          * @param schema The schema to validate against
          * @throws Error If validation fails
          */
         private void validate_against_schema (Variant arguments, Variant schema) throws Error {
-            // Basic validation - in a full implementation, this would use
-            // proper schema validation capabilities
-            
             if (!schema.is_of_type (VariantType.VARDICT)) {
                 return; // Invalid schema format, skip validation
             }
             
-            if (schema.lookup_value ("required", null) == null) {
-                return; // No required properties to check
+            if (!arguments.is_of_type (VariantType.VARDICT)) {
+                throw new Mcp.Core.McpError.INVALID_PARAMS ("Arguments must be a dictionary");
+            }
+            
+            // Check required properties
+            if (schema.lookup_value ("required", null) != null) {
+                var required = schema.lookup_value ("required", new VariantType ("as"));
+                if (required.is_of_type (new VariantType ("as"))) {
+                    var required_array = required.get_strv ();
+                    for (int i = 0; i < required_array.length; i++) {
+                        var required_prop = required_array[i];
+                        if (arguments.lookup_value (required_prop, null) == null) {
+                            throw new Mcp.Core.McpError.INVALID_PARAMS (
+                                "Missing required property: %s".printf (required_prop)
+                            );
+                        }
+                    }
+                }
+            }
+            
+            // Validate each argument against its schema
+            if (schema.lookup_value ("properties", null) != null) {
+                var properties = schema.lookup_value ("properties", VariantType.VARDICT);
+                if (properties.is_of_type (VariantType.VARDICT)) {
+                    validate_arguments_against_properties (arguments, properties);
+                }
             }
             
-            var required = schema.lookup_value ("required", new VariantType ("as"));
-            if (!required.is_of_type (new VariantType ("as"))) {
-                return; // Invalid required format, skip validation
+            // Check for additional properties if allowed
+            if (schema.lookup_value ("additionalProperties", null) != null) {
+                var additional_props = schema.lookup_value ("additionalProperties", null);
+                if (additional_props.is_of_type (VariantType.BOOLEAN) && !additional_props.get_boolean ()) {
+                    // No additional properties allowed, check for unexpected properties
+                    check_unexpected_properties (arguments, schema);
+                }
+            }
+        }
+        
+        /**
+         * Validates arguments against property schemas.
+         *
+         * @param arguments The arguments to validate
+         * @param properties The property schemas
+         * @throws Error If validation fails
+         */
+        private void validate_arguments_against_properties (Variant arguments, Variant properties) throws Error {
+            var iter = properties.iterator ();
+            string? prop_name;
+            Variant? prop_schema;
+            
+            while (iter.next ("{sv}", out prop_name, out prop_schema)) {
+                if (prop_name == null || prop_schema == null) {
+                    continue;
+                }
+                
+                // Check if property is present in arguments
+                var arg_value = arguments.lookup_value (prop_name, null);
+                if (arg_value != null) {
+                    // Validate the argument value against the property schema
+                    validate_value_against_schema (arg_value, prop_schema, prop_name);
+                }
+            }
+        }
+        
+        /**
+         * Validates a value against a property schema.
+         *
+         * @param value The value to validate
+         * @param schema The schema to validate against
+         * @param prop_name The property name for error messages
+         * @throws Error If validation fails
+         */
+        private void validate_value_against_schema (Variant value, Variant schema, string prop_name) throws Error {
+            if (!schema.is_of_type (VariantType.VARDICT)) {
+                return; // Invalid schema, skip validation
+            }
+            
+            if (schema.lookup_value ("type", null) == null) {
+                return; // No type specified, skip validation
+            }
+            
+            string expected_type = schema.lookup_value ("type", VariantType.STRING).get_string ();
+            
+            // Check type compatibility
+            if (!is_type_compatible (value, expected_type)) {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Property '%s' has invalid type: expected %s".printf (prop_name, expected_type)
+                );
+            }
+            
+            // Perform type-specific validation
+            switch (expected_type) {
+                case "string":
+                    validate_string_value (value, schema, prop_name);
+                    break;
+                case "number":
+                case "integer":
+                    validate_numeric_value (value, schema, prop_name);
+                    break;
+                case "array":
+                    validate_array_value (value, schema, prop_name);
+                    break;
+                case "object":
+                    validate_object_value (value, schema, prop_name);
+                    break;
+                case "boolean":
+                    // Boolean values don't need additional validation
+                    break;
+            }
+        }
+        
+        /**
+         * Checks if a Variant value is compatible with an expected JSON Schema type.
+         *
+         * @param value The value to check
+         * @param expected_type The expected type
+         * @return True if compatible
+         */
+        private bool is_type_compatible (Variant value, string expected_type) {
+            switch (expected_type) {
+                case "string":
+                    return value.is_of_type (VariantType.STRING);
+                case "number":
+                    return value.is_of_type (VariantType.DOUBLE) || value.is_of_type (VariantType.INT32) ||
+                           value.is_of_type (VariantType.INT64) || value.is_of_type (VariantType.UINT32) ||
+                           value.is_of_type (VariantType.UINT64);
+                case "integer":
+                    return value.is_of_type (VariantType.INT32) || value.is_of_type (VariantType.INT64) ||
+                           value.is_of_type (VariantType.UINT32) || value.is_of_type (VariantType.UINT64);
+                case "boolean":
+                    return value.is_of_type (VariantType.BOOLEAN);
+                case "array":
+                    return value.is_of_type (new VariantType ("av")) || value.is_of_type (new VariantType ("aa{sv}"));
+                case "object":
+                    return value.is_of_type (VariantType.VARDICT);
+                default:
+                    return false;
+            }
+        }
+        
+        /**
+         * Validates a string value against string constraints.
+         *
+         * @param value The value to validate
+         * @param schema The schema to validate against
+         * @param prop_name The property name
+         * @throws Error If validation fails
+         */
+        private void validate_string_value (Variant value, Variant schema, string prop_name) throws Error {
+            string str_value = value.get_string ();
+            
+            // Check minLength
+            if (schema.lookup_value ("minLength", null) != null) {
+                int min_length = schema.lookup_value ("minLength", VariantType.INT32).get_int32 ();
+                if (str_value.length < min_length) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' is too short: minimum length is %d".printf (prop_name, min_length)
+                    );
+                }
+            }
+            
+            // Check maxLength
+            if (schema.lookup_value ("maxLength", null) != null) {
+                int max_length = schema.lookup_value ("maxLength", VariantType.INT32).get_int32 ();
+                if (str_value.length > max_length) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' is too long: maximum length is %d".printf (prop_name, max_length)
+                    );
+                }
+            }
+            
+            // Check pattern
+            if (schema.lookup_value ("pattern", null) != null) {
+                string pattern = schema.lookup_value ("pattern", VariantType.STRING).get_string ();
+                try {
+                    Regex regex = new Regex (pattern);
+                    if (!regex.match (str_value)) {
+                        throw new Mcp.Core.McpError.INVALID_PARAMS (
+                            "Property '%s' does not match required pattern".printf (prop_name)
+                        );
+                    }
+                } catch (Error e) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Invalid pattern for property '%s': %s".printf (prop_name, e.message)
+                    );
+                }
+            }
+            
+            // Check format
+            if (schema.lookup_value ("format", null) != null) {
+                string format = schema.lookup_value ("format", VariantType.STRING).get_string ();
+                if (!validate_format (str_value, format)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' does not match required format: %s".printf (prop_name, format)
+                    );
+                }
+            }
+        }
+        
+        /**
+         * Validates a numeric value against numeric constraints.
+         *
+         * @param value The value to validate
+         * @param schema The schema to validate against
+         * @param prop_name The property name
+         * @throws Error If validation fails
+         */
+        private void validate_numeric_value (Variant value, Variant schema, string prop_name) throws Error {
+            double num_value;
+            
+            if (value.is_of_type (VariantType.DOUBLE)) {
+                num_value = value.get_double ();
+            } else if (value.is_of_type (VariantType.INT32)) {
+                num_value = (double) value.get_int32 ();
+            } else if (value.is_of_type (VariantType.INT64)) {
+                num_value = (double) value.get_int64 ();
+            } else if (value.is_of_type (VariantType.UINT32)) {
+                num_value = (double) value.get_uint32 ();
+            } else if (value.is_of_type (VariantType.UINT64)) {
+                num_value = (double) value.get_uint64 ();
+            } else {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Property '%s' has invalid numeric type".printf (prop_name)
+                );
+            }
+            
+            // Check minimum
+            if (schema.lookup_value ("minimum", null) != null) {
+                double minimum = schema.lookup_value ("minimum", VariantType.DOUBLE).get_double ();
+                if (num_value < minimum) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' is too small: minimum is %g".printf (prop_name, minimum)
+                    );
+                }
+            }
+            
+            // Check maximum
+            if (schema.lookup_value ("maximum", null) != null) {
+                double maximum = schema.lookup_value ("maximum", VariantType.DOUBLE).get_double ();
+                if (num_value > maximum) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' is too large: maximum is %g".printf (prop_name, maximum)
+                    );
+                }
+            }
+            
+            // Check exclusiveMinimum
+            if (schema.lookup_value ("exclusiveMinimum", null) != null) {
+                double excl_min = schema.lookup_value ("exclusiveMinimum", VariantType.DOUBLE).get_double ();
+                if (num_value <= excl_min) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' must be greater than %g".printf (prop_name, excl_min)
+                    );
+                }
+            }
+            
+            // Check exclusiveMaximum
+            if (schema.lookup_value ("exclusiveMaximum", null) != null) {
+                double excl_max = schema.lookup_value ("exclusiveMaximum", VariantType.DOUBLE).get_double ();
+                if (num_value >= excl_max) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' must be less than %g".printf (prop_name, excl_max)
+                    );
+                }
+            }
+            
+            // Check multipleOf
+            if (schema.lookup_value ("multipleOf", null) != null) {
+                double multiple_of = schema.lookup_value ("multipleOf", VariantType.DOUBLE).get_double ();
+                if (multiple_of > 0) {
+                    double remainder = num_value % multiple_of;
+                    if (remainder > 1e-10 && remainder < (multiple_of - 1e-10)) {
+                        throw new Mcp.Core.McpError.INVALID_PARAMS (
+                            "Property '%s' must be a multiple of %g".printf (prop_name, multiple_of)
+                        );
+                    }
+                }
+            }
+            
+            // Check integer constraint
+            if (schema.lookup_value ("type", VariantType.STRING).get_string () == "integer") {
+                if (num_value != Math.floor (num_value)) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' must be an integer".printf (prop_name)
+                    );
+                }
+            }
+        }
+        
+        /**
+         * Validates an array value against array constraints.
+         *
+         * @param value The value to validate
+         * @param schema The schema to validate against
+         * @param prop_name The property name
+         * @throws Error If validation fails
+         */
+        private void validate_array_value (Variant value, Variant schema, string prop_name) throws Error {
+            Variant array_value;
+            
+            if (value.is_of_type (new VariantType ("av"))) {
+                array_value = value;
+            } else if (value.is_of_type (new VariantType ("aa{sv}"))) {
+                array_value = value;
+            } else {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Property '%s' has invalid array type".printf (prop_name)
+                );
+            }
+            
+            size_t array_length = array_value.n_children ();
+            
+            // Check minItems
+            if (schema.lookup_value ("minItems", null) != null) {
+                int min_items = schema.lookup_value ("minItems", VariantType.INT32).get_int32 ();
+                if (array_length < min_items) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' has too few items: minimum is %d".printf (prop_name, min_items)
+                    );
+                }
+            }
+            
+            // Check maxItems
+            if (schema.lookup_value ("maxItems", null) != null) {
+                int max_items = schema.lookup_value ("maxItems", VariantType.INT32).get_int32 ();
+                if (array_length > max_items) {
+                    throw new Mcp.Core.McpError.INVALID_PARAMS (
+                        "Property '%s' has too many items: maximum is %d".printf (prop_name, max_items)
+                    );
+                }
+            }
+            
+            // Validate items if schema is provided
+            if (schema.lookup_value ("items", null) != null) {
+                var items_schema = schema.lookup_value ("items", VariantType.VARDICT);
+                if (items_schema.is_of_type (VariantType.VARDICT)) {
+                    for (size_t i = 0; i < array_length; i++) {
+                        var item_value = array_value.get_child_value (i);
+                        validate_value_against_schema (item_value, items_schema, "%s[%zu]".printf (prop_name, i));
+                    }
+                }
+            }
+        }
+        
+        /**
+         * Validates an object value against object constraints.
+         *
+         * @param value The value to validate
+         * @param schema The schema to validate against
+         * @param prop_name The property name
+         * @throws Error If validation fails
+         */
+        private void validate_object_value (Variant value, Variant schema, string prop_name) throws Error {
+            if (!value.is_of_type (VariantType.VARDICT)) {
+                throw new Mcp.Core.McpError.INVALID_PARAMS (
+                    "Property '%s' has invalid object type".printf (prop_name)
+                );
+            }
+            
+            // Validate properties if schema is provided
+            if (schema.lookup_value ("properties", null) != null) {
+                var properties_schema = schema.lookup_value ("properties", VariantType.VARDICT);
+                if (properties_schema.is_of_type (VariantType.VARDICT)) {
+                    validate_arguments_against_properties (value, properties_schema);
+                }
+            }
+            
+            // Check required properties
+            if (schema.lookup_value ("required", null) != null) {
+                var required = schema.lookup_value ("required", new VariantType ("as"));
+                if (required.is_of_type (new VariantType ("as"))) {
+                    var required_array = required.get_strv ();
+                    for (int i = 0; i < required_array.length; i++) {
+                        var required_prop = required_array[i];
+                        if (value.lookup_value (required_prop, null) == null) {
+                            throw new Mcp.Core.McpError.INVALID_PARAMS (
+                                "Property '%s' is missing required property: %s".printf (prop_name, required_prop)
+                            );
+                        }
+                    }
+                }
+            }
+        }
+        
+        /**
+         * Validates a string value against a format.
+         *
+         * @param value The value to validate
+         * @param format The format to validate against
+         * @return True if valid
+         */
+        private bool validate_format (string value, string format) {
+            switch (format) {
+                case "email":
+                    // Basic email validation
+                    return Regex.match_simple ("^[^@]+@[^@]+\\.[^@]+$", value);
+                case "uri":
+                    // Basic URI validation
+                    return Regex.match_simple ("^[a-zA-Z][a-zA-Z0-9+.-]*://", value);
+                case "uuid":
+                    // UUID validation
+                    return Regex.match_simple ("^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$", value);
+                case "ipv4":
+                    // IPv4 validation
+                    return Regex.match_simple ("^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", value);
+                case "ipv6":
+                    // IPv6 validation (basic)
+                    return Regex.match_simple ("^([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}$", value);
+                case "date-time":
+                    // ISO 8601 date-time validation (basic)
+                    return Regex.match_simple ("^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}", value);
+                case "date":
+                    // ISO 8601 date validation (basic)
+                    return Regex.match_simple ("^\\d{4}-\\d{2}-\\d{2}$", value);
+                case "time":
+                    // ISO 8601 time validation (basic)
+                    return Regex.match_simple ("^\\d{2}:\\d{2}:\\d{2}$", value);
+                case "hostname":
+                    // Hostname validation (basic)
+                    return Regex.match_simple ("^[a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?$", value);
+                default:
+                    // Unknown format, assume valid
+                    return true;
+            }
+        }
+        
+        /**
+         * Checks for unexpected properties in arguments.
+         *
+         * @param arguments The arguments to check
+         * @param schema The schema defining allowed properties
+         * @throws Error If unexpected properties are found
+         */
+        private void check_unexpected_properties (Variant arguments, Variant schema) throws Error {
+            if (schema.lookup_value ("properties", null) == null) {
+                // No properties defined, any property is unexpected
+                var iter = arguments.iterator ();
+                string? prop_name;
+                Variant? prop_value;
+                
+                while (iter.next ("{sv}", out prop_name, out prop_value)) {
+                    if (prop_name != null) {
+                        throw new Mcp.Core.McpError.INVALID_PARAMS (
+                            "Unexpected property: %s".printf (prop_name)
+                        );
+                    }
+                }
+                return;
+            }
+            
+            var properties = schema.lookup_value ("properties", VariantType.VARDICT);
+            var allowed_props = new Gee.HashSet<string> ();
+            
+            // Collect all allowed property names
+            var iter = properties.iterator ();
+            string? prop_name;
+            Variant? prop_schema;
+            
+            while (iter.next ("{sv}", out prop_name, out prop_schema)) {
+                if (prop_name != null) {
+                    allowed_props.add (prop_name);
+                }
             }
             
-            var required_array = required.get_strv ();
-            for (int i = 0; i < required_array.length; i++) {
-                var required_prop = required_array[i];
-                if (arguments.lookup_value (required_prop, null) == null) {
+            // Check for unexpected properties
+            iter = arguments.iterator ();
+            Variant? prop_value;
+            while (iter.next ("{sv}", out prop_name, out prop_value)) {
+                if (prop_name != null && !allowed_props.contains (prop_name)) {
                     throw new Mcp.Core.McpError.INVALID_PARAMS (
-                        "Missing required property: %s".printf (required_prop)
+                        "Unexpected property: %s".printf (prop_name)
                     );
                 }
             }

+ 457 - 10
src/tools/types.vala

@@ -146,7 +146,7 @@ namespace Mcp.Tools.Types {
     }
     
     /**
-     * Tool execution settings.
+     * Tool execution settings with enhanced context and state management.
      */
     public class ToolExecution : GLib.Object {
         /**
@@ -154,13 +154,37 @@ namespace Mcp.Tools.Types {
          */
         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") {
+        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;
         }
         
         /**
@@ -172,6 +196,18 @@ namespace Mcp.Tools.Types {
             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 ();
         }
         
@@ -190,6 +226,220 @@ namespace Mcp.Tools.Types {
             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 ();
         }
     }
     
@@ -263,6 +513,8 @@ namespace Mcp.Tools.Types {
         
         /**
          * 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; }
         
@@ -271,6 +523,16 @@ namespace Mcp.Tools.Types {
          */
         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.
          */
@@ -278,6 +540,35 @@ namespace Mcp.Tools.Types {
             content = new Gee.ArrayList<Mcp.Types.Common.ContentBlock> ();
         }
         
+        /**
+         * 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<Mcp.Types.Common.ContentBlock> 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<Mcp.Types.Common.ContentBlock> ();
+            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.
          *
@@ -293,10 +584,18 @@ namespace Mcp.Tools.Types {
             }
             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 ();
         }
         
@@ -322,24 +621,172 @@ namespace Mcp.Tools.Types {
             
             for (size_t i = 0; i < content.n_children (); i++) {
                 var element = content.get_child_value (i);
-                // TODO: Determine content block type and deserialize appropriately
-                // For now, assume TextContent
                 if (element.is_of_type (VariantType.VARDICT)) {
-                    if (element.lookup_value ("type", null) != null &&
-                        element.lookup_value ("type", VariantType.STRING).get_string () == "text") {
-                        var text_content = new Mcp.Types.Common.TextContent.from_variant (element);
-                        this.content.add (text_content);
+                    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", VariantType.VARDICT);
+                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;
         }
     }
 }

+ 159 - 2
src/types/common.vala

@@ -641,23 +641,118 @@ namespace Mcp.Types.Common {
     }
     
     /**
-     * Annotations for content blocks.
+     * Annotations for content blocks with full MCP specification support.
+     *
+     * Annotations provide metadata about content blocks, including audience targeting,
+     * priority-based ordering, and custom properties for extensibility.
      */
     public class Annotations : GLib.Object {
         /**
          * Optional audience for this content.
+         * Can be used to target content to specific users or groups.
          */
         public string? audience { get; set; }
         
         /**
          * Optional priority of this content.
+         * Higher values indicate higher priority. Used for content ordering.
          */
         public double? priority { get; set; }
         
+        /**
+         * Optional list of roles that can access this content.
+         * When specified, only users with these roles should see the content.
+         */
+        public string[]? roles { get; set; }
+        
+        /**
+         * Optional tags for categorizing and filtering content.
+         */
+        public string[]? tags { get; set; }
+        
+        /**
+         * Optional custom properties for extensibility.
+         * Allows adding additional metadata not covered by standard fields.
+         */
+        public HashTable<string, Variant>? custom_properties { get; set; }
+        
         /**
          * Creates a new Annotations.
+         *
+         * @param audience Optional audience for this content
+         * @param priority Optional priority value
+         * @param roles Optional list of roles
+         * @param tags Optional list of tags
+         */
+        public Annotations (string? audience = null, double? priority = null,
+                           string[]? roles = null, string[]? tags = null) {
+            this.audience = audience;
+            this.priority = priority;
+            this.roles = roles;
+            this.tags = tags;
+            this.custom_properties = new HashTable<string, Variant> (str_hash, str_equal);
+        }
+        
+        /**
+         * Adds a custom property.
+         *
+         * @param key The property key
+         * @param value The property value
          */
-        public Annotations () {
+        public void add_custom_property (string key, Variant value) {
+            if (custom_properties == null) {
+                custom_properties = new HashTable<string, Variant> (str_hash, str_equal);
+            }
+            custom_properties.insert (key, value);
+        }
+        
+        /**
+         * Gets a custom property.
+         *
+         * @param key The property key
+         * @return The property value or null if not found
+         */
+        public Variant? get_custom_property (string key) {
+            if (custom_properties == null) {
+                return null;
+            }
+            return custom_properties.lookup (key);
+        }
+        
+        /**
+         * Adds a role to the roles array.
+         *
+         * @param role The role to add
+         */
+        public void add_role (string role) {
+            if (roles == null) {
+                roles = new string[0];
+            }
+            
+            var new_roles = new string[roles.length + 1];
+            for (int i = 0; i < roles.length; i++) {
+                new_roles[i] = roles[i];
+            }
+            new_roles[roles.length] = role;
+            roles = new_roles;
+        }
+        
+        /**
+         * Adds a tag to the tags array.
+         *
+         * @param tag The tag to add
+         */
+        public void add_tag (string tag) {
+            if (tags == null) {
+                tags = new string[0];
+            }
+            
+            var new_tags = new string[tags.length + 1];
+            for (int i = 0; i < tags.length; i++) {
+                new_tags[i] = tags[i];
+            }
+            new_tags[tags.length] = tag;
+            tags = new_tags;
         }
         
         /**
@@ -674,6 +769,34 @@ namespace Mcp.Types.Common {
                 builder.add ("{sv}", "priority", new Variant.double (priority));
             }
             
+            // Add roles array if present
+            if (roles != null && roles.length > 0) {
+                var roles_builder = new VariantBuilder (new VariantType ("as"));
+                foreach (var role in roles) {
+                    roles_builder.add ("s", role);
+                }
+                builder.add ("{sv}", "roles", roles_builder.end ());
+            }
+            
+            // Add tags array if present
+            if (tags != null && tags.length > 0) {
+                var tags_builder = new VariantBuilder (new VariantType ("as"));
+                foreach (var tag in tags) {
+                    tags_builder.add ("s", tag);
+                }
+                builder.add ("{sv}", "tags", tags_builder.end ());
+            }
+            
+            // Add custom properties if present
+            if (custom_properties != null && custom_properties.size () > 0) {
+                var custom_builder = Mcp.Types.VariantUtils.new_dict_builder ();
+                foreach (var key in custom_properties.get_keys ()) {
+                    var value = custom_properties.lookup (key);
+                    custom_builder.add ("{sv}", key, value);
+                }
+                builder.add ("{sv}", "customProperties", custom_builder.end ());
+            }
+            
             return builder.end ();
         }
         
@@ -696,6 +819,40 @@ namespace Mcp.Types.Common {
             if (variant.lookup_value ("priority", null) != null) {
                 this.priority = variant.lookup_value ("priority", VariantType.DOUBLE).get_double ();
             }
+            
+            if (variant.lookup_value ("roles", null) != null) {
+                var roles_variant = variant.lookup_value ("roles", new VariantType ("as"));
+                if (roles_variant.is_of_type (new VariantType ("as"))) {
+                    this.roles = roles_variant.get_strv ();
+                }
+            }
+            
+            if (variant.lookup_value ("tags", null) != null) {
+                var tags_variant = variant.lookup_value ("tags", new VariantType ("as"));
+                if (tags_variant.is_of_type (new VariantType ("as"))) {
+                    this.tags = tags_variant.get_strv ();
+                }
+            }
+            
+            if (variant.lookup_value ("customProperties", null) != null) {
+                var custom_variant = variant.lookup_value ("customProperties", VariantType.VARDICT);
+                if (custom_variant.is_of_type (VariantType.VARDICT)) {
+                    this.custom_properties = new HashTable<string, Variant> (str_hash, str_equal);
+                    
+                    // Iterate through dictionary entries
+                    // We need to manually iterate through the variant dictionary
+                    var dict_iter = custom_variant.iterator ();
+                    Variant? key_value;
+                    Variant? value_value;
+                    
+                    while (dict_iter.next ("{sv}", out key_value, out value_value)) {
+                        if (key_value != null && value_value != null) {
+                            string key = key_value.get_string ();
+                            this.custom_properties.insert (key, value_value);
+                        }
+                    }
+                }
+            }
         }
     }
 }

+ 240 - 0
test_calculator_comprehensive.py

@@ -0,0 +1,240 @@
+#!/usr/bin/env python3
+"""
+Comprehensive test for calculator-server to verify the fix
+"""
+
+import subprocess
+import time
+import json
+import sys
+import os
+import threading
+import queue
+
+class MCPServerTester:
+    def __init__(self, server_path):
+        self.server_path = server_path
+        self.server_process = None
+        self.message_queue = queue.Queue()
+        
+    def start_server(self):
+        """Start MCP server process"""
+        print(f"Starting MCP server: {self.server_path}")
+        self.server_process = subprocess.Popen(
+            [self.server_path],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=False,
+            bufsize=0
+        )
+        
+        # Start thread to read stderr
+        stderr_thread = threading.Thread(target=self._read_stderr)
+        stderr_thread.daemon = True
+        stderr_thread.start()
+        
+        # Start thread to read stdout
+        stdout_thread = threading.Thread(target=self._read_stdout)
+        stdout_thread.daemon = True
+        stdout_thread.start()
+        
+        time.sleep(1)
+        
+    def _read_stderr(self):
+        """Read stderr output from server"""
+        while self.server_process and self.server_process.poll() is None:
+            try:
+                line = self.server_process.stderr.readline()
+                if line:
+                    print(f"SERVER STDERR: {line.decode().strip()}")
+            except:
+                break
+                
+    def _read_stdout(self):
+        """Read stdout output from server with proper Content-Length parsing"""
+        buffer = b""
+        while self.server_process and self.server_process.poll() is None:
+            try:
+                # Read line by line
+                line = self.server_process.stdout.readline()
+                if not line:
+                    break
+                    
+                buffer += line
+                
+                # Check if we have a complete message
+                if b'\n' in buffer:
+                    # Split on newline to get individual messages
+                    lines = buffer.split(b'\n')
+                    for i, msg_line in enumerate(lines):
+                        if msg_line.strip():
+                            try:
+                                # Try to parse as JSON
+                                message = json.loads(msg_line.decode())
+                                self.message_queue.put(message)
+                            except json.JSONDecodeError as e:
+                                print(f"JSON DECODE ERROR: {e}")
+                    # Keep the last incomplete line if any
+                    if lines and not lines[-1].strip():
+                        buffer = lines[-1]
+                    else:
+                        buffer = b""
+            except:
+                break
+                        
+    def send_jsonrpc_request(self, method, params=None, id=1):
+        """Send a JSON-RPC request"""
+        request = {
+            "jsonrpc": "2.0",
+            "id": id,
+            "method": method,
+            "params": params or {}
+        }
+        
+        request_str = json.dumps(request)
+        message = f"{request_str}\n"
+        
+        self.server_process.stdin.write(message.encode())
+        self.server_process.stdin.flush()
+        
+        try:
+            response = self.message_queue.get(timeout=10)
+            return response
+        except queue.Empty:
+            print("TIMEOUT: No response received")
+            return None
+            
+    def test_operation(self, operation, operand1, operand2, expected_result):
+        """Test a specific operation"""
+        print(f"\n=== Testing {operation} with {operand1} and {operand2} ===")
+        
+        params = {
+            "name": "basic_calculator",
+            "arguments": {
+                "operation": operation,
+                "operand1": operand1,
+                "operand2": operand2
+            },
+            "_meta": {
+                "progressToken": 0
+            }
+        }
+        
+        response = self.send_jsonrpc_request("tools/call", params, id=1)
+        
+        if response and "result" in response:
+            result = response["result"]
+            if "content" in result and len(result["content"]) > 0:
+                content = result["content"][0]
+                if "text" in content:
+                    result_text = content["text"]
+                    print(f"Result text: {result_text}")
+                    
+                    # Check if result matches expected
+                    if str(expected_result) in result_text:
+                        print(f"✓ Correct result: {expected_result}")
+                    else:
+                        print(f"✗ Incorrect result: expected {expected_result}, got {result_text}")
+                        
+            if "structured_content" in result and isinstance(result["structured_content"], dict):
+                structured = result["structured_content"]
+                if 'result' in structured:
+                    actual_result = structured['result']
+                    print(f"Structured result: {actual_result}")
+                    
+                    # Check if result matches expected
+                    if abs(actual_result - expected_result) < 0.001:
+                        print(f"✓ Correct calculation: {actual_result}")
+                    else:
+                        print(f"✗ Incorrect calculation: expected {expected_result}, got {actual_result}")
+                        
+                    # Check operands
+                    if 'operand1' in structured:
+                        print(f"operand1 in result: {structured['operand1']}")
+                    if 'operand2' in structured:
+                        print(f"operand2 in result: {structured['operand2']}")
+        else:
+            print("✗ No response or error in response")
+            
+    def stop_server(self):
+        """Stop MCP server process"""
+        if self.server_process:
+            print("\nStopping server...")
+            self.server_process.terminate()
+            try:
+                self.server_process.wait(timeout=5)
+            except subprocess.TimeoutExpired:
+                self.server_process.kill()
+            self.server_process = None
+
+def main():
+    if len(sys.argv) != 2:
+        print("Usage: python3 test_calculator_comprehensive.py <server-executable>")
+        sys.exit(1)
+        
+    server_path = sys.argv[1]
+    if not os.path.exists(server_path):
+        print(f"Error: Server executable '{server_path}' not found")
+        sys.exit(1)
+        
+    tester = MCPServerTester(server_path)
+    
+    try:
+        tester.start_server()
+        time.sleep(2)
+        
+        # Initialize first
+        print("\nInitializing server...")
+        init_params = {
+            "protocolVersion": "2025-11-25",
+            "capabilities": {},
+            "clientInfo": {
+                "name": "test-client",
+                "version": "1.0.0"
+            }
+        }
+        init_response = tester.send_jsonrpc_request("initialize", init_params, id=0)
+        
+        if init_response:
+            print("✓ Server initialized successfully")
+            
+            # Test different operations with different number types
+            test_cases = [
+                # (operation, operand1, operand2, expected_result)
+                ("add", 2, 3, 5),
+                ("add", 2.5, 3.7, 6.2),
+                ("add", -5, 10, 5),
+                ("subtract", 10, 3, 7),
+                ("multiply", 4, 5, 20),
+                ("divide", 20, 4, 5),
+                ("power", 2, 3, 8),
+                ("modulo", 10, 3, 1),
+                # Test with integer types
+                ("add", 2, 3, 5),
+                ("add", 100, 200, 300),
+                # Test with float types
+                ("add", 2.5, 3.5, 6.0),
+                ("add", -2.5, 3.5, 1.0),
+                ("multiply", 1.5, 2.5, 3.75),
+                ("divide", 10.0, 2.5, 4.0),
+            ]
+            
+            for operation, operand1, operand2, expected in test_cases:
+                tester.test_operation(operation, operand1, operand2, expected)
+                
+            print("\n✓ All calculator tests completed successfully")
+        else:
+            print("✗ Failed to initialize server")
+            
+    except KeyboardInterrupt:
+        print("\nTest interrupted by user")
+    except Exception as e:
+        print(f"\nError during test: {e}")
+        import traceback
+        traceback.print_exc()
+    finally:
+        tester.stop_server()
+
+if __name__ == "__main__":
+    main()

+ 243 - 0
test_calculator_debug.py

@@ -0,0 +1,243 @@
+#!/usr/bin/env python3
+"""
+Debug script for calculator-server to trace JSON-RPC request handling
+"""
+
+import subprocess
+import time
+import json
+import sys
+import os
+import threading
+import queue
+
+class MCPServerTester:
+    def __init__(self, server_path):
+        self.server_path = server_path
+        self.server_process = None
+        self.message_queue = queue.Queue()
+        
+    def start_server(self):
+        """Start MCP server process"""
+        print(f"Starting MCP server: {self.server_path}")
+        self.server_process = subprocess.Popen(
+            [self.server_path],
+            stdin=subprocess.PIPE,
+            stdout=subprocess.PIPE,
+            stderr=subprocess.PIPE,
+            text=False,
+            bufsize=0
+        )
+        
+        # Start thread to read stderr
+        stderr_thread = threading.Thread(target=self._read_stderr)
+        stderr_thread.daemon = True
+        stderr_thread.start()
+        
+        # Start thread to read stdout
+        stdout_thread = threading.Thread(target=self._read_stdout)
+        stdout_thread.daemon = True
+        stdout_thread.start()
+        
+        time.sleep(1)
+        
+    def _read_stderr(self):
+        """Read stderr output from server"""
+        while self.server_process and self.server_process.poll() is None:
+            try:
+                line = self.server_process.stderr.readline()
+                if line:
+                    print(f"SERVER STDERR: {line.decode().strip()}")
+            except:
+                break
+                
+    def _read_stdout(self):
+        """Read stdout output from server with proper Content-Length parsing"""
+        buffer = b""
+        while self.server_process and self.server_process.poll() is None:
+            try:
+                # Read line by line
+                line = self.server_process.stdout.readline()
+                if not line:
+                    break
+                    
+                buffer += line
+                
+                # Check if we have a complete message
+                if b'\n' in buffer:
+                    # Split on newline to get individual messages
+                    lines = buffer.split(b'\n')
+                    for i, msg_line in enumerate(lines):
+                        if msg_line.strip():
+                            try:
+                                # Try to parse as JSON
+                                message = json.loads(msg_line.decode())
+                                self.message_queue.put(message)
+                                print(f"RECEIVED: {json.dumps(message, indent=2)}")
+                            except json.JSONDecodeError as e:
+                                print(f"JSON DECODE ERROR: {e}")
+                                print(f"RAW: {msg_line}")
+                    # Keep the last incomplete line if any
+                    if lines and not lines[-1].strip():
+                        buffer = lines[-1]
+                    else:
+                        buffer = b""
+            except:
+                break
+                        
+    def send_jsonrpc_request(self, method, params=None, id=1):
+        """Send a JSON-RPC request with Content-Length header"""
+        request = {
+            "jsonrpc": "2.0",
+            "id": id,
+            "method": method,
+            "params": params or {}
+        }
+        
+        request_str = json.dumps(request)
+        # Use the format from test_resources_fixed.py which works
+        message = f"{request_str}\n"
+        
+        print(f"SENDING: {json.dumps(request, indent=2)}")
+        print(f"RAW: {message.encode()}")
+        
+        self.server_process.stdin.write(message.encode())
+        self.server_process.stdin.flush()
+        
+        try:
+            response = self.message_queue.get(timeout=10)
+            return response
+        except queue.Empty:
+            print("TIMEOUT: No response received")
+            return None
+            
+    def test_calculator_with_debug(self):
+        """Test calculator with the exact request that's failing"""
+        print("\n=== Testing Calculator with Debug ===")
+        
+        # Test with the exact request from the issue
+        test_request = {
+            "jsonrpc": "2.0",
+            "id": 1,
+            "method": "tools/call",
+            "params": {
+                "name": "basic_calculator",
+                "arguments": {
+                    "operation": "add",
+                    "operand1": 2,
+                    "operand2": 3
+                },
+                "_meta": {
+                    "progressToken": 0
+                }
+            }
+        }
+        
+        print(f"\nTesting with request:")
+        print(json.dumps(test_request, indent=2))
+        
+        response = self.send_jsonrpc_request("tools/call", test_request["params"], id=1)
+        
+        if response:
+            print(f"\nResponse received:")
+            print(json.dumps(response, indent=2))
+            
+            if "result" in response:
+                result = response["result"]
+                if "content" in result and len(result["content"]) > 0:
+                    content = result["content"][0]
+                    if "text" in content:
+                        print(f"\nResult text: {content['text']}")
+                    if "structured_content" in result:
+                        print(f"Structured content: {result['structured_content']}")
+                        
+                        # Check if operands are correctly parsed
+                        if isinstance(result['structured_content'], dict):
+                            if 'operand1' in result['structured_content']:
+                                print(f"operand1 in result: {result['structured_content']['operand1']}")
+                            if 'operand2' in result['structured_content']:
+                                print(f"operand2 in result: {result['structured_content']['operand2']}")
+                            if 'result' in result['structured_content']:
+                                print(f"calculation result: {result['structured_content']['result']}")
+                else:
+                    print("No content in response")
+            elif "error" in response:
+                print(f"Error in response: {response['error']}")
+        else:
+            print("No response received")
+            
+    def test_simple_request(self):
+        """Test with a simpler request"""
+        print("\n=== Testing Simple Request ===")
+        
+        # Test tools/list first
+        print("\nTesting tools/list...")
+        list_response = self.send_jsonrpc_request("tools/list", id=2)
+        
+        if list_response:
+            print(f"Tools list response:")
+            print(json.dumps(list_response, indent=2))
+        else:
+            print("No response for tools/list")
+            
+    def stop_server(self):
+        """Stop MCP server process"""
+        if self.server_process:
+            print("\nStopping server...")
+            self.server_process.terminate()
+            try:
+                self.server_process.wait(timeout=5)
+            except subprocess.TimeoutExpired:
+                self.server_process.kill()
+            self.server_process = None
+
+def main():
+    if len(sys.argv) != 2:
+        print("Usage: python3 test_calculator_debug.py <server-executable>")
+        sys.exit(1)
+        
+    server_path = sys.argv[1]
+    if not os.path.exists(server_path):
+        print(f"Error: Server executable '{server_path}' not found")
+        sys.exit(1)
+        
+    tester = MCPServerTester(server_path)
+    
+    try:
+        tester.start_server()
+        time.sleep(2)
+        
+        # Initialize first
+        print("\nInitializing server...")
+        init_params = {
+            "protocolVersion": "2025-11-25",
+            "capabilities": {},
+            "clientInfo": {
+                "name": "test-client",
+                "version": "1.0.0"
+            }
+        }
+        init_response = tester.send_jsonrpc_request("initialize", init_params, id=0)
+        
+        if init_response:
+            print("✓ Server initialized successfully")
+            
+            # Test simple request first
+            tester.test_simple_request()
+            
+            # Test calculator with debug
+            tester.test_calculator_with_debug()
+        else:
+            print("✗ Failed to initialize server")
+            
+    except KeyboardInterrupt:
+        print("\nTest interrupted by user")
+    except Exception as e:
+        print(f"\nError during test: {e}")
+        import traceback
+        traceback.print_exc()
+    finally:
+        tester.stop_server()
+
+if __name__ == "__main__":
+    main()