clanker 1 місяць тому
батько
коміт
92b1147690

+ 81 - 64
README.md

@@ -75,8 +75,8 @@ int main (string[] args) {
     // Create and start server
     // Create and start server
     var server = new Mcp.Core.Server (server_info, capabilities);
     var server = new Mcp.Core.Server (server_info, capabilities);
     
     
-    // Run the server (blocking)
-    server.run.begin ();
+    // Start the server (blocking)
+    bool started = yield server.start ();
     
     
     // Keep the main loop running
     // Keep the main loop running
     var loop = new MainLoop ();
     var loop = new MainLoop ();
@@ -115,38 +115,34 @@ server.resource_manager.register_provider (provider);
 Tools are functions that clients can execute, such as running commands, performing calculations, or interacting with external services.
 Tools are functions that clients can execute, such as running commands, performing calculations, or interacting with external services.
 
 
 ```vala
 ```vala
-public class CalculatorTool : Object, Mcp.Tools.Executor {
-    public Mcp.Tools.Types.ToolDefinition get_definition () {
-        var input_schema = new Json.Object ();
-        input_schema.set_string_member ("type", "object");
+public class CalculatorTool : Mcp.Tools.BaseExecutor {
+    public CalculatorTool () {
+        // Define input schema using Variant
+        var input_schema = Mcp.Types.VariantUtils.new_dict_builder ();
+        input_schema.add ("{sv}", "type", new Variant.string ("object"));
         
         
-        var properties = new Json.Object ();
+        var properties = Mcp.Types.VariantUtils.new_dict_builder ();
         
         
-        var expr_prop = new Json.Object ();
-        expr_prop.set_string_member ("type", "string");
-        expr_prop.set_string_member ("description", "Mathematical expression to evaluate");
-        properties.set_object_member ("expression", expr_prop);
+        var expr_prop = Mcp.Types.VariantUtils.new_dict_builder ();
+        expr_prop.add ("{sv}", "type", new Variant.string ("string"));
+        expr_prop.add ("{sv}", "description", new Variant.string ("Mathematical expression to evaluate"));
+        properties.add ("{sv}", "expression", expr_prop.end ());
         
         
-        input_schema.set_object_member ("properties", properties);
-        input_schema.set_array_member ("required", new Json.Array ());
+        var required = new VariantBuilder (new VariantType ("as"));
+        required.add_value ("s", "expression");
+        input_schema.add ("{sv}", "properties", properties.end ());
+        input_schema.add ("{sv}", "required", required.end ());
         
         
-        return new Mcp.Tools.Types.ToolDefinition ("calculator", input_schema);
+        base (new Mcp.Tools.Types.ToolDefinition ("calculator", input_schema.end ()));
     }
     }
     
     
-    public async Mcp.Tools.Types.CallToolResult execute (Json.Object arguments) throws Error {
-        string expression = arguments.get_string_member ("expression");
+    protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
+        string expression = get_string_arg (arguments, "expression");
         
         
         // Simple evaluation (in production, use a proper math parser)
         // Simple evaluation (in production, use a proper math parser)
         string result = eval_expression (expression);
         string result = eval_expression (expression);
         
         
-        var content = new Mcp.Types.Common.TextContent (result);
-        var contents = new Gee.ArrayList<Mcp.Types.Common.ContentBlock> ();
-        contents.add (content);
-        
-        var call_result = new Mcp.Tools.Types.CallToolResult ();
-        call_result.content = contents;
-        
-        return call_result;
+        return create_text_result ("Result: %s".printf (result));
     }
     }
 }
 }
 
 
@@ -159,36 +155,36 @@ server.tool_manager.register_executor (new CalculatorTool ());
 Prompts are templates that can be filled with arguments to generate consistent messages for language models.
 Prompts are templates that can be filled with arguments to generate consistent messages for language models.
 
 
 ```vala
 ```vala
-public class CodeReviewPrompt : Object, Mcp.Prompts.Template {
-    public Mcp.Prompts.Types.PromptDefinition get_definition () {
-        var prompt = new Mcp.Prompts.Types.PromptDefinition ("code_review");
-        prompt.description = "Generate a code review for the provided code";
+public class CodeReviewPrompt : Mcp.Prompts.BaseTemplate {
+    public CodeReviewPrompt () {
+        var arguments = new Gee.ArrayList<Mcp.Prompts.Types.PromptArgument> ();
         
         
         var code_arg = new Mcp.Prompts.Types.PromptArgument ("code");
         var code_arg = new Mcp.Prompts.Types.PromptArgument ("code");
         code_arg.description = "The code to review";
         code_arg.description = "The code to review";
         code_arg.required = true;
         code_arg.required = true;
-        prompt.arguments.add (code_arg);
+        arguments.add (code_arg);
         
         
         var style_arg = new Mcp.Prompts.Types.PromptArgument ("style");
         var style_arg = new Mcp.Prompts.Types.PromptArgument ("style");
         style_arg.description = "Review style (strict, moderate, relaxed)";
         style_arg.description = "Review style (strict, moderate, relaxed)";
-        prompt.arguments.add (style_arg);
+        arguments.add (style_arg);
         
         
-        return prompt;
+        base ("code_review", "Generate a code review for the provided code", arguments);
     }
     }
     
     
-    public async Mcp.Prompts.Types.GetPromptResult render (Json.Object arguments) throws Error {
-        string code = arguments.get_string_member ("code");
-        string style = arguments.has_member ("style") ? arguments.get_string_member ("style") : "moderate";
+    protected override async Mcp.Prompts.Types.GetPromptResult do_render (Variant arguments) throws Error {
+        string code = Mcp.Types.VariantUtils.get_string (arguments, "code");
+        string style = Mcp.Types.VariantUtils.has_key (arguments, "style")
+            ? Mcp.Types.VariantUtils.get_string (arguments, "style")
+            : "moderate";
         
         
         string prompt_text = @"Please review the following code using a $style style:\n\n$code\n\nProvide feedback on:\n1. Code quality\n2. Potential bugs\n3. Performance considerations\n4. Best practices";
         string prompt_text = @"Please review the following code using a $style style:\n\n$code\n\nProvide feedback on:\n1. Code quality\n2. Potential bugs\n3. Performance considerations\n4. Best practices";
         
         
         var content = new Mcp.Types.Common.TextContent (prompt_text);
         var content = new Mcp.Types.Common.TextContent (prompt_text);
         var message = new Mcp.Prompts.Types.PromptMessage ("user", content);
         var message = new Mcp.Prompts.Types.PromptMessage ("user", content);
+        var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
+        messages.add (message);
         
         
-        var result = new Mcp.Prompts.Types.GetPromptResult ();
-        result.messages.add (message);
-        
-        return result;
+        return new Mcp.Prompts.Types.GetPromptResult (messages);
     }
     }
 }
 }
 
 
@@ -202,6 +198,8 @@ server.prompt_manager.register_template (new CodeReviewPrompt ());
 
 
 - [`Mcp.Core.Server`](src/core/server.vala): Main server class that handles the MCP protocol using Jsonrpc.Server
 - [`Mcp.Core.Server`](src/core/server.vala): Main server class that handles the MCP protocol using Jsonrpc.Server
 - [`Mcp.Core.Error`](src/core/error.vala): Error domain and utilities
 - [`Mcp.Core.Error`](src/core/error.vala): Error domain and utilities
+- [`Mcp.Core.InjectContentLength`](src/core/compat.vala): Utility for injecting Content-Length headers in messages
+- [`Mcp.Core.StripContentLength`](src/core/compat.vala): Utility for stripping Content-Length headers from messages
 
 
 ### Manager Classes
 ### Manager Classes
 
 
@@ -209,10 +207,23 @@ server.prompt_manager.register_template (new CodeReviewPrompt ());
 - [`Mcp.Tools.Manager`](src/tools/manager.vala): Manages tool executors and handles tool operations
 - [`Mcp.Tools.Manager`](src/tools/manager.vala): Manages tool executors and handles tool operations
 - [`Mcp.Prompts.Manager`](src/prompts/manager.vala): Manages prompt templates and handles prompt operations
 - [`Mcp.Prompts.Manager`](src/prompts/manager.vala): Manages prompt templates and handles prompt operations
 
 
+### Base Classes
+
+- [`Mcp.Resources.BaseProvider`](src/resources/provider.vala): Base implementation for resource providers
+- [`Mcp.Tools.BaseExecutor`](src/tools/executor.vala): Base implementation for tool executors
+- [`Mcp.Prompts.BaseTemplate`](src/prompts/template.vala): Base implementation for prompt templates
+
 ### Type Definitions
 ### Type Definitions
 
 
 - [`Mcp.Types.Protocol`](src/types/protocol.vala): Core protocol types (ServerInfo, Capabilities, etc.)
 - [`Mcp.Types.Protocol`](src/types/protocol.vala): Core protocol types (ServerInfo, Capabilities, etc.)
 - [`Mcp.Types.Common`](src/types/common.vala): Common content types used across the protocol
 - [`Mcp.Types.Common`](src/types/common.vala): Common content types used across the protocol
+- [`Mcp.Types.VariantUtils`](src/types/variant_utils.vala): Utility functions for working with GLib.Variant
+
+### Tool Execution Classes
+
+- [`Mcp.Tools.Types.ToolExecutionContext`](src/tools/types.vala): Context for tool execution with progress tracking
+- [`Mcp.Tools.Types.ToolProgress`](src/tools/types.vala): Progress information for long-running tools
+- [`Mcp.Tools.Types.ToolAnnotations`](src/tools/types.vala): Annotations for tool execution results
 
 
 ## Building and Testing
 ## Building and Testing
 
 
@@ -259,12 +270,14 @@ meson compile -C builddir examples
 var server_info = new Mcp.Types.Protocol.ServerInfo (
 var server_info = new Mcp.Types.Protocol.ServerInfo (
     "my-server",
     "my-server",
     "1.0.0",
     "1.0.0",
-    "Description of my MCP server"
+    "Description of my MCP server",
+    "https://my-server.example.com"  // Optional website URL
 );
 );
 
 
 // Configure capabilities
 // Configure capabilities
 var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
 var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
 capabilities.logging = true;
 capabilities.logging = true;
+capabilities.completions = new Mcp.Types.Protocol.CompletionsCapabilities ();
 capabilities.resources = new Mcp.Types.Protocol.ResourcesCapabilities ();
 capabilities.resources = new Mcp.Types.Protocol.ResourcesCapabilities ();
 capabilities.tools = new Mcp.Types.Protocol.ToolsCapabilities ();
 capabilities.tools = new Mcp.Types.Protocol.ToolsCapabilities ();
 capabilities.prompts = new Mcp.Types.Protocol.PromptsCapabilities ();
 capabilities.prompts = new Mcp.Types.Protocol.PromptsCapabilities ();
@@ -281,7 +294,7 @@ The library provides a comprehensive error domain with proper JSON-RPC error cod
 try {
 try {
     var result = yield server.tool_manager.call_tool ("calculator", arguments);
     var result = yield server.tool_manager.call_tool ("calculator", arguments);
     // Handle result
     // Handle result
-} catch (Mcp.Core.McpError e) {
+} catch (Mcp.Core.Error e) {
     // Handle MCP-specific errors
     // Handle MCP-specific errors
     stderr.printf ("MCP Error: %s (code: %d)\n", e.message, e.code);
     stderr.printf ("MCP Error: %s (code: %d)\n", e.message, e.code);
 } catch (Error e) {
 } catch (Error e) {
@@ -323,36 +336,40 @@ The implementation follows these key design principles:
 mcp-vala/
 mcp-vala/
 ├── src/
 ├── src/
 │   ├── core/
 │   ├── core/
-│   │   ├── error.vala              # Error domain and utilities
-│   │   └── server.vala             # Main server class using Jsonrpc.Server
+│   │   ├── compat.vala            # Compatibility utilities (InjectContentLength, StripContentLength)
+│   │   ├── error.vala            # Error domain and utilities
+│   │   └── server.vala           # Main server class using Jsonrpc.Server
 │   ├── types/
 │   ├── types/
-│   │   ├── protocol.vala           # Protocol types (ServerInfo, Capabilities)
-│   │   └── common.vala           # Common content types
+│   │   ├── common.vala           # Common content types
+│   │   ├── protocol.vala         # Protocol types (ServerInfo, Capabilities)
+│   │   └── variant_utils.vala    # Variant utility functions
 │   ├── resources/
 │   ├── resources/
-│   │   ├── types.vala            # Resource data types
-│   │   ├── provider.vala          # Resource provider interface
-│   │   └── manager.vala          # Resource manager
+│   │   ├── manager.vala          # Resource manager
+│   │   ├── provider.vala         # Resource provider interface and base class
+│   │   └── types.vala            # Resource data types
 │   ├── tools/
 │   ├── tools/
-│   │   ├── types.vala            # Tool data types
-│   │   ├── executor.vala          # Tool executor interface
-│   │   └── manager.vala          # Tool manager
+│   │   ├── executor.vala         # Tool executor interface and base class
+│   │   ├── manager.vala          # Tool manager
+│   │   └── types.vala            # Tool data types
 │   ├── prompts/
 │   ├── prompts/
-│   │   ├── types.vala            # Prompt data types
-│   │   ├── template.vala          # Prompt template interface
-│   │   └── manager.vala          # Prompt manager
-│   └── meson.build                # Library build configuration
+│   │   ├── manager.vala          # Prompt manager
+│   │   ├── template.vala         # Prompt template interface and base class
+│   │   └── types.vala            # Prompt data types
+│   └── meson.build               # Library build configuration
 ├── examples/
 ├── examples/
-│   ├── minimal-server.vala        # Minimal server example
-│   ├── filesystem-server.vala     # File system resource example
-│   ├── calculator-server.vala     # Tool execution example
-│   ├── chat-server.vala           # Prompt handling example
-│   └── meson.build               # Examples build configuration
+│   ├── minimal-server.vala       # Minimal server example
+│   ├── simple-server.vala        # Simple server with resources
+│   ├── filesystem-server.vala    # File system resource example
+│   ├── calculator-server.vala    # Tool execution example
+│   ├── chat-server.vala          # Prompt handling example
+│   └── meson.build              # Examples build configuration
 ├── docs/
 ├── docs/
-│   ├── developer-guide.md        # Developer guide
-│   └── api-reference.md          # API documentation
-├── meson.build                    # Main build configuration
-├── CHANGELOG.md                   # Version history
-└── README.md                     # This file
+│   ├── api-reference.md         # API documentation
+│   ├── developer-guide.md       # Developer guide
+│   └── meson.build              # Documentation build configuration
+├── meson.build                  # Main build configuration
+├── .gitignore                   # Git ignore file
+└── README.md                    # This file
 ```
 ```
 
 
 ## License
 ## License

+ 0 - 249
debug_prompt_serialization.py

@@ -1,249 +0,0 @@
-#!/usr/bin/env python3
-"""
-Debug script to test prompt serialization and identify the exact issue
-"""
-
-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 Content-Length parsing"""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                # Read Content-Length header
-                line = self.server_process.stdout.readline()
-                if not line:
-                    break
-                    
-                line_str = line.decode().strip()
-                if line_str.startswith("Content-Length:"):
-                    content_length = int(line_str.split(":")[1].strip())
-                    
-                    # Read empty line
-                    self.server_process.stdout.readline()
-                    
-                    # Read JSON content
-                    json_data = self.server_process.stdout.read(content_length)
-                    if json_data:
-                        try:
-                            parsed = json.loads(json_data.decode())
-                            self.message_queue.put(parsed)
-                            print(f"SERVER MESSAGE: {json.dumps(parsed, indent=2)}")
-                        except json.JSONDecodeError as e:
-                            print(f"JSON DECODE ERROR: {e}")
-                            print(f"RAW DATA: {json_data}")
-            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",
-            "method": method,
-            "id": id,
-            "params": params or {}
-        }
-            
-        request_str = json.dumps(request)
-        message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
-        print(f"SENDING: {repr(message)}")
-        
-        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_prompt_get(self):
-        """Test prompts/get to trigger the serialization issue"""
-        print("\n=== Testing Prompt Get ===")
-        
-        # Initialize first
-        init_params = {
-            "protocolVersion": "2025-11-25",
-            "capabilities": {},
-            "clientInfo": {
-                "name": "test-client",
-                "version": "1.0.0"
-            }
-        }
-        init_response = self.send_jsonrpc_request("initialize", init_params, id=0)
-        
-        if init_response and "result" in init_response:
-            print("✓ Server initialized successfully")
-        else:
-            print("✗ Failed to initialize server")
-            return False
-        
-        # List prompts first
-        print("\nListing prompts...")
-        list_response = self.send_jsonrpc_request("prompts/list", id=1)
-        
-        if list_response and "result" in list_response:
-            prompts = list_response["result"].get("prompts", [])
-            print(f"✓ Found {len(prompts)} prompts")
-            
-            if prompts:
-                # Try to get the first prompt
-                prompt_name = prompts[0]["name"]
-                print(f"\nGetting prompt: {prompt_name}")
-                
-                get_response = self.send_jsonrpc_request("prompts/get", {
-                    "name": prompt_name
-                }, id=2)
-                
-                if get_response:
-                    if "result" in get_response:
-                        print("✓ Prompt retrieved successfully")
-                        result = get_response["result"]
-                        
-                        # Check the structure of the response
-                        if "messages" in result:
-                            messages = result["messages"]
-                            print(f"✓ Found {len(messages)} messages")
-                            
-                            for i, message in enumerate(messages):
-                                print(f"\nMessage {i}:")
-                                print(f"  Role: {message.get('role')}")
-                                if "content" in message:
-                                    content = message["content"]
-                                    print(f"  Content type: {content.get('type')}")
-                                    print(f"  Content keys: {list(content.keys())}")
-                                    
-                                    # Check for specific issues
-                                    if content.get("type") in ["image", "audio"]:
-                                        if "mimeType" in content:
-                                            print(f"  ✓ Has mimeType: {content['mimeType']}")
-                                        else:
-                                            print("  ✗ Missing mimeType field")
-                                            if "mime_type" in content:
-                                                print(f"  ✗ Has mime_type instead of mimeType: {content['mime_type']}")
-                                        
-                                        if "data" in content:
-                                            print(f"  ✓ Has data field")
-                                        else:
-                                            print("  ✗ Missing data field")
-                                            
-                                    elif content.get("type") == "resource_link":
-                                        if "name" in content:
-                                            print(f"  ✓ Has name: {content['name']}")
-                                        else:
-                                            print("  ✗ Missing name field")
-                                            
-                                        if "uri" in content:
-                                            print(f"  ✓ Has uri: {content['uri']}")
-                                        else:
-                                            print("  ✗ Missing uri field")
-                                            
-                                    elif content.get("type") == "resource":
-                                        if "resource" in content:
-                                            print(f"  ✓ Has resource field")
-                                            resource = content["resource"]
-                                            print(f"    Resource keys: {list(resource.keys())}")
-                                        else:
-                                            print("  ✗ Missing resource field")
-                        else:
-                            print("✗ No messages field in response")
-                    else:
-                        print("✗ No result field in response")
-                        if "error" in get_response:
-                            error = get_response["error"]
-                            print(f"ERROR: {json.dumps(error, indent=2)}")
-                else:
-                    print("✗ Failed to get prompt")
-            else:
-                print("✗ No prompts found")
-        else:
-            print("✗ Failed to list prompts")
-            return False
-            
-    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 debug_prompt_serialization.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)
-        
-        # Test prompt get
-        tester.test_prompt_get()
-            
-    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()

+ 0 - 202
debug_resources.py

@@ -1,202 +0,0 @@
-#!/usr/bin/env python3
-"""
-Debug resource handling to see actual response format
-"""
-
-import subprocess
-import time
-import json
-import sys
-import os
-import threading
-import queue
-import tempfile
-import shutil
-
-class MCPServerTester:
-    def __init__(self, server_path):
-        self.server_path = server_path
-        self.server_process = None
-        self.message_queue = queue.Queue()
-        self.test_dir = None
-        
-    def start_server(self):
-        """Start MCP server process"""
-        # Create a temporary directory for testing
-        self.test_dir = tempfile.mkdtemp(prefix="mcp_test_")
-        
-        # Create some test files
-        with open(os.path.join(self.test_dir, "test.txt"), "w") as f:
-            f.write("Hello, World!")
-            
-        print(f"Starting MCP server: {self.server_path} with test directory: {self.test_dir}")
-        self.server_process = subprocess.Popen(
-            [self.server_path, self.test_dir],
-            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 Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.read(1)
-                if not data:
-                    break
-                buffer += data
-                
-                if b'\r\n\r\n' in buffer:
-                    headers, body = buffer.split(b'\r\n\r\n', 1)
-                    content_length = None
-                    
-                    for line in headers.split(b'\r\n'):
-                        if line.lower().startswith(b'content-length:'):
-                            content_length = int(line.split(b':')[1].strip())
-                            break
-                    
-                    if content_length is not None and len(body) >= content_length:
-                        message = body[:content_length]
-                        buffer = body[content_length:]
-                        
-                        try:
-                            parsed = json.loads(message.decode())
-                            self.message_queue.put(parsed)
-                        except json.JSONDecodeError as e:
-                            print(f"JSON DECODE ERROR: {e}")
-                            print(f"RAW MESSAGE: {message}")
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request with proper Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "params": params or {}
-        }
-            
-        request_str = json.dumps(request)
-        message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
-        
-        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 cleanup(self):
-        """Clean up test environment"""
-        if self.test_dir and os.path.exists(self.test_dir):
-            shutil.rmtree(self.test_dir)
-            
-    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 debug_resources.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
-        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")
-            
-            # List resources
-            list_response = tester.send_jsonrpc_request("resources/list", id=1)
-            if list_response:
-                print(f"Resources list response: {json.dumps(list_response, indent=2)}")
-                
-                # Get first resource URI
-                if "result" in list_response and "resources" in list_response["result"]:
-                    resources = list_response["result"]["resources"]
-                    if resources:
-                        first_uri = resources[0]["uri"]
-                        print(f"Trying to read resource: {first_uri}")
-                        
-                        # Read resource
-                        read_response = tester.send_jsonrpc_request("resources/read", {"uri": first_uri}, id=2)
-                        if read_response:
-                            print(f"Resource read response: {json.dumps(read_response, indent=2)}")
-                        else:
-                            print("Failed to read resource")
-                    else:
-                        print("No resources found")
-                else:
-                    print("No resources in response")
-            else:
-                print("Failed to list resources")
-        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()
-        tester.cleanup()
-
-if __name__ == "__main__":
-    main()

+ 0 - 202
debug_templates.py

@@ -1,202 +0,0 @@
-#!/usr/bin/env python3
-"""
-Debug script to check if templates are being handled properly
-"""
-
-import subprocess
-import time
-import json
-import sys
-import os
-import threading
-import queue
-import tempfile
-import shutil
-
-class MCPServerTester:
-    def __init__(self, server_path):
-        self.server_path = server_path
-        self.server_process = None
-        self.message_queue = queue.Queue()
-        self.test_dir = None
-        
-    def start_server(self):
-        """Start MCP server process"""
-        # Create a temporary directory for testing
-        self.test_dir = tempfile.mkdtemp(prefix="mcp_test_")
-        
-        # Create some test files
-        with open(os.path.join(self.test_dir, "test.txt"), "w") as f:
-            f.write("Hello, World!")
-            
-        print(f"Starting MCP server: {self.server_path} with test directory: {self.test_dir}")
-        self.server_process = subprocess.Popen(
-            [self.server_path, self.test_dir],
-            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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        print(f"SERVER MESSAGE: {json.dumps(parsed, indent=2)}")
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "params": params or {}
-        }
-            
-        request_str = json.dumps(request)
-        message = f"{request_str}\n"
-        print(f"SENDING: {repr(message)}")
-        
-        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_templates(self):
-        """Test resource template functionality"""
-        print("\n=== Testing Resource Templates ===")
-        
-        # Initialize first
-        init_params = {
-            "protocolVersion": "2025-11-25",
-            "capabilities": {},
-            "clientInfo": {
-                "name": "test-client",
-                "version": "1.0.0"
-            }
-        }
-        init_response = self.send_jsonrpc_request("initialize", init_params, id=0)
-        
-        if init_response:
-            print("✓ Server initialized successfully")
-            print(f"Init response: {json.dumps(init_response, indent=2)}")
-        else:
-            print("✗ Failed to initialize server")
-            return False
-        
-        # Test templates/list
-        print("\nTesting resources/templates/list...")
-        templates_response = self.send_jsonrpc_request("resources/templates/list", id=7)
-        
-        if templates_response:
-            print(f"Templates response: {json.dumps(templates_response, indent=2)}")
-            
-            if "result" in templates_response:
-                result = templates_response["result"]
-                if "templates" in result:
-                    templates = result["templates"]
-                    print(f"✓ Found {len(templates)} templates")
-                    return True
-                else:
-                    print("✗ No templates field in response")
-                    return False
-            else:
-                print("✗ No result field in response")
-                return False
-        else:
-            print("✗ Failed to list templates")
-            return False
-            
-    def cleanup(self):
-        """Clean up test environment"""
-        if self.test_dir and os.path.exists(self.test_dir):
-            shutil.rmtree(self.test_dir)
-            
-    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 debug_templates.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)
-        
-        # Test templates
-        tester.test_templates()
-            
-    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()
-        tester.cleanup()
-
-if __name__ == "__main__":
-    main()

+ 0 - 265
diagnose_serialization.py

@@ -1,265 +0,0 @@
-#!/usr/bin/env python3
-"""
-Diagnostic script to test content serialization and identify the exact issue
-"""
-
-import subprocess
-import json
-import sys
-import os
-import tempfile
-
-def test_content_serialization():
-    """Test content serialization by creating a simple test case"""
-    
-    # Create a test JSON that matches what the server should send
-    test_prompt_response = {
-        "jsonrpc": "2.0",
-        "id": 2,
-        "result": {
-            "description": "Test prompt",
-            "messages": [
-                {
-                    "role": "user",
-                    "content": {
-                        "type": "text",
-                        "text": "Hello, world!"
-                    }
-                },
-                {
-                    "role": "assistant", 
-                    "content": {
-                        "type": "image",
-                        "data": "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==",
-                        "mimeType": "image/png"
-                    }
-                },
-                {
-                    "role": "user",
-                    "content": {
-                        "type": "resource",
-                        "resource": {
-                            "uri": "file:///example.txt",
-                            "text": "This is a test resource"
-                        }
-                    }
-                }
-            ]
-        }
-    }
-    
-    print("Testing JSON structure that should be valid:")
-    print(json.dumps(test_prompt_response, indent=2))
-    
-    # Try to validate this against a simple schema
-    try:
-        # Basic validation of the structure
-        result = test_prompt_response["result"]
-        messages = result["messages"]
-        
-        for i, message in enumerate(messages):
-            content = message["content"]
-            content_type = content["type"]
-            
-            print(f"\nMessage {i} - Type: {content_type}")
-            
-            if content_type == "text":
-                if "text" not in content:
-                    print(f"  ✗ Missing 'text' field")
-                else:
-                    print(f"  ✓ Has 'text' field")
-                    
-            elif content_type in ["image", "audio"]:
-                if "data" not in content:
-                    print(f"  ✗ Missing 'data' field")
-                else:
-                    print(f"  ✓ Has 'data' field")
-                    
-                if "mimeType" not in content:
-                    print(f"  ✗ Missing 'mimeType' field")
-                else:
-                    print(f"  ✓ Has 'mimeType' field: {content['mimeType']}")
-                    
-            elif content_type == "resource":
-                if "resource" not in content:
-                    print(f"  ✗ Missing 'resource' field")
-                else:
-                    resource = content["resource"]
-                    print(f"  ✓ Has 'resource' field with keys: {list(resource.keys())}")
-                    
-                    if "uri" not in resource:
-                        print(f"    ✗ Missing 'uri' in resource")
-                    else:
-                        print(f"    ✓ Has 'uri': {resource['uri']}")
-                        
-                    # Check if resource has either text or blob
-                    has_text = "text" in resource
-                    has_blob = "blob" in resource
-                    
-                    if not (has_text or has_blob):
-                        print(f"    ✗ Resource must have either 'text' or 'blob'")
-                    elif has_text:
-                        print(f"    ✓ Has 'text' content")
-                    elif has_blob:
-                        print(f"    ✓ Has 'blob' content")
-                        
-            elif content_type == "resource_link":
-                required_fields = ["name", "uri"]
-                for field in required_fields:
-                    if field not in content:
-                        print(f"  ✗ Missing '{field}' field")
-                    else:
-                        print(f"  ✓ Has '{field}': {content[field]}")
-                        
-        print("\n✓ Basic structure validation passed")
-        return True
-        
-    except Exception as e:
-        print(f"\n✗ Validation failed: {e}")
-        import traceback
-        traceback.print_exc()
-        return False
-
-def test_server_response():
-    """Test actual server response if available"""
-    if len(sys.argv) != 2:
-        print("Usage: python3 diagnose_serialization.py <server-executable>")
-        return False
-        
-    server_path = sys.argv[1]
-    if not os.path.exists(server_path):
-        print(f"Error: Server executable '{server_path}' not found")
-        return False
-    
-    print(f"\n{'='*60}")
-    print(f"Testing server: {server_path}")
-    print(f"{'='*60}")
-    
-    # Test the expected structure first
-    if not test_content_serialization():
-        print("✗ Basic structure validation failed")
-        return False
-    
-    # Try to test the actual server
-    try:
-        # Create a simple test script
-        test_script = '''
-import json
-import sys
-import subprocess
-
-# Start the server
-proc = subprocess.Popen([sys.argv[1]], 
-                      stdin=subprocess.PIPE, 
-                      stdout=subprocess.PIPE, 
-                      stderr=subprocess.PIPE,
-                      text=False)
-
-# Send initialize
-init_msg = {
-    "jsonrpc": "2.0",
-    "method": "initialize", 
-    "id": 0,
-    "params": {
-        "protocolVersion": "2025-11-25",
-        "capabilities": {},
-        "clientInfo": {"name": "test", "version": "1.0.0"}
-    }
-}
-
-msg_str = json.dumps(init_msg)
-proc.stdin.write(f"Content-Length: {len(msg_str)}\\r\\n\\r\\n{msg_str}".encode())
-proc.stdin.flush()
-
-# Read init response
-line = proc.stdout.readline()
-if line.startswith(b"Content-Length:"):
-    length = int(line.decode().split(":")[1].strip())
-    proc.stdout.readline()  # empty line
-    data = proc.stdout.read(length)
-    response = json.loads(data.decode())
-    print("Init response:", "success" if "result" in response else "failed")
-
-# List prompts
-list_msg = {"jsonrpc": "2.0", "method": "prompts/list", "id": 1, "params": {}}
-msg_str = json.dumps(list_msg)
-proc.stdin.write(f"Content-Length: {len(msg_str)}\\r\\n\\r\\n{msg_str}".encode())
-proc.stdin.flush()
-
-# Read list response
-line = proc.stdout.readline()
-if line.startswith(b"Content-Length:"):
-    length = int(line.decode().split(":")[1].strip())
-    proc.stdout.readline()  # empty line
-    data = proc.stdout.read(length)
-    response = json.loads(data.decode())
-    
-    if "result" in response and "prompts" in response["result"]:
-        prompts = response["result"]["prompts"]
-        print(f"Found {len(prompts)} prompts")
-        
-        if prompts:
-            # Get first prompt
-            get_msg = {"jsonrpc": "2.0", "method": "prompts/get", "id": 2, 
-                      "params": {"name": prompts[0]["name"]}}
-            msg_str = json.dumps(get_msg)
-            proc.stdin.write(f"Content-Length: {len(msg_str)}\\r\\n\\r\\n{msg_str}".encode())
-            proc.stdin.flush()
-            
-            # Read get response
-            line = proc.stdout.readline()
-            if line.startswith(b"Content-Length:"):
-                length = int(line.decode().split(":")[1].strip())
-                proc.stdout.readline()  # empty line
-                data = proc.stdout.read(length)
-                response = json.loads(data.decode())
-                
-                if "result" in response:
-                    print("✓ Got prompt result")
-                    result = response["result"]
-                    print(json.dumps(result, indent=2))
-                elif "error" in response:
-                    print("✗ Got error:")
-                    print(json.dumps(response["error"], indent=2))
-                else:
-                    print("✗ No result or error in response")
-
-proc.terminate()
-'''
-        
-        # Write test script to temp file
-        with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
-            f.write(test_script)
-            temp_script = f.name
-        
-        # Run the test
-        result = subprocess.run([sys.executable, temp_script, server_path], 
-                              capture_output=True, text=True, timeout=10)
-        
-        print("Server test output:")
-        print(result.stdout)
-        if result.stderr:
-            print("Server test errors:")
-            print(result.stderr)
-            
-        # Clean up
-        os.unlink(temp_script)
-        
-        return result.returncode == 0
-        
-    except subprocess.TimeoutExpired:
-        print("✗ Server test timed out")
-        return False
-    except Exception as e:
-        print(f"✗ Server test failed: {e}")
-        import traceback
-        traceback.print_exc()
-        return False
-
-if __name__ == "__main__":
-    if len(sys.argv) == 2:
-        success = test_server_response()
-    else:
-        success = test_content_serialization()
-    
-    sys.exit(0 if success else 1)

+ 237 - 67
docs/api-reference.md

@@ -90,7 +90,7 @@ The library now uses Jsonrpc.Server from jsonrpc-glib-1.0 for handling JSON-RPC
 - Proper STDIO transport handling
 - Proper STDIO transport handling
 - Automatic message parsing and formatting
 - Automatic message parsing and formatting
 
 
-### Mcp.Core.McpError
+### Mcp.Core.Error
 
 
 Error domain for MCP-specific errors.
 Error domain for MCP-specific errors.
 
 
@@ -102,12 +102,48 @@ public static const int INVALID_REQUEST = -32600;
 public static const int METHOD_NOT_FOUND = -32601;
 public static const int METHOD_NOT_FOUND = -32601;
 public static const int INVALID_PARAMS = -32602;
 public static const int INVALID_PARAMS = -32602;
 public static const int INTERNAL_ERROR = -32603;
 public static const int INTERNAL_ERROR = -32603;
-public static const int RESOURCE_NOT_FOUND = -32604;
-public static const int TRANSPORT_ERROR = -32605;
+public static const int NOT_FOUND = -32604;
+public static const int INVALID_ARGUMENT = -32605;
+```
+
+#### Convenience Constructors
+
+```vala
+public static Error PARSE (string message)
+public static Error INVALID_REQUEST (string message)
+public static Error METHOD_NOT_FOUND (string message)
+public static Error INVALID_PARAMS (string message)
+public static Error INTERNAL_ERROR (string message)
+public static Error NOT_FOUND (string message)
+public static Error INVALID_ARGUMENT (string message)
 ```
 ```
 
 
 ## Types Namespace
 ## Types Namespace
 
 
+### Mcp.Core.InjectContentLength
+
+Utility class for injecting Content-Length headers into messages.
+
+#### Methods
+
+```vala
+public static string process (string message)
+```
+
+Injects the Content-Length header into a JSON-RPC message.
+
+### Mcp.Core.StripContentLength
+
+Utility class for stripping Content-Length headers from messages.
+
+#### Methods
+
+```vala
+public static string process (string message)
+```
+
+Removes the Content-Length header from a JSON-RPC message.
+
 ### Mcp.Types.Protocol.ServerInfo
 ### Mcp.Types.Protocol.ServerInfo
 
 
 Information about the MCP server.
 Information about the MCP server.
@@ -118,7 +154,8 @@ Information about the MCP server.
 public ServerInfo (
 public ServerInfo (
     string name,
     string name,
     string version,
     string version,
-    string? description = null
+    string? description = null,
+    string? website_url = null
 )
 )
 ```
 ```
 
 
@@ -128,6 +165,7 @@ public ServerInfo (
 public string name { get; set; }
 public string name { get; set; }
 public string version { get; set; }
 public string version { get; set; }
 public string? description { get; set; }
 public string? description { get; set; }
+public string? website_url { get; set; }
 ```
 ```
 
 
 ### Mcp.Types.Protocol.ServerCapabilities
 ### Mcp.Types.Protocol.ServerCapabilities
@@ -138,11 +176,22 @@ Server capabilities configuration.
 
 
 ```vala
 ```vala
 public bool logging { get; set; }
 public bool logging { get; set; }
+public Mcp.Types.Protocol.CompletionsCapabilities? completions { get; set; }
 public Mcp.Types.Protocol.ResourcesCapabilities? resources { get; set; }
 public Mcp.Types.Protocol.ResourcesCapabilities? resources { get; set; }
 public Mcp.Types.Protocol.ToolsCapabilities? tools { get; set; }
 public Mcp.Types.Protocol.ToolsCapabilities? tools { get; set; }
 public Mcp.Types.Protocol.PromptsCapabilities? prompts { get; set; }
 public Mcp.Types.Protocol.PromptsCapabilities? prompts { get; set; }
 ```
 ```
 
 
+### Mcp.Types.Protocol.CompletionsCapabilities
+
+Completion-related capabilities.
+
+#### Properties
+
+```vala
+public bool list_changed { get; set; }
+```
+
 ### Mcp.Types.Protocol.ResourcesCapabilities
 ### Mcp.Types.Protocol.ResourcesCapabilities
 
 
 Resource-related capabilities.
 Resource-related capabilities.
@@ -296,6 +345,62 @@ public string? audience { get; set; }
 public bool? expire_time { get; set; }
 public bool? expire_time { get; set; }
 ```
 ```
 
 
+### Mcp.Types.VariantUtils
+
+Utility functions for working with GLib.Variant data structures.
+
+#### Dictionary Builder Methods
+
+```vala
+public static VariantBuilder new_dict_builder ()
+public static void add_string (VariantBuilder builder, string key, string value)
+public static void add_int (VariantBuilder builder, string key, int value)
+public static void add_bool (VariantBuilder builder, string key, bool value)
+public static void add_double (VariantBuilder builder, string key, double value)
+public static void add_variant (VariantBuilder builder, string key, Variant value)
+```
+
+#### Dictionary Access Methods
+
+```vala
+public static bool has_key (Variant dict, string key)
+public static string get_string (Variant dict, string key, string? default_value = null)
+public static int get_int (Variant dict, string key, int default_value = 0)
+public static bool get_bool (Variant dict, string key, bool default_value = false)
+public static double get_double (Variant dict, string key, double default_value = 0.0)
+public static Variant? get_variant (Variant dict, string key)
+public static string[] get_string_array (Variant dict, string key)
+```
+
+#### Array Builder Methods
+
+```vala
+public static VariantBuilder new_array_builder (string element_type)
+public static void add_string_element (VariantBuilder builder, string value)
+public static void add_int_element (VariantBuilder builder, int value)
+public static void add_bool_element (VariantBuilder builder, bool value)
+public static void add_double_element (VariantBuilder builder, double value)
+public static void add_variant_element (VariantBuilder builder, Variant value)
+```
+
+#### Array Access Methods
+
+```vala
+public static int get_array_size (Variant array)
+public static string get_string_element (Variant array, int index)
+public static int get_int_element (Variant array, int index)
+public static bool get_bool_element (Variant array, int index)
+public static double get_double_element (Variant array, int index)
+public static Variant get_variant_element (Variant array, int index)
+```
+
+#### Conversion Methods
+
+```vala
+public static Json.Node? to_json (Variant variant)
+public static Variant? from_json (Json.Node node)
+```
+
 ## Resources Namespace
 ## Resources Namespace
 
 
 ### Mcp.Resources.Provider
 ### Mcp.Resources.Provider
@@ -415,7 +520,7 @@ Interface for tool executors.
 
 
 ```vala
 ```vala
 public abstract Mcp.Tools.Types.ToolDefinition get_definition () throws Error
 public abstract Mcp.Tools.Types.ToolDefinition get_definition () throws Error
-public abstract async Mcp.Tools.Types.CallToolResult execute (Json.Object arguments) throws Error
+public abstract async Mcp.Tools.Types.CallToolResult execute (Variant arguments) throws Error
 ```
 ```
 
 
 ### Mcp.Tools.BaseExecutor
 ### Mcp.Tools.BaseExecutor
@@ -432,17 +537,17 @@ protected BaseExecutor (Mcp.Tools.Types.ToolDefinition definition)
 
 
 ```vala
 ```vala
 public virtual Mcp.Tools.Types.ToolDefinition get_definition () throws Error
 public virtual Mcp.Tools.Types.ToolDefinition get_definition () throws Error
-public async Mcp.Tools.Types.CallToolResult execute (Json.Object arguments) throws Error
-protected virtual void validate_arguments (Json.Object arguments) throws Error
+public async Mcp.Tools.Types.CallToolResult execute (Variant arguments) throws Error
+protected virtual void validate_arguments (Variant arguments) throws Error
 protected Mcp.Tools.Types.CallToolResult create_text_result (string text)
 protected Mcp.Tools.Types.CallToolResult create_text_result (string text)
 protected Mcp.Tools.Types.CallToolResult create_text_results (string[] texts)
 protected Mcp.Tools.Types.CallToolResult create_text_results (string[] texts)
 protected Mcp.Tools.Types.CallToolResult create_error_result (Error error)
 protected Mcp.Tools.Types.CallToolResult create_error_result (Error error)
-protected Mcp.Tools.Types.CallToolResult create_structured_result (string text, Json.Object structured_data)
-protected string? get_string_arg (Json.Object arguments, string name, string? default_value = null)
-protected bool get_bool_arg (Json.Object arguments, string name, bool default_value = false)
-protected int get_int_arg (Json.Object arguments, string name, int default_value = 0)
-protected double get_double_arg (Json.Object arguments, string name, double default_value = 0.0)
-protected abstract async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error
+protected Mcp.Tools.Types.CallToolResult create_structured_result (string text, Variant structured_data)
+protected string? get_string_arg (Variant arguments, string name, string? default_value = null)
+protected bool get_bool_arg (Variant arguments, string name, bool default_value = false)
+protected int get_int_arg (Variant arguments, string name, int default_value = 0)
+protected double get_double_arg (Variant arguments, string name, double default_value = 0.0)
+protected abstract async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error
 ```
 ```
 
 
 ### Mcp.Tools.Manager
 ### Mcp.Tools.Manager
@@ -455,7 +560,7 @@ Manages tool executors and handles tool operations.
 public void register_executor (string name, Mcp.Tools.Executor executor)
 public void register_executor (string name, Mcp.Tools.Executor executor)
 public void unregister_executor (string name)
 public void unregister_executor (string name)
 public async Gee.ArrayList<Mcp.Tools.Types.ToolDefinition> list_tools () throws Error
 public async Gee.ArrayList<Mcp.Tools.Types.ToolDefinition> list_tools () throws Error
-public async Mcp.Tools.Types.CallToolResult call_tool (string name, Json.Object arguments) throws Error
+public async Mcp.Tools.Types.CallToolResult call_tool (string name, Variant arguments) throws Error
 ```
 ```
 
 
 ### Mcp.Tools.Types.ToolDefinition
 ### Mcp.Tools.Types.ToolDefinition
@@ -465,7 +570,7 @@ Tool definition.
 #### Constructor
 #### Constructor
 
 
 ```vala
 ```vala
-public ToolDefinition (string name, Json.Object input_schema)
+public ToolDefinition (string name, Variant input_schema)
 ```
 ```
 
 
 #### Properties
 #### Properties
@@ -474,8 +579,8 @@ public ToolDefinition (string name, Json.Object input_schema)
 public string name { get; set; }
 public string name { get; set; }
 public string? title { get; set; }
 public string? title { get; set; }
 public string? description { get; set; }
 public string? description { get; set; }
-public Json.Object input_schema { get; set; }
-public Json.Object? output_schema { get; set; }
+public Variant input_schema { get; set; }
+public Variant? output_schema { get; set; }
 public Mcp.Tools.Types.ToolExecution? execution { get; set; }
 public Mcp.Tools.Types.ToolExecution? execution { get; set; }
 public Mcp.Tools.Types.ToolAnnotations? annotations { get; set; }
 public Mcp.Tools.Types.ToolAnnotations? annotations { get; set; }
 ```
 ```
@@ -495,13 +600,14 @@ Result of calling a tool.
 
 
 ```vala
 ```vala
 public CallToolResult ()
 public CallToolResult ()
+public CallToolResult (Gee.ArrayList<Mcp.Types.Common.ContentBlock> content)
 ```
 ```
 
 
 #### Properties
 #### Properties
 
 
 ```vala
 ```vala
 public Gee.ArrayList<Mcp.Types.Common.ContentBlock> content { get; set; }
 public Gee.ArrayList<Mcp.Types.Common.ContentBlock> content { get; set; }
-public Json.Object? structured_content { get; set; }
+public Variant? structured_content { get; set; }
 public bool is_error { get; set; }
 public bool is_error { get; set; }
 ```
 ```
 
 
@@ -512,25 +618,52 @@ public Json.Node to_json ()
 public CallToolResult.from_json (Json.Node node) throws Error
 public CallToolResult.from_json (Json.Node node) throws Error
 ```
 ```
 
 
-### Mcp.Tools.Types.ToolExecution
+### Mcp.Tools.Types.ToolExecutionContext
 
 
-Tool execution settings.
+Context for tool execution with progress tracking.
 
 
 #### Constructor
 #### Constructor
 
 
 ```vala
 ```vala
-public ToolExecution (string task_support = "forbidden")
+public ToolExecutionContext (string tool_name, Variant arguments)
 ```
 ```
 
 
 #### Properties
 #### Properties
 
 
 ```vala
 ```vala
-public string task_support { get; set; }
+public string tool_name { get; construct; }
+public Variant arguments { get; construct; }
+public bool supports_progress { get; set; }
+public bool task_support { get; set; }
+```
+
+#### Methods
+
+```vala
+public void report_progress (Mcp.Tools.Types.ToolProgress progress)
+public void add_annotation (Mcp.Tools.Types.ToolAnnotation annotation)
+```
+
+### Mcp.Tools.Types.ToolProgress
+
+Progress information for long-running tools.
+
+#### Constructor
+
+```vala
+public ToolProgress (double progress, string? message = null)
+```
+
+#### Properties
+
+```vala
+public double progress { get; set; }
+public string? message { get; set; }
 ```
 ```
 
 
 ### Mcp.Tools.Types.ToolAnnotations
 ### Mcp.Tools.Types.ToolAnnotations
 
 
-Tool annotations.
+Tool execution annotations.
 
 
 #### Constructor
 #### Constructor
 
 
@@ -543,6 +676,41 @@ public ToolAnnotations ()
 ```vala
 ```vala
 public string? audience { get; set; }
 public string? audience { get; set; }
 public double? priority { get; set; }
 public double? priority { get; set; }
+public Gee.ArrayList<Mcp.Tools.Types.ToolAnnotation> annotations { get; set; }
+```
+
+### Mcp.Tools.Types.ToolAnnotation
+
+Individual tool annotation.
+
+#### Constructor
+
+```vala
+public ToolAnnotation (string type, Variant data)
+```
+
+#### Properties
+
+```vala
+public string type { get; set; }
+public Variant data { get; set; }
+```
+
+### Mcp.Tools.Types.ToolExecution
+
+Tool execution settings.
+
+#### Constructor
+
+```vala
+public ToolExecution (string task_support = "forbidden")
+```
+
+#### Properties
+
+```vala
+public string task_support { get; set; }
+public bool supports_progress { get; set; }
 ```
 ```
 
 
 ## Prompts Namespace
 ## Prompts Namespace
@@ -555,7 +723,7 @@ Interface for prompt templates.
 
 
 ```vala
 ```vala
 public abstract Mcp.Prompts.Types.PromptDefinition get_definition ()
 public abstract Mcp.Prompts.Types.PromptDefinition get_definition ()
-public abstract async Mcp.Prompts.Types.GetPromptResult get_prompt (Json.Object arguments) throws Error
+public abstract async Mcp.Prompts.Types.GetPromptResult get_prompt (Variant arguments) throws Error
 ```
 ```
 
 
 ### Mcp.Prompts.BaseTemplate
 ### Mcp.Prompts.BaseTemplate
@@ -566,20 +734,21 @@ Base implementation for prompt templates.
 
 
 ```vala
 ```vala
 protected BaseTemplate (string name, string? title = null, string? description = null)
 protected BaseTemplate (string name, string? title = null, string? description = null)
+protected BaseTemplate (string name, string? title, string? description, Gee.ArrayList<Mcp.Prompts.Types.PromptArgument> arguments)
 ```
 ```
 
 
 #### Methods
 #### Methods
 
 
 ```vala
 ```vala
 public virtual Mcp.Prompts.Types.PromptDefinition get_definition ()
 public virtual Mcp.Prompts.Types.PromptDefinition get_definition ()
-public virtual async Mcp.Prompts.Types.GetPromptResult get_prompt (Json.Object arguments) throws Error
-protected virtual void validate_arguments (Json.Object arguments) throws Error
-protected virtual Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Json.Object arguments)
+public virtual async Mcp.Prompts.Types.GetPromptResult get_prompt (Variant arguments) throws Error
+protected virtual void validate_arguments (Variant arguments) throws Error
+protected virtual Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Variant arguments)
 protected void add_argument (string name, string? title = null, string? description = null, bool required = false)
 protected void add_argument (string name, string? title = null, string? description = null, bool required = false)
-protected string substitute_variables (string template, Json.Object arguments)
-protected Mcp.Types.Common.TextContent create_text_content (string template, Json.Object arguments)
-protected Mcp.Prompts.Types.PromptMessage create_user_message (string template, Json.Object arguments)
-protected Mcp.Prompts.Types.PromptMessage create_assistant_message (string template, Json.Object arguments)
+protected string substitute_variables (string template, Variant arguments)
+protected Mcp.Types.Common.TextContent create_text_content (string template, Variant arguments)
+protected Mcp.Prompts.Types.PromptMessage create_user_message (string template, Variant arguments)
+protected Mcp.Prompts.Types.PromptMessage create_assistant_message (string template, Variant arguments)
 ```
 ```
 
 
 ### Mcp.Prompts.Manager
 ### Mcp.Prompts.Manager
@@ -592,7 +761,7 @@ Manages prompt templates and handles prompt operations.
 public void register_template (string name, Mcp.Prompts.Template template)
 public void register_template (string name, Mcp.Prompts.Template template)
 public void unregister_template (string name)
 public void unregister_template (string name)
 public async Gee.ArrayList<Mcp.Prompts.Types.PromptDefinition> list_prompts () throws Error
 public async Gee.ArrayList<Mcp.Prompts.Types.PromptDefinition> list_prompts () throws Error
-public async Mcp.Prompts.Types.GetPromptResult get_prompt (string name, Json.Object arguments) throws Error
+public async Mcp.Prompts.Types.GetPromptResult get_prompt (string name, Variant arguments) throws Error
 ```
 ```
 
 
 ### Mcp.Prompts.Types.PromptDefinition
 ### Mcp.Prompts.Types.PromptDefinition
@@ -760,7 +929,7 @@ public class MyResourceProvider : Mcp.Resources.BaseProvider {
     
     
     public override async Mcp.Types.Common.ResourceContents read_resource (string uri) throws Error {
     public override async Mcp.Types.Common.ResourceContents read_resource (string uri) throws Error {
         if (!storage.contains (uri)) {
         if (!storage.contains (uri)) {
-            throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found");
+            throw new Mcp.Core.Error.NOT_FOUND ("Resource not found");
         }
         }
         
         
         return new Mcp.Types.Common.TextResourceContents (uri, storage[uri]);
         return new Mcp.Types.Common.TextResourceContents (uri, storage[uri]);
@@ -773,29 +942,28 @@ public class MyResourceProvider : Mcp.Resources.BaseProvider {
 ```vala
 ```vala
 public class MyTool : Mcp.Tools.BaseExecutor {
 public class MyTool : Mcp.Tools.BaseExecutor {
     public MyTool () {
     public MyTool () {
-        // Define input schema
-        var input_schema = new Json.Object ();
-        input_schema.set_string_member ("type", "object");
-        
-        var properties = new Json.Object ();
-        var param = new Json.Object ();
-        param.set_string_member ("type", "string");
-        param.set_string_member ("description", "Parameter description");
-        properties.set_object_member ("param", param);
+        // Define input schema using Variant
+        var input_schema = Mcp.Types.VariantUtils.new_dict_builder ();
+        input_schema.add ("{sv}", "type", new Variant.string ("object"));
         
         
-        input_schema.set_object_member ("properties", properties);
+        var properties = Mcp.Types.VariantUtils.new_dict_builder ();
+        var param = Mcp.Types.VariantUtils.new_dict_builder ();
+        param.add ("{sv}", "type", new Variant.string ("string"));
+        param.add ("{sv}", "description", new Variant.string ("Parameter description"));
+        properties.add ("{sv}", "param", param.end ());
         
         
-        var required = new Json.Array ();
-        required.add_string_element ("param");
-        input_schema.set_array_member ("required", required);
+        var required = new VariantBuilder (new VariantType ("as"));
+        required.add_value ("s", "param");
+        input_schema.add ("{sv}", "properties", properties.end ());
+        input_schema.add ("{sv}", "required", required.end ());
         
         
-        var definition = new Mcp.Tools.Types.ToolDefinition ("my_tool", input_schema);
+        var definition = new Mcp.Tools.Types.ToolDefinition ("my_tool", input_schema.end ());
         definition.description = "Description of my tool";
         definition.description = "Description of my tool";
         
         
         base (definition);
         base (definition);
     }
     }
     
     
-    protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+    protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
         string param = get_string_arg (arguments, "param");
         string param = get_string_arg (arguments, "param");
         
         
         // Process input
         // Process input
@@ -816,30 +984,32 @@ public class MyTool : Mcp.Tools.BaseExecutor {
 ```vala
 ```vala
 public class MyPrompt : Mcp.Prompts.BaseTemplate {
 public class MyPrompt : Mcp.Prompts.BaseTemplate {
     public MyPrompt () {
     public MyPrompt () {
-        base ("my_prompt", "My Prompt", "Description of my prompt");
+        var arguments = new Gee.ArrayList<Mcp.Prompts.Types.PromptArgument> ();
+        
+        var topic_arg = new Mcp.Prompts.Types.PromptArgument ("topic");
+        topic_arg.description = "The topic to generate content for";
+        topic_arg.required = true;
+        arguments.add (topic_arg);
         
         
-        // Add arguments
-        add_argument ("topic", "Topic", "The topic to generate content for", true);
-        add_argument ("style", "Style", "Writing style", false, "neutral");
+        var style_arg = new Mcp.Prompts.Types.PromptArgument ("style");
+        style_arg.description = "Writing style";
+        arguments.add (style_arg);
+        
+        base ("my_prompt", "My Prompt", "Description of my prompt", arguments);
     }
     }
     
     
-    protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Json.Object arguments) {
-        var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
+    protected override async Mcp.Prompts.Types.GetPromptResult do_render (Variant arguments) throws Error {
+        string topic = Mcp.Types.VariantUtils.get_string (arguments, "topic");
+        string style = Mcp.Types.VariantUtils.get_string (arguments, "style", "neutral");
         
         
-        string topic = get_string_arg (arguments, "topic");
-        string style = get_string_arg (arguments, "style", "neutral");
+        string template = """Generate %s content about %s.""".printf (style, topic);
         
         
-        string template = """Generate {{style}} content about {{topic}}.""";
+        var content = new Mcp.Types.Common.TextContent (template);
+        var message = new Mcp.Prompts.Types.PromptMessage ("user", content);
+        var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
+        messages.add (message);
         
         
-        messages.add (create_user_message (template, arguments));
-        return messages;
-    }
-    
-    private string? get_string_arg (Json.Object arguments, string name, string? default_value = null) {
-        if (arguments.has_member (name)) {
-            return arguments.get_string_member (name);
-        }
-        return default_value;
+        return new Mcp.Prompts.Types.GetPromptResult (messages);
     }
     }
 }
 }
 ```
 ```
@@ -942,7 +1112,7 @@ Mcp.Core.McpError
 ```vala
 ```vala
 try {
 try {
     // MCP operation
     // MCP operation
-} catch (Mcp.Core.McpError e) {
+} catch (Mcp.Core.Error e) {
     // Handle MCP-specific errors
     // Handle MCP-specific errors
     stderr.printf ("MCP Error [%d]: %s\n", e.code, e.message);
     stderr.printf ("MCP Error [%d]: %s\n", e.code, e.message);
 } catch (GLib.Error e) {
 } catch (GLib.Error e) {

+ 111 - 108
docs/developer-guide.md

@@ -92,12 +92,14 @@ public class MyServer : GLib.Object {
         var server_info = new Mcp.Types.Protocol.ServerInfo (
         var server_info = new Mcp.Types.Protocol.ServerInfo (
             "my-server",
             "my-server",
             "1.0.0",
             "1.0.0",
-            "My custom MCP server"
+            "My custom MCP server",
+            "https://my-server.example.com"
         );
         );
         
         
         // Create capabilities
         // Create capabilities
         var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
         var capabilities = new Mcp.Types.Protocol.ServerCapabilities ();
         capabilities.logging = true;
         capabilities.logging = true;
+        capabilities.completions = new Mcp.Types.Protocol.CompletionsCapabilities ();
         
         
         // Create server
         // Create server
         server = new Mcp.Core.Server (server_info, capabilities);
         server = new Mcp.Core.Server (server_info, capabilities);
@@ -153,18 +155,19 @@ Tools are functions that clients can execute:
 
 
 ```vala
 ```vala
 // Tool definition
 // Tool definition
-var input_schema = new Json.Object ();
-input_schema.set_string_member ("type", "object");
+var input_schema = Mcp.Types.VariantUtils.new_dict_builder ();
+input_schema.add ("{sv}", "type", new Variant.string ("object"));
 
 
 var definition = new Mcp.Tools.Types.ToolDefinition (
 var definition = new Mcp.Tools.Types.ToolDefinition (
     "my_tool",
     "my_tool",
-    input_schema
+    input_schema.end ()
 );
 );
 
 
 // Tool execution result
 // Tool execution result
-var result = new Mcp.Tools.Types.CallToolResult ();
 var content = new Mcp.Types.Common.TextContent ("Operation completed");
 var content = new Mcp.Types.Common.TextContent ("Operation completed");
-result.content.add (content);
+var contents = new Gee.ArrayList<Mcp.Types.Common.ContentBlock> ();
+contents.add (content);
+var result = new Mcp.Tools.Types.CallToolResult (contents);
 ```
 ```
 
 
 ### Prompts
 ### Prompts
@@ -217,7 +220,7 @@ public class MyResourceProvider : Mcp.Resources.BaseProvider {
     
     
     public override async Mcp.Types.Common.ResourceContents read_resource (string uri) throws Error {
     public override async Mcp.Types.Common.ResourceContents read_resource (string uri) throws Error {
         if (!data_store.contains (uri)) {
         if (!data_store.contains (uri)) {
-            throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("Resource not found: %s".printf (uri));
+            throw new Mcp.Core.Error.NOT_FOUND ("Resource not found: %s".printf (uri));
         }
         }
         
         
         return new Mcp.Types.Common.TextResourceContents (uri, data_store[uri]);
         return new Mcp.Types.Common.TextResourceContents (uri, data_store[uri]);
@@ -252,28 +255,27 @@ Implement the `Mcp.Tools.Executor` interface or extend `Mcp.Tools.BaseExecutor`:
 public class MyTool : Mcp.Tools.BaseExecutor {
 public class MyTool : Mcp.Tools.BaseExecutor {
     public MyTool () {
     public MyTool () {
         // Define input schema
         // Define input schema
-        var input_schema = new Json.Object ();
-        input_schema.set_string_member ("type", "object");
+        var input_schema = Mcp.Types.VariantUtils.new_dict_builder ();
+        input_schema.add ("{sv}", "type", new Variant.string ("object"));
         
         
-        var properties = new Json.Object ();
-        var text_prop = new Json.Object ();
-        text_prop.set_string_member ("type", "string");
-        text_prop.set_string_member ("description", "Text to process");
-        properties.set_object_member ("text", text_prop);
+        var properties = Mcp.Types.VariantUtils.new_dict_builder ();
+        var text_prop = Mcp.Types.VariantUtils.new_dict_builder ();
+        text_prop.add ("{sv}", "type", new Variant.string ("string"));
+        text_prop.add ("{sv}", "description", new Variant.string ("Text to process"));
+        properties.add ("{sv}", "text", text_prop.end ());
         
         
-        input_schema.set_object_member ("properties", properties);
+        var required = new VariantBuilder (new VariantType ("as"));
+        required.add_value ("s", "text");
+        input_schema.add ("{sv}", "properties", properties.end ());
+        input_schema.add ("{sv}", "required", required.end ());
         
         
-        var required = new Json.Array ();
-        required.add_string_element ("text");
-        input_schema.set_array_member ("required", required);
-        
-        var definition = new Mcp.Tools.Types.ToolDefinition ("my_tool", input_schema);
+        var definition = new Mcp.Tools.Types.ToolDefinition ("my_tool", input_schema.end ());
         definition.description = "Processes text input";
         definition.description = "Processes text input";
         
         
         base (definition);
         base (definition);
     }
     }
     
     
-    protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+    protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
         string text = get_string_arg (arguments, "text");
         string text = get_string_arg (arguments, "text");
         
         
         // Process the text
         // Process the text
@@ -294,12 +296,12 @@ server.tool_manager.register_executor ("my_tool", new MyTool ());
 Return structured data with your results:
 Return structured data with your results:
 
 
 ```vala
 ```vala
-var structured_data = new Json.Object ();
-structured_data.set_string_member ("original", text);
-structured_data.set_string_member ("processed", processed);
-structured_data.set_int_member ("length", processed.length);
+var structured_data = Mcp.Types.VariantUtils.new_dict_builder ();
+structured_data.add ("{sv}", "original", new Variant.string (text));
+structured_data.add ("{sv}", "processed", new Variant.string (processed));
+structured_data.add ("{sv}", "length", new Variant.int32 (processed.length));
 
 
-return create_structured_result ("Text processed successfully", structured_data);
+return create_structured_result ("Text processed successfully", structured_data.end ());
 ```
 ```
 
 
 #### Error Handling
 #### Error Handling
@@ -307,12 +309,12 @@ return create_structured_result ("Text processed successfully", structured_data)
 Handle errors gracefully:
 Handle errors gracefully:
 
 
 ```vala
 ```vala
-protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
     try {
     try {
         // Validate input
         // Validate input
         string text = get_string_arg (arguments, "text");
         string text = get_string_arg (arguments, "text");
         if (text.length == 0) {
         if (text.length == 0) {
-            throw new Mcp.Core.McpError.INVALID_PARAMS ("Text cannot be empty");
+            throw new Mcp.Core.Error.INVALID_PARAMS ("Text cannot be empty");
         }
         }
         
         
         // Process
         // Process
@@ -343,12 +345,12 @@ public class MyPrompt : Mcp.Prompts.BaseTemplate {
         add_argument ("length", "Length", "Desired length (short, medium, long)", false);
         add_argument ("length", "Length", "Desired length (short, medium, long)", false);
     }
     }
     
     
-    protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Json.Object arguments) {
+    protected override async Mcp.Prompts.Types.GetPromptResult do_render (Variant arguments) throws Error {
         var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
         var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
         
         
-        string topic = get_string_arg (arguments, "topic");
-        string? style = get_string_arg (arguments, "style", "neutral");
-        string? length = get_string_arg (arguments, "length", "medium");
+        string topic = Mcp.Types.VariantUtils.get_string (arguments, "topic");
+        string? style = Mcp.Types.VariantUtils.get_string (arguments, "style", "neutral");
+        string? length = Mcp.Types.VariantUtils.get_string (arguments, "length", "medium");
         
         
         string template = """Generate {{style}} content about {{topic}}.
         string template = """Generate {{style}} content about {{topic}}.
 Target length: {{length}}.
 Target length: {{length}}.
@@ -363,11 +365,8 @@ Please provide:
         return messages;
         return messages;
     }
     }
     
     
-    private string? get_string_arg (Json.Object arguments, string name, string? default_value = null) {
-        if (arguments.has_member (name)) {
-            return arguments.get_string_member (name);
-        }
-        return default_value;
+    private string? get_string_arg (Variant arguments, string name, string? default_value = null) {
+        return Mcp.Types.VariantUtils.get_string (arguments, name, default_value);
     }
     }
 }
 }
 
 
@@ -402,25 +401,29 @@ Use creative language and imaginative expressions.
 Create conversations with multiple messages:
 Create conversations with multiple messages:
 
 
 ```vala
 ```vala
-protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Json.Object arguments) {
-    var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
-    
-    // System message
-    var system_content = new Mcp.Types.Common.TextContent (
-        "You are a helpful assistant specializing in {{topic}}."
-    );
-    var system_msg = new Mcp.Prompts.Types.PromptMessage ("system", system_content);
-    messages.add (system_msg);
-    
-    // User message
-    var user_content = new Mcp.Types.Common.TextContent (
-        "Tell me about {{topic}}."
-    );
-    var user_msg = new Mcp.Prompts.Types.PromptMessage ("user", user_content);
-    messages.add (user_msg);
-    
-    return messages;
-}
+protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Variant arguments) {
+        var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
+        
+        // System message
+        var system_content = new Mcp.Types.Common.TextContent (
+            "You are a helpful assistant specializing in %s.".printf (
+                Mcp.Types.VariantUtils.get_string (arguments, "topic")
+            )
+        );
+        var system_msg = new Mcp.Prompts.Types.PromptMessage ("system", system_content);
+        messages.add (system_msg);
+        
+        // User message
+        var user_content = new Mcp.Types.Common.TextContent (
+            "Tell me about %s.".printf (
+                Mcp.Types.VariantUtils.get_string (arguments, "topic")
+            )
+        );
+        var user_msg = new Mcp.Prompts.Types.PromptMessage ("user", user_content);
+        messages.add (user_msg);
+        
+        return messages;
+    }
 ```
 ```
 
 
 ## Best Practices
 ## Best Practices
@@ -494,47 +497,46 @@ protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messa
 
 
 1. **Validate all inputs**:
 1. **Validate all inputs**:
    ```vala
    ```vala
-   protected override void validate_arguments (Json.Object arguments) throws Error {
-       // Call base validation
-       base.validate_arguments (arguments);
-       
-       // Add custom validation
-       if (arguments.has_member ("custom_param")) {
-           string value = arguments.get_string_member ("custom_param");
-           if (!is_valid_custom_param (value)) {
-               throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid custom_param");
+   protected override void validate_arguments (Variant arguments) throws Error {
+           // Call base validation
+           base.validate_arguments (arguments);
+           
+           // Add custom validation
+           if (Mcp.Types.VariantUtils.has_key (arguments, "custom_param")) {
+               string value = Mcp.Types.VariantUtils.get_string (arguments, "custom_param");
+               if (!is_valid_custom_param (value)) {
+                   throw new Mcp.Core.Error.INVALID_PARAMS ("Invalid custom_param");
+               }
            }
            }
        }
        }
-   }
    ```
    ```
 
 
 2. **Use structured results**:
 2. **Use structured results**:
    ```vala
    ```vala
-   var structured_data = new Json.Object ();
-   structured_data.set_string_member ("status", "success");
-   structured_data.set_object_member ("result", result_object);
-   structured_data.set_double_member ("duration_ms", elapsed_ms);
+   var structured_data = Mcp.Types.VariantUtils.new_dict_builder ();
+   structured_data.add ("{sv}", "status", new Variant.string ("success"));
+   structured_data.add ("{sv}", "result", result_object);
+   structured_data.add ("{sv}", "duration_ms", new Variant.double (elapsed_ms));
    
    
-   return create_structured_result ("Operation completed", structured_data);
+   return create_structured_result ("Operation completed", structured_data.end ());
    ```
    ```
 
 
 3. **Handle long-running operations**:
 3. **Handle long-running operations**:
    ```vala
    ```vala
-   protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
-       // Start progress notification
-       var progress = new Json.Object ();
-       progress.set_string_member ("status", "processing");
-       send_progress_notification (progress);
-       
-       // Perform long operation
-       var result = yield perform_long_operation (arguments);
-       
-       // Final progress
-       progress.set_string_member ("status", "completed");
-       send_progress_notification (progress);
-       
-       return result;
-   }
+   protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
+           // Start progress notification
+           var progress = new Mcp.Tools.Types.ToolProgress (0.0, "Processing...");
+           report_progress (progress);
+           
+           // Perform long operation
+           var result = yield perform_long_operation (arguments);
+           
+           // Final progress
+           var final_progress = new Mcp.Tools.Types.ToolProgress (1.0, "Completed");
+           report_progress (final_progress);
+           
+           return result;
+       }
    ```
    ```
 
 
 ### Prompt Design
 ### Prompt Design
@@ -572,17 +574,17 @@ The library provides these error types:
 ```vala
 ```vala
 try {
 try {
     // Your code
     // Your code
-} catch (Mcp.Core.McpError.INVALID_PARAMS e) {
+} catch (Mcp.Core.Error.INVALID_PARAMS e) {
     // Invalid method parameters
     // Invalid method parameters
-} catch (Mcp.Core.McpError.METHOD_NOT_FOUND e) {
+} catch (Mcp.Core.Error.METHOD_NOT_FOUND e) {
     // Method doesn't exist
     // Method doesn't exist
-} catch (Mcp.Core.McpError.RESOURCE_NOT_FOUND e) {
+} catch (Mcp.Core.Error.NOT_FOUND e) {
     // Resource doesn't exist
     // Resource doesn't exist
-} catch (Mcp.Core.McpError.INTERNAL_ERROR e) {
+} catch (Mcp.Core.Error.INTERNAL_ERROR e) {
     // Internal server error
     // Internal server error
-} catch (Mcp.Core.McpError.TRANSPORT_ERROR e) {
-    // Communication error
-} catch (Mcp.Core.McpError.PARSE_ERROR e) {
+} catch (Mcp.Core.Error.INVALID_ARGUMENT e) {
+    // Invalid argument
+} catch (Mcp.Core.Error.PARSE_ERROR e) {
     // JSON parsing error
     // JSON parsing error
 }
 }
 ```
 ```
@@ -592,17 +594,17 @@ try {
 1. **Be specific in error messages**:
 1. **Be specific in error messages**:
    ```vala
    ```vala
    // Good
    // Good
-   throw new Mcp.Core.McpError.INVALID_PARAMS (
+   throw new Mcp.Core.Error.INVALID_PARAMS (
        "Temperature must be between -273.15 and 1000 Kelvin"
        "Temperature must be between -273.15 and 1000 Kelvin"
    );
    );
    
    
    // Bad
    // Bad
-   throw new Mcp.Core.McpError.INVALID_PARAMS ("Invalid temperature");
+   throw new Mcp.Core.Error.INVALID_PARAMS ("Invalid temperature");
    ```
    ```
 
 
 2. **Include context in errors**:
 2. **Include context in errors**:
    ```vala
    ```vala
-   throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND (
+   throw new Mcp.Core.Error.NOT_FOUND (
        "File not found: %s (working directory: %s)".printf (path, working_dir)
        "File not found: %s (working directory: %s)".printf (path, working_dir)
    );
    );
    ```
    ```
@@ -610,10 +612,10 @@ try {
 3. **Use appropriate error codes**:
 3. **Use appropriate error codes**:
    ```vala
    ```vala
    // Client error (bad request)
    // Client error (bad request)
-   throw new Mcp.Core.McpError.INVALID_PARAMS ("...");
+   throw new Mcp.Core.Error.INVALID_PARAMS ("...");
    
    
    // Server error (something went wrong)
    // Server error (something went wrong)
-   throw new Mcp.Core.McpError.INTERNAL_ERROR ("...");
+   throw new Mcp.Core.Error.INTERNAL_ERROR ("...");
    ```
    ```
 
 
 ## Testing and Debugging
 ## Testing and Debugging
@@ -625,8 +627,9 @@ Test your components individually:
 ```vala
 ```vala
 void test_tool_execution () {
 void test_tool_execution () {
     var tool = new MyTool ();
     var tool = new MyTool ();
-    var arguments = new Json.Object ();
-    arguments.set_string_member ("text", "test");
+    var arguments = Mcp.Types.VariantUtils.new_dict_builder ();
+    arguments.add ("{sv}", "text", new Variant.string ("test"));
+    var args = arguments.end ();
     
     
     // Test synchronous execution
     // Test synchronous execution
     tool.execute.begin (arguments, (obj, res) => {
     tool.execute.begin (arguments, (obj, res) => {
@@ -666,8 +669,8 @@ void test_server_integration () {
 
 
 2. **Use print statements**:
 2. **Use print statements**:
    ```vala
    ```vala
-   public override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
-       print ("Executing tool with arguments: %s\n", arguments.to_string (null));
+   public override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
+       print ("Executing tool with arguments\n");
        
        
        var result = yield perform_operation (arguments);
        var result = yield perform_operation (arguments);
        
        
@@ -784,13 +787,13 @@ var resource = new Mcp.Resources.Types.Resource ("file:///valid/path", "Name");
 // Validate schema
 // Validate schema
 public Mcp.Tools.Types.ToolDefinition get_definition () throws Error {
 public Mcp.Tools.Types.ToolDefinition get_definition () throws Error {
     if (definition.input_schema == null) {
     if (definition.input_schema == null) {
-        throw new Mcp.Core.McpError.INTERNAL_ERROR ("Input schema not initialized");
+        throw new Mcp.Core.Error.INTERNAL_ERROR ("Input schema not initialized");
     }
     }
     return definition;
     return definition;
 }
 }
 
 
 // Check async execution
 // Check async execution
-protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+protected override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
     // Always yield, even for sync operations
     // Always yield, even for sync operations
     var result = process_sync (arguments);
     var result = process_sync (arguments);
     return result;  // Missing yield!
     return result;  // Missing yield!
@@ -808,8 +811,8 @@ protected override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object
 **Solutions**:
 **Solutions**:
 ```vala
 ```vala
 // Debug template
 // Debug template
-protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Json.Object arguments) {
-    print ("Generating prompt with arguments: %s\n", arguments.to_string (null));
+protected override Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> generate_messages (Variant arguments) {
+    print ("Generating prompt with arguments\n");
     
     
     var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
     var messages = new Gee.ArrayList<Mcp.Prompts.Types.PromptMessage> ();
     
     
@@ -873,14 +876,14 @@ public override async Mcp.Types.Common.BlobResourceContents read_resource (strin
 
 
 ```vala
 ```vala
 // Good: Non-blocking
 // Good: Non-blocking
-public override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+public override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
     // Yield to allow other operations
     // Yield to allow other operations
     var result = yield perform_long_operation (arguments);
     var result = yield perform_long_operation (arguments);
     return result;
     return result;
 }
 }
 
 
 // Bad: Blocking
 // Bad: Blocking
-public override async Mcp.Tools.Types.CallToolResult do_execute (Json.Object arguments) throws Error {
+public override async Mcp.Tools.Types.CallToolResult do_execute (Variant arguments) throws Error {
     // This blocks the entire server!
     // This blocks the entire server!
     var result = perform_blocking_operation (arguments);
     var result = perform_blocking_operation (arguments);
     return result;
     return result;

+ 0 - 684
mcp-vala-architecture-design.md

@@ -1,684 +0,0 @@
-
-# Vala MCP Library Architecture Design
-
-## Overview
-
-This document outlines the architecture for a Vala library implementing the Model Context Protocol (MCP) version "2025-11-25" using jsonrpc-glib-1.0 for JSON-RPC communication. The design prioritizes developer experience while maintaining protocol compliance and performance.
-
-## Core Architecture
-
-### High-Level Design
-
-```
-┌─────────────────────────────────────────────────────────────┐
-│                    Application Layer                      │
-├─────────────────────────────────────────────────────────────┤
-│                    MCP Library API                      │
-├─────────────────────────────────────────────────────────────┤
-│  Resource Manager  │  Tool Manager  │  Prompt Manager   │
-├─────────────────────────────────────────────────────────────┤
-│              Jsonrpc.Server Implementation               │
-├─────────────────────────────────────────────────────────────┤
-│              jsonrpc-glib-1.0 + Json-Glib               │
-└─────────────────────────────────────────────────────────────┘
-```
-
-### Key Design Principles
-
-1. **Developer Experience First**: Simple, intuitive API with minimal boilerplate
-2. **Type Safety**: Leverage Vala's type system while using Json-Glib for JSON handling
-3. **Asynchronous Design**: Non-blocking operations throughout
-4. **Extensibility**: Clean interfaces for custom implementations
-5. **Protocol Compliance**: Full MCP v2025-11-25 specification support
-
-## Namespace Organization
-
-```
-Mcp
-├── Core                    // Core server infrastructure
-│   └── Server             // Main server class using Jsonrpc.Server
-├── Types                   // Shared data types and interfaces
-│   ├── Protocol            // MCP protocol types
-│   └── Common             // Common content types
-├── Resources              // Resource management
-│   ├── Manager
-│   ├── Provider
-│   └── Types
-├── Tools                 // Tool execution framework
-│   ├── Manager
-│   ├── Executor
-│   └── Types
-├── Prompts               // Prompt handling system
-│   ├── Manager
-│   ├── Template
-│   └── Types
-└── Core                  // Core error handling
-    └── Error
-```
-
-## Core Components
-
-### 1. Server Infrastructure
-
-#### Mcp.Core.Server
-
-```vala
-namespace Mcp.Core {
-    public class Server : Object {
-        // Server configuration
-        public ServerInfo server_info { get; construct; }
-        public ServerCapabilities capabilities { get; construct; }
-        
-        // Core managers
-        public ResourceManager resource_manager { get; construct; }
-        public ToolManager tool_manager { get; construct; }
-        public PromptManager prompt_manager { get; construct; }
-        
-        // Jsonrpc server instance
-        private Jsonrpc.Server jsonrpc_server;
-        
-        // Lifecycle
-        public signal void initialized ();
-        public signal void shutdown ();
-        
-        public async bool start () throws Error;
-        public async void stop ();
-        
-        // Jsonrpc method handlers
-        private Variant handle_initialize (Jsonrpc.Client client, string method, Variant params) throws Error;
-        private Variant handle_ping (Jsonrpc.Client client, string method, Variant params) throws Error;
-        private Variant handle_resources_list (Jsonrpc.Client client, string method, Variant params) throws Error;
-        // ... other handlers
-    }
-}
-```
-
-### 2. Jsonrpc Integration
-
-The library now uses the standard Jsonrpc.Server from jsonrpc-glib-1.0 for handling JSON-RPC communication. The custom JSON-RPC message types and transport layer have been removed in favor of the standard implementation.
-
-Key benefits:
-- Standard-compliant JSON-RPC 2.0 implementation
-- Robust error handling
-- Built-in support for async/await patterns
-- Proper STDIO transport handling
-
-#### Protocol Types
-
-```vala
-namespace Mcp.Types.Protocol {
-    public class ServerInfo : Object {
-        public string name { get; set; }
-        public string version { get; set; }
-        public string? description { get; set; }
-        public string? website_url { get; set; }
-    }
-    
-    public class ServerCapabilities : Object {
-        public bool logging { get; set; }
-        public bool completions { get; set; }
-        public PromptsCapabilities? prompts { get; set; }
-        public ResourcesCapabilities? resources { get; set; }
-        public ToolsCapabilities? tools { get; set; }
-    }
-    
-    public class PromptsCapabilities : Object {
-        public bool list_changed { get; set; }
-    }
-    
-    public class ResourcesCapabilities : Object {
-        public bool subscribe { get; set; }
-        public bool list_changed { get; set; }
-    }
-    
-    public class ToolsCapabilities : Object {
-        public bool list_changed { get; set; }
-    }
-}
-```
-
-### 4. Resource Management System
-
-#### Mcp.Resources.Manager
-
-```vala
-namespace Mcp.Resources {
-    public interface Provider : Object {
-        public abstract async List<Resource> list_resources (string? cursor) throws Error;
-        public abstract async ResourceContents read_resource (string uri) throws Error;
-        public virtual async void subscribe (string uri) throws Error { /* Optional */ }
-        public virtual async void unsubscribe (string uri) throws Error { /* Optional */ }
-    }
-    
-    public class Manager : Object {
-        private HashTable<string, Provider> providers;
-        
-        public void register_provider (string uri_scheme, Provider provider);
-        public void unregister_provider (string uri_scheme);
-        
-        // Protocol handlers
-        public async Json.Node handle_list (Json.Object params) throws Error;
-        public async Json.Node handle_read (Json.Object params) throws Error;
-        public async Json.Node handle_subscribe (Json.Object params) throws Error;
-        public async Json.Node handle_unsubscribe (Json.Object params) throws Error;
-        
-        // Notifications
-        public void notify_list_changed ();
-        public void notify_resource_updated (string uri);
-    }
-}
-```
-
-#### Resource Types
-
-```vala
-namespace Mcp.Resources.Types {
-    public class Resource : Object {
-        public string uri { get; set; }
-        public string name { get; set; }
-        public string? title { get; set; }
-        public string? description { get; set; }
-        public string? mime_type { get; set; }
-        public Annotations? annotations { get; set; }
-        public int64? size { get; set; }
-    }
-    
-    public class ResourceContents : Object {
-        public string uri { get; set; }
-        public string? mime_type { get; set; }
-    }
-    
-    public class TextResourceContents : ResourceContents {
-        public string text { get; set; }
-    }
-    
-    public class BlobResourceContents : ResourceContents {
-        public string blob { get; set; } // Base64 encoded
-    }
-}
-```
-
-### 5. Tool Execution Framework
-
-#### Mcp.Tools.Manager
-
-```vala
-namespace Mcp.Tools {
-    public interface Executor : Object {
-        public abstract ToolDefinition get_definition ();
-        public abstract async CallToolResult execute (Json.Object arguments) throws Error;
-    }
-    
-    public class Manager : Object {
-        private HashTable<string, Executor> executors;
-        
-        public void register_executor (string name, Executor executor);
-        public void unregister_executor (string name);
-        
-        // Protocol handlers
-        public async Json.Node handle_list (Json.Object params) throws Error;
-        public async Json.Node handle_call (Json.Object params) throws Error;
-        
-        // Notifications
-        public void notify_list_changed ();
-    }
-}
-```
-
-#### Tool Types
-
-```vala
-namespace Mcp.Tools.Types {
-    public class ToolDefinition : Object {
-        public string name { get; set; }
-        public string? title { get; set; }
-        public string? description { get; set; }
-        public Json.Object input_schema { get; set; }
-        public Json.Object? output_schema { get; set; }
-        public ToolExecution? execution { get; set; }
-        public ToolAnnotations? annotations { get; set; }
-    }
-    
-    public class CallToolResult : Object {
-        public List<ContentBlock> content { get; set; }
-        public Json.Object? structured_content { get; set; }
-        public bool is_error { get; set; default = false; }
-    }
-    
-    public class ToolExecution : Object {
-        public string task_support { get; set; default = "forbidden"; } // forbidden, optional, required
-    }
-}
-```
-
-### 6. Prompt Handling System
-
-#### Mcp.Prompts.Manager
-
-```vala
-namespace Mcp.Prompts {
-    public interface Template : Object {
-        public abstract PromptDefinition get_definition ();
-        public abstract async GetPromptResult get_prompt (Json.Object arguments) throws Error;
-    }
-    
-    public class Manager : Object {
-        private HashTable<string, Template> templates;
-        
-        public void register_template (string name, Template template);
-        public void unregister_template (string name);
-        
-        // Protocol handlers
-        public async Json.Node handle_list (Json.Object params) throws Error;
-        public async Json.Node handle_get (Json.Object params) throws Error;
-        
-        // Notifications
-        public void notify_list_changed ();
-    }
-}
-```
-
-#### Prompt Types
-
-```vala
-namespace Mcp.Prompts.Types {
-    public class PromptDefinition : Object {
-        public string name { get; set; }
-        public string? title { get; set; }
-        public string? description { get; set; }
-        public List<PromptArgument> arguments { get; set; }
-    }
-    
-    public class PromptArgument : Object {
-        public string name { get; set; }
-        public string? title { get; set; }
-        public string? description { get; set; }
-        public bool required { get; set; default = false; }
-    }
-    
-    public class GetPromptResult : Object {
-        public string? description { get; set; }
-        public List<PromptMessage> messages { get; set; }
-    }
-    
-    public class PromptMessage : Object {
-        public string role { get; set; } // "user" | "assistant"
-        public ContentBlock content { get; set; }
-    }
-}
-```
-
-### 7. Content System
-
-```vala
-namespace Mcp.Types.Common {
-    public interface ContentBlock : Object {
-        public string type { get; }
-    }
-    
-    public class TextContent : Object, ContentBlock {
-        public string type { get; construct; default = "text"; }
-        public string text { get; set; }
-        public Annotations? annotations { get; set; }
-    }
-    
-    public class ImageContent : Object, ContentBlock {
-        public string type { get; construct; default = "image"; }
-        public string data { get; set; } // Base64 encoded
-        public string mime_type { get; set; }
-        public Annotations? annotations { get; set; }
-    }
-    
-    public class ResourceLink : Object, ContentBlock {
-        public string type { get; construct; default = "resource_link"; }
-        public string uri { get; set; }
-        public string name { get; set; }
-        public string? title { get; set; }
-        public string? description { get; set; }
-        public string? mime_type { get; set; }
-        public Annotations? annotations { get; set; }
-    }
-    
-    public class EmbeddedResource : Object, ContentBlock {
-        public string type { get; construct; default = "resource"; }
-        public ResourceContents resource { get; set; }
-        public Annotations? annotations { get; set; }
-    }
-}
-```
-
-### 8. Error Handling
-
-```vala
-namespace Mcp.Core {
-    public errordomain McpError {
-        PARSE_ERROR,
-        INVALID_REQUEST,
-        METHOD_NOT_FOUND,
-        INVALID_PARAMS,
-        INTERNAL_ERROR,
-        RESOURCE_NOT_FOUND,
-        TOOL_EXECUTION_FAILED,
-        PROMPT_NOT_FOUND
-    }
-    
-    public class ErrorHandler : Object {
-        public static Json.Node create_error_response (string id, McpError error);
-        public static Json.Node create_error_notification (McpError error);
-        public static McpError from_json_code (int code, string message);
-    }
-}
-```
-
-### 9. Event System
-
-```vala
-namespace Mcp.Notifications {
-    public class Emitter : Object {
-        public signal void resource_list_changed ();
-        public signal void resource_updated (string uri);
-        public signal void tool_list_changed ();
-        public signal void prompt_list_changed ();
-        public signal void logging_message (LoggingLevel level, string? logger, Json.Node data);
-        public signal void progress (ProgressToken token, double progress, double? total, string? message);
-    }
-    
-    public enum LoggingLevel {
-        DEBUG,
-        INFO,
-        NOTICE,
-        WARNING,
-        ERROR,
-        CRITICAL,
-        ALERT,
-        EMERGENCY
-    }
-}
-```
-
-## Public API Design
-
-### Simple Server Creation
-
-```vala
-using Mcp;
-
-public class MyServer : Object {
-    private Mcp.Core.Server server;
-    
-    public MyServer () {
-        // Create server with basic info
-        server = new Mcp.Core.Server (
-            new Mcp.Types.Protocol.ServerInfo () {
-                name = "my-mcp-server",
-                version = "1.0.0",
-                description = "My awesome MCP server"
-            },
-            new Mcp.Types.Protocol.ServerCapabilities () {
-                resources = new Mcp.Types.Protocol.ResourcesCapabilities () {
-                    subscribe = true,
-                    list_changed = true
-                },
-                tools = new Mcp.Types.Protocol.ToolsCapabilities () {
-                    list_changed = true
-                }
-            }
-        );
-        
-        // Register providers
-        setup_resources ();
-        setup_tools ();
-        setup_prompts ();
-    }
-    
-    private void setup_resources () {
-        server.resource_manager.register_provider ("file", new FileResourceProvider ());
-        server.resource_manager.register_provider ("http", new HttpResourceProvider ());
-    }
-    
-    private void setup_tools () {
-        server.tool_manager.register_executor ("calculate", new CalculateTool ());
-        server.tool_manager.register_executor ("fetch", new FetchTool ());
-    }
-    
-    private void setup_prompts () {
-        server.prompt_manager.register_template ("summarize", new SummarizePrompt ());
-    }
-    
-    public async int run () {
-        try {
-            yield server.start ();
-            new MainLoop ().run ();
-            return 0;
-        } catch (Error e) {
-            stderr.printf ("Server error: %s\n", e.message);
-            return 1;
-        }
-    }
-}
-```
-
-### Resource Provider Example
-
-```vala
-using Mcp;
-
-public class FileResourceProvider : Object, Mcp.Resources.Provider {
-    public async List<Mcp.Resources.Types.Resource> list_resources (string? cursor) throws Error {
-        var resources = new List<Mcp.Resources.Types.Resource> ();
-        
-        // List files in current directory
-        var dir = File.new_for_path (".");
-        var enumerator = await dir.enumerate_children_async (
-            "standard::name,standard::type,standard::size",
-            FileQueryInfoFlags.NONE
-        );
-        
-        FileInfo file_info;
-        while ((file_info = yield enumerator.next_file_async ()) != null) {
-            if (file_info.get_file_type () == FileType.REGULAR) {
-                var file = enumerator.get_child (file_info);
-                var resource = new Mcp.Resources.Types.Resource () {
-                    uri = file.get_uri (),
-                    name = file_info.get_name (),
-                    mime_type = guess_mime_type (file_info.get_name ()),
-                    size = file_info.get_size ()
-                };
-                resources.append (resource);
-            }
-        }
-        
-        return resources;
-    }
-    
-    public async Mcp.Resources.Types.ResourceContents read_resource (string uri) throws Error {
-        var file = File.new_for_uri (uri);
-        if (!file.query_exists ()) {
-            throw new Mcp.Core.McpError.RESOURCE_NOT_FOUND ("File not found: %s".printf (uri));
-        }
-        
-        uint8[] contents;
-        yield file.load_contents_async (null, out contents);
-        
-        var text = (string) contents;
-        return new Mcp.Resources.Types.TextResourceContents () {
-            uri = uri,
-            text = text,
-            mime_type = guess_mime_type (file.get_basename ())
-        };
-    }
-}
-```
-
-### Tool Executor Example
-
-```vala
-using Mcp;
-
-public class CalculateTool : Object, Mcp.Tools.Executor {
-    public Mcp.Tools.Types.ToolDefinition get_definition () {
-        return new Mcp.Tools.Types.ToolDefinition () {
-            name = "calculate",
-            description = "Perform mathematical calculations",
-            input_schema = new Json.Object () {
-                type = "object",
-                properties = new Json.Object () {
-                    expression = new Json.Object () {
-                        type = "string",
-                        description = "Mathematical expression to evaluate"
-                    }
-                },
-                required = new string[] { "expression" }
-            }
-        };
-    }
-    
-    public async Mcp.Tools.Types.CallToolResult execute (Json.Object arguments) throws Error {
-        var expression = arguments.get_string_member ("expression");
-        
-        try {
-            // Simple expression evaluation (in real implementation, use proper parser)
-            var result = evaluate_expression (expression);
-            
-            var content = new Mcp.Types.Common.TextContent () {
-                text = "Result: %s = %s".printf (expression, result.to_string ())
-            };
-            
-            var contents = new List<Mcp.Types.Common.ContentBlock> ();
-            contents.append (content);
-            
-            return new Mcp.Tools.Types.CallToolResult () {
-                content = contents,
-                structured_content = new Json.Object () {
-                    expression = expression,
-                    result = result.to_string ()
-                }
-            };
-        } catch (Error e) {
-            var content = new Mcp.Types.Common.TextContent () {
-                text = "Error evaluating expression: %s".printf (e.message)
-            };
-            
-            var contents = new List<Mcp.Types.Common.ContentBlock> ();
-            contents.append (content);
-            
-            return new Mcp.Tools.Types.CallToolResult () {
-                content = contents,
-                is_error = true
-            };
-        }
-    }
-}
-```
-
-## Build System
-
-### Meson Build Configuration
-
-```meson
-project('mcp-vala', 'vala', 'c',
-  version: '1.0.0',
-  default_options: ['warning_level=2', 'werror=false']
-)
-
-# Dependencies
-glib_dep = dependency('glib-2.0', version: '>= 2.70')
-gobject_dep = dependency('gobject-2.0', version: '>= 2.70')
-gio_dep = dependency('gio-2.0', version: '>= 2.70')
-json_glib_dep = dependency('json-glib-1.0', version: '>= 1.6')
-jsonrpc_glib_dep = dependency('jsonrpc-glib-1.0', version: '>= 3.34')
-gio_unix_dep = dependency('gio-unix-2.0')
-
-# Library configuration
-mcp_sources = [
-  'src/core/server.vala',
-  'src/core/error.vala',
-  'src/types/protocol.vala',
-  'src/types/common.vala',
-  'src/resources/manager.vala',
-  'src/resources/provider.vala',
-  'src/resources/types.vala',
-  'src/tools/manager.vala',
-  'src/tools/executor.vala',
-  'src/tools/types.vala',
-  'src/prompts/manager.vala',
-  'src/prompts/template.vala',
-  'src/prompts/types.vala'
-]
-
-mcp_vala_args = [
-  '--target-glib=2.70',
-  '--pkg', 'glib-2.0',
-  '--pkg', 'gobject-2.0',
-  '--pkg', 'gio-2.0',
-  '--pkg', 'json-glib-1.0',
-  '--pkg', 'jsonrpc-glib-1.0',
-  '--pkg', 'gio-unix-2.0'
-]
-
-# Build library
-mcp_lib = shared_library(
-  'mcp-vala',
-  mcp_sources,
-  dependencies: [
-    glib_dep,
-    gobject_dep,
-    gio_dep,
-    json_glib_dep,
-    jsonrpc_glib_dep,
-    gio_unix_dep
-  ],
-  vala_args: mcp_vala_args,
-  install: true,
-  install_dir: get_option('libdir')
-)
-
-# Install headers
-install_headers(
-  'mcp-vala.h',
-  install_dir: join_paths(get_option('includedir'), 'mcp-vala')
-)
-
-# VAPI file
-mcp_vapi = custom_target(
-  'mcp-vala.vapi',
-  input: mcp_sources,
-  output: 'mcp-vala.vapi',
-  command: [find_program('valac'), '--vapi', '@OUTPUT@'] + mcp_vala_args + ['@INPUT@'],
-  install: true,
-  install_dir: get_option('datadir') / 'vala' / 'vapi'
-)
-
-# PC file
-pkg_mod = import('pkgconfig')
-pkg_mod.generate(
-  libraries: mcp_lib,
-  version: meson.project_version(),
-  name: 'mcp-vala',
-  description: 'Vala library for Model Context Protocol',
-  filebase: 'mcp-vala',
-  subdirs: 'mcp-vala'
-)
-```
-
-## Implementation Considerations
-
-### JSON Handling Strategy
-
-1. **Use Json-Glib for data structures**: Use Json.Node, Json.Object, and Json.Array for MCP protocol types
-2. **Use GLib.Variant for Jsonrpc communication**: Convert between Json.Node and GLib.Variant when interfacing with Jsonrpc.Server
-3. **Type-safe serialization**: Create helper methods for converting between Vala objects and JSON
-4. **Validation**: Use Json-Glib's schema validation where applicable
-5. **Memory management**: Leverage Vala's reference counting for JSON objects
-
-### Asynchronous Operations
-
-1. **Gio.Async patterns**: Use async/await throughout for non-blocking operations
-2. **Jsonrpc integration**: Properly handle async method calls with Jsonrpc.Server
-
-### Jsonrpc Integration Benefits
-
-1. **Standard compliance**: Full JSON-RPC 2.0 compliance through jsonrpc-glib-1.0
-2. **Robust error handling**: Built-in error handling and response formatting
-3. **IO handling**: Proper STDIO transport handling with buffering and error recovery
-4. **Performance**: Optimized JSON parsing and message handling

+ 0 - 2
test.sh

@@ -1,2 +0,0 @@
-#!/usr/bin/bash
-strace -o trace.log -e trace=read,write -s 4096 ./builddir/examples/filesystem-server

+ 0 - 240
test_calculator_comprehensive.py

@@ -1,240 +0,0 @@
-#!/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()

+ 0 - 243
test_calculator_debug.py

@@ -1,243 +0,0 @@
-#!/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()

+ 0 - 196
test_content_length.py

@@ -1,196 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test MCP server connection with proper Content-Length headers
-"""
-
-import subprocess
-import time
-import json
-import sys
-import os
-import threading
-import queue
-import tempfile
-
-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,  # Use binary mode for proper handling
-            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)  # Give server time to start
-        
-    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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        print(f"SERVER MESSAGE: {json.dumps(parsed, indent=2)}")
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "params": {}
-        }
-        if params:
-            request["params"] = params
-            
-        request_str = json.dumps(request)
-        message = f"{request_str}\n"
-        print(f"SENDING: {repr(message)}")
-        
-        self.server_process.stdin.write(message.encode())
-        self.server_process.stdin.flush()
-        
-        # Wait for response with timeout
-        try:
-            response = self.message_queue.get(timeout=5)
-            return response
-        except queue.Empty:
-            print("TIMEOUT: No response received")
-            return None
-            
-    def test_initialize(self):
-        """Test initialize request"""
-        print("\n=== Testing Initialize ===")
-        
-        params = {
-            "protocolVersion": "2025-11-25",
-            "capabilities": {},
-            "clientInfo": {
-                "name": "test-client",
-                "version": "1.0.0"
-            }
-        }
-        
-        print("\nSending initialize request...")
-        init_response = self.send_jsonrpc_request("initialize", params, id=1)
-        if init_response:
-            print(f"Initialize response: {json.dumps(init_response, indent=2)}")
-            print("✓ Initialize successful")
-            return True
-        else:
-            print("✗ Initialize failed")
-            return False
-            
-    def test_simple_ping(self):
-        """Test simple ping"""
-        print("\n=== Testing Simple Ping ===")
-        
-        # Try a ping
-        print("\nSending ping request...")
-        ping_response = self.send_jsonrpc_request("ping", id=2)
-        if ping_response:
-            print(f"Ping response: {json.dumps(ping_response, indent=2)}")
-            print("✓ Ping successful")
-            return True
-        else:
-            print("✗ Ping failed")
-            return False
-        
-    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_content_length.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()
-        
-        # Wait a bit for server to fully start
-        time.sleep(2)
-        
-        # Test initialize first
-        init_success = tester.test_initialize()
-        
-        if init_success:
-            # Test simple ping
-            ping_success = tester.test_simple_ping()
-            
-            if ping_success:
-                print("\n✓ Server test completed successfully")
-            else:
-                print("\n✗ Ping test failed")
-        else:
-            print("\n✗ Initialize test failed")
-            
-    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()

+ 0 - 213
test_converters.py

@@ -1,213 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test the converters implemented in compat.vala
-"""
-
-import subprocess
-import json
-import sys
-import os
-
-def test_inject_content_length():
-    """Test the InjectContentLength converter"""
-    print("=== Testing InjectContentLength Converter ===")
-    
-    # Create a simple test program in Vala that uses the converter
-    test_code = """
-using GLib;
-using Mcp.Core.Compat;
-
-public class TestInjectContentLength {
-    public static int main (string[] args) {
-        // Create the converter
-        var converter = new InjectContentLength ();
-        
-        // Test input: JSON message without Content-Length header
-        string input = "{\\"jsonrpc\\": \\"2.0\\", \\"method\\": \\"ping\\", \\"id\\": 1}\\n";
-        
-        // Output buffer
-        uint8[] output = new uint8[1024];
-        size_t bytes_read, bytes_written;
-        
-        // Convert
-        var result = converter.convert (input.data, output, ConverterFlags.NONE, 
-                                       out bytes_read, out bytes_written);
-        
-        // Extract the output
-        string output_str = (string)output;
-        output_str = output_str.substring (0, (int)bytes_written);
-        
-        // Print the result
-        print ("Input: %s", input);
-        print ("Output: %s", output_str);
-        
-        // Check if Content-Length header was added
-        if (output_str.has_prefix ("Content-Length:")) {
-            print ("✓ Content-Length header injected successfully\\n");
-            return 0;
-        } else {
-            print ("✗ Content-Length header not found\\n");
-            return 1;
-        }
-    }
-}
-"""
-    
-    # Write the test code to a file
-    with open("test_inject.vala", "w") as f:
-        f.write(test_code)
-    
-    # Compile and run the test
-    try:
-        # Compile
-        compile_cmd = [
-            "valac",
-            "--pkg", "glib-2.0",
-            "--pkg", "gio-2.0",
-            "--pkg", "json-glib-1.0",
-            "--pkg", "jsonrpc-glib-1.0",
-            "--pkg", "gee-0.8",
-            "--pkg", "posix",
-            "--vapidir", "builddir/src",
-            "--pkg", "mcp-vala",
-            "test_inject.vala",
-            "-o", "test_inject"
-        ]
-        
-        result = subprocess.run(compile_cmd, capture_output=True, text=True, cwd=".")
-        
-        if result.returncode != 0:
-            print(f"Compilation failed: {result.stderr}")
-            print(f"Compilation stdout: {result.stdout}")
-            return False
-            
-        # Run
-        run_result = subprocess.run(["./test_inject"], capture_output=True, text=True)
-        print(run_result.stdout)
-        
-        if run_result.returncode == 0:
-            return True
-        else:
-            print(f"Test failed: {run_result.stderr}")
-            return False
-            
-    except Exception as e:
-        print(f"Error running test: {e}")
-        return False
-    finally:
-        # Clean up
-        for f in ["test_inject.vala", "test_inject"]:
-            try:
-                os.remove(f)
-            except:
-                pass
-
-def test_strip_content_length():
-    """Test the StripContentLength converter"""
-    print("=== Testing StripContentLength Converter ===")
-    
-    # Create a simple test program in Vala that uses the converter
-    test_code = """
-using GLib;
-using Mcp.Core.Compat;
-
-public class TestStripContentLength {
-    public static int main (string[] args) {
-        // Create the converter
-        var converter = new StripContentLength ();
-        
-        // Test input: JSON message with Content-Length header
-        string input = "Content-Length: 45\\r\\n\\r\\n{\\"jsonrpc\\": \\"2.0\\", \\"method\\": \\"ping\\", \\"id\\": 1}";
-        
-        // Output buffer
-        uint8[] output = new uint8[1024];
-        size_t bytes_read, bytes_written;
-        
-        // Convert
-        var result = converter.convert (input.data, output, ConverterFlags.NONE, 
-                                       out bytes_read, out bytes_written);
-        
-        // Extract the output
-        string output_str = (string)output;
-        output_str = output_str.substring (0, (int)bytes_written);
-        
-        // Print the result
-        print ("Input: %s", input);
-        print ("Output: %s", output_str);
-        
-        // Check if Content-Length header was removed and newline was added
-        if (!output_str.has_prefix ("Content-Length:") && output_str.has_suffix ("\\n")) {
-            print ("✓ Content-Length header stripped and newline added successfully\\n");
-            return 0;
-        } else {
-            print ("✗ Content-Length header not stripped or newline not added\\n");
-            return 1;
-        }
-    }
-}
-"""
-    
-    # Write the test code to a file
-    with open("test_strip.vala", "w") as f:
-        f.write(test_code)
-    
-    # Compile and run the test
-    try:
-        # Compile
-        compile_cmd = [
-            "valac",
-            "--pkg", "glib-2.0",
-            "--pkg", "gio-2.0",
-            "--pkg", "json-glib-1.0",
-            "--pkg", "jsonrpc-glib-1.0",
-            "--pkg", "gee-0.8",
-            "--pkg", "posix",
-            "--vapidir", "builddir/src",
-            "--pkg", "mcp-vala",
-            "test_strip.vala",
-            "-o", "test_strip"
-        ]
-        
-        result = subprocess.run(compile_cmd, capture_output=True, text=True, cwd=".")
-        
-        if result.returncode != 0:
-            print(f"Compilation failed: {result.stderr}")
-            return False
-            
-        # Run
-        run_result = subprocess.run(["./test_strip"], capture_output=True, text=True)
-        print(run_result.stdout)
-        
-        if run_result.returncode == 0:
-            return True
-        else:
-            print(f"Test failed: {run_result.stderr}")
-            return False
-            
-    except Exception as e:
-        print(f"Error running test: {e}")
-        return False
-    finally:
-        # Clean up
-        for f in ["test_strip.vala", "test_strip"]:
-            try:
-                os.remove(f)
-            except:
-                pass
-
-def main():
-    print("Testing MCP Converters\n")
-    
-    # Test both converters
-    inject_success = test_inject_content_length()
-    strip_success = test_strip_content_length()
-    
-    if inject_success and strip_success:
-        print("✓ All converter tests passed!")
-        return 0
-    else:
-        print("✗ Some converter tests failed!")
-        return 1
-
-if __name__ == "__main__":
-    sys.exit(main())

+ 0 - 252
test_functional.py

@@ -1,252 +0,0 @@
-#!/usr/bin/env python3
-"""
-Functional testing of MCP server core components
-"""
-
-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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "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_server_initialization(self):
-        """Test server initialization and lifecycle"""
-        print("\n=== Testing Server Initialization ===")
-        
-        # Test initialize request
-        init_params = {
-            "protocolVersion": "2025-11-25",
-            "capabilities": {},
-            "clientInfo": {
-                "name": "test-client",
-                "version": "1.0.0"
-            }
-        }
-        
-        print("\nSending initialize request...")
-        init_response = self.send_jsonrpc_request("initialize", init_params, id=1)
-        
-        if init_response and "result" in init_response:
-            result = init_response["result"]
-            
-            # Check protocol version
-            if result.get("protocolVersion") == "2025-11-25":
-                print("✓ Protocol version correct")
-            else:
-                print("✗ Protocol version incorrect")
-                
-            # Check capabilities
-            if "capabilities" in result:
-                caps = result["capabilities"]
-                if "resources" in caps and "tools" in caps and "prompts" in caps:
-                    print("✓ Capabilities structure correct")
-                else:
-                    print("✗ Capabilities structure incorrect")
-            else:
-                print("✗ Capabilities missing")
-                
-            # Check server info
-            if "serverInfo" in result:
-                info = result["serverInfo"]
-                if "name" in info and "version" in info:
-                    print("✓ Server info correct")
-                else:
-                    print("✗ Server info incorrect")
-            else:
-                print("✗ Server info missing")
-                
-            return True
-        else:
-            print("✗ Initialize response invalid")
-            return False
-            
-    def test_jsonrpc_message_handling(self):
-        """Test JSON-RPC message handling with GLib.Variant"""
-        print("\n=== Testing JSON-RPC Message Handling ===")
-        
-        # Test valid JSON-RPC request
-        print("\nTesting valid JSON-RPC request...")
-        ping_response = self.send_jsonrpc_request("ping", {}, id=2)
-        if ping_response and "result" in ping_response and ping_response.get("id") == 2:
-            print("✓ Valid JSON-RPC request handled correctly")
-        else:
-            print("✗ Valid JSON-RPC request failed")
-            
-        # Test request with complex params
-        print("\nTesting request with complex parameters...")
-        complex_params = {
-            "nested": {
-                "array": [1, 2, 3],
-                "object": {"key": "value"},
-                "boolean": True,
-                "number": 42.5,
-                "null": None
-            }
-        }
-        complex_response = self.send_jsonrpc_request("ping", complex_params, id=3)
-        if complex_response and "result" in complex_response:
-            print("✓ Complex parameters handled correctly")
-        else:
-            print("✗ Complex parameters failed")
-            
-        # Test invalid method
-        print("\nTesting invalid method...")
-        invalid_response = self.send_jsonrpc_request("invalid_method", {}, id=4)
-        if invalid_response and "error" in invalid_response:
-            print("✓ Invalid method handled correctly")
-        else:
-            print("✗ Invalid method not handled properly")
-            
-    def test_error_handling(self):
-        """Test error handling throughout the system"""
-        print("\n=== Testing Error Handling ===")
-        
-        # Test malformed JSON (this would need to be sent at a lower level)
-        print("\nTesting error responses...")
-        
-        # Test missing required parameters
-        print("\nTesting missing required parameters...")
-        # This would depend on specific method implementations
-        
-        # Test invalid parameter types
-        print("\nTesting invalid parameter types...")
-        # This would depend on specific method implementations
-        
-        print("✓ Error handling tests completed")
-        
-    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_functional.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)
-        
-        # Run functional tests
-        init_success = tester.test_server_initialization()
-        
-        if init_success:
-            tester.test_jsonrpc_message_handling()
-            tester.test_error_handling()
-            
-            print("\n✓ All functional tests completed successfully")
-        else:
-            print("\n✗ Functional tests failed - initialization error")
-            
-    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()

+ 0 - 160
test_mcp_with_headers.py

@@ -1,160 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test MCP server connection with proper Content-Length headers
-"""
-
-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=True,
-            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)  # Give server time to start
-        
-    def _read_stderr(self):
-        """Read stderr output from server"""
-        while self.server_process and self.server_process.poll() is None:
-            line = self.server_process.stderr.readline()
-            if line:
-                print(f"SERVER STDERR: {line.strip()}")
-                
-    def _read_stdout(self):
-        """Read stdout output from server without Content-Length parsing"""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                line = self.server_process.stdout.readline()
-                if not line:
-                    break
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = line.strip()
-                    if message:
-                        parsed = json.loads(message)
-                        self.message_queue.put(parsed)
-                        print(f"SERVER MESSAGE: {json.dumps(parsed, indent=2)}")
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "params": {}
-        }
-        if params:
-            request["params"] = params
-            
-        request_str = json.dumps(request)
-        message = f"{request_str}\n"
-        print(f"SENDING: {repr(message)}")
-        
-        self.server_process.stdin.write(message)
-        self.server_process.stdin.flush()
-        
-        # Wait for response with timeout
-        try:
-            response = self.message_queue.get(timeout=5)
-            return response
-        except queue.Empty:
-            print("TIMEOUT: No response received")
-            return None
-            
-    def test_simple_ping(self):
-        """Test simple ping"""
-        print("\n=== Testing Simple Ping ===")
-        
-        # Try a ping
-        print("\nSending ping request...")
-        ping_response = self.send_jsonrpc_request("ping", id=1)
-        if ping_response:
-            print(f"Ping response: {json.dumps(ping_response, indent=2)}")
-            print("✓ Ping successful")
-            return True
-        else:
-            print("✗ Ping failed")
-            return False
-        
-    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_mcp_with_headers.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()
-        
-        # Wait a bit for server to fully start
-        time.sleep(2)
-        
-        # Test simple ping
-        success = tester.test_simple_ping()
-        
-        if success:
-            print("\n✓ Server test completed successfully")
-        else:
-            print("\n✗ Server test failed")
-            
-    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()

+ 0 - 272
test_message_sizes.py

@@ -1,272 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test MCP server with various message sizes and edge cases
-"""
-
-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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "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_message_sizes(self):
-        """Test various message sizes"""
-        print("\n=== Testing Various Message Sizes ===")
-        
-        # Test small message
-        print("\nTesting small message...")
-        small_response = self.send_jsonrpc_request("ping", id=1)
-        if small_response:
-            print("✓ Small message successful")
-        else:
-            print("✗ Small message failed")
-            
-        # Test medium message
-        print("\nTesting medium message...")
-        medium_params = {
-            "data": "x" * 1000,  # 1KB of data
-            "metadata": {
-                "timestamp": "2025-12-12T03:29:00Z",
-                "source": "test-client",
-                "version": "1.0.0"
-            }
-        }
-        medium_response = self.send_jsonrpc_request("ping", medium_params, id=2)
-        if medium_response:
-            print("✓ Medium message successful")
-        else:
-            print("✗ Medium message failed")
-            
-        # Test large message
-        print("\nTesting large message...")
-        large_params = {
-            "data": "x" * 10000,  # 10KB of data
-            "metadata": {
-                "timestamp": "2025-12-12T03:29:00Z",
-                "source": "test-client",
-                "version": "1.0.0",
-                "extra": "y" * 5000
-            }
-        }
-        large_response = self.send_jsonrpc_request("ping", large_params, id=3)
-        if large_response:
-            print("✓ Large message successful")
-        else:
-            print("✗ Large message failed")
-            
-        # Test very large message
-        print("\nTesting very large message...")
-        very_large_params = {
-            "data": "x" * 50000,  # 50KB of data
-            "metadata": {
-                "timestamp": "2025-12-12T03:29:00Z",
-                "source": "test-client",
-                "version": "1.0.0",
-                "extra": "y" * 25000
-            }
-        }
-        very_large_response = self.send_jsonrpc_request("ping", very_large_params, id=4)
-        if very_large_response:
-            print("✓ Very large message successful")
-        else:
-            print("✗ Very large message failed")
-            
-    def test_edge_cases(self):
-        """Test edge cases"""
-        print("\n=== Testing Edge Cases ===")
-        
-        # Test empty params
-        print("\nTesting empty params...")
-        empty_response = self.send_jsonrpc_request("ping", {}, id=5)
-        if empty_response:
-            print("✓ Empty params successful")
-        else:
-            print("✗ Empty params failed")
-            
-        # Test null params
-        print("\nTesting null params...")
-        null_response = self.send_jsonrpc_request("ping", None, id=6)
-        if null_response:
-            print("✓ Null params successful")
-        else:
-            print("✗ Null params failed")
-            
-        # Test special characters
-        print("\nTesting special characters...")
-        special_params = {
-            "data": "Special chars: \n\t\r\"'\\",
-            "unicode": "Unicode: ñáéíóú 中文 🚀",
-            "emoji": "🎉👍💻🔧"
-        }
-        special_response = self.send_jsonrpc_request("ping", special_params, id=7)
-        if special_response:
-            print("✓ Special characters successful")
-        else:
-            print("✗ Special characters failed")
-            
-    def test_concurrent_requests(self):
-        """Test concurrent requests"""
-        print("\n=== Testing Concurrent Requests ===")
-        
-        def send_request(request_id):
-            params = {"request_id": request_id, "data": f"concurrent_{request_id}"}
-            return self.send_jsonrpc_request("ping", params, request_id)
-        
-        # Send multiple requests quickly
-        print("\nSending 10 concurrent requests...")
-        responses = []
-        for i in range(10):
-            response = send_request(10 + i)
-            responses.append(response)
-            
-        successful = sum(1 for r in responses if r is not None)
-        print(f"✓ {successful}/10 concurrent requests successful")
-        
-    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_message_sizes.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
-        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")
-            
-            # Run tests
-            tester.test_message_sizes()
-            tester.test_edge_cases()
-            tester.test_concurrent_requests()
-            
-            print("\n✓ All message size 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()

+ 0 - 89
test_pagination.py

@@ -1,89 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test script to verify pagination implementation in MCP prompts.
-"""
-
-import json
-import subprocess
-import sys
-import os
-
-def run_test(test_name, input_data, expected_keys):
-    """Run a test case and verify the response."""
-    print(f"Running test: {test_name}")
-    
-    # Create a temporary input file
-    input_file = "test_input.json"
-    with open(input_file, "w") as f:
-        json.dump(input_data, f)
-    
-    try:
-        # Run the server with the test input
-        # Note: This is a simplified test - in a real scenario, we'd need to
-        # set up a proper MCP server connection
-        result = subprocess.run(
-            ["cat", input_file],
-            capture_output=True,
-            text=True
-        )
-        
-        # For now, just verify the input format is correct
-        print(f"  Input: {json.dumps(input_data, indent=2)}")
-        print(f"  Expected keys: {expected_keys}")
-        print(f"  Test passed: Input format is correct")
-        return True
-        
-    except Exception as e:
-        print(f"  Error: {e}")
-        return False
-    finally:
-        # Clean up
-        if os.path.exists(input_file):
-            os.remove(input_file)
-
-def main():
-    """Run pagination tests."""
-    print("Testing MCP Prompts Pagination Implementation")
-    print("=" * 50)
-    
-    # Test 1: Request without cursor (first page)
-    test1_input = {
-        "jsonrpc": "2.0",
-        "id": 1,
-        "method": "prompts/list",
-        "params": {}
-    }
-    run_test("First page request", test1_input, ["prompts"])
-    
-    # Test 2: Request with cursor (subsequent page)
-    test2_input = {
-        "jsonrpc": "2.0",
-        "id": 2,
-        "method": "prompts/list",
-        "params": {
-            "cursor": "1"
-        }
-    }
-    run_test("Second page request", test2_input, ["prompts"])
-    
-    # Test 3: Request with invalid cursor (should default to first page)
-    test3_input = {
-        "jsonrpc": "2.0",
-        "id": 3,
-        "method": "prompts/list",
-        "params": {
-            "cursor": "invalid"
-        }
-    }
-    run_test("Invalid cursor request", test3_input, ["prompts"])
-    
-    print("\n" + "=" * 50)
-    print("All pagination tests completed!")
-    print("\nImplementation Summary:")
-    print("- Added cursor parameter support to prompts/list")
-    print("- Implemented pagination with 10 prompts per page")
-    print("- Added nextCursor field when more prompts are available")
-    print("- Return null for nextCursor when all prompts have been returned")
-
-if __name__ == "__main__":
-    main()

+ 0 - 179
test_prompt_validation.py

@@ -1,179 +0,0 @@
-#!/usr/bin/env python3
-"""
-Test script to reproduce the prompt validation error
-"""
-
-import subprocess
-import json
-import sys
-import os
-
-def test_server_prompt():
-    """Test the MCP server prompt functionality"""
-    if len(sys.argv) != 2:
-        print("Usage: python3 test_prompt_validation.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)
-    
-    print(f"Testing server: {server_path}")
-    
-    # Start the server process
-    process = subprocess.Popen(
-        [server_path],
-        stdin=subprocess.PIPE,
-        stdout=subprocess.PIPE,
-        stderr=subprocess.PIPE,
-        text=False
-    )
-    
-    try:
-        # Send initialize request
-        init_request = {
-            "jsonrpc": "2.0",
-            "method": "initialize",
-            "id": 0,
-            "params": {
-                "protocolVersion": "2025-11-25",
-                "capabilities": {},
-                "clientInfo": {
-                    "name": "test-client",
-                    "version": "1.0.0"
-                }
-            }
-        }
-        
-        request_str = json.dumps(init_request)
-        message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
-        process.stdin.write(message.encode())
-        process.stdin.flush()
-        
-        # Read response
-        response_line = process.stdout.readline()
-        if response_line.startswith(b"Content-Length:"):
-            content_length = int(response_line.decode().split(":")[1].strip())
-            process.stdout.readline()  # Empty line
-            response_data = process.stdout.read(content_length)
-            response = json.loads(response_data.decode())
-            
-            if "result" in response:
-                print("✓ Server initialized successfully")
-            else:
-                print("✗ Failed to initialize server")
-                print(f"Response: {response}")
-                return False
-        
-        # List prompts
-        list_request = {
-            "jsonrpc": "2.0",
-            "method": "prompts/list",
-            "id": 1,
-            "params": {}
-        }
-        
-        request_str = json.dumps(list_request)
-        message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
-        process.stdin.write(message.encode())
-        process.stdin.flush()
-        
-        # Read response
-        response_line = process.stdout.readline()
-        if response_line.startswith(b"Content-Length:"):
-            content_length = int(response_line.decode().split(":")[1].strip())
-            process.stdout.readline()  # Empty line
-            response_data = process.stdout.read(content_length)
-            response = json.loads(response_data.decode())
-            
-            if "result" in response and "prompts" in response["result"]:
-                prompts = response["result"]["prompts"]
-                print(f"✓ Found {len(prompts)} prompts")
-                
-                if prompts:
-                    # Get the first prompt
-                    prompt_name = prompts[0]["name"]
-                    print(f"Getting prompt: {prompt_name}")
-                    
-                    get_request = {
-                        "jsonrpc": "2.0",
-                        "method": "prompts/get",
-                        "id": 2,
-                        "params": {
-                            "name": prompt_name
-                        }
-                    }
-                    
-                    request_str = json.dumps(get_request)
-                    message = f"Content-Length: {len(request_str)}\r\n\r\n{request_str}"
-                    process.stdin.write(message.encode())
-                    process.stdin.flush()
-                    
-                    # Read response
-                    response_line = process.stdout.readline()
-                    if response_line.startswith(b"Content-Length:"):
-                        content_length = int(response_line.decode().split(":")[1].strip())
-                        process.stdout.readline()  # Empty line
-                        response_data = process.stdout.read(content_length)
-                        response = json.loads(response_data.decode())
-                        
-                        if "result" in response:
-                            print("✓ Prompt retrieved successfully")
-                            result = response["result"]
-                            
-                            # Validate the structure
-                            if "messages" in result:
-                                messages = result["messages"]
-                                print(f"✓ Found {len(messages)} messages")
-                                
-                                for i, message in enumerate(messages):
-                                    print(f"\nMessage {i}:")
-                                    print(f"  Role: {message.get('role')}")
-                                    
-                                    if "content" in message:
-                                        content = message["content"]
-                                        content_type = content.get("type")
-                                        print(f"  Content type: {content_type}")
-                                        print(f"  Content: {json.dumps(content, indent=2)}")
-                                        
-                                        # Check for validation issues
-                                        if content_type == "text":
-                                            if "text" not in content:
-                                                print("  ✗ Missing 'text' field for text content")
-                                        elif content_type in ["image", "audio"]:
-                                            if "data" not in content:
-                                                print(f"  ✗ Missing 'data' field for {content_type} content")
-                                            if "mimeType" not in content:
-                                                print(f"  ✗ Missing 'mimeType' field for {content_type} content")
-                                        elif content_type == "resource_link":
-                                            if "name" not in content:
-                                                print("  ✗ Missing 'name' field for resource_link content")
-                                            if "uri" not in content:
-                                                print("  ✗ Missing 'uri' field for resource_link content")
-                                        elif content_type == "resource":
-                                            if "resource" not in content:
-                                                print("  ✗ Missing 'resource' field for resource content")
-                            else:
-                                print("✗ No 'messages' field in response")
-                        else:
-                            print("✗ No 'result' field in response")
-                            if "error" in response:
-                                print(f"ERROR: {json.dumps(response['error'], indent=2)}")
-                                return False
-                else:
-                    print("✗ No prompts found")
-            else:
-                print("✗ Failed to list prompts")
-                print(f"Response: {response}")
-                return False
-        
-        return True
-        
-    finally:
-        process.terminate()
-        process.wait()
-
-if __name__ == "__main__":
-    success = test_server_prompt()
-    sys.exit(0 if success else 1)

+ 0 - 343
test_resources.py

@@ -1,343 +0,0 @@
-#!/usr/bin/env python3
-"""
-Resource handling testing for MCP server
-"""
-
-import subprocess
-import time
-import json
-import sys
-import os
-import threading
-import queue
-import tempfile
-import shutil
-
-class MCPServerTester:
-    def __init__(self, server_path):
-        self.server_path = server_path
-        self.server_process = None
-        self.message_queue = queue.Queue()
-        self.test_dir = None
-        
-    def start_server(self):
-        """Start MCP server process"""
-        # Create a temporary directory for testing
-        self.test_dir = tempfile.mkdtemp(prefix="mcp_test_")
-        
-        # Create some test files
-        with open(os.path.join(self.test_dir, "test.txt"), "w") as f:
-            f.write("Hello, World!")
-        with open(os.path.join(self.test_dir, "test.json"), "w") as f:
-            json.dump({"message": "test", "value": 42}, f)
-        with open(os.path.join(self.test_dir, "binary.dat"), "wb") as f:
-            f.write(b"\x00\x01\x02\x03\x04\x05")
-            
-        print(f"Starting MCP server: {self.server_path} with test directory: {self.test_dir}")
-        self.server_process = subprocess.Popen(
-            [self.server_path, self.test_dir],
-            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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "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_resource_registration_and_discovery(self):
-        """Test resource registration and discovery"""
-        print("\n=== Testing Resource Registration and Discovery ===")
-        
-        # Test resources/list
-        print("\nTesting resources/list...")
-        list_response = self.send_jsonrpc_request("resources/list", id=1)
-        
-        if list_response and "result" in list_response:
-            result = list_response["result"]
-            if "resources" in result:
-                resources = result["resources"]
-                print(f"✓ Found {len(resources)} resources")
-                
-                # Check if our test files are listed
-                resource_uris = [r.get("uri", "") for r in resources]
-                test_files = ["test.txt", "test.json", "binary.dat"]
-                
-                for filename in test_files:
-                    found = any(filename in uri for uri in resource_uris)
-                    if found:
-                        print(f"✓ Found {filename} in resource list")
-                    else:
-                        print(f"✗ Missing {filename} in resource list")
-                        
-                return True
-            else:
-                print("✗ No resources field in response")
-                return False
-        else:
-            print("✗ Failed to list resources")
-            return False
-            
-    def test_resource_content_retrieval(self):
-        """Test resource content retrieval"""
-        print("\n=== Testing Resource Content Retrieval ===")
-        
-        # Test reading text file
-        print("\nTesting text file read...")
-        txt_uri = f"file://{os.path.join(self.test_dir, 'test.txt')}"
-        txt_response = self.send_jsonrpc_request("resources/read", {"uri": txt_uri}, id=2)
-        
-        if txt_response and "result" in txt_response:
-            result = txt_response["result"]
-            if "contents" in result and len(result["contents"]) > 0:
-                content = result["contents"][0]
-                if content.get("text") == "Hello, World!":
-                    print("✓ Text file content correct")
-                else:
-                    print(f"✗ Text file content incorrect: {content.get('text')}")
-            else:
-                print("✗ No content in text file response")
-        else:
-            print("✗ Failed to read text file")
-            
-        # Test reading JSON file
-        print("\nTesting JSON file read...")
-        json_uri = f"file://{os.path.join(self.test_dir, 'test.json')}"
-        json_response = self.send_jsonrpc_request("resources/read", {"uri": json_uri}, id=3)
-        
-        if json_response and "result" in json_response:
-            result = json_response["result"]
-            if "contents" in result and len(result["contents"]) > 0:
-                content = result["contents"][0]
-                try:
-                    json_data = json.loads(content.get("text", "{}"))
-                    if json_data.get("message") == "test" and json_data.get("value") == 42:
-                        print("✓ JSON file content correct")
-                    else:
-                        print(f"✗ JSON file content incorrect: {json_data}")
-                except json.JSONDecodeError:
-                    print("✗ JSON file content is not valid JSON")
-            else:
-                print("✗ No content in JSON file response")
-        else:
-            print("✗ Failed to read JSON file")
-            
-        # Test reading binary file
-        print("\nTesting binary file read...")
-        bin_uri = f"file://{os.path.join(self.test_dir, 'binary.dat')}"
-        bin_response = self.send_jsonrpc_request("resources/read", {"uri": bin_uri}, id=4)
-        
-        if bin_response and "result" in bin_response:
-            result = bin_response["result"]
-            if "contents" in result and len(result["contents"]) > 0:
-                content = result["contents"][0]
-                if "blob" in content:
-                    print("✓ Binary file read as blob")
-                else:
-                    print("✗ Binary file not read as blob")
-            else:
-                print("✗ No content in binary file response")
-        else:
-            print("✗ Failed to read binary file")
-            
-    def test_resource_subscription_and_notifications(self):
-        """Test resource subscription and notifications"""
-        print("\n=== Testing Resource Subscription and Notifications ===")
-        
-        # Test subscription
-        print("\nTesting resource subscription...")
-        txt_uri = f"file://{os.path.join(self.test_dir, 'test.txt')}"
-        sub_response = self.send_jsonrpc_request("resources/subscribe", {"uri": txt_uri}, id=5)
-        
-        if sub_response and "result" in sub_response:
-            print("✓ Resource subscription successful")
-            
-            # Test unsubscription
-            print("\nTesting resource unsubscription...")
-            unsub_response = self.send_jsonrpc_request("resources/unsubscribe", {"uri": txt_uri}, id=6)
-            
-            if unsub_response and "result" in unsub_response:
-                print("✓ Resource unsubscription successful")
-            else:
-                print("✗ Resource unsubscription failed")
-        else:
-            print("✗ Resource subscription failed")
-            
-    def test_resource_templates(self):
-        """Test resource template functionality"""
-        print("\n=== Testing Resource Templates ===")
-        
-        # Test templates/list
-        print("\nTesting resources/templates/list...")
-        templates_response = self.send_jsonrpc_request("resources/templates/list", id=7)
-        
-        if templates_response and "result" in templates_response:
-            result = templates_response["result"]
-            if "templates" in result:
-                templates = result["templates"]
-                print(f"✓ Found {len(templates)} templates")
-                return True
-            else:
-                print("✗ No templates field in response")
-                return False
-        else:
-            print("✗ Failed to list templates")
-            return False
-            
-    def test_error_handling(self):
-        """Test error handling for resources"""
-        print("\n=== Testing Resource Error Handling ===")
-        
-        # Test reading non-existent file
-        print("\nTesting non-existent file...")
-        fake_uri = "file:///non/existent/file.txt"
-        error_response = self.send_jsonrpc_request("resources/read", {"uri": fake_uri}, id=8)
-        
-        if error_response and "error" in error_response:
-            print("✓ Non-existent file handled correctly")
-        else:
-            print("✗ Non-existent file not handled properly")
-            
-        # Test missing URI parameter
-        print("\nTesting missing URI parameter...")
-        missing_response = self.send_jsonrpc_request("resources/read", {}, id=9)
-        
-        if missing_response and "error" in missing_response:
-            print("✓ Missing URI parameter handled correctly")
-        else:
-            print("✗ Missing URI parameter not handled properly")
-            
-    def cleanup(self):
-        """Clean up test environment"""
-        if self.test_dir and os.path.exists(self.test_dir):
-            shutil.rmtree(self.test_dir)
-            
-    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_resources.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
-        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")
-            
-            # Run resource tests
-            tester.test_resource_registration_and_discovery()
-            tester.test_resource_content_retrieval()
-            tester.test_resource_subscription_and_notifications()
-            tester.test_resource_templates()
-            tester.test_error_handling()
-            
-            print("\n✓ All resource 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()
-        tester.cleanup()
-
-if __name__ == "__main__":
-    main()

+ 0 - 361
test_resources_fixed.py

@@ -1,361 +0,0 @@
-#!/usr/bin/env python3
-"""
-Resource handling testing for MCP server - fixed version
-"""
-
-import subprocess
-import time
-import json
-import sys
-import os
-import threading
-import queue
-import tempfile
-import shutil
-
-class MCPServerTester:
-    def __init__(self, server_path):
-        self.server_path = server_path
-        self.server_process = None
-        self.message_queue = queue.Queue()
-        self.test_dir = None
-        
-    def start_server(self):
-        """Start MCP server process"""
-        # Create a temporary directory for testing
-        self.test_dir = tempfile.mkdtemp(prefix="mcp_test_")
-        
-        # Create some test files
-        with open(os.path.join(self.test_dir, "test.txt"), "w") as f:
-            f.write("Hello, World!")
-        with open(os.path.join(self.test_dir, "test.json"), "w") as f:
-            json.dump({"message": "test", "value": 42}, f)
-        with open(os.path.join(self.test_dir, "binary.dat"), "wb") as f:
-            f.write(b"\x00\x01\x02\x03\x04\x05")
-            
-        print(f"Starting MCP server: {self.server_path} with test directory: {self.test_dir}")
-        self.server_process = subprocess.Popen(
-            [self.server_path, self.test_dir],
-            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 without Content-Length parsing"""
-        buffer = b""
-        while self.server_process and self.server_process.poll() is None:
-            try:
-                data = self.server_process.stdout.readline()
-                if not data:
-                    break
-                buffer += data
-                
-                # Try to parse JSON directly from each line
-                try:
-                    message = buffer.strip()
-                    if message:
-                        parsed = json.loads(message.decode())
-                        self.message_queue.put(parsed)
-                        buffer = b""
-                except json.JSONDecodeError:
-                    # If not valid JSON yet, continue reading
-                    continue
-            except:
-                break
-                        
-    def send_jsonrpc_request(self, method, params=None, id=1):
-        """Send a JSON-RPC request without Content-Length header"""
-        request = {
-            "jsonrpc": "2.0",
-            "method": method,
-            "id": id,
-            "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_resource_registration_and_discovery(self):
-        """Test resource registration and discovery"""
-        print("\n=== Testing Resource Registration and Discovery ===")
-        
-        # Test resources/list
-        print("\nTesting resources/list...")
-        list_response = self.send_jsonrpc_request("resources/list", id=1)
-        
-        if list_response and "result" in list_response:
-            result = list_response["result"]
-            if "resources" in result:
-                resources = result["resources"]
-                print(f"✓ Found {len(resources)} resources")
-                
-                # Check if our test files are listed
-                resource_uris = [r.get("uri", "") for r in resources]
-                test_files = ["test.txt", "test.json", "binary.dat"]
-                
-                for filename in test_files:
-                    found = any(filename in uri for uri in resource_uris)
-                    if found:
-                        print(f"✓ Found {filename} in resource list")
-                    else:
-                        print(f"✗ Missing {filename} in resource list")
-                        
-                return True
-            else:
-                print("✗ No resources field in response")
-                return False
-        else:
-            print("✗ Failed to list resources")
-            return False
-            
-    def test_resource_content_retrieval(self):
-        """Test resource content retrieval"""
-        print("\n=== Testing Resource Content Retrieval ===")
-        
-        # Test reading text file
-        print("\nTesting text file read...")
-        txt_uri = f"file://{os.path.join(self.test_dir, 'test.txt')}"
-        txt_response = self.send_jsonrpc_request("resources/read", {"uri": txt_uri}, id=2)
-        
-        if txt_response and "result" in txt_response:
-            result = txt_response["result"]
-            # Handle both response formats
-            if "text" in result:
-                # Direct text format
-                if result["text"] == "Hello, World!":
-                    print("✓ Text file content correct")
-                else:
-                    print(f"✗ Text file content incorrect: {result.get('text')}")
-            elif "contents" in result and len(result["contents"]) > 0:
-                # Contents array format
-                content = result["contents"][0]
-                if content.get("text") == "Hello, World!":
-                    print("✓ Text file content correct")
-                else:
-                    print(f"✗ Text file content incorrect: {content.get('text')}")
-            else:
-                print("✗ No content in text file response")
-        else:
-            print("✗ Failed to read text file")
-            
-        # Test reading JSON file
-        print("\nTesting JSON file read...")
-        json_uri = f"file://{os.path.join(self.test_dir, 'test.json')}"
-        json_response = self.send_jsonrpc_request("resources/read", {"uri": json_uri}, id=3)
-        
-        if json_response and "result" in json_response:
-            result = json_response["result"]
-            if "text" in result:
-                # Direct text format
-                try:
-                    json_data = json.loads(result["text"])
-                    if json_data.get("message") == "test" and json_data.get("value") == 42:
-                        print("✓ JSON file content correct")
-                    else:
-                        print(f"✗ JSON file content incorrect: {json_data}")
-                except json.JSONDecodeError:
-                    print("✗ JSON file content is not valid JSON")
-            elif "contents" in result and len(result["contents"]) > 0:
-                # Contents array format
-                content = result["contents"][0]
-                if content.get("text"):
-                    try:
-                        json_data = json.loads(content.get("text"))
-                        if json_data.get("message") == "test" and json_data.get("value") == 42:
-                            print("✓ JSON file content correct")
-                        else:
-                            print(f"✗ JSON file content incorrect: {json_data}")
-                    except json.JSONDecodeError:
-                        print("✗ JSON file content is not valid JSON")
-                else:
-                    print("✗ JSON file content not in text format")
-            else:
-                print("✗ No content in JSON file response")
-        else:
-            print("✗ Failed to read JSON file")
-            
-        # Test reading binary file
-        print("\nTesting binary file read...")
-        bin_uri = f"file://{os.path.join(self.test_dir, 'binary.dat')}"
-        bin_response = self.send_jsonrpc_request("resources/read", {"uri": bin_uri}, id=4)
-        
-        if bin_response and "result" in bin_response:
-            result = bin_response["result"]
-            if "blob" in result:
-                print("✓ Binary file read as blob")
-            else:
-                print("✗ Binary file not read as blob")
-        else:
-            print("✗ Failed to read binary file")
-            
-    def test_resource_subscription_and_notifications(self):
-        """Test resource subscription and notifications"""
-        print("\n=== Testing Resource Subscription and Notifications ===")
-        
-        # Test subscription
-        print("\nTesting resource subscription...")
-        txt_uri = f"file://{os.path.join(self.test_dir, 'test.txt')}"
-        sub_response = self.send_jsonrpc_request("resources/subscribe", {"uri": txt_uri}, id=5)
-        
-        if sub_response and "result" in sub_response:
-            print("✓ Resource subscription successful")
-            
-            # Test unsubscription
-            print("\nTesting resource unsubscription...")
-            unsub_response = self.send_jsonrpc_request("resources/unsubscribe", {"uri": txt_uri}, id=6)
-            
-            if unsub_response and "result" in unsub_response:
-                print("✓ Resource unsubscription successful")
-            else:
-                print("✗ Resource unsubscription failed")
-        else:
-            print("✗ Resource subscription failed")
-            
-    def test_resource_templates(self):
-        """Test resource template functionality"""
-        print("\n=== Testing Resource Templates ===")
-        
-        # Test templates/list
-        print("\nTesting resources/templates/list...")
-        templates_response = self.send_jsonrpc_request("resources/templates/list", id=7)
-        
-        if templates_response and "result" in templates_response:
-            result = templates_response["result"]
-            if "templates" in result:
-                templates = result["templates"]
-                print(f"✓ Found {len(templates)} templates")
-                return True
-            else:
-                print("✗ No templates field in response")
-                return False
-        else:
-            print("✗ Failed to list templates")
-            return False
-            
-    def test_error_handling(self):
-        """Test error handling for resources"""
-        print("\n=== Testing Resource Error Handling ===")
-        
-        # Test reading non-existent file
-        print("\nTesting non-existent file...")
-        fake_uri = "file:///non/existent/file.txt"
-        error_response = self.send_jsonrpc_request("resources/read", {"uri": fake_uri}, id=8)
-        
-        if error_response and "error" in error_response:
-            print("✓ Non-existent file handled correctly")
-        else:
-            print("✗ Non-existent file not handled properly")
-            
-        # Test missing URI parameter
-        print("\nTesting missing URI parameter...")
-        missing_response = self.send_jsonrpc_request("resources/read", {}, id=9)
-        
-        if missing_response and "error" in missing_response:
-            print("✓ Missing URI parameter handled correctly")
-        else:
-            print("✗ Missing URI parameter not handled properly")
-            
-    def cleanup(self):
-        """Clean up test environment"""
-        if self.test_dir and os.path.exists(self.test_dir):
-            shutil.rmtree(self.test_dir)
-            
-    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_resources_fixed.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
-        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")
-            
-            # Run resource tests
-            tester.test_resource_registration_and_discovery()
-            tester.test_resource_content_retrieval()
-            tester.test_resource_subscription_and_notifications()
-            tester.test_resource_templates()
-            tester.test_error_handling()
-            
-            print("\n✓ All resource 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()
-        tester.cleanup()
-
-if __name__ == "__main__":
-    main()

+ 0 - 23
test_with_strace.sh

@@ -1,23 +0,0 @@
-#!/bin/bash
-
-# Test with strace to see what's happening with file descriptors
-echo "Testing with strace..."
-
-strace -e trace=write,read,ioctl -f -o strace.log ./builddir/examples/minimal-server &
-SERVER_PID=$!
-
-# Give it a moment to start
-sleep 0.5
-
-# Send initialize request
-echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2025-11-25", "capabilities": {}, "clientInfo": {"name": "test-client", "version": "1.0.0"}}}' | nc -N localhost 0 2>/dev/null || echo "nc failed, trying direct pipe"
-
-# Wait a bit
-sleep 1
-
-# Kill the server
-kill $SERVER_PID 2>/dev/null
-
-# Show the strace log
-echo "=== Strace log ==="
-cat strace.log