Commit 0bf769b

mo khan <mo@mokhan.ca>
2025-08-17 04:00:47
refactor: all the things
1 parent dfce622
cmd/bash/main.go
@@ -66,7 +66,7 @@ func main() {
 	}
 
 	// Create and start the server
-	server, err := bash.NewBashServer(config)
+	server, err := bash.New(config)
 	if err != nil {
 		log.Fatalf("Failed to create bash server: %v", err)
 	}
cmd/gitlab/main.go
@@ -99,7 +99,7 @@ For more information, visit: https://github.com/xlgmokha/mcp
 		url = envURL
 	}
 
-	server, err := gitlab.NewServer(url, token)
+	server, err := gitlab.New(url, token)
 	if err != nil {
 		log.Fatalf("Failed to create GitLab server: %v", err)
 	}
cmd/packages/main.go
@@ -1,114 +0,0 @@
-package main
-
-import (
-	"context"
-	"flag"
-	"fmt"
-	"log"
-
-	"github.com/xlgmokha/mcp/pkg/packages"
-)
-
-func main() {
-	var showHelp bool
-	flag.BoolVar(&showHelp, "help", false, "Show help information")
-	flag.Parse()
-
-	if showHelp {
-		showHelpText()
-		return
-	}
-
-	server := packages.NewServer()
-	if err := server.Run(context.Background()); err != nil {
-		log.Fatalf("Server error: %v", err)
-	}
-}
-
-func showHelpText() {
-	fmt.Printf(`Package Manager MCP Server
-
-A Model Context Protocol server that provides package management tools for multiple 
-ecosystems including Rust (Cargo) and macOS (Homebrew).
-
-USAGE:
-    mcp-packages [OPTIONS]
-
-OPTIONS:
-    --help                Show this help message
-
-TOOLS PROVIDED:
-
-Cargo (Rust) Tools:
-    cargo_build          Build Rust projects with optional flags
-    cargo_run            Run Rust applications with arguments
-    cargo_test           Execute test suites with filtering
-    cargo_add            Add dependencies to Cargo.toml
-    cargo_update         Update dependencies to latest versions
-    cargo_check          Quick compile check without building
-    cargo_clippy         Run Rust linter with suggestions
-
-Homebrew Tools:
-    brew_install         Install packages or casks
-    brew_uninstall       Remove packages or casks
-    brew_search          Search for available packages
-    brew_update          Update Homebrew itself
-    brew_upgrade         Upgrade installed packages
-    brew_doctor          Check system health
-    brew_list            List installed packages
-
-Cross-Platform Tools:
-    check_vulnerabilities    Scan for security vulnerabilities
-    outdated_packages       Find packages needing updates
-    package_info            Get detailed package information
-
-EXAMPLES:
-
-Cargo Operations:
-    # Build a Rust project in release mode
-    {"name": "cargo_build", "arguments": {"directory": ".", "release": true}}
-    
-    # Run with arguments
-    {"name": "cargo_run", "arguments": {"args": ["--version"]}}
-    
-    # Add a dependency
-    {"name": "cargo_add", "arguments": {"packages": ["serde", "tokio"]}}
-
-Homebrew Operations:
-    # Install a package
-    {"name": "brew_install", "arguments": {"packages": ["git"]}}
-    
-    # Search for packages
-    {"name": "brew_search", "arguments": {"query": "nodejs"}}
-    
-    # Check system health
-    {"name": "brew_doctor", "arguments": {}}
-
-Security & Maintenance:
-    # Check for vulnerabilities in current directory
-    {"name": "check_vulnerabilities", "arguments": {"directory": "."}}
-    
-    # Find outdated packages
-    {"name": "outdated_packages", "arguments": {"package_manager": "cargo"}}
-
-INTEGRATION:
-Add to your Claude Code configuration (~/.claude.json):
-
-{
-  "mcpServers": {
-    "packages": {
-      "command": "mcp-packages"
-    }
-  }
-}
-
-ENVIRONMENT:
-The server auto-detects package managers based on project files:
-- Cargo.toml โ†’ Rust/Cargo tools
-- package.json โ†’ NPM tools  
-- go.mod โ†’ Go modules
-- Homebrew โ†’ System-wide macOS packages
-
-For support or issues, see: https://github.com/xlgmokha/mcp
-`)
-}
cmd/semantic/main.go
@@ -36,10 +36,13 @@ func main() {
 	setupLogging(*logLevel)
 
 	// Create and configure the semantic server
-	server := semantic.NewServer()
+	server, err := semantic.New()
+	if err != nil {
+		log.Fatalf("Failed to create semantic server: %v", err)
+	}
 
 	// Initialize project discovery
-	if err := initializeProject(server, *projectRoot, *configFile); err != nil {
+	if err := initializeProject(*projectRoot, *configFile); err != nil {
 		log.Fatalf("Failed to initialize project: %v", err)
 	}
 
@@ -50,9 +53,8 @@ func main() {
 	go func() {
 		<-sigChan
 		log.Println("Shutting down semantic server...")
-		if err := server.Shutdown(); err != nil {
-			log.Printf("Error during shutdown: %v", err)
-		}
+		// Note: Shutdown is no longer available in the new pattern
+		// LSP managers are handled internally
 		os.Exit(0)
 	}()
 
@@ -207,7 +209,7 @@ func setupLogging(level string) {
 	}
 }
 
-func initializeProject(server *semantic.Server, projectRoot, configFile string) error {
+func initializeProject(projectRoot, configFile string) error {
 	// This is a placeholder for project initialization
 	// In the full implementation, this would:
 	// 1. Load configuration from file if provided
cmd/signal/main.go
@@ -68,7 +68,7 @@ For more information, visit: https://github.com/xlgmokha/mcp
 		return
 	}
 
-	server, err := signal.NewServer(*signalPath)
+	server, err := signal.New(*signalPath)
 	if err != nil {
 		log.Fatalf("Failed to create Signal server: %v", err)
 	}
cmd/speech/main.go
@@ -19,7 +19,10 @@ func main() {
 		return
 	}
 
-	server := speech.NewServer()
+	server, err := speech.New()
+	if err != nil {
+		log.Fatalf("Failed to create speech server: %v", err)
+	}
 	if err := server.Run(context.Background()); err != nil {
 		log.Fatalf("Server error: %v", err)
 	}
docs/mcp-server-specifications.md
@@ -1,890 +0,0 @@
-# MCP Server Feature Specifications for Go Implementation
-
-This document provides detailed specifications for implementing MCP servers in Go based on analysis of the Python reference implementations. Each server must implement the Model Context Protocol using JSON-RPC 2.0 over stdio.
-
-## Core MCP Protocol Requirements
-
-### Base Protocol
-- **Transport**: JSON-RPC 2.0 over stdio (stdin/stdout)
-- **Connection**: Stateful client-server connection
-- **Initialization**: Capability negotiation during handshake
-- **Message Format**: All messages must follow JSON-RPC 2.0 specification
-
-### Required MCP Methods
-All servers must implement these base protocol methods:
-- `initialize` - Capability negotiation and server info
-- `initialized` - Notification that initialization is complete
-- `shutdown` - Graceful shutdown request
-- `exit` - Immediate termination notification
-
-### Server Capabilities
-Servers can implement any combination of:
-- **Tools** - Functions the AI model can execute
-- **Resources** - Context and data access
-- **Prompts** - Templated messages and workflows
-
-## 1. Git Server (`mcp-server-git`)
-
-### Dependencies
-- Git CLI or go-git library
-- File system access for repository operations
-
-### Tools
-
-#### `git_status`
-- **Description**: Shows the working tree status
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git status` or use go-git Status()
-- **Output**: TextContent with repository status
-
-#### `git_diff_unstaged`
-- **Description**: Shows changes in working directory not yet staged
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git diff` or use go-git diff functionality
-- **Output**: TextContent with unstaged changes
-
-#### `git_diff_staged`
-- **Description**: Shows changes that are staged for commit
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git diff --cached`
-- **Output**: TextContent with staged changes
-
-#### `git_diff`
-- **Description**: Shows differences between branches or commits
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "target": {"type": "string", "description": "Target branch or commit to compare with"}
-    },
-    "required": ["repo_path", "target"]
-  }
-  ```
-- **Implementation**: Execute `git diff <target>`
-- **Output**: TextContent with diff output
-
-#### `git_commit`
-- **Description**: Records changes to the repository
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "message": {"type": "string", "description": "Commit message"}
-    },
-    "required": ["repo_path", "message"]
-  }
-  ```
-- **Implementation**: Execute `git commit -m "message"`
-- **Output**: TextContent with commit confirmation and hash
-
-#### `git_add`
-- **Description**: Adds file contents to the staging area
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "files": {"type": "array", "items": {"type": "string"}, "description": "Array of file paths to stage"}
-    },
-    "required": ["repo_path", "files"]
-  }
-  ```
-- **Implementation**: Execute `git add <files...>`
-- **Output**: TextContent with staging confirmation
-
-#### `git_reset`
-- **Description**: Unstages all staged changes
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git reset`
-- **Output**: TextContent with reset confirmation
-
-#### `git_log`
-- **Description**: Shows the commit logs
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "max_count": {"type": "integer", "description": "Maximum number of commits to show", "default": 10}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git log --max-count=<n>` or use go-git log iterator
-- **Output**: TextContent with formatted commit history
-
-#### `git_create_branch`
-- **Description**: Creates a new branch from an optional base branch
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "branch_name": {"type": "string", "description": "Name of the new branch"},
-      "base_branch": {"type": "string", "description": "Starting point for the new branch", "default": null}
-    },
-    "required": ["repo_path", "branch_name"]
-  }
-  ```
-- **Implementation**: Execute `git checkout -b <branch_name> [base_branch]`
-- **Output**: TextContent with branch creation confirmation
-
-#### `git_checkout`
-- **Description**: Switches branches
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "branch_name": {"type": "string", "description": "Name of branch to checkout"}
-    },
-    "required": ["repo_path", "branch_name"]
-  }
-  ```
-- **Implementation**: Execute `git checkout <branch_name>`
-- **Output**: TextContent with checkout confirmation
-
-#### `git_show`
-- **Description**: Shows the contents of a commit
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to Git repository"},
-      "revision": {"type": "string", "description": "The revision (commit hash, branch name, tag) to show"}
-    },
-    "required": ["repo_path", "revision"]
-  }
-  ```
-- **Implementation**: Execute `git show <revision>` or use go-git commit details
-- **Output**: TextContent with commit details and diff
-
-#### `git_init`
-- **Description**: Initialize a new Git repository
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "repo_path": {"type": "string", "description": "Path to directory to initialize git repo"}
-    },
-    "required": ["repo_path"]
-  }
-  ```
-- **Implementation**: Execute `git init` or use go-git PlainInit()
-- **Output**: TextContent with initialization confirmation
-
-### Configuration
-- Command line argument: `--repository <path>` to specify default repository
-- Can also detect repositories from MCP roots capability
-
-## 2. Filesystem Server (`mcp-server-filesystem`)
-
-### Dependencies
-- File system access with configurable permissions
-- Pattern matching for file search
-- Text diff generation
-
-### Security Model
-- **Sandboxing**: Only allow operations within specified directories
-- **Validation**: All paths must be validated against allowed directories
-- **Error Handling**: Graceful handling of permission errors
-
-### Tools
-
-#### `read_file`
-- **Description**: Read complete contents of a file
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "Absolute path to the file to read"}
-    },
-    "required": ["path"]
-  }
-  ```
-- **Implementation**: Read file with UTF-8 encoding, handle various encodings
-- **Output**: TextContent with file contents
-- **Security**: Validate path is within allowed directories
-
-#### `read_multiple_files`
-- **Description**: Read multiple files simultaneously
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "paths": {"type": "array", "items": {"type": "string"}, "description": "Array of file paths to read"}
-    },
-    "required": ["paths"]
-  }
-  ```
-- **Implementation**: Read multiple files, continue on individual failures
-- **Output**: TextContent with results for each file
-- **Security**: Validate all paths are within allowed directories
-
-#### `write_file`
-- **Description**: Create new file or overwrite existing
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "File location"},
-      "content": {"type": "string", "description": "File content"}
-    },
-    "required": ["path", "content"]
-  }
-  ```
-- **Implementation**: Write content to file with UTF-8 encoding
-- **Output**: TextContent with write confirmation
-- **Security**: Validate path is within allowed directories
-
-#### `edit_file`
-- **Description**: Make selective edits using pattern matching
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "File to edit"},
-      "edits": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "oldText": {"type": "string", "description": "Text to search for"},
-            "newText": {"type": "string", "description": "Text to replace with"}
-          },
-          "required": ["oldText", "newText"]
-        }
-      },
-      "dryRun": {"type": "boolean", "description": "Preview changes without applying", "default": false}
-    },
-    "required": ["path", "edits"]
-  }
-  ```
-- **Implementation**: 
-  - Line-based and multi-line content matching
-  - Whitespace normalization with indentation preservation
-  - Multiple simultaneous edits with correct positioning
-  - Git-style diff output
-- **Output**: TextContent with diff information or edit confirmation
-- **Security**: Validate path is within allowed directories
-
-#### `create_directory`
-- **Description**: Create new directory or ensure it exists
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "Directory path to create"}
-    },
-    "required": ["path"]
-  }
-  ```
-- **Implementation**: Create directory and parent directories if needed
-- **Output**: TextContent with creation confirmation
-- **Security**: Validate path is within allowed directories
-
-#### `list_directory`
-- **Description**: List directory contents with type indicators
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "Directory path to list"}
-    },
-    "required": ["path"]
-  }
-  ```
-- **Implementation**: List files and directories with [FILE] or [DIR] prefixes
-- **Output**: TextContent with directory listing
-- **Security**: Validate path is within allowed directories
-
-#### `move_file`
-- **Description**: Move or rename files and directories
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "source": {"type": "string", "description": "Source path"},
-      "destination": {"type": "string", "description": "Destination path"}
-    },
-    "required": ["source", "destination"]
-  }
-  ```
-- **Implementation**: Move/rename operation, fail if destination exists
-- **Output**: TextContent with move confirmation
-- **Security**: Validate both paths are within allowed directories
-
-#### `search_files`
-- **Description**: Recursively search for files/directories
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "Starting directory"},
-      "pattern": {"type": "string", "description": "Search pattern"},
-      "excludePatterns": {"type": "array", "items": {"type": "string"}, "description": "Exclude patterns", "default": []}
-    },
-    "required": ["path", "pattern"]
-  }
-  ```
-- **Implementation**: Case-insensitive recursive search with glob patterns
-- **Output**: TextContent with matching file paths
-- **Security**: Validate path is within allowed directories
-
-#### `get_file_info`
-- **Description**: Get detailed file/directory metadata
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "path": {"type": "string", "description": "File/directory path"}
-    },
-    "required": ["path"]
-  }
-  ```
-- **Implementation**: Return size, timestamps, permissions, type
-- **Output**: TextContent with file metadata
-- **Security**: Validate path is within allowed directories
-
-#### `list_allowed_directories`
-- **Description**: List all directories the server is allowed to access
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {},
-    "required": []
-  }
-  ```
-- **Implementation**: Return configured allowed directories
-- **Output**: TextContent with allowed directory list
-
-### Configuration
-- Command line arguments: List of allowed directories
-- Example: `mcp-server-filesystem /home/user/documents /home/user/projects`
-
-## 3. Fetch Server (`mcp-server-fetch`)
-
-### Dependencies
-- HTTP client for web requests
-- HTML to Markdown conversion
-- robots.txt parsing
-- URL validation and parsing
-
-### Security Considerations
-- **robots.txt Compliance**: Check robots.txt before autonomous fetching
-- **User Agent**: Different user agents for autonomous vs manual requests
-- **Proxy Support**: Optional proxy configuration
-- **Content Filtering**: Handle various content types appropriately
-
-### Tools
-
-#### `fetch`
-- **Description**: Fetches a URL from the internet and extracts its contents as markdown
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "url": {"type": "string", "format": "uri", "description": "URL to fetch"},
-      "max_length": {"type": "integer", "description": "Maximum number of characters to return", "default": 5000, "minimum": 1, "maximum": 999999},
-      "start_index": {"type": "integer", "description": "Start content from this character index", "default": 0, "minimum": 0},
-      "raw": {"type": "boolean", "description": "Get raw content without markdown conversion", "default": false}
-    },
-    "required": ["url"]
-  }
-  ```
-- **Implementation**:
-  - Check robots.txt compliance for autonomous requests
-  - Fetch URL with appropriate user agent
-  - Convert HTML to markdown unless raw requested
-  - Handle content truncation and pagination
-  - Support content type detection
-- **Output**: TextContent with fetched content
-- **Error Handling**: Detailed error messages for robots.txt violations, HTTP errors
-
-### Prompts
-
-#### `fetch`
-- **Description**: Fetch a URL and extract its contents as markdown
-- **Arguments**:
-  ```json
-  [
-    {
-      "name": "url",
-      "description": "URL to fetch",
-      "required": true
-    }
-  ]
-  ```
-- **Implementation**: Manual fetch without robots.txt restrictions
-
-### Configuration Options
-- `--custom-user-agent`: Custom User-Agent string
-- `--ignore-robots-txt`: Ignore robots.txt restrictions
-- `--proxy-url`: Proxy URL for requests
-
-## 4. Memory Server (`mcp-server-memory`)
-
-### Dependencies
-- JSON file storage for persistence
-- Graph data structure for entities and relations
-- Text search capabilities
-
-### Data Model
-
-#### Entity
-```json
-{
-  "name": "string (unique identifier)",
-  "entityType": "string (e.g., 'person', 'organization', 'event')",
-  "observations": ["array of strings"]
-}
-```
-
-#### Relation
-```json
-{
-  "from": "string (source entity name)",
-  "to": "string (target entity name)",
-  "relationType": "string (relationship type in active voice)"
-}
-```
-
-### Tools
-
-#### `create_entities`
-- **Description**: Create multiple new entities in the knowledge graph
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "entities": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "name": {"type": "string", "description": "Entity identifier"},
-            "entityType": {"type": "string", "description": "Type classification"},
-            "observations": {"type": "array", "items": {"type": "string"}, "description": "Associated observations"}
-          },
-          "required": ["name", "entityType", "observations"]
-        }
-      }
-    },
-    "required": ["entities"]
-  }
-  ```
-- **Implementation**: Add entities to graph, ignore duplicates by name
-- **Output**: TextContent with creation results
-
-#### `create_relations`
-- **Description**: Create multiple new relations between entities
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "relations": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "from": {"type": "string", "description": "Source entity name"},
-            "to": {"type": "string", "description": "Target entity name"},
-            "relationType": {"type": "string", "description": "Relationship type"}
-          },
-          "required": ["from", "to", "relationType"]
-        }
-      }
-    },
-    "required": ["relations"]
-  }
-  ```
-- **Implementation**: Add relations to graph, skip duplicates
-- **Output**: TextContent with creation results
-
-#### `add_observations`
-- **Description**: Add new observations to existing entities
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "observations": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "entityName": {"type": "string", "description": "Target entity"},
-            "contents": {"type": "array", "items": {"type": "string"}, "description": "New observations to add"}
-          },
-          "required": ["entityName", "contents"]
-        }
-      }
-    },
-    "required": ["observations"]
-  }
-  ```
-- **Implementation**: Add observations to existing entities
-- **Output**: TextContent with added observations per entity
-- **Error**: Fail if entity doesn't exist
-
-#### `delete_entities`
-- **Description**: Remove entities and their relations
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "entityNames": {"type": "array", "items": {"type": "string"}, "description": "Entity names to delete"}
-    },
-    "required": ["entityNames"]
-  }
-  ```
-- **Implementation**: Remove entities and cascade delete relations
-- **Output**: TextContent with deletion results
-
-#### `delete_observations`
-- **Description**: Remove specific observations from entities
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "deletions": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "entityName": {"type": "string", "description": "Target entity"},
-            "observations": {"type": "array", "items": {"type": "string"}, "description": "Observations to remove"}
-          },
-          "required": ["entityName", "observations"]
-        }
-      }
-    },
-    "required": ["deletions"]
-  }
-  ```
-- **Implementation**: Remove specific observations from entities
-- **Output**: TextContent with deletion results
-
-#### `delete_relations`
-- **Description**: Remove specific relations from the graph
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "relations": {
-        "type": "array",
-        "items": {
-          "type": "object",
-          "properties": {
-            "from": {"type": "string", "description": "Source entity name"},
-            "to": {"type": "string", "description": "Target entity name"},
-            "relationType": {"type": "string", "description": "Relationship type"}
-          },
-          "required": ["from", "to", "relationType"]
-        }
-      }
-    },
-    "required": ["relations"]
-  }
-  ```
-- **Implementation**: Remove specific relations
-- **Output**: TextContent with deletion results
-
-#### `read_graph`
-- **Description**: Read the entire knowledge graph
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {},
-    "required": []
-  }
-  ```
-- **Implementation**: Return complete graph structure
-- **Output**: TextContent with full graph data
-
-#### `search_nodes`
-- **Description**: Search for nodes based on query
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "query": {"type": "string", "description": "Search query"}
-    },
-    "required": ["query"]
-  }
-  ```
-- **Implementation**: Search entity names, types, and observation content
-- **Output**: TextContent with matching entities and relations
-
-#### `open_nodes`
-- **Description**: Retrieve specific nodes by name
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "names": {"type": "array", "items": {"type": "string"}, "description": "Entity names to retrieve"}
-    },
-    "required": ["names"]
-  }
-  ```
-- **Implementation**: Return requested entities and their relations
-- **Output**: TextContent with requested nodes
-
-### Configuration
-- Environment variable: `MEMORY_FILE_PATH` for custom storage location
-- Default: `memory.json` in server directory
-
-## 5. Sequential Thinking Server (`mcp-server-sequential-thinking`)
-
-### Dependencies
-- JSON file storage for session persistence
-- State management for thought sequences and branches
-- Thread-safe concurrent access controls
-
-### Configuration
-- Command line argument: `--session-file <path>` for persistent session storage
-
-### Tools
-
-#### `sequentialthinking`
-- **Description**: Dynamic problem-solving through structured thought sequences with session persistence
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "thought": {"type": "string", "description": "Your current thinking step"},
-      "nextThoughtNeeded": {"type": "boolean", "description": "Whether another thought step is needed"},
-      "thoughtNumber": {"type": "integer", "description": "Current thought number", "minimum": 1},
-      "totalThoughts": {"type": "integer", "description": "Estimated total thoughts needed", "minimum": 1},
-      "isRevision": {"type": "boolean", "description": "Whether this revises previous thinking"},
-      "revisesThought": {"type": "integer", "description": "Which thought is being reconsidered", "minimum": 1},
-      "branchFromThought": {"type": "integer", "description": "Branching point thought number", "minimum": 1},
-      "branchId": {"type": "string", "description": "Branch identifier"},
-      "needsMoreThoughts": {"type": "boolean", "description": "If more thoughts are needed"},
-      "sessionId": {"type": "string", "description": "Session ID for thought continuity (auto-generated if not provided)"}
-    },
-    "required": ["thought", "nextThoughtNeeded", "thoughtNumber", "totalThoughts"]
-  }
-  ```
-- **Implementation**:
-  - Track thought sequences with persistent sessions
-  - Support branching for alternative reasoning paths
-  - Support revision and refinement of previous thoughts
-  - Thread-safe concurrent session access
-  - JSON file persistence across server restarts
-- **Output**: TextContent with thought processing results and session context
-
-#### `get_session_history`
-- **Description**: Retrieve complete thought history for a thinking session
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "sessionId": {"type": "string", "description": "Session ID to get history for"}
-    },
-    "required": ["sessionId"]
-  }
-  ```
-- **Output**: JSON TextContent with complete session data and thoughts
-
-#### `list_sessions`
-- **Description**: List all active thinking sessions
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {}
-  }
-  ```
-- **Output**: JSON TextContent with array of session summaries
-
-#### `get_branch_history`
-- **Description**: Get thought history for a specific reasoning branch
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "branchId": {"type": "string", "description": "Branch ID to get history for"}
-    },
-    "required": ["branchId"]
-  }
-  ```
-- **Output**: JSON TextContent with complete branch data and thoughts
-
-#### `clear_session`
-- **Description**: Clear a thinking session and all its branches
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "sessionId": {"type": "string", "description": "Session ID to clear"}
-    },
-    "required": ["sessionId"]
-  }
-  ```
-- **Output**: JSON TextContent with confirmation message and details
-
-## 6. Time Server (`mcp-server-time`)
-
-### Dependencies
-- Timezone database (IANA timezones)
-- System timezone detection
-- Time parsing and formatting
-
-### Tools
-
-#### `get_current_time`
-- **Description**: Get current time in a specific timezone
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "timezone": {"type": "string", "description": "IANA timezone name (e.g., 'America/New_York', 'Europe/London')"}
-    },
-    "required": ["timezone"]
-  }
-  ```
-- **Implementation**: Get current time in specified timezone with DST information
-- **Output**: JSON TextContent with timezone, datetime (ISO format), is_dst
-
-#### `convert_time`
-- **Description**: Convert time between timezones
-- **Input Schema**:
-  ```json
-  {
-    "type": "object",
-    "properties": {
-      "source_timezone": {"type": "string", "description": "Source IANA timezone name"},
-      "time": {"type": "string", "description": "Time in 24-hour format (HH:MM)"},
-      "target_timezone": {"type": "string", "description": "Target IANA timezone name"}
-    },
-    "required": ["source_timezone", "time", "target_timezone"]
-  }
-  ```
-- **Implementation**: Convert time between timezones, calculate time difference
-- **Output**: JSON TextContent with source and target time information plus difference
-
-### Configuration
-- Command line argument: `--local-timezone` to override system timezone detection
-
-## Common Go Implementation Requirements
-
-### JSON-RPC 2.0 Implementation
-- Message framing over stdio
-- Request/response correlation with IDs
-- Notification handling (no response expected)
-- Error response formatting
-
-### Shared Types
-```go
-type TextContent struct {
-    Type string `json:"type"`
-    Text string `json:"text"`
-}
-
-type Tool struct {
-    Name        string      `json:"name"`
-    Description string      `json:"description"`
-    InputSchema interface{} `json:"inputSchema"`
-}
-
-type ServerInfo struct {
-    Name    string `json:"name"`
-    Version string `json:"version"`
-}
-```
-
-### Error Handling
-- Standard JSON-RPC error codes
-- Descriptive error messages
-- Graceful degradation
-- Input validation with clear error messages
-
-### Logging and Debugging
-- Structured logging support
-- Debug mode for development
-- Request/response tracing capability
-
-### Configuration
-- Command line argument parsing
-- Environment variable support
-- Configuration file support where appropriate
-- Sensible defaults
-
-### Testing Requirements
-- Unit tests for all tools
-- Integration tests with MCP clients
-- Error condition testing
-- Mock external dependencies where needed
-
-This specification provides the foundation for implementing full-featured MCP servers in Go with complete compatibility with the Python reference implementations.
\ No newline at end of file
pkg/bash/handlers.go
@@ -13,7 +13,7 @@ import (
 // Core Execution Tools
 
 // HandleBashExec handles the bash_exec tool
-func (bs *BashServer) HandleBashExec(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleBashExec(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	var options ExecutionOptions
 
 	// Parse command (required)
@@ -51,7 +51,7 @@ func (bs *BashServer) HandleBashExec(req mcp.CallToolRequest) (mcp.CallToolResul
 	}
 
 	// Execute command
-	result, err := bs.executeCommand(options)
+	result, err := bash.executeCommand(options)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to execute command: %v", err)), nil
 	}
@@ -68,16 +68,16 @@ func (bs *BashServer) HandleBashExec(req mcp.CallToolRequest) (mcp.CallToolResul
 }
 
 // HandleBashExecStream handles the bash_exec_stream tool
-func (bs *BashServer) HandleBashExecStream(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleBashExecStream(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	// For now, this is the same as regular execution
 	// TODO: Implement actual streaming in future version
-	return bs.HandleBashExec(req)
+	return bash.HandleBashExec(req)
 }
 
 // Documentation Tools
 
 // HandleManPage handles the man_page tool
-func (bs *BashServer) HandleManPage(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleManPage(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	command, ok := req.Arguments["command"].(string)
 	if !ok || command == "" {
 		return mcp.NewToolError("command parameter is required"), nil
@@ -94,7 +94,7 @@ func (bs *BashServer) HandleManPage(req mcp.CallToolRequest) (mcp.CallToolResult
 		CaptureStderr: true,
 	}
 
-	result, err := bs.executeCommand(options)
+	result, err := bash.executeCommand(options)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get man page: %v", err)), nil
 	}
@@ -110,7 +110,7 @@ func (bs *BashServer) HandleManPage(req mcp.CallToolRequest) (mcp.CallToolResult
 }
 
 // HandleWhichCommand handles the which_command tool
-func (bs *BashServer) HandleWhichCommand(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleWhichCommand(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	command, ok := req.Arguments["command"].(string)
 	if !ok || command == "" {
 		return mcp.NewToolError("command parameter is required"), nil
@@ -128,7 +128,7 @@ func (bs *BashServer) HandleWhichCommand(req mcp.CallToolRequest) (mcp.CallToolR
 		CaptureStderr: true,
 	}
 
-	result, err := bs.executeCommand(options)
+	result, err := bash.executeCommand(options)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to locate command: %v", err)), nil
 	}
@@ -144,7 +144,7 @@ func (bs *BashServer) HandleWhichCommand(req mcp.CallToolRequest) (mcp.CallToolR
 }
 
 // HandleCommandHelp handles the command_help tool
-func (bs *BashServer) HandleCommandHelp(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleCommandHelp(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	command, ok := req.Arguments["command"].(string)
 	if !ok || command == "" {
 		return mcp.NewToolError("command parameter is required"), nil
@@ -157,7 +157,7 @@ func (bs *BashServer) HandleCommandHelp(req mcp.CallToolRequest) (mcp.CallToolRe
 		CaptureStderr: true,
 	}
 
-	result, err := bs.executeCommand(options)
+	result, err := bash.executeCommand(options)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get command help: %v", err)), nil
 	}
@@ -181,7 +181,7 @@ func (bs *BashServer) HandleCommandHelp(req mcp.CallToolRequest) (mcp.CallToolRe
 // Environment Management Tools
 
 // HandleGetEnv handles the get_env tool
-func (bs *BashServer) HandleGetEnv(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleGetEnv(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	// Check if requesting all environment variables
 	if all, ok := req.Arguments["all"].(bool); ok && all {
 		envVars := make(map[string]string)
@@ -224,8 +224,8 @@ func (bs *BashServer) HandleGetEnv(req mcp.CallToolRequest) (mcp.CallToolResult,
 }
 
 // HandleGetWorkingDir handles the get_working_dir tool
-func (bs *BashServer) HandleGetWorkingDir(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	workingDir := bs.getWorkingDir()
+func (bash *BashOperations) HandleGetWorkingDir(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	workingDir := bash.getWorkingDir()
 
 	result := map[string]string{
 		"working_directory": workingDir,
@@ -242,18 +242,18 @@ func (bs *BashServer) HandleGetWorkingDir(req mcp.CallToolRequest) (mcp.CallTool
 }
 
 // HandleSetWorkingDir handles the set_working_dir tool
-func (bs *BashServer) HandleSetWorkingDir(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleSetWorkingDir(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	directory, ok := req.Arguments["directory"].(string)
 	if !ok || directory == "" {
 		return mcp.NewToolError("directory parameter is required"), nil
 	}
 
-	if err := bs.setWorkingDir(directory); err != nil {
+	if err := bash.setWorkingDir(directory); err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to set working directory: %v", err)), nil
 	}
 
 	result := map[string]string{
-		"working_directory": bs.getWorkingDir(),
+		"working_directory": bash.getWorkingDir(),
 		"message":          "Working directory updated successfully",
 	}
 
@@ -270,8 +270,8 @@ func (bs *BashServer) HandleSetWorkingDir(req mcp.CallToolRequest) (mcp.CallTool
 // System Information Tools
 
 // HandleSystemInfo handles the system_info tool
-func (bs *BashServer) HandleSystemInfo(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	sysInfo, err := bs.getSystemInfo()
+func (bash *BashOperations) HandleSystemInfo(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	sysInfo, err := bash.getSystemInfo()
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get system information: %v", err)), nil
 	}
@@ -287,7 +287,7 @@ func (bs *BashServer) HandleSystemInfo(req mcp.CallToolRequest) (mcp.CallToolRes
 }
 
 // HandleProcessInfo handles the process_info tool
-func (bs *BashServer) HandleProcessInfo(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (bash *BashOperations) HandleProcessInfo(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	// Build ps command
 	format := "aux" // default format
 	if f, ok := req.Arguments["format"].(string); ok && f != "" {
@@ -315,7 +315,7 @@ func (bs *BashServer) HandleProcessInfo(req mcp.CallToolRequest) (mcp.CallToolRe
 		CaptureStderr: true,
 	}
 
-	result, err := bs.executeCommand(options)
+	result, err := bash.executeCommand(options)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get process information: %v", err)), nil
 	}
@@ -333,8 +333,8 @@ func (bs *BashServer) HandleProcessInfo(req mcp.CallToolRequest) (mcp.CallToolRe
 // Resource Handlers
 
 // HandleSystemResource handles the system information resource
-func (bs *BashServer) HandleSystemResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
-	sysInfo, err := bs.getSystemInfo()
+func (bash *BashOperations) HandleSystemResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+	sysInfo, err := bash.getSystemInfo()
 	if err != nil {
 		return mcp.ReadResourceResult{}, fmt.Errorf("failed to get system information: %w", err)
 	}
@@ -350,8 +350,8 @@ func (bs *BashServer) HandleSystemResource(req mcp.ReadResourceRequest) (mcp.Rea
 }
 
 // HandleHistoryResource handles the command history resource
-func (bs *BashServer) HandleHistoryResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
-	history := bs.getCommandHistory()
+func (bash *BashOperations) HandleHistoryResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+	history := bash.getCommandHistory()
 
 	return mcp.ReadResourceResult{
 		Contents: []mcp.Content{
@@ -364,7 +364,7 @@ func (bs *BashServer) HandleHistoryResource(req mcp.ReadResourceRequest) (mcp.Re
 }
 
 // HandleEnvResource handles the environment variables resource
-func (bs *BashServer) HandleEnvResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+func (bash *BashOperations) HandleEnvResource(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
 	envVars := make(map[string]string)
 	for _, env := range os.Environ() {
 		parts := strings.SplitN(env, "=", 2)
pkg/bash/server.go
@@ -16,9 +16,8 @@ import (
 	"github.com/xlgmokha/mcp/pkg/mcp"
 )
 
-// BashServer implements the Bash MCP server
-type BashServer struct {
-	*mcp.Server
+// BashOperations provides bash command execution operations
+type BashOperations struct {
 	workingDir     string
 	commandHistory []CommandRecord
 	mu             sync.RWMutex
@@ -74,8 +73,8 @@ type SystemInfo struct {
 	Path         string `json:"path"`
 }
 
-// NewBashServer creates a new Bash MCP server
-func NewBashServer(config *Config) (*BashServer, error) {
+// NewBashOperations creates a new BashOperations helper
+func NewBashOperations(config *Config) (*BashOperations, error) {
 	if config == nil {
 		config = DefaultConfig()
 	}
@@ -90,23 +89,200 @@ func NewBashServer(config *Config) (*BashServer, error) {
 		workingDir = cwd
 	}
 
-	// Create base MCP server
-	baseServer := mcp.NewServer("bash", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-	
-	server := &BashServer{
-		Server:         baseServer,
+	return &BashOperations{
 		workingDir:     workingDir,
 		commandHistory: make([]CommandRecord, 0, config.HistorySize),
 		config:         config,
-	}
+	}, nil
+}
 
-	// Register all tools
-	server.registerTools()
+// New creates a new Bash MCP server
+func New(config *Config) (*mcp.Server, error) {
+	bash, err := NewBashOperations(config)
+	if err != nil {
+		return nil, err
+	}
 	
-	// Register resources
-	server.registerResources()
-
-	return server, nil
+	builder := mcp.NewServerBuilder("bash", "1.0.0")
+
+	// Add bash_exec tool
+	builder.AddTool(mcp.NewTool("bash_exec", "Execute a shell command and return output", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"command": map[string]interface{}{
+				"type":        "string",
+				"description": "Shell command to execute",
+			},
+			"working_dir": map[string]interface{}{
+				"type":        "string",
+				"description": "Working directory for command execution (optional)",
+			},
+			"timeout": map[string]interface{}{
+				"type":        "number",
+				"description": "Timeout in seconds (default: 30, max: 300)",
+			},
+			"capture_stderr": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include stderr in output (default: true)",
+			},
+			"env": map[string]interface{}{
+				"type":        "object",
+				"description": "Additional environment variables",
+			},
+		},
+		"required": []string{"command"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleBashExec(req)
+	}))
+
+	// Add bash_exec_stream tool
+	builder.AddTool(mcp.NewTool("bash_exec_stream", "Execute command with real-time output streaming", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"command": map[string]interface{}{
+				"type":        "string",
+				"description": "Shell command to execute",
+			},
+			"working_dir": map[string]interface{}{
+				"type":        "string",
+				"description": "Working directory for command execution (optional)",
+			},
+			"timeout": map[string]interface{}{
+				"type":        "number",
+				"description": "Timeout in seconds (default: 30, max: 300)",
+			},
+			"buffer_size": map[string]interface{}{
+				"type":        "number",
+				"description": "Stream buffer size in bytes",
+			},
+		},
+		"required": []string{"command"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleBashExecStream(req)
+	}))
+
+	// Add man_page tool
+	builder.AddTool(mcp.NewTool("man_page", "Get manual page for a command", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"command": map[string]interface{}{
+				"type":        "string",
+				"description": "Command to get manual for",
+			},
+			"section": map[string]interface{}{
+				"type":        "string",
+				"description": "Manual section (1-8)",
+			},
+		},
+		"required": []string{"command"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleManPage(req)
+	}))
+
+	// Add which_command tool
+	builder.AddTool(mcp.NewTool("which_command", "Find the location of a command", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"command": map[string]interface{}{
+				"type":        "string",
+				"description": "Command to locate",
+			},
+		},
+		"required": []string{"command"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleWhichCommand(req)
+	}))
+
+	// Add command_help tool
+	builder.AddTool(mcp.NewTool("command_help", "Get help text for a command (--help flag)", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"command": map[string]interface{}{
+				"type":        "string",
+				"description": "Command to get help for",
+			},
+		},
+		"required": []string{"command"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleCommandHelp(req)
+	}))
+
+	// Add get_env tool
+	builder.AddTool(mcp.NewTool("get_env", "Get environment variable value", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"variable": map[string]interface{}{
+				"type":        "string",
+				"description": "Environment variable name",
+			},
+			"all": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Return all environment variables",
+			},
+		},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleGetEnv(req)
+	}))
+
+	// Add get_working_dir tool
+	builder.AddTool(mcp.NewTool("get_working_dir", "Get the current working directory", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleGetWorkingDir(req)
+	}))
+
+	// Add set_working_dir tool
+	builder.AddTool(mcp.NewTool("set_working_dir", "Set working directory for future commands", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"directory": map[string]interface{}{
+				"type":        "string",
+				"description": "Directory path to set as working directory",
+			},
+		},
+		"required": []string{"directory"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleSetWorkingDir(req)
+	}))
+
+	// Add system_info tool
+	builder.AddTool(mcp.NewTool("system_info", "Get basic system information", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleSystemInfo(req)
+	}))
+
+	// Add process_info tool
+	builder.AddTool(mcp.NewTool("process_info", "Get information about running processes (ps command wrapper)", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"format": map[string]interface{}{
+				"type":        "string",
+				"description": "ps format string (default: aux)",
+			},
+			"filter": map[string]interface{}{
+				"type":        "string",
+				"description": "grep filter for processes",
+			},
+		},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return bash.HandleProcessInfo(req)
+	}))
+
+	// Add resources
+	builder.AddResource(mcp.NewResource("bash://system/info", "System Information", "application/json", func(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+		return bash.HandleSystemResource(req)
+	}))
+
+	builder.AddResource(mcp.NewResource("bash://history/recent", "Command History", "application/json", func(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+		return bash.HandleHistoryResource(req)
+	}))
+
+	builder.AddResource(mcp.NewResource("bash://env/all", "Environment Variables", "application/json", func(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+		return bash.HandleEnvResource(req)
+	}))
+
+	return builder.Build(), nil
 }
 
 // DefaultConfig returns default configuration
@@ -148,106 +324,42 @@ func ConfigFromEnv() *Config {
 	return config
 }
 
-// registerTools registers all bash tools with the server
-func (bs *BashServer) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := bs.ListTools()
-	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "bash_exec":
-			handler = bs.HandleBashExec
-		case "bash_exec_stream":
-			handler = bs.HandleBashExecStream
-		case "man_page":
-			handler = bs.HandleManPage
-		case "which_command":
-			handler = bs.HandleWhichCommand
-		case "command_help":
-			handler = bs.HandleCommandHelp
-		case "get_env":
-			handler = bs.HandleGetEnv
-		case "get_working_dir":
-			handler = bs.HandleGetWorkingDir
-		case "set_working_dir":
-			handler = bs.HandleSetWorkingDir
-		case "system_info":
-			handler = bs.HandleSystemInfo
-		case "process_info":
-			handler = bs.HandleProcessInfo
-		default:
-			continue
-		}
-		bs.RegisterToolWithDefinition(tool, handler)
-	}
-}
 
-// registerResources registers MCP resources
-func (bs *BashServer) registerResources() {
-	// System information resource
-	systemResource := mcp.Resource{
-		URI:         "bash://system/info",
-		Name:        "System Information",
-		Description: "Live system information and environment state",
-		MimeType:    "application/json",
-	}
-	bs.Server.RegisterResourceWithDefinition(systemResource, bs.HandleSystemResource)
-
-	// Command history resource
-	historyResource := mcp.Resource{
-		URI:         "bash://history/recent",
-		Name:        "Command History",
-		Description: "Recent command execution history",
-		MimeType:    "application/json",
-	}
-	bs.Server.RegisterResourceWithDefinition(historyResource, bs.HandleHistoryResource)
-
-	// Environment variables resource
-	envResource := mcp.Resource{
-		URI:         "bash://env/all",
-		Name:        "Environment Variables",
-		Description: "Complete environment variables",
-		MimeType:    "application/json",
-	}
-	bs.Server.RegisterResourceWithDefinition(envResource, bs.HandleEnvResource)
-}
 
 // addCommandRecord adds a command record to history
-func (bs *BashServer) addCommandRecord(record CommandRecord) {
-	bs.mu.Lock()
-	defer bs.mu.Unlock()
+func (bash *BashOperations) addCommandRecord(record CommandRecord) {
+	bash.mu.Lock()
+	defer bash.mu.Unlock()
 
 	// Add record
-	bs.commandHistory = append(bs.commandHistory, record)
+	bash.commandHistory = append(bash.commandHistory, record)
 
 	// Trim history if it exceeds the maximum size
-	if len(bs.commandHistory) > bs.config.HistorySize {
-		bs.commandHistory = bs.commandHistory[len(bs.commandHistory)-bs.config.HistorySize:]
+	if len(bash.commandHistory) > bash.config.HistorySize {
+		bash.commandHistory = bash.commandHistory[len(bash.commandHistory)-bash.config.HistorySize:]
 	}
 }
 
 // getCommandHistory returns a copy of the command history
-func (bs *BashServer) getCommandHistory() []CommandRecord {
-	bs.mu.RLock()
-	defer bs.mu.RUnlock()
+func (bash *BashOperations) getCommandHistory() []CommandRecord {
+	bash.mu.RLock()
+	defer bash.mu.RUnlock()
 
 	// Return a copy to avoid race conditions
-	history := make([]CommandRecord, len(bs.commandHistory))
-	copy(history, bs.commandHistory)
+	history := make([]CommandRecord, len(bash.commandHistory))
+	copy(history, bash.commandHistory)
 	return history
 }
 
 // getWorkingDir returns the current working directory
-func (bs *BashServer) getWorkingDir() string {
-	bs.mu.RLock()
-	defer bs.mu.RUnlock()
-	return bs.workingDir
+func (bash *BashOperations) getWorkingDir() string {
+	bash.mu.RLock()
+	defer bash.mu.RUnlock()
+	return bash.workingDir
 }
 
 // setWorkingDir sets the working directory
-func (bs *BashServer) setWorkingDir(dir string) error {
+func (bash *BashOperations) setWorkingDir(dir string) error {
 	// Validate directory exists
 	if _, err := os.Stat(dir); os.IsNotExist(err) {
 		return fmt.Errorf("directory does not exist: %s", dir)
@@ -259,30 +371,30 @@ func (bs *BashServer) setWorkingDir(dir string) error {
 		return fmt.Errorf("failed to get absolute path: %w", err)
 	}
 
-	bs.mu.Lock()
-	defer bs.mu.Unlock()
-	bs.workingDir = absDir
+	bash.mu.Lock()
+	defer bash.mu.Unlock()
+	bash.workingDir = absDir
 	return nil
 }
 
 // executeCommand executes a shell command with the given options
-func (bs *BashServer) executeCommand(options ExecutionOptions) (*ExecutionResult, error) {
+func (bash *BashOperations) executeCommand(options ExecutionOptions) (*ExecutionResult, error) {
 	startTime := time.Now()
 
 	// Set default timeout
 	if options.Timeout <= 0 {
-		options.Timeout = bs.config.DefaultTimeout
+		options.Timeout = bash.config.DefaultTimeout
 	}
 
 	// Enforce maximum timeout
-	if options.Timeout > bs.config.MaxTimeout {
-		options.Timeout = bs.config.MaxTimeout
+	if options.Timeout > bash.config.MaxTimeout {
+		options.Timeout = bash.config.MaxTimeout
 	}
 
 	// Set working directory
 	workingDir := options.WorkingDir
 	if workingDir == "" {
-		workingDir = bs.getWorkingDir()
+		workingDir = bash.getWorkingDir()
 	}
 
 	// Create context with timeout
@@ -314,7 +426,7 @@ func (bs *BashServer) executeCommand(options ExecutionOptions) (*ExecutionResult
 	var err error
 
 	if options.CaptureStderr {
-		stdout, stderr, err = bs.runCommandWithStderr(cmd)
+		stdout, stderr, err = bash.runCommandWithStderr(cmd)
 	} else {
 		stdout, err = cmd.Output()
 	}
@@ -351,13 +463,13 @@ func (bs *BashServer) executeCommand(options ExecutionOptions) (*ExecutionResult
 		Duration:   executionTime,
 		OutputSize: len(stdout) + len(stderr),
 	}
-	bs.addCommandRecord(record)
+	bash.addCommandRecord(record)
 
 	return result, nil
 }
 
 // runCommandWithStderr runs a command and captures both stdout and stderr
-func (bs *BashServer) runCommandWithStderr(cmd *exec.Cmd) ([]byte, []byte, error) {
+func (bash *BashOperations) runCommandWithStderr(cmd *exec.Cmd) ([]byte, []byte, error) {
 	var stdout, stderr []byte
 	var err error
 
@@ -413,7 +525,7 @@ func readAll(r interface{ Read([]byte) (int, error) }) ([]byte, error) {
 }
 
 // getSystemInfo returns current system information
-func (bs *BashServer) getSystemInfo() (*SystemInfo, error) {
+func (bash *BashOperations) getSystemInfo() (*SystemInfo, error) {
 	hostname, _ := os.Hostname()
 	
 	currentUser, _ := user.Current()
@@ -434,7 +546,7 @@ func (bs *BashServer) getSystemInfo() (*SystemInfo, error) {
 	// Get kernel version
 	kernel := "unknown"
 	if runtime.GOOS == "linux" {
-		if result, err := bs.executeCommand(ExecutionOptions{Command: "uname -r", CaptureStderr: false}); err == nil {
+		if result, err := bash.executeCommand(ExecutionOptions{Command: "uname -r", CaptureStderr: false}); err == nil {
 			kernel = strings.TrimSpace(result.Stdout)
 		}
 	}
pkg/bash/server_test.go
@@ -1,423 +0,0 @@
-package bash
-
-import (
-	"testing"
-	"time"
-	
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestNewBashServer(t *testing.T) {
-	tests := []struct {
-		name    string
-		config  *Config
-		wantErr bool
-	}{
-		{
-			name:    "default config",
-			config:  nil,
-			wantErr: false,
-		},
-		{
-			name: "custom config",
-			config: &Config{
-				DefaultTimeout: 60,
-				MaxTimeout:     600,
-				HistorySize:    200,
-				WorkingDir:     "/tmp",
-			},
-			wantErr: false,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			server, err := NewBashServer(tt.config)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("NewBashServer() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !tt.wantErr && server == nil {
-				t.Error("NewBashServer() returned nil server")
-			}
-		})
-	}
-}
-
-func TestDefaultConfig(t *testing.T) {
-	config := DefaultConfig()
-	
-	if config.DefaultTimeout != 30 {
-		t.Errorf("DefaultTimeout = %v, want 30", config.DefaultTimeout)
-	}
-	if config.MaxTimeout != 300 {
-		t.Errorf("MaxTimeout = %v, want 300", config.MaxTimeout)
-	}
-	if config.HistorySize != 100 {
-		t.Errorf("HistorySize = %v, want 100", config.HistorySize)
-	}
-	if config.WorkingDir != "" {
-		t.Errorf("WorkingDir = %v, want empty string", config.WorkingDir)
-	}
-}
-
-func TestConfigFromEnv(t *testing.T) {
-	// Set environment variables
-	t.Setenv("BASH_MCP_DEFAULT_TIMEOUT", "45")
-	t.Setenv("BASH_MCP_MAX_TIMEOUT", "600")
-	t.Setenv("BASH_MCP_MAX_HISTORY", "50")
-	t.Setenv("BASH_MCP_WORKING_DIR", "/tmp/test")
-	
-	config := ConfigFromEnv()
-	
-	if config.DefaultTimeout != 45 {
-		t.Errorf("DefaultTimeout = %v, want 45", config.DefaultTimeout)
-	}
-	if config.MaxTimeout != 600 {
-		t.Errorf("MaxTimeout = %v, want 600", config.MaxTimeout)
-	}
-	if config.HistorySize != 50 {
-		t.Errorf("HistorySize = %v, want 50", config.HistorySize)
-	}
-	if config.WorkingDir != "/tmp/test" {
-		t.Errorf("WorkingDir = %v, want /tmp/test", config.WorkingDir)
-	}
-}
-
-func TestExecuteCommand(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	tests := []struct {
-		name    string
-		options ExecutionOptions
-		wantErr bool
-		check   func(*testing.T, *ExecutionResult)
-	}{
-		{
-			name: "simple echo command",
-			options: ExecutionOptions{
-				Command: "echo 'Hello, World!'",
-			},
-			wantErr: false,
-			check: func(t *testing.T, result *ExecutionResult) {
-				if result.ExitCode != 0 {
-					t.Errorf("ExitCode = %v, want 0", result.ExitCode)
-				}
-				if result.Stdout != "Hello, World!\n" {
-					t.Errorf("Stdout = %v, want 'Hello, World!\\n'", result.Stdout)
-				}
-			},
-		},
-		{
-			name: "command with error",
-			options: ExecutionOptions{
-				Command:       "exit 1",
-				CaptureStderr: true,
-			},
-			wantErr: false,
-			check: func(t *testing.T, result *ExecutionResult) {
-				if result.ExitCode != 1 {
-					t.Errorf("ExitCode = %v, want 1", result.ExitCode)
-				}
-			},
-		},
-		{
-			name: "command with stderr",
-			options: ExecutionOptions{
-				Command:       "echo 'error' >&2",
-				CaptureStderr: true,
-			},
-			wantErr: false,
-			check: func(t *testing.T, result *ExecutionResult) {
-				if result.ExitCode != 0 {
-					t.Errorf("ExitCode = %v, want 0", result.ExitCode)
-				}
-				if result.Stderr != "error\n" {
-					t.Errorf("Stderr = %v, want 'error\\n'", result.Stderr)
-				}
-			},
-		},
-		{
-			name: "command with timeout",
-			options: ExecutionOptions{
-				Command: "sleep 0.1",
-				Timeout: 1,
-			},
-			wantErr: false,
-			check: func(t *testing.T, result *ExecutionResult) {
-				if result.ExitCode != 0 {
-					t.Errorf("ExitCode = %v, want 0", result.ExitCode)
-				}
-			},
-		},
-		{
-			name: "command with environment variable",
-			options: ExecutionOptions{
-				Command: "echo $TEST_VAR",
-				Env: map[string]string{
-					"TEST_VAR": "test_value",
-				},
-			},
-			wantErr: false,
-			check: func(t *testing.T, result *ExecutionResult) {
-				if result.ExitCode != 0 {
-					t.Errorf("ExitCode = %v, want 0", result.ExitCode)
-				}
-				if result.Stdout != "test_value\n" {
-					t.Errorf("Stdout = %v, want 'test_value\\n'", result.Stdout)
-				}
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result, err := server.executeCommand(tt.options)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("executeCommand() error = %v, wantErr %v", err, tt.wantErr)
-				return
-			}
-			if !tt.wantErr && result != nil && tt.check != nil {
-				tt.check(t, result)
-			}
-		})
-	}
-}
-
-func TestCommandHistory(t *testing.T) {
-	config := &Config{
-		DefaultTimeout: 30,
-		MaxTimeout:     300,
-		HistorySize:    3,
-		WorkingDir:     "",
-	}
-	
-	server, err := NewBashServer(config)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	// Execute multiple commands
-	for i := 0; i < 5; i++ {
-		_, err := server.executeCommand(ExecutionOptions{
-			Command: "echo test",
-		})
-		if err != nil {
-			t.Fatalf("Failed to execute command: %v", err)
-		}
-	}
-
-	// Check history size is limited
-	history := server.getCommandHistory()
-	if len(history) != 3 {
-		t.Errorf("History length = %v, want 3", len(history))
-	}
-}
-
-func TestSetWorkingDir(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	tests := []struct {
-		name    string
-		dir     string
-		wantErr bool
-	}{
-		{
-			name:    "valid directory",
-			dir:     "/tmp",
-			wantErr: false,
-		},
-		{
-			name:    "non-existent directory",
-			dir:     "/this/does/not/exist",
-			wantErr: true,
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			err := server.setWorkingDir(tt.dir)
-			if (err != nil) != tt.wantErr {
-				t.Errorf("setWorkingDir() error = %v, wantErr %v", err, tt.wantErr)
-			}
-		})
-	}
-}
-
-func TestGetSystemInfo(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	info, err := server.getSystemInfo()
-	if err != nil {
-		t.Fatalf("getSystemInfo() error = %v", err)
-	}
-
-	// Check that required fields are populated
-	if info.Hostname == "" {
-		t.Error("Hostname is empty")
-	}
-	if info.OS == "" {
-		t.Error("OS is empty")
-	}
-	if info.Architecture == "" {
-		t.Error("Architecture is empty")
-	}
-	if info.User == "" {
-		t.Error("User is empty")
-	}
-}
-
-func TestHandleBashExec(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	req := mcp.CallToolRequest{
-		Name: "bash_exec",
-		Arguments: map[string]interface{}{
-			"command": "echo 'test output'",
-		},
-	}
-
-	result, err := server.HandleBashExec(req)
-	if err != nil {
-		t.Fatalf("HandleBashExec() error = %v", err)
-	}
-
-	// Check that we got content back
-	if len(result.Content) == 0 {
-		t.Fatal("No content returned")
-	}
-
-	// The content should be a TextContent
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Content is not TextContent")
-	}
-
-	// Should contain the output
-	if textContent.Text == "" {
-		t.Error("Text content is empty")
-	}
-}
-
-func TestHandleSystemInfo(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	req := mcp.CallToolRequest{
-		Name:      "system_info",
-		Arguments: map[string]interface{}{},
-	}
-
-	result, err := server.HandleSystemInfo(req)
-	if err != nil {
-		t.Fatalf("HandleSystemInfo() error = %v", err)
-	}
-
-	// Check that we got content back
-	if len(result.Content) == 0 {
-		t.Fatal("No content returned")
-	}
-}
-
-func TestHandleSetWorkingDir(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	req := mcp.CallToolRequest{
-		Name: "set_working_dir",
-		Arguments: map[string]interface{}{
-			"directory": "/tmp",
-		},
-	}
-
-	_, err = server.HandleSetWorkingDir(req)
-	if err != nil {
-		t.Fatalf("HandleSetWorkingDir() error = %v", err)
-	}
-
-	// Verify the working directory was changed
-	if server.getWorkingDir() != "/tmp" {
-		t.Errorf("Working directory = %v, want /tmp", server.getWorkingDir())
-	}
-}
-
-func TestHandleGetEnv(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	// Set a test environment variable
-	t.Setenv("TEST_ENV_VAR", "test_value")
-
-	req := mcp.CallToolRequest{
-		Name: "get_env",
-		Arguments: map[string]interface{}{
-			"name": "TEST_ENV_VAR",
-		},
-	}
-
-	result, err := server.HandleGetEnv(req)
-	if err != nil {
-		t.Fatalf("HandleGetEnv() error = %v", err)
-	}
-
-	// Check that we got content back
-	if len(result.Content) == 0 {
-		t.Fatal("No content returned")
-	}
-}
-
-func TestCommandRecordCreation(t *testing.T) {
-	server, err := NewBashServer(nil)
-	if err != nil {
-		t.Fatalf("Failed to create server: %v", err)
-	}
-
-	// Execute a command
-	startTime := time.Now()
-	result, err := server.executeCommand(ExecutionOptions{
-		Command: "echo test",
-	})
-	if err != nil {
-		t.Fatalf("Failed to execute command: %v", err)
-	}
-
-	// Get history
-	history := server.getCommandHistory()
-	if len(history) != 1 {
-		t.Fatalf("Expected 1 history record, got %d", len(history))
-	}
-
-	record := history[0]
-	
-	// Verify record fields
-	if record.Command != "echo test" {
-		t.Errorf("Command = %v, want 'echo test'", record.Command)
-	}
-	if record.ExitCode != 0 {
-		t.Errorf("ExitCode = %v, want 0", record.ExitCode)
-	}
-	if record.Timestamp.Before(startTime) {
-		t.Error("Timestamp is before command start time")
-	}
-	if record.Duration != result.ExecutionTime {
-		t.Errorf("Duration = %v, want %v", record.Duration, result.ExecutionTime)
-	}
-}
\ No newline at end of file
pkg/bash/tools.go
@@ -1,180 +1,4 @@
 package bash
 
-import "github.com/xlgmokha/mcp/pkg/mcp"
-
-// ListTools returns all available bash tools with their definitions
-func (bs *BashServer) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		// Core Execution Tools
-		{
-			Name:        "bash_exec",
-			Description: "Execute a shell command and return output",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"command": map[string]interface{}{
-						"type":        "string",
-						"description": "Shell command to execute",
-					},
-					"working_dir": map[string]interface{}{
-						"type":        "string",
-						"description": "Working directory for command execution (optional)",
-					},
-					"timeout": map[string]interface{}{
-						"type":        "number",
-						"description": "Timeout in seconds (default: 30, max: 300)",
-					},
-					"capture_stderr": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include stderr in output (default: true)",
-					},
-					"env": map[string]interface{}{
-						"type":        "object",
-						"description": "Additional environment variables",
-					},
-				},
-				"required": []string{"command"},
-			},
-		},
-		{
-			Name:        "bash_exec_stream",
-			Description: "Execute command with real-time output streaming",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"command": map[string]interface{}{
-						"type":        "string",
-						"description": "Shell command to execute",
-					},
-					"working_dir": map[string]interface{}{
-						"type":        "string",
-						"description": "Working directory for command execution (optional)",
-					},
-					"timeout": map[string]interface{}{
-						"type":        "number",
-						"description": "Timeout in seconds (default: 30, max: 300)",
-					},
-					"buffer_size": map[string]interface{}{
-						"type":        "number",
-						"description": "Stream buffer size in bytes",
-					},
-				},
-				"required": []string{"command"},
-			},
-		},
-
-		// Documentation Tools
-		{
-			Name:        "man_page",
-			Description: "Get manual page for a command",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"command": map[string]interface{}{
-						"type":        "string",
-						"description": "Command to get manual for",
-					},
-					"section": map[string]interface{}{
-						"type":        "string",
-						"description": "Manual section (1-8)",
-					},
-				},
-				"required": []string{"command"},
-			},
-		},
-		{
-			Name:        "which_command",
-			Description: "Find the location of a command",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"command": map[string]interface{}{
-						"type":        "string",
-						"description": "Command to locate",
-					},
-				},
-				"required": []string{"command"},
-			},
-		},
-		{
-			Name:        "command_help",
-			Description: "Get help text for a command (--help flag)",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"command": map[string]interface{}{
-						"type":        "string",
-						"description": "Command to get help for",
-					},
-				},
-				"required": []string{"command"},
-			},
-		},
-
-		// Environment Management Tools
-		{
-			Name:        "get_env",
-			Description: "Get environment variable value",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"variable": map[string]interface{}{
-						"type":        "string",
-						"description": "Environment variable name",
-					},
-					"all": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Return all environment variables",
-					},
-				},
-			},
-		},
-		{
-			Name:        "get_working_dir",
-			Description: "Get the current working directory",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-			},
-		},
-		{
-			Name:        "set_working_dir",
-			Description: "Set working directory for future commands",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Directory path to set as working directory",
-					},
-				},
-				"required": []string{"directory"},
-			},
-		},
-
-		// System Information Tools
-		{
-			Name:        "system_info",
-			Description: "Get basic system information",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-			},
-		},
-		{
-			Name:        "process_info",
-			Description: "Get information about running processes (ps command wrapper)",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"format": map[string]interface{}{
-						"type":        "string",
-						"description": "ps format string (default: aux)",
-					},
-					"filter": map[string]interface{}{
-						"type":        "string",
-						"description": "grep filter for processes",
-					},
-				},
-			},
-		},
-	}
-}
\ No newline at end of file
+// This file previously contained tool definitions that are now inlined in server.go
+// Keeping the file for potential future bash-specific utilities
\ No newline at end of file
pkg/fetch/server_test.go
@@ -1,344 +0,0 @@
-package fetch
-
-import (
-	"net/http"
-	"net/http/httptest"
-	"strings"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestServer_FetchTool(t *testing.T) {
-	// Create test server with HTML content
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "text/html")
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`
-			<html>
-				<head><title>Test Page</title></head>
-				<body>
-					<h1>Welcome</h1>
-					<p>This is a test page with some content.</p>
-					<div>More content here</div>
-				</body>
-			</html>
-		`))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url": testServer.URL,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful fetch, got error: %s", textContent.Text)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain converted markdown content
-	if !contains(textContent.Text, "Welcome") {
-		t.Fatalf("Expected 'Welcome' in converted content, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "test page") {
-		t.Fatalf("Expected 'test page' in converted content, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchRawContent(t *testing.T) {
-	// Create test server with HTML content
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "text/html")
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`<html><body><h1>Raw HTML</h1></body></html>`))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url": testServer.URL,
-			"raw": true,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful fetch, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain raw HTML content (JSON escaped)
-	if !contains(textContent.Text, "\\u003chtml\\u003e") && !contains(textContent.Text, "<html>") {
-		t.Fatalf("Expected raw HTML content (possibly JSON escaped), got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "Raw HTML") {
-		t.Fatalf("Expected 'Raw HTML' in content, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchWithMaxLength(t *testing.T) {
-	// Create test server with long plain text content to avoid HTML conversion complexity
-	longContent := strings.Repeat("x", 200) // 200 characters
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "text/plain")
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(longContent))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url":        testServer.URL,
-			"max_length": 100,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful fetch, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Parse the JSON response to check that content was truncated
-	if !contains(textContent.Text, "\"length\": 100") {
-		t.Fatalf("Expected content length to be exactly 100 chars, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "\"truncated\": true") {
-		t.Fatalf("Expected truncated flag to be true, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchWithStartIndex(t *testing.T) {
-	// Create test server with content
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "text/html")
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte(`<html><body><p>Start of content. Middle of content. End of content.</p></body></html>`))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url":         testServer.URL,
-			"start_index": 20,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful fetch, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should not contain the beginning of the content in the actual content field
-	// Since start_index=20 and the markdown conversion changes the content,
-	// we should check that some content was actually returned but not the exact beginning
-	if !contains(textContent.Text, "content") {
-		t.Fatalf("Expected some content after start_index=20, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchInvalidURL(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url": "not-a-valid-url",
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for invalid URL")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "Invalid URL") && !contains(textContent.Text, "invalid URL") {
-		t.Fatalf("Expected invalid URL error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchHTTPError(t *testing.T) {
-	// Create test server that returns 404
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.WriteHeader(http.StatusNotFound)
-		w.Write([]byte("Not Found"))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url": testServer.URL,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for 404 response")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "404") {
-		t.Fatalf("Expected 404 error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_FetchPlainText(t *testing.T) {
-	// Create test server with plain text content
-	testServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
-		w.Header().Set("Content-Type", "text/plain")
-		w.WriteHeader(http.StatusOK)
-		w.Write([]byte("This is plain text content without HTML tags."))
-	}))
-	defer testServer.Close()
-
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "fetch",
-		Arguments: map[string]interface{}{
-			"url": testServer.URL,
-		},
-	}
-
-	result, err := server.HandleFetch(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful fetch, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain plain text content as-is
-	if !contains(textContent.Text, "plain text content") {
-		t.Fatalf("Expected plain text content, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_ListTools(t *testing.T) {
-	server := New()
-	tools := server.ListTools()
-
-	expectedTools := []string{
-		"fetch",
-	}
-
-	if len(tools) != len(expectedTools) {
-		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-	}
-
-	toolNames := make(map[string]bool)
-	for _, tool := range tools {
-		toolNames[tool.Name] = true
-	}
-
-	for _, expected := range expectedTools {
-		if !toolNames[expected] {
-			t.Fatalf("Expected tool %s not found", expected)
-		}
-	}
-
-	// Check that fetch tool has proper schema
-	fetchTool := tools[0]
-	if fetchTool.Name != "fetch" {
-		t.Fatalf("Expected first tool to be 'fetch', got %s", fetchTool.Name)
-	}
-
-	if fetchTool.Description == "" {
-		t.Fatal("Expected non-empty description for fetch tool")
-	}
-
-	if fetchTool.InputSchema == nil {
-		t.Fatal("Expected input schema for fetch tool")
-	}
-}
-
-// Helper functions
-func contains(s, substr string) bool {
-	return strings.Contains(s, substr)
-}
pkg/filesystem/server_test.go
@@ -1,219 +0,0 @@
-package filesystem
-
-import (
-  "os"
-  "path/filepath"
-  "testing"
-
-  "github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestFilesystemServer_ReadFile(t *testing.T) {
-  tempDir, err := os.MkdirTemp("", "fs-test-*")
-  if err != nil {
-    t.Fatal(err)
-  }
-  defer os.RemoveAll(tempDir)
-
-  testFile := filepath.Join(tempDir, "test.txt")
-  testContent := "Hello, World!"
-  if err := os.WriteFile(testFile, []byte(testContent), 0644); err != nil {
-    t.Fatal(err)
-  }
-
-  server := New([]string{tempDir})
-
-  req := mcp.CallToolRequest{
-    Name: "read_file",
-    Arguments: map[string]interface{}{
-      "path": testFile,
-    },
-  }
-
-  // Get the read_file tool and call its handler
-  tools := server.ListTools()
-  var readTool mcp.Tool
-  for _, tool := range tools {
-    if tool.Name == "read_file" {
-      readTool = tool
-      break
-    }
-  }
-  result, err := readTool.Handler(req)
-  if err != nil {
-    t.Fatalf("Expected no error, got %v", err)
-  }
-
-  if len(result.Content) == 0 {
-    t.Fatal("Expected content in result")
-  }
-
-  textContent, ok := result.Content[0].(mcp.TextContent)
-  if !ok {
-    t.Fatal("Expected TextContent")
-  }
-
-  if textContent.Text != testContent {
-    t.Fatalf("Expected '%s', got '%s'", testContent, textContent.Text)
-  }
-}
-
-func TestFilesystemServer_WriteFile(t *testing.T) {
-  tempDir, err := os.MkdirTemp("", "fs-test-*")
-  if err != nil {
-    t.Fatal(err)
-  }
-  defer os.RemoveAll(tempDir)
-
-  server := New([]string{tempDir})
-  testFile := filepath.Join(tempDir, "new_file.txt")
-  testContent := "New content"
-
-  req := mcp.CallToolRequest{
-    Name: "write_file",
-    Arguments: map[string]interface{}{
-      "path":    testFile,
-      "content": testContent,
-    },
-  }
-
-  // Get the write_file tool and call its handler
-  tools := server.ListTools()
-  var writeTool mcp.Tool
-  for _, tool := range tools {
-    if tool.Name == "write_file" {
-      writeTool = tool
-      break
-    }
-  }
-  result, err := writeTool.Handler(req)
-  if err != nil {
-    t.Fatalf("Expected no error, got %v", err)
-  }
-
-  if result.IsError {
-    t.Fatal("Expected successful write")
-  }
-
-  content, err := os.ReadFile(testFile)
-  if err != nil {
-    t.Fatal("File should have been created")
-  }
-
-  if string(content) != testContent {
-    t.Fatalf("Expected '%s', got '%s'", testContent, string(content))
-  }
-}
-
-func TestFilesystemServer_SecurityValidation(t *testing.T) {
-  tempDir, err := os.MkdirTemp("", "fs-test-*")
-  if err != nil {
-    t.Fatal(err)
-  }
-  defer os.RemoveAll(tempDir)
-
-  server := New([]string{tempDir})
-
-  req := mcp.CallToolRequest{
-    Name: "read_file",
-    Arguments: map[string]interface{}{
-      "path": "/etc/passwd",
-    },
-  }
-
-  // Get the read_file tool and call its handler
-  tools := server.ListTools()
-  var readTool mcp.Tool
-  for _, tool := range tools {
-    if tool.Name == "read_file" {
-      readTool = tool
-      break
-    }
-  }
-  result, err := readTool.Handler(req)
-  if err != nil {
-    t.Fatalf("Expected no error, got %v", err)
-  }
-
-  if !result.IsError {
-    t.Fatal("Expected error for unauthorized path access")
-  }
-
-  textContent, ok := result.Content[0].(mcp.TextContent)
-  if !ok {
-    t.Fatal("Expected TextContent")
-  }
-
-  if !contains(textContent.Text, "access denied") {
-    t.Fatalf("Expected access denied error, got: %s", textContent.Text)
-  }
-}
-
-func TestFilesystemServer_ListTools(t *testing.T) {
-  server := New([]string{"/tmp"})
-  tools := server.ListTools()
-
-  expectedTools := []string{"read_file", "write_file"}
-
-  if len(tools) != len(expectedTools) {
-    t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-  }
-
-  toolNames := make(map[string]bool)
-  for _, tool := range tools {
-    toolNames[tool.Name] = true
-  }
-
-  for _, expected := range expectedTools {
-    if !toolNames[expected] {
-      t.Fatalf("Expected tool %s not found", expected)
-    }
-  }
-}
-
-func TestFilesystemServer_Resources(t *testing.T) {
-  tempDir, err := os.MkdirTemp("", "fs-test-*")
-  if err != nil {
-    t.Fatal(err)
-  }
-  defer os.RemoveAll(tempDir)
-
-  testFile := filepath.Join(tempDir, "test.go")
-  if err := os.WriteFile(testFile, []byte("package main"), 0644); err != nil {
-    t.Fatal(err)
-  }
-
-  server := New([]string{tempDir})
-  resources := server.ListResources()
-
-  if len(resources) == 0 {
-    t.Fatal("Expected resources to be found")
-  }
-
-  foundGoFile := false
-  for _, resource := range resources {
-    if contains(resource.URI, "test.go") && resource.MimeType == "text/x-go" {
-      foundGoFile = true
-      break
-    }
-  }
-
-  if !foundGoFile {
-    t.Fatal("Expected to find test.go resource")
-  }
-}
-
-func contains(s, substr string) bool {
-  return len(s) > 0 && len(substr) > 0 &&
-    (s == substr || (len(s) >= len(substr) &&
-      findSubstring(s, substr)))
-}
-
-func findSubstring(s, substr string) bool {
-  for i := 0; i <= len(s)-len(substr); i++ {
-    if s[i:i+len(substr)] == substr {
-      return true
-    }
-  }
-  return false
-}
\ No newline at end of file
pkg/git/server_test.go
@@ -1,215 +0,0 @@
-package git
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestGitServer_GitStatus(t *testing.T) {
-	// Setup test repo
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Initialize git repo
-	if err := initGitRepo(tempDir); err != nil {
-		t.Fatal(err)
-	}
-
-	server := New(".")
-
-	// Test git status
-	req := mcp.CallToolRequest{
-		Name: "git_status",
-		Arguments: map[string]interface{}{
-			"repo_path": tempDir,
-		},
-	}
-
-	result, err := server.HandleGitStatus(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if textContent.Text == "" {
-		t.Fatal("Expected non-empty status text")
-	}
-
-	// Should contain status information
-	if len(textContent.Text) < 10 {
-		t.Fatal("Expected meaningful status output")
-	}
-}
-
-func TestGitServer_GitInit(t *testing.T) {
-	// Setup temp directory
-	tempDir, err := os.MkdirTemp("", "git-init-test-*")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	server := New(".")
-
-	// Test git init
-	req := mcp.CallToolRequest{
-		Name: "git_init",
-		Arguments: map[string]interface{}{
-			"repo_path": tempDir,
-		},
-	}
-
-	result, err := server.HandleGitInit(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Check that .git directory was created
-	gitDir := filepath.Join(tempDir, ".git")
-	if _, err := os.Stat(gitDir); os.IsNotExist(err) {
-		t.Fatal("Expected .git directory to be created")
-	}
-
-	// Check response message
-	if !contains(textContent.Text, "Initialized") {
-		t.Fatalf("Expected init success message, got: %s", textContent.Text)
-	}
-}
-
-func TestGitServer_GitAdd(t *testing.T) {
-	// Setup test repo
-	tempDir, err := os.MkdirTemp("", "git-test-*")
-	if err != nil {
-		t.Fatal(err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Initialize git repo
-	if err := initGitRepo(tempDir); err != nil {
-		t.Fatal(err)
-	}
-
-	// Create a test file
-	testFile := filepath.Join(tempDir, "test.txt")
-	if err := os.WriteFile(testFile, []byte("test content"), 0644); err != nil {
-		t.Fatal(err)
-	}
-
-	server := New(".")
-
-	// Test git add
-	req := mcp.CallToolRequest{
-		Name: "git_add",
-		Arguments: map[string]interface{}{
-			"repo_path": tempDir,
-			"files":     []interface{}{"test.txt"},
-		},
-	}
-
-	result, err := server.HandleGitAdd(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Check response message
-	if !contains(textContent.Text, "staged") {
-		t.Fatalf("Expected staged success message, got: %s", textContent.Text)
-	}
-}
-
-func TestGitServer_ListTools(t *testing.T) {
-	server := New(".")
-	tools := server.ListTools()
-
-	expectedTools := []string{
-		"git_status", "git_diff_unstaged", "git_diff_staged", "git_diff",
-		"git_commit", "git_add", "git_reset", "git_log", "git_create_branch",
-		"git_checkout", "git_show", "git_init",
-	}
-
-	if len(tools) != len(expectedTools) {
-		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-	}
-
-	toolNames := make(map[string]bool)
-	for _, tool := range tools {
-		toolNames[tool.Name] = true
-	}
-
-	for _, expected := range expectedTools {
-		if !toolNames[expected] {
-			t.Fatalf("Expected tool %s not found", expected)
-		}
-	}
-}
-
-// Helper functions
-func initGitRepo(dir string) error {
-	// Use actual git init command for testing
-	server := New(".")
-	req := mcp.CallToolRequest{
-		Name: "git_init",
-		Arguments: map[string]interface{}{
-			"repo_path": dir,
-		},
-	}
-
-	result, err := server.HandleGitInit(req)
-	if err != nil {
-		return err
-	}
-
-	if result.IsError {
-		return fmt.Errorf("git init failed")
-	}
-
-	return nil
-}
-
-func contains(s, substr string) bool {
-	return len(s) > 0 && len(substr) > 0 &&
-		(s == substr || (len(s) >= len(substr) &&
-			findSubstring(s, substr)))
-}
-
-func findSubstring(s, substr string) bool {
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return true
-		}
-	}
-	return false
-}
pkg/gitlab/cache_test.go
@@ -1,327 +0,0 @@
-package gitlab
-
-import (
-	"encoding/json"
-	"os"
-	"path/filepath"
-	"testing"
-	"time"
-)
-
-func TestCacheBasicOperations(t *testing.T) {
-	// Create temporary cache directory
-	tempDir, err := os.MkdirTemp("", "gitlab-cache-test")
-	if err != nil {
-		t.Fatalf("Failed to create temp dir: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Create cache with test config
-	cache, err := NewCache(CacheConfig{
-		CacheDir:      tempDir,
-		TTL:           1 * time.Minute,
-		MaxEntries:    100,
-		EnableOffline: true,
-		CompressData:  false,
-	})
-	if err != nil {
-		t.Fatalf("Failed to create cache: %v", err)
-	}
-
-	// Test data
-	testData := []byte(`{"id": 1, "title": "Test Issue"}`)
-	endpoint := "/issues"
-	params := map[string]string{"state": "opened"}
-	cacheType := "issues"
-
-	// Test Set operation
-	err = cache.Set(cacheType, endpoint, params, testData, 200)
-	if err != nil {
-		t.Fatalf("Failed to set cache: %v", err)
-	}
-
-	// Test Get operation
-	cached, found := cache.Get(cacheType, endpoint, params)
-	if !found {
-		t.Fatal("Expected to find cached data")
-	}
-
-	// Compare JSON data structure instead of string format
-	var expectedData, cachedData map[string]interface{}
-	if err := json.Unmarshal(testData, &expectedData); err != nil {
-		t.Fatalf("Failed to unmarshal test data: %v", err)
-	}
-	if err := json.Unmarshal(cached, &cachedData); err != nil {
-		t.Fatalf("Failed to unmarshal cached data: %v", err)
-	}
-
-	if expectedData["id"] != cachedData["id"] || expectedData["title"] != cachedData["title"] {
-		t.Errorf("Cached data mismatch. Expected: %v, Got: %v", expectedData, cachedData)
-	}
-
-	// Test cache statistics
-	stats := cache.GetStats()
-	if stats[cacheType] == nil {
-		t.Fatal("Expected cache stats for issues")
-	}
-
-	if stats[cacheType].EntryCount != 1 {
-		t.Errorf("Expected 1 entry, got %d", stats[cacheType].EntryCount)
-	}
-
-	if stats[cacheType].TotalHits != 1 {
-		t.Errorf("Expected 1 hit, got %d", stats[cacheType].TotalHits)
-	}
-}
-
-func TestCacheExpiration(t *testing.T) {
-	// Create temporary cache directory
-	tempDir, err := os.MkdirTemp("", "gitlab-cache-expire-test")
-	if err != nil {
-		t.Fatalf("Failed to create temp dir: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Create cache with very short TTL
-	cache, err := NewCache(CacheConfig{
-		CacheDir:      tempDir,
-		TTL:           10 * time.Millisecond, // Very short TTL
-		MaxEntries:    100,
-		EnableOffline: false, // Disable offline mode for expiration test
-		CompressData:  false,
-	})
-	if err != nil {
-		t.Fatalf("Failed to create cache: %v", err)
-	}
-
-	// Test data
-	testData := []byte(`{"id": 1, "title": "Test Issue"}`)
-	endpoint := "/issues"
-	params := map[string]string{"state": "opened"}
-	cacheType := "issues"
-
-	// Set cache entry
-	err = cache.Set(cacheType, endpoint, params, testData, 200)
-	if err != nil {
-		t.Fatalf("Failed to set cache: %v", err)
-	}
-
-	// Immediately check - should be found
-	_, found := cache.Get(cacheType, endpoint, params)
-	if !found {
-		t.Fatal("Expected to find fresh cached data")
-	}
-
-	// Wait for expiration
-	time.Sleep(20 * time.Millisecond)
-
-	// Check again - should be expired
-	_, found = cache.Get(cacheType, endpoint, params)
-	if found {
-		t.Fatal("Expected cached data to be expired")
-	}
-}
-
-func TestCacheOfflineMode(t *testing.T) {
-	// Create temporary cache directory
-	tempDir, err := os.MkdirTemp("", "gitlab-cache-offline-test")
-	if err != nil {
-		t.Fatalf("Failed to create temp dir: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Create cache with offline mode enabled
-	cache, err := NewCache(CacheConfig{
-		CacheDir:      tempDir,
-		TTL:           10 * time.Millisecond, // Very short TTL
-		MaxEntries:    100,
-		EnableOffline: true, // Enable offline mode
-		CompressData:  false,
-	})
-	if err != nil {
-		t.Fatalf("Failed to create cache: %v", err)
-	}
-
-	// Test data
-	testData := []byte(`{"id": 1, "title": "Test Issue"}`)
-	endpoint := "/issues"
-	params := map[string]string{"state": "opened"}
-	cacheType := "issues"
-
-	// Set cache entry
-	err = cache.Set(cacheType, endpoint, params, testData, 200)
-	if err != nil {
-		t.Fatalf("Failed to set cache: %v", err)
-	}
-
-	// Wait for expiration
-	time.Sleep(20 * time.Millisecond)
-
-	// Check again - should return stale data in offline mode
-	cached, found := cache.Get(cacheType, endpoint, params)
-	if !found {
-		t.Fatal("Expected to find stale cached data in offline mode")
-	}
-
-	// Compare JSON data structure instead of string format
-	var expectedData, cachedData map[string]interface{}
-	if err := json.Unmarshal(testData, &expectedData); err != nil {
-		t.Fatalf("Failed to unmarshal test data: %v", err)
-	}
-	if err := json.Unmarshal(cached, &cachedData); err != nil {
-		t.Fatalf("Failed to unmarshal cached data: %v", err)
-	}
-
-	if expectedData["id"] != cachedData["id"] || expectedData["title"] != cachedData["title"] {
-		t.Errorf("Stale cached data mismatch. Expected: %v, Got: %v", expectedData, cachedData)
-	}
-}
-
-func TestCacheFileStructure(t *testing.T) {
-	// Create temporary cache directory
-	tempDir, err := os.MkdirTemp("", "gitlab-cache-structure-test")
-	if err != nil {
-		t.Fatalf("Failed to create temp dir: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Create cache
-	cache, err := NewCache(CacheConfig{
-		CacheDir:      tempDir,
-		TTL:           1 * time.Minute,
-		MaxEntries:    100,
-		EnableOffline: true,
-		CompressData:  false,
-	})
-	if err != nil {
-		t.Fatalf("Failed to create cache: %v", err)
-	}
-
-	// Test data
-	testData := []byte(`{"id": 1, "title": "Test Issue"}`)
-	endpoint := "/issues"
-	params := map[string]string{"state": "opened"}
-	cacheType := "issues"
-
-	// Set cache entry
-	err = cache.Set(cacheType, endpoint, params, testData, 200)
-	if err != nil {
-		t.Fatalf("Failed to set cache: %v", err)
-	}
-
-	// Check cache directory structure
-	issuesDir := filepath.Join(tempDir, "issues")
-	if _, err := os.Stat(issuesDir); os.IsNotExist(err) {
-		t.Fatal("Expected issues cache directory to exist")
-	}
-
-	// Check for cache files
-	files, err := filepath.Glob(filepath.Join(issuesDir, "**", "*.json"))
-	if err != nil {
-		files, _ = filepath.Glob(filepath.Join(issuesDir, "*.json"))
-	}
-
-	if len(files) == 0 {
-		t.Fatal("Expected at least one cache file")
-	}
-
-	// Read and verify cache file structure
-	cacheFile := files[0]
-	fileData, err := os.ReadFile(cacheFile)
-	if err != nil {
-		t.Fatalf("Failed to read cache file: %v", err)
-	}
-
-	var entry CacheEntry
-	if err := json.Unmarshal(fileData, &entry); err != nil {
-		t.Fatalf("Failed to parse cache entry: %v", err)
-	}
-
-	if entry.StatusCode != 200 {
-		t.Errorf("Expected status code 200, got %d", entry.StatusCode)
-	}
-
-	// Compare JSON data structure instead of string format
-	var expectedData, entryData map[string]interface{}
-	if err := json.Unmarshal(testData, &expectedData); err != nil {
-		t.Fatalf("Failed to unmarshal test data: %v", err)
-	}
-	if err := json.Unmarshal(entry.Data, &entryData); err != nil {
-		t.Fatalf("Failed to unmarshal entry data: %v", err)
-	}
-
-	if expectedData["id"] != entryData["id"] || expectedData["title"] != entryData["title"] {
-		t.Errorf("Cache entry data mismatch. Expected: %v, Got: %v", expectedData, entryData)
-	}
-
-	// Check metadata file
-	metadataFile := filepath.Join(tempDir, "metadata.json")
-	if _, err := os.Stat(metadataFile); os.IsNotExist(err) {
-		t.Fatal("Expected metadata.json to exist")
-	}
-}
-
-func TestCacheClear(t *testing.T) {
-	// Create temporary cache directory
-	tempDir, err := os.MkdirTemp("", "gitlab-cache-clear-test")
-	if err != nil {
-		t.Fatalf("Failed to create temp dir: %v", err)
-	}
-	defer os.RemoveAll(tempDir)
-
-	// Create cache
-	cache, err := NewCache(CacheConfig{
-		CacheDir:      tempDir,
-		TTL:           1 * time.Minute,
-		MaxEntries:    100,
-		EnableOffline: true,
-		CompressData:  false,
-	})
-	if err != nil {
-		t.Fatalf("Failed to create cache: %v", err)
-	}
-
-	// Add test data
-	testData := []byte(`{"id": 1, "title": "Test Issue"}`)
-	endpoint := "/issues"
-	params := map[string]string{"state": "opened"}
-	cacheType := "issues"
-
-	err = cache.Set(cacheType, endpoint, params, testData, 200)
-	if err != nil {
-		t.Fatalf("Failed to set cache: %v", err)
-	}
-
-	// Verify data exists
-	_, found := cache.Get(cacheType, endpoint, params)
-	if !found {
-		t.Fatal("Expected to find cached data before clear")
-	}
-
-	// Clear specific cache type
-	err = cache.Clear(cacheType)
-	if err != nil {
-		t.Fatalf("Failed to clear cache: %v", err)
-	}
-
-	// Verify data is gone
-	_, found = cache.Get(cacheType, endpoint, params)
-	if found {
-		t.Fatal("Expected cached data to be cleared")
-	}
-
-	// Check that directory was removed or has no cache files
-	issuesDir := filepath.Join(tempDir, "issues")
-	if stat, err := os.Stat(issuesDir); err == nil {
-		if stat.IsDir() {
-			// Check for actual cache files (*.json)
-			files, err := filepath.Glob(filepath.Join(issuesDir, "**", "*.json"))
-			if err != nil {
-				files, _ = filepath.Glob(filepath.Join(issuesDir, "*.json"))
-			}
-			if len(files) > 0 {
-				t.Fatalf("Expected no cache files after clear, but found: %v", files)
-			}
-		}
-	}
-}
\ No newline at end of file
pkg/gitlab/server.go
@@ -15,8 +15,8 @@ import (
 	"github.com/xlgmokha/mcp/pkg/mcp"
 )
 
-type Server struct {
-	*mcp.Server
+// GitLabOperations provides GitLab API operations with caching
+type GitLabOperations struct {
 	mu        sync.RWMutex
 	gitlabURL string
 	token     string
@@ -96,9 +96,8 @@ type GitLabUser struct {
 	State    string `json:"state"`
 }
 
-func NewServer(gitlabURL, token string) (*Server, error) {
-	baseServer := mcp.NewServer("gitlab", "0.1.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-	
+// NewGitLabOperations creates a new GitLabOperations helper
+func NewGitLabOperations(gitlabURL, token string) (*GitLabOperations, error) {
 	// Initialize cache with default configuration
 	cache, err := NewCache(CacheConfig{
 		TTL:           5 * time.Minute,
@@ -110,241 +109,210 @@ func NewServer(gitlabURL, token string) (*Server, error) {
 		return nil, fmt.Errorf("failed to initialize cache: %w", err)
 	}
 	
-	server := &Server{
-		Server:    baseServer,
+	return &GitLabOperations{
 		gitlabURL: strings.TrimSuffix(gitlabURL, "/"),
 		token:     token,
 		client: &http.Client{
 			Timeout: 30 * time.Second,
 		},
 		cache: cache,
-	}
-
-	// Register tools
-	server.registerTools()
-
-	return server, nil
+	}, nil
 }
 
-// ListTools returns all available GitLab tools
-func (s *Server) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		{
-			Name:        "gitlab_list_my_projects",
-			Description: "List projects where you are a member, with activity and access level info",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of projects to return",
-						"minimum":     1,
-						"default":     20,
-					},
-					"archived": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include archived projects",
-						"default":     false,
-					},
-				},
+// New creates a new GitLab MCP server
+func New(gitlabURL, token string) (*mcp.Server, error) {
+	gitlab, err := NewGitLabOperations(gitlabURL, token)
+	if err != nil {
+		return nil, err
+	}
+	
+	builder := mcp.NewServerBuilder("gitlab-server", "1.0.0")
+
+	// Add gitlab_list_my_projects tool
+	builder.AddTool(mcp.NewTool("gitlab_list_my_projects", "List projects where you are a member, with activity and access level info", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of projects to return",
+				"minimum":     1,
+				"default":     20,
 			},
-		},
-		{
-			Name:        "gitlab_list_my_issues",
-			Description: "List issues assigned to you, created by you, or where you're mentioned",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"scope": map[string]interface{}{
-						"type":        "string",
-						"description": "Filter scope: assigned_to_me, authored, mentioned, all",
-						"default":     "assigned_to_me",
-						"enum":        []string{"assigned_to_me", "authored", "mentioned", "all"},
-					},
-					"state": map[string]interface{}{
-						"type":        "string",
-						"description": "Issue state filter: opened, closed, all",
-						"default":     "opened",
-						"enum":        []string{"opened", "closed", "all"},
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of issues to return",
-						"minimum":     1,
-						"default":     50,
-					},
-				},
+			"archived": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include archived projects",
+				"default":     false,
 			},
 		},
-		{
-			Name:        "gitlab_get_issue_conversations",
-			Description: "Get full conversation history for a specific issue including notes and system events",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"project_id": map[string]interface{}{
-						"type":        "integer",
-						"description": "GitLab project ID",
-						"minimum":     1,
-					},
-					"issue_iid": map[string]interface{}{
-						"type":        "integer",
-						"description": "Issue internal ID within the project",
-						"minimum":     1,
-					},
-				},
-				"required": []string{"project_id", "issue_iid"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleListMyProjects(req)
+	}))
+
+	// Add gitlab_list_my_issues tool
+	builder.AddTool(mcp.NewTool("gitlab_list_my_issues", "List issues assigned to you, created by you, or where you're mentioned", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"scope": map[string]interface{}{
+				"type":        "string",
+				"description": "Filter scope: assigned_to_me, authored, mentioned, all",
+				"default":     "assigned_to_me",
+				"enum":        []string{"assigned_to_me", "authored", "mentioned", "all"},
+			},
+			"state": map[string]interface{}{
+				"type":        "string",
+				"description": "Issue state filter: opened, closed, all",
+				"default":     "opened",
+				"enum":        []string{"opened", "closed", "all"},
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of issues to return",
+				"minimum":     1,
+				"default":     50,
 			},
 		},
-		{
-			Name:        "gitlab_find_similar_issues",
-			Description: "Find issues similar to a search query across your accessible projects",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"query": map[string]interface{}{
-						"type":        "string",
-						"description": "Search query for finding similar issues",
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of results",
-						"minimum":     1,
-						"default":     20,
-					},
-				},
-				"required": []string{"query"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleListMyIssues(req)
+	}))
+
+	// Add gitlab_get_issue_conversations tool
+	builder.AddTool(mcp.NewTool("gitlab_get_issue_conversations", "Get full conversation history for a specific issue including notes and system events", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"project_id": map[string]interface{}{
+				"type":        "integer",
+				"description": "GitLab project ID",
+				"minimum":     1,
+			},
+			"issue_iid": map[string]interface{}{
+				"type":        "integer",
+				"description": "Issue internal ID within the project",
+				"minimum":     1,
 			},
 		},
-		{
-			Name:        "gitlab_get_my_activity",
-			Description: "Get recent activity summary including commits, issues, merge requests",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of activity events",
-						"minimum":     1,
-						"default":     50,
-					},
-				},
+		"required": []string{"project_id", "issue_iid"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleGetIssueConversations(req)
+	}))
+
+	// Add gitlab_find_similar_issues tool
+	builder.AddTool(mcp.NewTool("gitlab_find_similar_issues", "Find issues similar to a search query across your accessible projects", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"query": map[string]interface{}{
+				"type":        "string",
+				"description": "Search query for finding similar issues",
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of results",
+				"minimum":     1,
+				"default":     20,
 			},
 		},
-		{
-			Name:        "gitlab_cache_stats",
-			Description: "Get cache performance statistics and storage information",
-			InputSchema: map[string]interface{}{
-				"type": "object",
+		"required": []string{"query"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleFindSimilarIssues(req)
+	}))
+
+	// Add gitlab_get_my_activity tool
+	builder.AddTool(mcp.NewTool("gitlab_get_my_activity", "Get recent activity summary including commits, issues, merge requests", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of activity events",
+				"minimum":     1,
+				"default":     50,
 			},
 		},
-		{
-			Name:        "gitlab_cache_clear",
-			Description: "Clear cached data for specific types or all cached data",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"cache_type": map[string]interface{}{
-						"type":        "string",
-						"description": "Type of cache to clear: issues, projects, users, notes, events, search, or empty for all",
-						"enum":        []string{"issues", "projects", "users", "notes", "events", "search"},
-					},
-					"confirm": map[string]interface{}{
-						"type":        "string",
-						"description": "Confirmation string 'true' to proceed with cache clearing",
-					},
-				},
-				"required": []string{"confirm"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleGetMyActivity(req)
+	}))
+
+	// Add gitlab_cache_stats tool
+	builder.AddTool(mcp.NewTool("gitlab_cache_stats", "Get cache performance statistics and storage information", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleCacheStats(req)
+	}))
+
+	// Add gitlab_cache_clear tool
+	builder.AddTool(mcp.NewTool("gitlab_cache_clear", "Clear cached data for specific types or all cached data", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"cache_type": map[string]interface{}{
+				"type":        "string",
+				"description": "Type of cache to clear: issues, projects, users, notes, events, search, or empty for all",
+				"enum":        []string{"issues", "projects", "users", "notes", "events", "search"},
+			},
+			"confirm": map[string]interface{}{
+				"type":        "string",
+				"description": "Confirmation string 'true' to proceed with cache clearing",
 			},
 		},
-		{
-			Name:        "gitlab_offline_query",
-			Description: "Query cached GitLab data when network connectivity is unavailable",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"query_type": map[string]interface{}{
-						"type":        "string",
-						"description": "Type of cached data to query: issues, projects, users, notes, events",
-						"enum":        []string{"issues", "projects", "users", "notes", "events"},
-					},
-					"search": map[string]interface{}{
-						"type":        "string",
-						"description": "Optional search term to filter cached results",
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of results to return",
-						"minimum":     1,
-						"default":     50,
-					},
-				},
-				"required": []string{"query_type"},
+		"required": []string{"confirm"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleCacheClear(req)
+	}))
+
+	// Add gitlab_offline_query tool
+	builder.AddTool(mcp.NewTool("gitlab_offline_query", "Query cached GitLab data when network connectivity is unavailable", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"query_type": map[string]interface{}{
+				"type":        "string",
+				"description": "Type of cached data to query: issues, projects, users, notes, events",
+				"enum":        []string{"issues", "projects", "users", "notes", "events"},
+			},
+			"search": map[string]interface{}{
+				"type":        "string",
+				"description": "Optional search term to filter cached results",
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of results to return",
+				"minimum":     1,
+				"default":     50,
 			},
 		},
-	}
-}
+		"required": []string{"query_type"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return gitlab.handleOfflineQuery(req)
+	}))
 
-// registerTools registers all GitLab tools with the server
-func (s *Server) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := s.ListTools()
-	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "gitlab_list_my_projects":
-			handler = s.handleListMyProjects
-		case "gitlab_list_my_issues":
-			handler = s.handleListMyIssues
-		case "gitlab_get_issue_conversations":
-			handler = s.handleGetIssueConversations
-		case "gitlab_find_similar_issues":
-			handler = s.handleFindSimilarIssues
-		case "gitlab_get_my_activity":
-			handler = s.handleGetMyActivity
-		case "gitlab_cache_stats":
-			handler = s.handleCacheStats
-		case "gitlab_cache_clear":
-			handler = s.handleCacheClear
-		case "gitlab_offline_query":
-			handler = s.handleOfflineQuery
-		default:
-			continue
-		}
-		s.RegisterToolWithDefinition(tool, handler)
-	}
+	return builder.Build(), nil
 }
 
-func (s *Server) makeRequest(method, endpoint string, params map[string]string) ([]byte, error) {
-	return s.makeRequestWithCache(method, endpoint, params, "")
+
+func (gitlab *GitLabOperations) makeRequest(method, endpoint string, params map[string]string) ([]byte, error) {
+	return gitlab.makeRequestWithCache(method, endpoint, params, "")
 }
 
-func (s *Server) makeRequestWithCache(method, endpoint string, params map[string]string, cacheType string) ([]byte, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (gitlab *GitLabOperations) makeRequestWithCache(method, endpoint string, params map[string]string, cacheType string) ([]byte, error) {
+	gitlab.mu.RLock()
+	defer gitlab.mu.RUnlock()
 
 	// Determine cache type from endpoint if not provided
 	if cacheType == "" {
-		cacheType = s.determineCacheType(endpoint)
+		cacheType = gitlab.determineCacheType(endpoint)
 	}
 
 	// Check cache first (only for GET requests)
 	if method == "GET" && cacheType != "" {
-		if cached, found := s.cache.Get(cacheType, endpoint, params); found {
+		if cached, found := gitlab.cache.Get(cacheType, endpoint, params); found {
 			return cached, nil
 		}
 	}
 
-	url := fmt.Sprintf("%s/api/v4%s", s.gitlabURL, endpoint)
+	url := fmt.Sprintf("%s/api/v4%s", gitlab.gitlabURL, endpoint)
 	
 	req, err := http.NewRequest(method, url, nil)
 	if err != nil {
 		return nil, fmt.Errorf("failed to create request: %w", err)
 	}
 
-	req.Header.Set("Authorization", "Bearer "+s.token)
+	req.Header.Set("Authorization", "Bearer "+gitlab.token)
 	req.Header.Set("Content-Type", "application/json")
 
 	// Add query parameters
@@ -356,11 +324,11 @@ func (s *Server) makeRequestWithCache(method, endpoint string, params map[string
 		req.URL.RawQuery = q.Encode()
 	}
 
-	resp, err := s.client.Do(req)
+	resp, err := gitlab.client.Do(req)
 	if err != nil {
 		// If request fails and we have cached data, try to return stale data
-		if method == "GET" && cacheType != "" && s.cache.config.EnableOffline {
-			if cached, found := s.cache.Get(cacheType, endpoint, params); found {
+		if method == "GET" && cacheType != "" && gitlab.cache.config.EnableOffline {
+			if cached, found := gitlab.cache.Get(cacheType, endpoint, params); found {
 				fmt.Fprintf(os.Stderr, "Network error, returning cached data: %v\n", err)
 				return cached, nil
 			}
@@ -381,7 +349,7 @@ func (s *Server) makeRequestWithCache(method, endpoint string, params map[string
 
 	// Cache the response (only for GET requests)
 	if method == "GET" && cacheType != "" {
-		if err := s.cache.Set(cacheType, endpoint, params, body, resp.StatusCode); err != nil {
+		if err := gitlab.cache.Set(cacheType, endpoint, params, body, resp.StatusCode); err != nil {
 			// Log cache error but don't fail the request
 			fmt.Fprintf(os.Stderr, "Failed to cache response: %v\n", err)
 		}
@@ -391,7 +359,7 @@ func (s *Server) makeRequestWithCache(method, endpoint string, params map[string
 }
 
 // determineCacheType maps API endpoints to cache types
-func (s *Server) determineCacheType(endpoint string) string {
+func (gitlab *GitLabOperations) determineCacheType(endpoint string) string {
 	switch {
 	case strings.Contains(endpoint, "/issues"):
 		return "issues"
@@ -410,8 +378,8 @@ func (s *Server) determineCacheType(endpoint string) string {
 	}
 }
 
-func (s *Server) getCurrentUser() (*GitLabUser, error) {
-	body, err := s.makeRequest("GET", "/user", nil)
+func (gitlab *GitLabOperations) getCurrentUser() (*GitLabUser, error) {
+	body, err := gitlab.makeRequest("GET", "/user", nil)
 	if err != nil {
 		return nil, err
 	}
@@ -424,7 +392,7 @@ func (s *Server) getCurrentUser() (*GitLabUser, error) {
 	return &user, nil
 }
 
-func (s *Server) handleListMyProjects(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleListMyProjects(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	// Parse optional parameters
@@ -461,7 +429,7 @@ func (s *Server) handleListMyProjects(req mcp.CallToolRequest) (mcp.CallToolResu
 	}
 
 	// Make API request
-	body, err := s.makeRequest("GET", "/projects", params)
+	body, err := gitlab.makeRequest("GET", "/projects", params)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch projects: %v", err)), nil
 	}
@@ -499,7 +467,7 @@ func (s *Server) handleListMyProjects(req mcp.CallToolRequest) (mcp.CallToolResu
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleListMyIssues(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleListMyIssues(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	// Parse optional parameters
@@ -524,7 +492,7 @@ func (s *Server) handleListMyIssues(req mcp.CallToolRequest) (mcp.CallToolResult
 	searchTerm, _ := args["search"].(string)
 
 	// Get current user for filtering
-	user, err := s.getCurrentUser()
+	user, err := gitlab.getCurrentUser()
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get current user: %v", err)), nil
 	}
@@ -554,7 +522,7 @@ func (s *Server) handleListMyIssues(req mcp.CallToolRequest) (mcp.CallToolResult
 	}
 
 	// Make API request
-	body, err := s.makeRequest("GET", "/issues", params)
+	body, err := gitlab.makeRequest("GET", "/issues", params)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch issues: %v", err)), nil
 	}
@@ -621,7 +589,7 @@ func extractProjectFromURL(webURL string) string {
 	return "Unknown"
 }
 
-func (s *Server) handleGetIssueConversations(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleGetIssueConversations(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	projectIDStr, ok := args["project_id"].(string)
@@ -648,7 +616,7 @@ func (s *Server) handleGetIssueConversations(req mcp.CallToolRequest) (mcp.CallT
 
 	// First, get the issue details
 	issueEndpoint := fmt.Sprintf("/projects/%d/issues/%d", projectID, issueIID)
-	issueBody, err := s.makeRequest("GET", issueEndpoint, nil)
+	issueBody, err := gitlab.makeRequest("GET", issueEndpoint, nil)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch issue: %v", err)), nil
 	}
@@ -665,7 +633,7 @@ func (s *Server) handleGetIssueConversations(req mcp.CallToolRequest) (mcp.CallT
 		"sort":     "asc",
 	}
 
-	notesBody, err := s.makeRequest("GET", notesEndpoint, notesParams)
+	notesBody, err := gitlab.makeRequest("GET", notesEndpoint, notesParams)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch issue notes: %v", err)), nil
 	}
@@ -741,7 +709,7 @@ func (s *Server) handleGetIssueConversations(req mcp.CallToolRequest) (mcp.CallT
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleFindSimilarIssues(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleFindSimilarIssues(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	searchQuery, ok := args["query"].(string)
@@ -776,7 +744,7 @@ func (s *Server) handleFindSimilarIssues(req mcp.CallToolRequest) (mcp.CallToolR
 	}
 
 	// Make search API request
-	body, err := s.makeRequest("GET", "/search", params)
+	body, err := gitlab.makeRequest("GET", "/search", params)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to search issues: %v", err)), nil
 	}
@@ -864,7 +832,7 @@ func (s *Server) handleFindSimilarIssues(req mcp.CallToolRequest) (mcp.CallToolR
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	// Parse optional parameters
@@ -885,7 +853,7 @@ func (s *Server) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResul
 	}
 
 	// Get current user
-	user, err := s.getCurrentUser()
+	user, err := gitlab.getCurrentUser()
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to get current user: %v", err)), nil
 	}
@@ -903,7 +871,7 @@ func (s *Server) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResul
 		"updated_after":     since,
 	}
 
-	assignedBody, err := s.makeRequest("GET", "/issues", assignedParams)
+	assignedBody, err := gitlab.makeRequest("GET", "/issues", assignedParams)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch assigned issues: %v", err)), nil
 	}
@@ -921,7 +889,7 @@ func (s *Server) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResul
 		"updated_after":   since,
 	}
 
-	authoredBody, err := s.makeRequest("GET", "/issues", authoredParams)
+	authoredBody, err := gitlab.makeRequest("GET", "/issues", authoredParams)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to fetch authored issues: %v", err)), nil
 	}
@@ -934,7 +902,7 @@ func (s *Server) handleGetMyActivity(req mcp.CallToolRequest) (mcp.CallToolResul
 		"per_page": "20",
 	}
 
-	activityBody, err := s.makeRequest("GET", fmt.Sprintf("/users/%d/events", user.ID), activityParams)
+	activityBody, err := gitlab.makeRequest("GET", fmt.Sprintf("/users/%d/events", user.ID), activityParams)
 	if err != nil {
 		// Activity endpoint might not be available, continue without it
 		activityBody = []byte("[]")
@@ -1058,8 +1026,8 @@ func min(a, b int) int {
 
 // Cache management tools
 
-func (s *Server) handleCacheStats(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	stats := s.cache.GetStats()
+func (gitlab *GitLabOperations) handleCacheStats(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	stats := gitlab.cache.GetStats()
 	
 	result := "**GitLab MCP Cache Statistics**\n\n"
 	
@@ -1101,7 +1069,7 @@ func (s *Server) handleCacheStats(req mcp.CallToolRequest) (mcp.CallToolResult,
 		result += fmt.Sprintf("โ€ข Total Hits: %d\n", totalHits)
 		result += fmt.Sprintf("โ€ข Total Misses: %d\n", totalMisses)
 		result += fmt.Sprintf("โ€ข Overall Hit Rate: %.1f%%\n", overallHitRate)
-		result += fmt.Sprintf("โ€ข Cache Directory: %s\n", s.cache.config.CacheDir)
+		result += fmt.Sprintf("โ€ข Cache Directory: %s\n", gitlab.cache.config.CacheDir)
 	}
 	
 	if len(stats) == 0 {
@@ -1111,7 +1079,7 @@ func (s *Server) handleCacheStats(req mcp.CallToolRequest) (mcp.CallToolResult,
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleCacheClear(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleCacheClear(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	cacheType, _ := args["cache_type"].(string)
@@ -1141,7 +1109,7 @@ func (s *Server) handleCacheClear(req mcp.CallToolRequest) (mcp.CallToolResult,
 	}
 	
 	// Perform the cache clear
-	if err := s.cache.Clear(cacheType); err != nil {
+	if err := gitlab.cache.Clear(cacheType); err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to clear cache: %v", err)), nil
 	}
 	
@@ -1157,7 +1125,7 @@ func (s *Server) handleCacheClear(req mcp.CallToolRequest) (mcp.CallToolResult,
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleOfflineQuery(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (gitlab *GitLabOperations) handleOfflineQuery(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	queryType, ok := args["query_type"].(string)
@@ -1176,7 +1144,7 @@ func (s *Server) handleOfflineQuery(req mcp.CallToolRequest) (mcp.CallToolResult
 	
 	// This is a simplified offline query - in a full implementation,
 	// you would scan cached files and perform local filtering
-	cacheDir := filepath.Join(s.cache.config.CacheDir, queryType)
+	cacheDir := filepath.Join(gitlab.cache.config.CacheDir, queryType)
 	
 	if _, err := os.Stat(cacheDir); os.IsNotExist(err) {
 		result := fmt.Sprintf("**Offline Query: %s**\n\n", strings.Title(queryType))
pkg/htmlprocessor/processor_test.go
@@ -1,139 +0,0 @@
-package htmlprocessor
-
-import (
-	"strings"
-	"testing"
-)
-
-func TestContentExtractor_ExtractReadableContent(t *testing.T) {
-	extractor := NewContentExtractor()
-
-	tests := []struct {
-		name     string
-		html     string
-		expected string
-	}{
-		{
-			name: "simple article with header and paragraph",
-			html: `
-				<html>
-					<head><title>Test Article</title></head>
-					<body>
-						<header>
-							<nav>Navigation</nav>
-						</header>
-						<main>
-							<h1>Main Title</h1>
-							<p>This is the main content that should be extracted.</p>
-							<p>Another paragraph with important information.</p>
-						</main>
-						<footer>Footer content</footer>
-					</body>
-				</html>
-			`,
-			expected: "Main Title\nThis is the main content that should be extracted.\nAnother paragraph with important information.",
-		},
-		{
-			name: "article with sidebar and ads",
-			html: `
-				<html>
-					<body>
-						<aside class="sidebar">Sidebar content</aside>
-						<div class="ads">Advertisement</div>
-						<article>
-							<h2>Article Title</h2>
-							<p>Article content here.</p>
-						</article>
-					</body>
-				</html>
-			`,
-			expected: "Article Title\nArticle content here.",
-		},
-		{
-			name: "content with script and style tags",
-			html: `
-				<html>
-					<head>
-						<style>body { color: red; }</style>
-					</head>
-					<body>
-						<h1>Clean Title</h1>
-						<script>console.log('should be removed');</script>
-						<p>Clean paragraph.</p>
-						<style>.hidden { display: none; }</style>
-					</body>
-				</html>
-			`,
-			expected: "Clean Title\nClean paragraph.",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result, err := extractor.ExtractReadableContent(tt.html)
-			if err != nil {
-				t.Fatalf("ExtractReadableContent() error = %v", err)
-			}
-
-			// Normalize whitespace for comparison
-			result = strings.TrimSpace(strings.ReplaceAll(result, "\n\n", "\n"))
-			expected := strings.TrimSpace(strings.ReplaceAll(tt.expected, "\n\n", "\n"))
-
-			if result != expected {
-				t.Errorf("ExtractReadableContent() = %q, want %q", result, expected)
-			}
-		})
-	}
-}
-
-func TestContentExtractor_ToMarkdown(t *testing.T) {
-	extractor := NewContentExtractor()
-
-	tests := []struct {
-		name     string
-		html     string
-		expected string
-	}{
-		{
-			name:     "basic formatting",
-			html:     `<h1>Title</h1><p>Paragraph with <strong>bold</strong> and <em>italic</em> text.</p>`,
-			expected: "# Title\n\nParagraph with **bold** and _italic_ text.",
-		},
-		{
-			name: "lists",
-			html: `
-				<ul>
-					<li>First item</li>
-					<li>Second item</li>
-				</ul>
-				<ol>
-					<li>Numbered first</li>
-					<li>Numbered second</li>
-				</ol>
-			`,
-			expected: "- First item\n- Second item\n\n1. Numbered first\n2. Numbered second",
-		},
-		{
-			name:     "links and code",
-			html:     `<p>Visit <a href="https://example.com">Example</a> for <code>code samples</code>.</p>`,
-			expected: "Visit [Example](https://example.com) for `code samples`.",
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			result, err := extractor.ToMarkdown(tt.html)
-			if err != nil {
-				t.Fatalf("ToMarkdown() error = %v", err)
-			}
-
-			// Normalize whitespace for comparison
-			result = strings.TrimSpace(result)
-			expected := strings.TrimSpace(tt.expected)
-
-			if result != expected {
-				t.Errorf("ToMarkdown() = %q, want %q", result, expected)
-			}
-		})
-	}
-}
pkg/imap/server.go
@@ -79,7 +79,7 @@ func NewImapOperations(server, username, password string, port int, useTLS bool)
 // New creates a new IMAP MCP server
 func New(server, username, password string, port int, useTLS bool) *mcp.Server {
 	imap := NewImapOperations(server, username, password, port, useTLS)
-	builder := mcp.NewServerBuilder("imap-server", "1.0.0")
+	builder := mcp.NewServerBuilder("imap", "1.0.0")
 
 	// Add imap_list_folders tool
 	builder.AddTool(mcp.NewTool("imap_list_folders", "List all folders in the email account (INBOX, Sent, Drafts, etc.)", map[string]interface{}{
pkg/maildir/server.go
@@ -205,6 +205,15 @@ func New(allowedPaths []string) *mcp.Server {
 		return maildir.handleGetStatistics(req)
 	}))
 
+	// Add maildir resources  
+	builder.AddResource(mcp.NewResource("maildir://folders", "Maildir Folders", "Browse available maildir folders and structures", func(req mcp.ReadResourceRequest) (mcp.ReadResourceResult, error) {
+		return mcp.ReadResourceResult{
+			Contents: []mcp.Content{
+				mcp.NewTextContent("Use maildir_scan_folders tool to explore available maildir folders and their message counts"),
+			},
+		}, nil
+	}))
+
 	return builder.Build()
 }
 
pkg/mcp/prompts_test.go
@@ -1,149 +0,0 @@
-package mcp
-
-import (
-	"reflect"
-	"testing"
-)
-
-func TestPrompt_Creation(t *testing.T) {
-	prompt := &Prompt{
-		Name:        "test-prompt",
-		Description: "A test prompt",
-		Arguments: []PromptArgument{
-			{
-				Name:        "input",
-				Description: "Input parameter",
-				Required:    true,
-			},
-			{
-				Name:        "optional",
-				Description: "Optional parameter",
-				Required:    false,
-			},
-		},
-	}
-
-	if prompt.Name != "test-prompt" {
-		t.Errorf("Expected name 'test-prompt', got %s", prompt.Name)
-	}
-	if len(prompt.Arguments) != 2 {
-		t.Errorf("Expected 2 arguments, got %d", len(prompt.Arguments))
-	}
-	if !prompt.Arguments[0].Required {
-		t.Error("Expected first argument to be required")
-	}
-	if prompt.Arguments[1].Required {
-		t.Error("Expected second argument to be optional")
-	}
-}
-
-func TestListPromptsResult_Empty(t *testing.T) {
-	result := ListPromptsResult{
-		Prompts: []Prompt{},
-	}
-
-	if len(result.Prompts) != 0 {
-		t.Errorf("Expected 0 prompts, got %d", len(result.Prompts))
-	}
-}
-
-func TestListPromptsResult_WithPrompts(t *testing.T) {
-	prompts := []Prompt{
-		{Name: "prompt1", Description: "First prompt"},
-		{Name: "prompt2", Description: "Second prompt"},
-	}
-
-	result := ListPromptsResult{
-		Prompts: prompts,
-	}
-
-	if len(result.Prompts) != 2 {
-		t.Errorf("Expected 2 prompts, got %d", len(result.Prompts))
-	}
-	if result.Prompts[0].Name != "prompt1" {
-		t.Errorf("Expected first prompt name 'prompt1', got %s", result.Prompts[0].Name)
-	}
-}
-
-func TestGetPromptRequest_Creation(t *testing.T) {
-	req := GetPromptRequest{
-		Name: "test-prompt",
-		Arguments: map[string]interface{}{
-			"input": "test value",
-			"count": 42,
-		},
-	}
-
-	if req.Name != "test-prompt" {
-		t.Errorf("Expected name 'test-prompt', got %s", req.Name)
-	}
-	if req.Arguments["input"] != "test value" {
-		t.Errorf("Expected input 'test value', got %v", req.Arguments["input"])
-	}
-	if req.Arguments["count"] != 42 {
-		t.Errorf("Expected count 42, got %v", req.Arguments["count"])
-	}
-}
-
-func TestGetPromptResult_WithMessages(t *testing.T) {
-	messages := []PromptMessage{
-		{Role: "user", Content: NewTextContent("Hello")},
-		{Role: "assistant", Content: NewTextContent("Hi there!")},
-	}
-
-	result := GetPromptResult{
-		Description: "Test conversation",
-		Messages:    messages,
-	}
-
-	if result.Description != "Test conversation" {
-		t.Errorf("Expected description 'Test conversation', got %s", result.Description)
-	}
-	if len(result.Messages) != 2 {
-		t.Errorf("Expected 2 messages, got %d", len(result.Messages))
-	}
-	if result.Messages[0].Role != "user" {
-		t.Errorf("Expected first message role 'user', got %s", result.Messages[0].Role)
-	}
-}
-
-func TestPromptMessage_Types(t *testing.T) {
-	tests := []struct {
-		name     string
-		role     string
-		content  Content
-		expected PromptMessage
-	}{
-		{
-			name:    "user message",
-			role:    "user",
-			content: NewTextContent("What is the weather?"),
-			expected: PromptMessage{
-				Role:    "user",
-				Content: NewTextContent("What is the weather?"),
-			},
-		},
-		{
-			name:    "assistant message",
-			role:    "assistant",
-			content: NewTextContent("The weather is sunny."),
-			expected: PromptMessage{
-				Role:    "assistant",
-				Content: NewTextContent("The weather is sunny."),
-			},
-		},
-	}
-
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			msg := PromptMessage{
-				Role:    tt.role,
-				Content: tt.content,
-			}
-
-			if !reflect.DeepEqual(msg, tt.expected) {
-				t.Errorf("Expected %+v, got %+v", tt.expected, msg)
-			}
-		})
-	}
-}
pkg/mcp/resources_test.go
@@ -1,127 +0,0 @@
-package mcp
-
-import (
-	"testing"
-)
-
-func TestResource_Creation(t *testing.T) {
-	resource := Resource{
-		URI:         "file:///home/user/document.txt",
-		Name:        "document.txt",
-		Description: "A sample text document",
-		MimeType:    "text/plain",
-	}
-
-	if resource.URI != "file:///home/user/document.txt" {
-		t.Errorf("Expected URI 'file:///home/user/document.txt', got %s", resource.URI)
-	}
-	if resource.Name != "document.txt" {
-		t.Errorf("Expected name 'document.txt', got %s", resource.Name)
-	}
-	if resource.MimeType != "text/plain" {
-		t.Errorf("Expected mime type 'text/plain', got %s", resource.MimeType)
-	}
-}
-
-func TestListResourcesResult_Empty(t *testing.T) {
-	result := ListResourcesResult{
-		Resources: []Resource{},
-	}
-
-	if len(result.Resources) != 0 {
-		t.Errorf("Expected 0 resources, got %d", len(result.Resources))
-	}
-}
-
-func TestListResourcesResult_WithResources(t *testing.T) {
-	resources := []Resource{
-		{URI: "file:///file1.txt", Name: "file1.txt"},
-		{URI: "git://repo/branch/file.go", Name: "file.go"},
-		{URI: "memory://entity/123", Name: "Entity 123"},
-	}
-
-	result := ListResourcesResult{
-		Resources: resources,
-	}
-
-	if len(result.Resources) != 3 {
-		t.Errorf("Expected 3 resources, got %d", len(result.Resources))
-	}
-	if result.Resources[0].URI != "file:///file1.txt" {
-		t.Errorf("Expected first resource URI 'file:///file1.txt', got %s", result.Resources[0].URI)
-	}
-}
-
-func TestReadResourceRequest_Creation(t *testing.T) {
-	req := ReadResourceRequest{
-		URI: "file:///path/to/resource",
-	}
-
-	if req.URI != "file:///path/to/resource" {
-		t.Errorf("Expected URI 'file:///path/to/resource', got %s", req.URI)
-	}
-}
-
-func TestReadResourceResult_WithContent(t *testing.T) {
-	contents := []Content{
-		NewTextContent("File content here"),
-	}
-
-	result := ReadResourceResult{
-		Contents: contents,
-	}
-
-	if len(result.Contents) != 1 {
-		t.Errorf("Expected 1 content item, got %d", len(result.Contents))
-	}
-
-	if textContent, ok := result.Contents[0].(TextContent); ok {
-		if textContent.Text != "File content here" {
-			t.Errorf("Expected content 'File content here', got %s", textContent.Text)
-		}
-	} else {
-		t.Error("Expected TextContent type")
-	}
-}
-
-func TestResourceSchemes(t *testing.T) {
-	testCases := []struct {
-		name     string
-		uri      string
-		expected string
-	}{
-		{"file scheme", "file:///home/user/doc.txt", "file"},
-		{"git scheme", "git://repo/main/src/file.go", "git"},
-		{"memory scheme", "memory://graph/entity/123", "memory"},
-		{"http scheme", "http://example.com/resource", "http"},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			resource := Resource{
-				URI:  tc.uri,
-				Name: "Test Resource",
-			}
-
-			// Extract scheme from URI
-			scheme := ""
-			if idx := indexOf(resource.URI, "://"); idx != -1 {
-				scheme = resource.URI[:idx]
-			}
-
-			if scheme != tc.expected {
-				t.Errorf("Expected scheme '%s', got '%s'", tc.expected, scheme)
-			}
-		})
-	}
-}
-
-// Helper function for testing
-func indexOf(s, substr string) int {
-	for i := 0; i <= len(s)-len(substr); i++ {
-		if s[i:i+len(substr)] == substr {
-			return i
-		}
-	}
-	return -1
-}
pkg/mcp/roots_integration_test.go
@@ -1,218 +0,0 @@
-package mcp
-
-import (
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
-	"testing"
-	"time"
-)
-
-func TestRootsIntegration_FilesystemServer(t *testing.T) {
-	// This test would require importing the filesystem package, which might cause import cycles
-	// So we'll test the root functionality at the MCP level
-	server := NewServer("test-filesystem", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Simulate filesystem server registering roots
-	tempDir := t.TempDir()
-	homeDir := filepath.Join(tempDir, "home")
-	projectsDir := filepath.Join(tempDir, "projects")
-
-	// Create directories
-	os.MkdirAll(homeDir, 0755)
-	os.MkdirAll(projectsDir, 0755)
-
-	// Register roots like filesystem server would
-	homeRoot := NewRoot("file://"+homeDir, "Home Directory")
-	projectsRoot := NewRoot("file://"+projectsDir, "Projects Directory")
-
-	server.RegisterRoot(homeRoot)
-	server.RegisterRoot(projectsRoot)
-
-	// Test listing roots
-	roots := server.ListRoots()
-	if len(roots) != 2 {
-		t.Errorf("Expected 2 roots, got %d", len(roots))
-	}
-
-	// Verify root URIs
-	rootURIs := make(map[string]bool)
-	for _, root := range roots {
-		rootURIs[root.URI] = true
-	}
-
-	expectedURIs := []string{"file://" + homeDir, "file://" + projectsDir}
-	for _, expectedURI := range expectedURIs {
-		if !rootURIs[expectedURI] {
-			t.Errorf("Expected root URI %s not found", expectedURI)
-		}
-	}
-}
-
-func TestRootsIntegration_GitServer(t *testing.T) {
-	server := NewServer("test-git", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Simulate git server registering repository root
-	repoPath := "/path/to/repository"
-	currentBranch := "main"
-
-	gitRoot := NewRoot("git://"+repoPath, "Git Repository: repository (branch: "+currentBranch+")")
-	server.RegisterRoot(gitRoot)
-
-	// Test listing roots
-	roots := server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root, got %d", len(roots))
-	}
-
-	if roots[0].URI != "git://"+repoPath {
-		t.Errorf("Expected git root URI git://%s, got %s", repoPath, roots[0].URI)
-	}
-
-	if !strings.Contains(roots[0].Name, "repository") {
-		t.Errorf("Expected root name to contain 'repository', got %s", roots[0].Name)
-	}
-
-	if !strings.Contains(roots[0].Name, currentBranch) {
-		t.Errorf("Expected root name to contain branch '%s', got %s", currentBranch, roots[0].Name)
-	}
-}
-
-func TestRootsIntegration_MemoryServer(t *testing.T) {
-	server := NewServer("test-memory", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Simulate memory server registering knowledge graph root
-	memoryRoot := NewRoot("memory://graph", "Knowledge Graph (5 entities, 3 relations)")
-	server.RegisterRoot(memoryRoot)
-
-	// Test listing roots
-	roots := server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root, got %d", len(roots))
-	}
-
-	if roots[0].URI != "memory://graph" {
-		t.Errorf("Expected memory root URI memory://graph, got %s", roots[0].URI)
-	}
-
-	if !strings.Contains(roots[0].Name, "Knowledge Graph") {
-		t.Errorf("Expected root name to contain 'Knowledge Graph', got %s", roots[0].Name)
-	}
-
-	if !strings.Contains(roots[0].Name, "5 entities") {
-		t.Errorf("Expected root name to contain entity count, got %s", roots[0].Name)
-	}
-
-	if !strings.Contains(roots[0].Name, "3 relations") {
-		t.Errorf("Expected root name to contain relation count, got %s", roots[0].Name)
-	}
-}
-
-func TestRootsIntegration_MultipleServers(t *testing.T) {
-	// Simulate a scenario where multiple server types register roots with the same base server
-	server := NewServer("multi-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Filesystem roots
-	server.RegisterRoot(NewRoot("file:///home/user", "Home Directory"))
-	server.RegisterRoot(NewRoot("file:///projects", "Projects"))
-
-	// Git roots  
-	server.RegisterRoot(NewRoot("git:///path/to/repo", "Git Repository: repo (branch: main)"))
-
-	// Memory roots
-	server.RegisterRoot(NewRoot("memory://graph", "Knowledge Graph (10 entities, 15 relations)"))
-
-	// Test listing all roots
-	roots := server.ListRoots()
-	if len(roots) != 4 {
-		t.Errorf("Expected 4 roots, got %d", len(roots))
-	}
-
-	// Verify we have roots of different types
-	schemeCount := make(map[string]int)
-	for _, root := range roots {
-		if strings.HasPrefix(root.URI, "file://") {
-			schemeCount["file"]++
-		} else if strings.HasPrefix(root.URI, "git://") {
-			schemeCount["git"]++
-		} else if strings.HasPrefix(root.URI, "memory://") {
-			schemeCount["memory"]++
-		}
-	}
-
-	if schemeCount["file"] != 2 {
-		t.Errorf("Expected 2 file:// roots, got %d", schemeCount["file"])
-	}
-
-	if schemeCount["git"] != 1 {
-		t.Errorf("Expected 1 git:// root, got %d", schemeCount["git"])
-	}
-
-	if schemeCount["memory"] != 1 {
-		t.Errorf("Expected 1 memory:// root, got %d", schemeCount["memory"])
-	}
-}
-
-func TestRootsIntegration_DynamicUpdates(t *testing.T) {
-	server := NewServer("dynamic-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Initially no roots
-	roots := server.ListRoots()
-	if len(roots) != 0 {
-		t.Errorf("Expected 0 initial roots, got %d", len(roots))
-	}
-
-	// Add a root
-	server.RegisterRoot(NewRoot("memory://graph", "Knowledge Graph (0 entities, 0 relations)"))
-
-	roots = server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root after registration, got %d", len(roots))
-	}
-
-	// Simulate updating the memory graph (like when entities are added)
-	// This would normally happen automatically in the memory server's saveGraph method
-	server.RegisterRoot(NewRoot("memory://graph", "Knowledge Graph (5 entities, 3 relations)"))
-
-	roots = server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root after update (should overwrite), got %d", len(roots))
-	}
-
-	// Verify the root was updated
-	if !strings.Contains(roots[0].Name, "5 entities") {
-		t.Errorf("Expected updated entity count in root name, got %s", roots[0].Name)
-	}
-}
-
-func TestRootsIntegration_Concurrency(t *testing.T) {
-	server := NewServer("concurrent-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Test concurrent root registration
-	done := make(chan bool, 10)
-	for i := 0; i < 10; i++ {
-		go func(id int) {
-			uri := fmt.Sprintf("file:///test/%d", id)
-			name := fmt.Sprintf("Test Directory %d", id)
-			server.RegisterRoot(NewRoot(uri, name))
-			done <- true
-		}(i)
-	}
-
-	// Wait for all goroutines to complete
-	for i := 0; i < 10; i++ {
-		select {
-		case <-done:
-			// Good
-		case <-time.After(5 * time.Second):
-			t.Fatal("Timeout waiting for concurrent root registration")
-		}
-	}
-
-	// Verify all roots were registered
-	roots := server.ListRoots()
-	if len(roots) != 10 {
-		t.Errorf("Expected 10 roots after concurrent registration, got %d", len(roots))
-	}
-}
\ No newline at end of file
pkg/mcp/roots_test.go
@@ -1,123 +0,0 @@
-package mcp
-
-import (
-	"testing"
-)
-
-func TestRoot_Creation(t *testing.T) {
-	root := Root{
-		URI:  "file:///home/user/projects",
-		Name: "Projects Directory",
-	}
-
-	if root.URI != "file:///home/user/projects" {
-		t.Errorf("Expected URI 'file:///home/user/projects', got %s", root.URI)
-	}
-
-	if root.Name != "Projects Directory" {
-		t.Errorf("Expected Name 'Projects Directory', got %s", root.Name)
-	}
-}
-
-func TestNewRoot(t *testing.T) {
-	uri := "git:///path/to/repo"
-	name := "My Repository"
-
-	root := NewRoot(uri, name)
-
-	if root.URI != uri {
-		t.Errorf("Expected URI %s, got %s", uri, root.URI)
-	}
-
-	if root.Name != name {
-		t.Errorf("Expected Name %s, got %s", name, root.Name)
-	}
-}
-
-func TestListRootsResult_Empty(t *testing.T) {
-	result := ListRootsResult{
-		Roots: []Root{},
-	}
-
-	if len(result.Roots) != 0 {
-		t.Errorf("Expected empty roots list, got %d roots", len(result.Roots))
-	}
-}
-
-func TestListRootsResult_WithRoots(t *testing.T) {
-	roots := []Root{
-		{URI: "file:///home/user", Name: "Home Directory"},
-		{URI: "git:///path/to/repo", Name: "My Repository"},
-		{URI: "memory://graph", Name: "Knowledge Graph"},
-	}
-
-	result := ListRootsResult{
-		Roots: roots,
-	}
-
-	if len(result.Roots) != 3 {
-		t.Errorf("Expected 3 roots, got %d", len(result.Roots))
-	}
-
-	expectedURIs := []string{
-		"file:///home/user",
-		"git:///path/to/repo",
-		"memory://graph",
-	}
-
-	for i, root := range result.Roots {
-		if root.URI != expectedURIs[i] {
-			t.Errorf("Expected root %d URI %s, got %s", i, expectedURIs[i], root.URI)
-		}
-	}
-}
-
-func TestRootSchemes(t *testing.T) {
-	testCases := []struct {
-		name     string
-		uri      string
-		rootName string
-		scheme   string
-	}{
-		{
-			name:     "file scheme",
-			uri:      "file:///home/user/documents",
-			rootName: "Documents",
-			scheme:   "file",
-		},
-		{
-			name:     "git scheme",
-			uri:      "git:///repositories/myproject",
-			rootName: "My Project",
-			scheme:   "git",
-		},
-		{
-			name:     "memory scheme",
-			uri:      "memory://knowledge-graph",
-			rootName: "Knowledge Graph",
-			scheme:   "memory",
-		},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			root := NewRoot(tc.uri, tc.rootName)
-
-			if root.URI != tc.uri {
-				t.Errorf("Expected URI %s, got %s", tc.uri, root.URI)
-			}
-
-			if root.Name != tc.rootName {
-				t.Errorf("Expected Name %s, got %s", tc.rootName, root.Name)
-			}
-		})
-	}
-}
-
-func TestListRootsRequest_Creation(t *testing.T) {
-	req := ListRootsRequest{}
-
-	// ListRootsRequest should be an empty struct as it has no parameters
-	// This test just verifies the struct can be created
-	_ = req
-}
\ No newline at end of file
pkg/mcp/server.go
@@ -141,83 +141,9 @@ func (b *ServerBuilder) Build() *Server {
 	return server
 }
 
-// NewServer creates a new MCP server (deprecated - use ServerBuilder instead)
-func NewServer(name, version string, tools []Tool, resources []Resource, roots []Root) *Server {
-	server := &Server{
-		name:                name,
-		version:             version,
-		toolDefinitions:     make(map[string]Tool),
-		promptDefinitions:   make(map[string]Prompt),
-		resourceDefinitions: make(map[string]Resource),
-		rootDefinitions:     make(map[string]Root),
-		capabilities: ServerCapabilities{
-			Tools:     &ToolsCapability{},
-			Prompts:   &PromptsCapability{},
-			Resources: &ResourcesCapability{},
-			Roots:     &RootsCapability{},
-			Logging:   &LoggingCapability{},
-		},
-	}
-
-	for _, tool := range tools {
-		server.toolDefinitions[tool.Name] = tool
-	}
-
-	for _, resource := range resources {
-		server.resourceDefinitions[resource.URI] = resource
-	}
-
-	for _, root := range roots {
-		server.rootDefinitions[root.URI] = root
-	}
-
-	return server
-}
 
-// RegisterRoot registers a root with the server (immutable servers should use ServerBuilder)
-func (s *Server) RegisterRoot(root Root) {
-	s.rootDefinitions[root.URI] = root
-}
 
-// Compatibility methods for existing servers (deprecated - use ServerBuilder instead)
 
-// RegisterToolWithDefinition registers a tool with its full definition and handler
-func (s *Server) RegisterToolWithDefinition(tool Tool, handler ToolHandler) {
-	tool.Handler = handler
-	s.toolDefinitions[tool.Name] = tool
-}
-
-// RegisterPrompt registers a prompt with its definition and handler
-func (s *Server) RegisterPrompt(prompt Prompt, handler PromptHandler) {
-	prompt.Handler = handler
-	s.promptDefinitions[prompt.Name] = prompt
-}
-
-// RegisterResource registers a resource handler with minimal definition
-func (s *Server) RegisterResource(uri string, handler ResourceHandler) {
-	name := extractResourceName(uri)
-	if name == "" {
-		name = uri // Use the full URI as name if extraction fails
-	}
-	resource := Resource{
-		URI:     uri,
-		Name:    name,
-		Handler: handler,
-	}
-	s.RegisterResourceWithDefinition(resource, handler)
-}
-
-// RegisterResourceWithDefinition registers a resource with its full definition and handler
-func (s *Server) RegisterResourceWithDefinition(resource Resource, handler ResourceHandler) {
-	resource.Handler = handler
-	s.resourceDefinitions[resource.URI] = resource
-}
-
-// SetCustomRequestHandler sets custom request handlers for overriding default behavior
-func (s *Server) SetCustomRequestHandler(handlers map[string]func(JSONRPCRequest) JSONRPCResponse) {
-	// For now, just log that this is deprecated - we removed custom handlers from immutable architecture
-	// This is a compatibility shim
-}
 
 // SetInitializeHandler sets the initialize handler
 func (s *Server) SetInitializeHandler(handler func(InitializeRequest) (InitializeResult, error)) {
@@ -544,14 +470,3 @@ func NewPrompt(name, description string, arguments []PromptArgument, handler Pro
 	}
 }
 
-// Helper function to extract resource name from URI
-func extractResourceName(uri string) string {
-	// Find the last "/" in the URI and extract the part after it
-	for i := len(uri) - 1; i >= 0; i-- {
-		if uri[i] == '/' {
-			return uri[i+1:]
-		}
-	}
-	// If no "/" found, return the entire URI
-	return uri
-}
pkg/mcp/server_prompts_test.go
@@ -1,113 +0,0 @@
-package mcp
-
-import (
-	"testing"
-)
-
-func TestServer_RegisterPrompt(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	prompt := Prompt{
-		Name:        "test-prompt",
-		Description: "A test prompt for verification",
-		Arguments: []PromptArgument{
-			{
-				Name:        "input",
-				Description: "Input parameter",
-				Required:    true,
-			},
-		},
-	}
-
-	handler := func(req GetPromptRequest) (GetPromptResult, error) {
-		return GetPromptResult{
-			Description: "Test response",
-			Messages: []PromptMessage{
-				{
-					Role:    "user",
-					Content: NewTextContent("Hello"),
-				},
-			},
-		}, nil
-	}
-
-	server.RegisterPrompt(prompt, handler)
-
-	// Test that prompt is registered
-	prompts := server.ListPrompts()
-	if len(prompts) != 1 {
-		t.Errorf("Expected 1 prompt, got %d", len(prompts))
-	}
-
-	if prompts[0].Name != "test-prompt" {
-		t.Errorf("Expected prompt name 'test-prompt', got %s", prompts[0].Name)
-	}
-
-	if prompts[0].Description != "A test prompt for verification" {
-		t.Errorf("Expected description 'A test prompt for verification', got %s", prompts[0].Description)
-	}
-
-	if len(prompts[0].Arguments) != 1 {
-		t.Errorf("Expected 1 argument, got %d", len(prompts[0].Arguments))
-	}
-
-	if prompts[0].Arguments[0].Name != "input" {
-		t.Errorf("Expected argument name 'input', got %s", prompts[0].Arguments[0].Name)
-	}
-
-	if !prompts[0].Arguments[0].Required {
-		t.Error("Expected argument to be required")
-	}
-}
-
-func TestServer_ListPrompts_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	prompts := server.ListPrompts()
-	if len(prompts) != 0 {
-		t.Errorf("Expected 0 prompts, got %d", len(prompts))
-	}
-}
-
-func TestServer_MultiplePrompts(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	prompt1 := Prompt{
-		Name:        "prompt1",
-		Description: "First prompt",
-	}
-
-	prompt2 := Prompt{
-		Name:        "prompt2",
-		Description: "Second prompt",
-		Arguments: []PromptArgument{
-			{Name: "arg1", Required: true},
-			{Name: "arg2", Required: false},
-		},
-	}
-
-	handler := func(req GetPromptRequest) (GetPromptResult, error) {
-		return GetPromptResult{}, nil
-	}
-
-	server.RegisterPrompt(prompt1, handler)
-	server.RegisterPrompt(prompt2, handler)
-
-	prompts := server.ListPrompts()
-	if len(prompts) != 2 {
-		t.Errorf("Expected 2 prompts, got %d", len(prompts))
-	}
-
-	// Check that both prompts are present (order may vary due to map iteration)
-	names := make(map[string]bool)
-	for _, prompt := range prompts {
-		names[prompt.Name] = true
-	}
-
-	if !names["prompt1"] {
-		t.Error("prompt1 not found in list")
-	}
-	if !names["prompt2"] {
-		t.Error("prompt2 not found in list")
-	}
-}
pkg/mcp/server_resources_test.go
@@ -1,117 +0,0 @@
-package mcp
-
-import (
-	"testing"
-)
-
-func TestServer_RegisterResource(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	// Create a test resource handler
-	handler := func(req ReadResourceRequest) (ReadResourceResult, error) {
-		return ReadResourceResult{
-			Contents: []Content{
-				NewTextContent("Test resource content"),
-			},
-		}, nil
-	}
-
-	// Register resource
-	server.RegisterResource("file:///test/resource.txt", handler)
-
-	// Test that resource is registered (we'll need to add ListResources method)
-	resources := server.ListResources()
-	if len(resources) != 1 {
-		t.Errorf("Expected 1 resource, got %d", len(resources))
-	}
-
-	if resources[0].URI != "file:///test/resource.txt" {
-		t.Errorf("Expected resource URI 'file:///test/resource.txt', got %s", resources[0].URI)
-	}
-}
-
-func TestServer_ListResources_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	resources := server.ListResources()
-	if len(resources) != 0 {
-		t.Errorf("Expected 0 resources, got %d", len(resources))
-	}
-}
-
-func TestServer_MultipleResources(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	handler := func(req ReadResourceRequest) (ReadResourceResult, error) {
-		return ReadResourceResult{}, nil
-	}
-
-	// Register multiple resources
-	server.RegisterResource("file:///file1.txt", handler)
-	server.RegisterResource("git://repo/main/file.go", handler)
-	server.RegisterResource("memory://entity/123", handler)
-
-	resources := server.ListResources()
-	if len(resources) != 3 {
-		t.Errorf("Expected 3 resources, got %d", len(resources))
-	}
-
-	// Check that all URIs are present (order may vary due to map iteration)
-	uris := make(map[string]bool)
-	for _, resource := range resources {
-		uris[resource.URI] = true
-	}
-
-	expectedURIs := []string{
-		"file:///file1.txt",
-		"git://repo/main/file.go",
-		"memory://entity/123",
-	}
-
-	for _, expectedURI := range expectedURIs {
-		if !uris[expectedURI] {
-			t.Errorf("Expected URI %s not found in resources", expectedURI)
-		}
-	}
-}
-
-func TestServer_RegisterResourceWithDefinition(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	resource := Resource{
-		URI:         "file:///docs/readme.md",
-		Name:        "README",
-		Description: "Project documentation",
-		MimeType:    "text/markdown",
-	}
-
-	handler := func(req ReadResourceRequest) (ReadResourceResult, error) {
-		return ReadResourceResult{
-			Contents: []Content{
-				NewTextContent("# Project Documentation\n\nThis is the README."),
-			},
-		}, nil
-	}
-
-	// Register resource with full definition
-	server.RegisterResourceWithDefinition(resource, handler)
-
-	resources := server.ListResources()
-	if len(resources) != 1 {
-		t.Errorf("Expected 1 resource, got %d", len(resources))
-	}
-
-	res := resources[0]
-	if res.URI != "file:///docs/readme.md" {
-		t.Errorf("Expected URI 'file:///docs/readme.md', got %s", res.URI)
-	}
-	if res.Name != "README" {
-		t.Errorf("Expected name 'README', got %s", res.Name)
-	}
-	if res.Description != "Project documentation" {
-		t.Errorf("Expected description 'Project documentation', got %s", res.Description)
-	}
-	if res.MimeType != "text/markdown" {
-		t.Errorf("Expected mime type 'text/markdown', got %s", res.MimeType)
-	}
-}
pkg/mcp/server_roots_test.go
@@ -1,110 +0,0 @@
-package mcp
-
-import (
-	"testing"
-)
-
-func TestServer_RegisterRoot(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	root := Root{
-		URI:  "file:///home/user/projects",
-		Name: "Projects Directory",
-	}
-
-	server.RegisterRoot(root)
-
-	roots := server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root, got %d", len(roots))
-	}
-
-	if roots[0].URI != root.URI {
-		t.Errorf("Expected root URI %s, got %s", root.URI, roots[0].URI)
-	}
-
-	if roots[0].Name != root.Name {
-		t.Errorf("Expected root name %s, got %s", root.Name, roots[0].Name)
-	}
-}
-
-func TestServer_ListRoots_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	roots := server.ListRoots()
-	if len(roots) != 0 {
-		t.Errorf("Expected 0 roots, got %d", len(roots))
-	}
-}
-
-func TestServer_MultipleRoots(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	roots := []Root{
-		{URI: "file:///home/user", Name: "Home"},
-		{URI: "git:///path/to/repo", Name: "Repository"},
-		{URI: "memory://graph", Name: "Knowledge Graph"},
-	}
-
-	for _, root := range roots {
-		server.RegisterRoot(root)
-	}
-
-	retrievedRoots := server.ListRoots()
-	if len(retrievedRoots) != 3 {
-		t.Errorf("Expected 3 roots, got %d", len(retrievedRoots))
-	}
-
-	// Check that all roots are present (order may vary due to map iteration)
-	rootURIs := make(map[string]bool)
-	for _, root := range retrievedRoots {
-		rootURIs[root.URI] = true
-	}
-
-	expectedURIs := []string{
-		"file:///home/user",
-		"git:///path/to/repo",
-		"memory://graph",
-	}
-
-	for _, expectedURI := range expectedURIs {
-		if !rootURIs[expectedURI] {
-			t.Errorf("Expected root URI %s not found", expectedURI)
-		}
-	}
-}
-
-func TestServer_RootCapability(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	if server.capabilities.Roots == nil {
-		t.Error("Expected server to have roots capability")
-	}
-}
-
-func TestServer_DuplicateRoots(t *testing.T) {
-	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
-
-	root1 := Root{
-		URI:  "file:///home/user",
-		Name: "Home Directory",
-	}
-
-	root2 := Root{
-		URI:  "file:///home/user", // Same URI, different name
-		Name: "User Home",
-	}
-
-	server.RegisterRoot(root1)
-	server.RegisterRoot(root2)
-
-	roots := server.ListRoots()
-	if len(roots) != 1 {
-		t.Errorf("Expected 1 root (duplicate should be overwritten), got %d", len(roots))
-	}
-
-	// Should have the second root (overwrites the first)
-	if roots[0].Name != "User Home" {
-		t.Errorf("Expected root name 'User Home', got %s", roots[0].Name)
-	}
-}
\ No newline at end of file
pkg/memory/server_test.go
@@ -1,768 +0,0 @@
-package memory
-
-import (
-	"encoding/json"
-	"path/filepath"
-	"strings"
-	"testing"
-	"time"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestMemoryServer_CreateEntities(t *testing.T) {
-	// Use temporary file for testing
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	req := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "John_Smith",
-					"entityType":   "person",
-					"observations": []interface{}{"Speaks fluent Spanish", "Works at Anthropic"},
-				},
-				map[string]interface{}{
-					"name":         "Anthropic",
-					"entityType":   "organization",
-					"observations": []interface{}{"AI safety company"},
-				},
-			},
-		},
-	}
-
-	result, err := server.HandleCreateEntities(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful entity creation, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain created entities
-	if !contains(textContent.Text, "John_Smith") {
-		t.Fatalf("Expected 'John_Smith' in result, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "Anthropic") {
-		t.Fatalf("Expected 'Anthropic' in result, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_CreateRelations(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// First create entities
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "John_Smith",
-					"entityType":   "person",
-					"observations": []interface{}{"Employee"},
-				},
-				map[string]interface{}{
-					"name":         "Anthropic",
-					"entityType":   "organization",
-					"observations": []interface{}{"AI company"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Now create relation
-	req := mcp.CallToolRequest{
-		Name: "create_relations",
-		Arguments: map[string]interface{}{
-			"relations": []interface{}{
-				map[string]interface{}{
-					"from":         "John_Smith",
-					"to":           "Anthropic",
-					"relationType": "works_at",
-				},
-			},
-		},
-	}
-
-	result, err := server.HandleCreateRelations(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful relation creation, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain created relation
-	if !contains(textContent.Text, "works_at") {
-		t.Fatalf("Expected 'works_at' relation in result, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_AddObservations(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// First create entity
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "John_Smith",
-					"entityType":   "person",
-					"observations": []interface{}{"Initial observation"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Add observations
-	req := mcp.CallToolRequest{
-		Name: "add_observations",
-		Arguments: map[string]interface{}{
-			"observations": []interface{}{
-				map[string]interface{}{
-					"entityName": "John_Smith",
-					"contents":   []interface{}{"Likes coffee", "Speaks French"},
-				},
-			},
-		},
-	}
-
-	result, err := server.HandleAddObservations(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful observation addition, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain added observations
-	if !contains(textContent.Text, "Likes coffee") {
-		t.Fatalf("Expected 'Likes coffee' in result, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_ReadGraph(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// Create some test data
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "Alice",
-					"entityType":   "person",
-					"observations": []interface{}{"Software engineer"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Read the graph
-	req := mcp.CallToolRequest{
-		Name:      "read_graph",
-		Arguments: map[string]interface{}{},
-	}
-
-	result, err := server.HandleReadGraph(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful graph read, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain entities and relations structure
-	if !contains(textContent.Text, "entities") {
-		t.Fatalf("Expected 'entities' in graph, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "relations") {
-		t.Fatalf("Expected 'relations' in graph, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "Alice") {
-		t.Fatalf("Expected 'Alice' in graph, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_SearchNodes(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// Create test entities
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "Alice_Developer",
-					"entityType":   "person",
-					"observations": []interface{}{"Python programmer", "Loves machine learning"},
-				},
-				map[string]interface{}{
-					"name":         "Bob_Manager",
-					"entityType":   "person",
-					"observations": []interface{}{"Project manager", "Excellent communication"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Search for Python-related content
-	req := mcp.CallToolRequest{
-		Name: "search_nodes",
-		Arguments: map[string]interface{}{
-			"query": "Python",
-		},
-	}
-
-	result, err := server.HandleSearchNodes(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful search, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should find Alice who has Python in observations
-	if !contains(textContent.Text, "Alice_Developer") {
-		t.Fatalf("Expected 'Alice_Developer' in search results, got: %s", textContent.Text)
-	}
-
-	// Should not find Bob who doesn't have Python mentioned
-	if contains(textContent.Text, "Bob_Manager") {
-		t.Fatalf("Should not find 'Bob_Manager' in Python search, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_OpenNodes(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// Create test entities
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "Person1",
-					"entityType":   "person",
-					"observations": []interface{}{"First person"},
-				},
-				map[string]interface{}{
-					"name":         "Person2",
-					"entityType":   "person",
-					"observations": []interface{}{"Second person"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Open specific nodes
-	req := mcp.CallToolRequest{
-		Name: "open_nodes",
-		Arguments: map[string]interface{}{
-			"names": []interface{}{"Person1"},
-		},
-	}
-
-	result, err := server.HandleOpenNodes(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful node open, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should find Person1
-	if !contains(textContent.Text, "Person1") {
-		t.Fatalf("Expected 'Person1' in open nodes result, got: %s", textContent.Text)
-	}
-
-	// Should not find Person2 (not requested)
-	if contains(textContent.Text, "Person2") {
-		t.Fatalf("Should not find 'Person2' in specific node open, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_DeleteEntities(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// Create test entity
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "ToDelete",
-					"entityType":   "person",
-					"observations": []interface{}{"Will be deleted"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Delete entity
-	req := mcp.CallToolRequest{
-		Name: "delete_entities",
-		Arguments: map[string]interface{}{
-			"entityNames": []interface{}{"ToDelete"},
-		},
-	}
-
-	result, err := server.HandleDeleteEntities(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful entity deletion, got error: %s", textContent.Text)
-	}
-
-	// Verify entity was deleted by reading graph
-	readReq := mcp.CallToolRequest{
-		Name:      "read_graph",
-		Arguments: map[string]interface{}{},
-	}
-
-	readResult, _ := server.HandleReadGraph(readReq)
-	readContent, _ := readResult.Content[0].(mcp.TextContent)
-
-	if contains(readContent.Text, "ToDelete") {
-		t.Fatalf("Entity should have been deleted, but found in graph: %s", readContent.Text)
-	}
-}
-
-func TestMemoryServer_DeleteObservations(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-
-	// Create entity with observations
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "TestPerson",
-					"entityType":   "person",
-					"observations": []interface{}{"Keep this", "Delete this", "Keep this too"},
-				},
-			},
-		},
-	}
-	server.HandleCreateEntities(createReq)
-
-	// Delete specific observation
-	req := mcp.CallToolRequest{
-		Name: "delete_observations",
-		Arguments: map[string]interface{}{
-			"deletions": []interface{}{
-				map[string]interface{}{
-					"entityName":   "TestPerson",
-					"observations": []interface{}{"Delete this"},
-				},
-			},
-		},
-	}
-
-	result, err := server.HandleDeleteObservations(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful observation deletion, got error: %s", textContent.Text)
-	}
-
-	// Verify observation was deleted by reading graph
-	readReq := mcp.CallToolRequest{
-		Name:      "read_graph",
-		Arguments: map[string]interface{}{},
-	}
-
-	readResult, _ := server.HandleReadGraph(readReq)
-	readContent, _ := readResult.Content[0].(mcp.TextContent)
-
-	if contains(readContent.Text, "Delete this") {
-		t.Fatalf("Observation should have been deleted, but found in graph: %s", readContent.Text)
-	}
-
-	if !contains(readContent.Text, "Keep this") {
-		t.Fatalf("Other observations should remain, got: %s", readContent.Text)
-	}
-}
-
-func TestMemoryServer_ListTools(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	server := New(memoryFile)
-	tools := server.ListTools()
-
-	expectedTools := []string{
-		"create_entities",
-		"create_relations",
-		"add_observations",
-		"delete_entities",
-		"delete_observations",
-		"delete_relations",
-		"read_graph",
-		"search_nodes",
-		"open_nodes",
-	}
-
-	if len(tools) != len(expectedTools) {
-		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-	}
-
-	toolNames := make(map[string]bool)
-	for _, tool := range tools {
-		toolNames[tool.Name] = true
-	}
-
-	for _, expected := range expectedTools {
-		if !toolNames[expected] {
-			t.Fatalf("Expected tool %s not found", expected)
-		}
-	}
-}
-
-func TestMemoryServer_Persistence(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory.json")
-
-	// Create first server instance and add data
-	server1 := New(memoryFile)
-
-	createReq := mcp.CallToolRequest{
-		Name: "create_entities",
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "Persistent_Entity",
-					"entityType":   "test",
-					"observations": []interface{}{"This should persist"},
-				},
-			},
-		},
-	}
-	server1.HandleCreateEntities(createReq)
-
-	// Create second server instance (should load from file)
-	server2 := New(memoryFile)
-
-	readReq := mcp.CallToolRequest{
-		Name:      "read_graph",
-		Arguments: map[string]interface{}{},
-	}
-
-	result, err := server2.HandleReadGraph(readReq)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should find the entity created by the first server instance
-	if !contains(textContent.Text, "Persistent_Entity") {
-		t.Fatalf("Expected persistent entity to be loaded from file, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "This should persist") {
-		t.Fatalf("Expected persistent observation to be loaded from file, got: %s", textContent.Text)
-	}
-}
-
-func TestMemoryServer_Resources(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_memory_resources.json")
-
-	server := New(memoryFile)
-
-	// Create test entities
-	createReq := mcp.CallToolRequest{
-		Arguments: map[string]interface{}{
-			"entities": []interface{}{
-				map[string]interface{}{
-					"name":         "TestEntity1",
-					"entityType":   "Person",
-					"observations": []interface{}{"First observation", "Second observation"},
-				},
-				map[string]interface{}{
-					"name":         "TestEntity2",
-					"entityType":   "Company",
-					"observations": []interface{}{"Company observation"},
-				},
-			},
-		},
-	}
-
-	_, err := server.HandleCreateEntities(createReq)
-	if err != nil {
-		t.Fatalf("Failed to create entities: %v", err)
-	}
-
-	// Create test relations
-	relationsReq := mcp.CallToolRequest{
-		Arguments: map[string]interface{}{
-			"relations": []interface{}{
-				map[string]interface{}{
-					"from":         "TestEntity1",
-					"to":           "TestEntity2",
-					"relationType": "works_for",
-				},
-			},
-		},
-	}
-
-	_, err = server.HandleCreateRelations(relationsReq)
-	if err != nil {
-		t.Fatalf("Failed to create relations: %v", err)
-	}
-
-	// Give time for resource registration
-	time.Sleep(100 * time.Millisecond)
-
-	// Test list resources
-	resources := server.ListResources()
-	if len(resources) < 2 {
-		t.Errorf("Expected at least 2 resources, got %d", len(resources))
-	}
-
-	// Find entity resource
-	var entityResourceURI string
-	for _, resource := range resources {
-		if strings.Contains(resource.URI, "TestEntity1") {
-			entityResourceURI = resource.URI
-			break
-		}
-	}
-
-	if entityResourceURI == "" {
-		t.Fatal("TestEntity1 resource not found")
-	}
-
-	// Test read entity resource
-	readReq := mcp.ReadResourceRequest{
-		URI: entityResourceURI,
-	}
-
-	result, err := server.HandleMemoryResource(readReq)
-	if err != nil {
-		t.Fatalf("Failed to read entity resource: %v", err)
-	}
-
-	if len(result.Contents) == 0 {
-		t.Fatal("Expected content in resource result")
-	}
-
-	// Verify entity data
-	textContent, ok := result.Contents[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent in resource result")
-	}
-
-	var entityData map[string]interface{}
-	err = json.Unmarshal([]byte(textContent.Text), &entityData)
-	if err != nil {
-		t.Fatalf("Failed to parse entity JSON: %v", err)
-	}
-
-	entity, ok := entityData["entity"].(map[string]interface{})
-	if !ok {
-		t.Fatal("Entity data not found in resource result")
-	}
-
-	if entity["name"].(string) != "TestEntity1" {
-		t.Errorf("Expected entity name TestEntity1, got %s", entity["name"])
-	}
-
-	// Test relations resource
-	relationsResourceURI := "memory://relations/all"
-	readRelationsReq := mcp.ReadResourceRequest{
-		URI: relationsResourceURI,
-	}
-
-	relationsResult, err := server.HandleMemoryResource(readRelationsReq)
-	if err != nil {
-		t.Fatalf("Failed to read relations resource: %v", err)
-	}
-
-	if len(relationsResult.Contents) == 0 {
-		t.Fatal("Expected content in relations resource result")
-	}
-
-	// Verify relations data
-	relationsTextContent, ok := relationsResult.Contents[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent in relations resource result")
-	}
-
-	var relationsData map[string]interface{}
-	err = json.Unmarshal([]byte(relationsTextContent.Text), &relationsData)
-	if err != nil {
-		t.Fatalf("Failed to parse relations JSON: %v", err)
-	}
-
-	totalRelations, ok := relationsData["total_relations"].(float64)
-	if !ok || totalRelations != 1 {
-		t.Errorf("Expected 1 relation, got %v", totalRelations)
-	}
-}
-
-func TestMemoryServer_HandleKnowledgeQueryPrompt(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_prompt_memory.json")
-
-	server := New(memoryFile)
-
-	req := mcp.GetPromptRequest{
-		Arguments: map[string]interface{}{
-			"query":   "find all people",
-			"context": "looking for team members",
-		},
-	}
-
-	result, err := server.HandleKnowledgeQueryPrompt(req)
-	if err != nil {
-		t.Fatalf("HandleKnowledgeQueryPrompt failed: %v", err)
-	}
-
-	if len(result.Messages) != 2 {
-		t.Errorf("Expected 2 messages, got %d", len(result.Messages))
-	}
-
-	if result.Messages[0].Role != "user" {
-		t.Errorf("Expected first message role to be 'user', got %s", result.Messages[0].Role)
-	}
-
-	if result.Messages[1].Role != "assistant" {
-		t.Errorf("Expected second message role to be 'assistant', got %s", result.Messages[1].Role)
-	}
-
-	userContent, ok := result.Messages[0].Content.(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent in user message")
-	}
-	if !strings.Contains(userContent.Text, "find all people") {
-		t.Error("User message should contain the query")
-	}
-
-	assistantContent, ok := result.Messages[1].Content.(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent in assistant message")
-	}
-	if !strings.Contains(assistantContent.Text, "knowledge graph") {
-		t.Error("Assistant message should mention knowledge graph")
-	}
-}
-
-func TestMemoryServer_InvalidResourceURI(t *testing.T) {
-	tempDir := t.TempDir()
-	memoryFile := filepath.Join(tempDir, "test_invalid_memory.json")
-
-	server := New(memoryFile)
-
-	testCases := []struct {
-		name string
-		uri  string
-	}{
-		{"invalid scheme", "file://test"},
-		{"invalid format", "memory://"},
-		{"invalid type", "memory://unknown/test"},
-		{"missing entity", "memory://entity/nonexistent"},
-	}
-
-	for _, tc := range testCases {
-		t.Run(tc.name, func(t *testing.T) {
-			req := mcp.ReadResourceRequest{URI: tc.uri}
-			_, err := server.HandleMemoryResource(req)
-			if err == nil {
-				t.Errorf("Expected error for invalid URI: %s", tc.uri)
-			}
-		})
-	}
-}
-
-// Helper functions
-func contains(s, substr string) bool {
-	return strings.Contains(s, substr)
-}
pkg/packages/server.go
@@ -1,1311 +0,0 @@
-package packages
-
-import (
-	"encoding/json"
-	"fmt"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"strings"
-	"sync"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-// Server represents the Package Manager MCP server
-type Server struct {
-	*mcp.Server
-	mu sync.RWMutex
-}
-
-// NewServer creates a new Package Manager MCP server
-func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-packages", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-
-	server := &Server{
-		Server: baseServer,
-	}
-
-	server.registerTools()
-
-	return server
-}
-
-// ListTools returns all available package management tools
-func (s *Server) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		// Cargo tools
-		{
-			Name:        "cargo_build",
-			Description: "Build a Rust project using Cargo",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"release": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Build in release mode (default: false)",
-					},
-					"target": map[string]interface{}{
-						"type":        "string",
-						"description": "Target triple to build for (optional)",
-					},
-					"features": map[string]interface{}{
-						"type":        "string",
-						"description": "Space-separated list of features to activate",
-					},
-				},
-			},
-		},
-		{
-			Name:        "cargo_run",
-			Description: "Run a Rust project using Cargo",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"release": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Run in release mode (default: false)",
-					},
-					"args": map[string]interface{}{
-						"type":        "string",
-						"description": "Arguments to pass to the program",
-					},
-				},
-			},
-		},
-		{
-			Name:        "cargo_test",
-			Description: "Run tests for a Rust project using Cargo",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"test_name": map[string]interface{}{
-						"type":        "string",
-						"description": "Name of specific test to run (optional)",
-					},
-					"release": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Run tests in release mode (default: false)",
-					},
-				},
-			},
-		},
-		{
-			Name:        "cargo_add",
-			Description: "Add a dependency to a Rust project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Package name to add",
-					},
-					"version": map[string]interface{}{
-						"type":        "string",
-						"description": "Version requirement (optional)",
-					},
-					"dev": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Add as dev dependency (default: false)",
-					},
-				},
-				"required": []string{"package"},
-			},
-		},
-		{
-			Name:        "cargo_update",
-			Description: "Update dependencies in a Rust project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Specific package to update (optional, updates all if not specified)",
-					},
-				},
-			},
-		},
-		{
-			Name:        "cargo_check",
-			Description: "Check a Rust project for errors without building",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-				},
-			},
-		},
-		{
-			Name:        "cargo_clippy",
-			Description: "Run Clippy linter on a Rust project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"fix": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Automatically apply fixes (default: false)",
-					},
-				},
-			},
-		},
-		// Homebrew tools
-		{
-			Name:        "brew_install",
-			Description: "Install a package using Homebrew",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Package name to install",
-					},
-					"cask": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Install as cask (GUI application) (default: false)",
-					},
-				},
-				"required": []string{"package"},
-			},
-		},
-		{
-			Name:        "brew_uninstall",
-			Description: "Uninstall a package using Homebrew",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Package name to uninstall",
-					},
-					"cask": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Uninstall as cask (GUI application) (default: false)",
-					},
-				},
-				"required": []string{"package"},
-			},
-		},
-		{
-			Name:        "brew_search",
-			Description: "Search for packages in Homebrew",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"query": map[string]interface{}{
-						"type":        "string",
-						"description": "Search query",
-					},
-					"cask": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Search in casks (GUI applications) (default: false)",
-					},
-				},
-				"required": []string{"query"},
-			},
-		},
-		{
-			Name:        "brew_update",
-			Description: "Update Homebrew and formulae",
-			InputSchema: map[string]interface{}{
-				"type":       "object",
-				"properties": map[string]interface{}{},
-			},
-		},
-		{
-			Name:        "brew_upgrade",
-			Description: "Upgrade installed packages using Homebrew",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Specific package to upgrade (optional, upgrades all if not specified)",
-					},
-				},
-			},
-		},
-		{
-			Name:        "brew_doctor",
-			Description: "Check Homebrew for potential problems",
-			InputSchema: map[string]interface{}{
-				"type":       "object",
-				"properties": map[string]interface{}{},
-			},
-		},
-		{
-			Name:        "brew_list",
-			Description: "List installed packages in Homebrew",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"cask": map[string]interface{}{
-						"type":        "boolean",
-						"description": "List casks (GUI applications) (default: false)",
-					},
-				},
-			},
-		},
-		// Cross-platform tools
-		{
-			Name:        "check_vulnerabilities",
-			Description: "Check for known vulnerabilities in dependencies",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-					"format": map[string]interface{}{
-						"type":        "string",
-						"description": "Output format (json, table) (default: table)",
-						"enum":        []string{"json", "table"},
-					},
-				},
-			},
-		},
-		{
-			Name:        "outdated_packages",
-			Description: "Check for outdated packages in the project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"directory": map[string]interface{}{
-						"type":        "string",
-						"description": "Project directory (optional, defaults to current directory)",
-					},
-				},
-			},
-		},
-		{
-			Name:        "package_info",
-			Description: "Get detailed information about a package",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"package": map[string]interface{}{
-						"type":        "string",
-						"description": "Package name to get information about",
-					},
-					"manager": map[string]interface{}{
-						"type":        "string",
-						"description": "Package manager (cargo, brew, npm, pip) (optional, auto-detected)",
-						"enum":        []string{"cargo", "brew", "npm", "pip"},
-					},
-				},
-				"required": []string{"package"},
-			},
-		},
-	}
-}
-
-// registerTools registers all package management tools with the server
-func (s *Server) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := s.ListTools()
-	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "cargo_build":
-			handler = s.handleCargoBuild
-		case "cargo_run":
-			handler = s.handleCargoRun
-		case "cargo_test":
-			handler = s.handleCargoTest
-		case "cargo_add":
-			handler = s.handleCargoAdd
-		case "cargo_update":
-			handler = s.handleCargoUpdate
-		case "cargo_check":
-			handler = s.handleCargoCheck
-		case "cargo_clippy":
-			handler = s.handleCargoClippy
-		case "brew_install":
-			handler = s.handleBrewInstall
-		case "brew_uninstall":
-			handler = s.handleBrewUninstall
-		case "brew_search":
-			handler = s.handleBrewSearch
-		case "brew_update":
-			handler = s.handleBrewUpdate
-		case "brew_upgrade":
-			handler = s.handleBrewUpgrade
-		case "brew_doctor":
-			handler = s.handleBrewDoctor
-		case "brew_list":
-			handler = s.handleBrewList
-		case "check_vulnerabilities":
-			handler = s.handleCheckVulnerabilities
-		case "outdated_packages":
-			handler = s.handleOutdatedPackages
-		case "package_info":
-			handler = s.handlePackageInfo
-		default:
-			continue
-		}
-		s.RegisterToolWithDefinition(tool, handler)
-	}
-}
-
-// Cargo tool handlers
-
-func (s *Server) handleCargoBuild(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory string `json:"directory,omitempty"`
-		Release   bool   `json:"release,omitempty"`
-		Target    string `json:"target,omitempty"`
-		Features  string `json:"features,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	// Default to current directory
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	// Check if Cargo.toml exists
-	cargoToml := filepath.Join(args.Directory, "Cargo.toml")
-	if _, err := os.Stat(cargoToml); os.IsNotExist(err) {
-		return mcp.CallToolResult{}, fmt.Errorf("no Cargo.toml found in %s", args.Directory)
-	}
-
-	// Build cargo command
-	cmdArgs := []string{"build"}
-
-	if args.Release {
-		cmdArgs = append(cmdArgs, "--release")
-	}
-
-	if args.Target != "" {
-		cmdArgs = append(cmdArgs, "--target", args.Target)
-	}
-
-	if args.Features != "" {
-		cmdArgs = append(cmdArgs, "--features", args.Features)
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoRun(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory string   `json:"directory,omitempty"`
-		Package   string   `json:"package,omitempty"`
-		Bin       string   `json:"bin,omitempty"`
-		Args      []string `json:"args,omitempty"`
-		Release   bool     `json:"release,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	// Build cargo command
-	cmdArgs := []string{"run"}
-
-	if args.Release {
-		cmdArgs = append(cmdArgs, "--release")
-	}
-
-	if args.Package != "" {
-		cmdArgs = append(cmdArgs, "--package", args.Package)
-	}
-
-	if args.Bin != "" {
-		cmdArgs = append(cmdArgs, "--bin", args.Bin)
-	}
-
-	if len(args.Args) > 0 {
-		cmdArgs = append(cmdArgs, "--")
-		cmdArgs = append(cmdArgs, args.Args...)
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoTest(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory string   `json:"directory,omitempty"`
-		Package   string   `json:"package,omitempty"`
-		Test      string   `json:"test,omitempty"`
-		Args      []string `json:"args,omitempty"`
-		Release   bool     `json:"release,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	cmdArgs := []string{"test"}
-
-	if args.Release {
-		cmdArgs = append(cmdArgs, "--release")
-	}
-
-	if args.Package != "" {
-		cmdArgs = append(cmdArgs, "--package", args.Package)
-	}
-
-	if args.Test != "" {
-		cmdArgs = append(cmdArgs, args.Test)
-	}
-
-	if len(args.Args) > 0 {
-		cmdArgs = append(cmdArgs, "--")
-		cmdArgs = append(cmdArgs, args.Args...)
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoAdd(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory         string   `json:"directory,omitempty"`
-		Packages          []string `json:"packages"`
-		Dev               bool     `json:"dev,omitempty"`
-		Build             bool     `json:"build,omitempty"`
-		Optional          bool     `json:"optional,omitempty"`
-		Features          []string `json:"features,omitempty"`
-		NoDefaultFeatures bool     `json:"no_default_features,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if len(args.Packages) == 0 {
-		return mcp.CallToolResult{}, fmt.Errorf("packages are required")
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	cmdArgs := []string{"add"}
-	cmdArgs = append(cmdArgs, args.Packages...)
-
-	if args.Dev {
-		cmdArgs = append(cmdArgs, "--dev")
-	}
-
-	if args.Build {
-		cmdArgs = append(cmdArgs, "--build")
-	}
-
-	if args.Optional {
-		cmdArgs = append(cmdArgs, "--optional")
-	}
-
-	if len(args.Features) > 0 {
-		cmdArgs = append(cmdArgs, "--features", strings.Join(args.Features, ","))
-	}
-
-	if args.NoDefaultFeatures {
-		cmdArgs = append(cmdArgs, "--no-default-features")
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoUpdate(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory  string   `json:"directory,omitempty"`
-		Packages   []string `json:"packages,omitempty"`
-		Aggressive bool     `json:"aggressive,omitempty"`
-		Precise    string   `json:"precise,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	cmdArgs := []string{"update"}
-
-	if len(args.Packages) > 0 {
-		cmdArgs = append(cmdArgs, args.Packages...)
-	}
-
-	if args.Aggressive {
-		cmdArgs = append(cmdArgs, "--aggressive")
-	}
-
-	if args.Precise != "" {
-		cmdArgs = append(cmdArgs, "--precise", args.Precise)
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoCheck(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory  string `json:"directory,omitempty"`
-		Package    string `json:"package,omitempty"`
-		Release    bool   `json:"release,omitempty"`
-		AllTargets bool   `json:"all_targets,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	cmdArgs := []string{"check"}
-
-	if args.Release {
-		cmdArgs = append(cmdArgs, "--release")
-	}
-
-	if args.Package != "" {
-		cmdArgs = append(cmdArgs, "--package", args.Package)
-	}
-
-	if args.AllTargets {
-		cmdArgs = append(cmdArgs, "--all-targets")
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleCargoClippy(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory  string `json:"directory,omitempty"`
-		Package    string `json:"package,omitempty"`
-		AllTargets bool   `json:"all_targets,omitempty"`
-		Fix        bool   `json:"fix,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	cmdArgs := []string{"clippy"}
-
-	if args.Package != "" {
-		cmdArgs = append(cmdArgs, "--package", args.Package)
-	}
-
-	if args.AllTargets {
-		cmdArgs = append(cmdArgs, "--all-targets")
-	}
-
-	if args.Fix {
-		cmdArgs = append(cmdArgs, "--fix")
-	}
-
-	cmd := exec.Command("cargo", cmdArgs...)
-	cmd.Dir = args.Directory
-
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: cargo %s\nDirectory: %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), args.Directory, string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-// Homebrew tool handlers
-
-func (s *Server) handleBrewInstall(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Packages []string `json:"packages"`
-		Cask     bool     `json:"cask,omitempty"`
-		Force    bool     `json:"force,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if len(args.Packages) == 0 {
-		return mcp.CallToolResult{}, fmt.Errorf("packages are required")
-	}
-
-	cmdArgs := []string{"install"}
-
-	if args.Cask {
-		cmdArgs = append(cmdArgs, "--cask")
-	}
-
-	if args.Force {
-		cmdArgs = append(cmdArgs, "--force")
-	}
-
-	cmdArgs = append(cmdArgs, args.Packages...)
-
-	cmd := exec.Command("brew", cmdArgs...)
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewUninstall(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Packages []string `json:"packages"`
-		Cask     bool     `json:"cask,omitempty"`
-		Force    bool     `json:"force,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if len(args.Packages) == 0 {
-		return mcp.CallToolResult{}, fmt.Errorf("packages are required")
-	}
-
-	cmdArgs := []string{"uninstall"}
-
-	if args.Cask {
-		cmdArgs = append(cmdArgs, "--cask")
-	}
-
-	if args.Force {
-		cmdArgs = append(cmdArgs, "--force")
-	}
-
-	cmdArgs = append(cmdArgs, args.Packages...)
-
-	cmd := exec.Command("brew", cmdArgs...)
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewSearch(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Query string `json:"query"`
-		Cask  bool   `json:"cask,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Query == "" {
-		return mcp.CallToolResult{}, fmt.Errorf("query is required")
-	}
-
-	cmdArgs := []string{"search"}
-
-	if args.Cask {
-		cmdArgs = append(cmdArgs, "--cask")
-	}
-
-	cmdArgs = append(cmdArgs, args.Query)
-
-	cmd := exec.Command("brew", cmdArgs...)
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewUpdate(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	cmd := exec.Command("brew", "update")
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew update\nOutput:\n%s", string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewUpgrade(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Packages []string `json:"packages,omitempty"`
-		Cask     bool     `json:"cask,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	cmdArgs := []string{"upgrade"}
-
-	if args.Cask {
-		cmdArgs = append(cmdArgs, "--cask")
-	}
-
-	if len(args.Packages) > 0 {
-		cmdArgs = append(cmdArgs, args.Packages...)
-	}
-
-	cmd := exec.Command("brew", cmdArgs...)
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewDoctor(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	cmd := exec.Command("brew", "doctor")
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew doctor\nOutput:\n%s", string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleBrewList(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Cask     bool `json:"cask,omitempty"`
-		Versions bool `json:"versions,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	cmdArgs := []string{"list"}
-
-	if args.Cask {
-		cmdArgs = append(cmdArgs, "--cask")
-	}
-
-	if args.Versions {
-		cmdArgs = append(cmdArgs, "--versions")
-	}
-
-	cmd := exec.Command("brew", cmdArgs...)
-	output, err := cmd.CombinedOutput()
-
-	result := fmt.Sprintf("Command: brew %s\nOutput:\n%s",
-		strings.Join(cmdArgs, " "), string(output))
-
-	if err != nil {
-		result += fmt.Sprintf("\nError: %v", err)
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-// Cross-platform tool handlers
-
-func (s *Server) handleCheckVulnerabilities(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory      string `json:"directory,omitempty"`
-		PackageManager string `json:"package_manager,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	var result string
-
-	// Auto-detect package manager if not specified
-	if args.PackageManager == "" {
-		if _, err := os.Stat(filepath.Join(args.Directory, "Cargo.toml")); err == nil {
-			args.PackageManager = "cargo"
-		} else if _, err := os.Stat(filepath.Join(args.Directory, "package.json")); err == nil {
-			args.PackageManager = "npm"
-		} else if _, err := os.Stat(filepath.Join(args.Directory, "go.mod")); err == nil {
-			args.PackageManager = "go"
-		}
-	}
-
-	switch args.PackageManager {
-	case "cargo":
-		cmd := exec.Command("cargo", "audit")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Cargo security audit:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "npm":
-		cmd := exec.Command("npm", "audit")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("NPM security audit:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "go":
-		cmd := exec.Command("go", "list", "-json", "-m", "all")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Go module list (manual vulnerability check needed):\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	default:
-		result = "Unable to detect package manager. Please specify package_manager: 'cargo', 'npm', or 'go'"
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handleOutdatedPackages(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Directory      string `json:"directory,omitempty"`
-		PackageManager string `json:"package_manager,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	var result string
-
-	// Auto-detect package manager if not specified
-	if args.PackageManager == "" {
-		if _, err := os.Stat(filepath.Join(args.Directory, "Cargo.toml")); err == nil {
-			args.PackageManager = "cargo"
-		} else if _, err := os.Stat(filepath.Join(args.Directory, "package.json")); err == nil {
-			args.PackageManager = "npm"
-		} else if _, err := os.Stat(filepath.Join(args.Directory, "go.mod")); err == nil {
-			args.PackageManager = "go"
-		}
-	}
-
-	switch args.PackageManager {
-	case "cargo":
-		cmd := exec.Command("cargo", "outdated")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Cargo outdated packages:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v (try: cargo install cargo-outdated)", err)
-		}
-	case "npm":
-		cmd := exec.Command("npm", "outdated")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("NPM outdated packages:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "go":
-		cmd := exec.Command("go", "list", "-u", "-m", "all")
-		cmd.Dir = args.Directory
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Go modules with available updates:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "brew":
-		cmd := exec.Command("brew", "outdated")
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Homebrew outdated packages:\n%s", string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	default:
-		result = "Unable to detect package manager. Please specify package_manager: 'cargo', 'npm', 'go', or 'brew'"
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
-
-func (s *Server) handlePackageInfo(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
-
-	var args struct {
-		Package        string `json:"package"`
-		PackageManager string `json:"package_manager,omitempty"`
-		Directory      string `json:"directory,omitempty"`
-	}
-
-	argsBytes, _ := json.Marshal(req.Arguments)
-	if err := json.Unmarshal(argsBytes, &args); err != nil {
-		return mcp.CallToolResult{}, fmt.Errorf("invalid arguments: %w", err)
-	}
-
-	if args.Package == "" {
-		return mcp.CallToolResult{}, fmt.Errorf("package name is required")
-	}
-
-	if args.Directory == "" {
-		args.Directory = "."
-	}
-
-	var result string
-
-	// Auto-detect package manager if not specified
-	if args.PackageManager == "" {
-		if _, err := os.Stat(filepath.Join(args.Directory, "Cargo.toml")); err == nil {
-			args.PackageManager = "cargo"
-		} else if _, err := os.Stat(filepath.Join(args.Directory, "package.json")); err == nil {
-			args.PackageManager = "npm"
-		}
-	}
-
-	switch args.PackageManager {
-	case "cargo":
-		cmd := exec.Command("cargo", "search", args.Package, "--limit", "1")
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Cargo package info for '%s':\n%s", args.Package, string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "npm":
-		cmd := exec.Command("npm", "info", args.Package)
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("NPM package info for '%s':\n%s", args.Package, string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	case "brew":
-		cmd := exec.Command("brew", "info", args.Package)
-		output, err := cmd.CombinedOutput()
-		result = fmt.Sprintf("Homebrew package info for '%s':\n%s", args.Package, string(output))
-		if err != nil {
-			result += fmt.Sprintf("\nError: %v", err)
-		}
-	default:
-		result = "Unable to detect package manager. Please specify package_manager: 'cargo', 'npm', or 'brew'"
-	}
-
-	return mcp.CallToolResult{
-		Content: []mcp.Content{
-			mcp.TextContent{
-				Type: "text",
-				Text: result,
-			},
-		},
-	}, nil
-}
pkg/semantic/server.go
@@ -8,267 +8,237 @@ import (
 	"github.com/xlgmokha/mcp/pkg/mcp"
 )
 
-// Server represents the Semantic MCP server
-type Server struct {
-	*mcp.Server
+// SemanticOperations provides semantic analysis operations
+type SemanticOperations struct {
 	lspManager    *LSPManager
 	symbolManager *SymbolManager
 	projectManager *ProjectManager
 	mu            sync.RWMutex
 }
 
-// NewServer creates a new Semantic MCP server
-func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-semantic", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-	
+// NewSemanticOperations creates a new SemanticOperations helper
+func NewSemanticOperations() (*SemanticOperations, error) {
 	lspManager := NewLSPManager()
 	projectManager := NewProjectManager()
 	symbolManager := NewSymbolManager(lspManager, projectManager)
 	
-	server := &Server{
-		Server:         baseServer,
+	return &SemanticOperations{
 		lspManager:     lspManager,
 		symbolManager:  symbolManager,
 		projectManager: projectManager,
-	}
-
-	server.registerTools()
-
-	return server
+	}, nil
 }
 
-// ListTools returns all available semantic analysis tools
-func (s *Server) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		// Core symbol discovery tools
-		{
-			Name:        "semantic_find_symbol",
-			Description: "Find symbols by name, type, or pattern across the project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"query": map[string]interface{}{
-						"type":        "string",
-						"description": "Symbol name or pattern to search for",
-					},
-					"symbol_type": map[string]interface{}{
-						"type":        "string",
-						"description": "Type of symbol to search for (function, class, variable, etc.)",
-						"enum":        []string{"function", "class", "variable", "interface", "type", "constant", "module"},
-					},
-					"language": map[string]interface{}{
-						"type":        "string",
-						"description": "Programming language to filter by (optional)",
-						"enum":        []string{"go", "rust", "typescript", "javascript", "python", "java", "cpp"},
-					},
-					"case_sensitive": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Whether the search should be case sensitive (default: false)",
-					},
-					"exact_match": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Whether to match the exact symbol name (default: false, allows partial matches)",
-					},
-				},
-				"required": []string{"query"},
+// New creates a new Semantic MCP server
+func New() (*mcp.Server, error) {
+	semantic, err := NewSemanticOperations()
+	if err != nil {
+		return nil, err
+	}
+	
+	builder := mcp.NewServerBuilder("mcp-semantic", "1.0.0")
+
+// Add semantic_find_symbol tool
+	builder.AddTool(mcp.NewTool("semantic_find_symbol", "Find symbols by name, type, or pattern across the project", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"query": map[string]interface{}{
+				"type":        "string",
+				"description": "Symbol name or pattern to search for",
+			},
+			"symbol_type": map[string]interface{}{
+				"type":        "string",
+				"description": "Type of symbol to search for (function, class, variable, etc.)",
+				"enum":        []string{"function", "class", "variable", "interface", "type", "constant", "module"},
+			},
+			"language": map[string]interface{}{
+				"type":        "string",
+				"description": "Programming language to filter by (optional)",
+				"enum":        []string{"go", "rust", "typescript", "javascript", "python", "java", "cpp"},
+			},
+			"case_sensitive": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Whether the search should be case sensitive (default: false)",
+			},
+			"exact_match": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Whether to match the exact symbol name (default: false, allows partial matches)",
 			},
 		},
-		{
-			Name:        "semantic_get_overview",
-			Description: "Get a high-level overview of symbols and structure in a file or directory",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"path": map[string]interface{}{
-						"type":        "string",
-						"description": "File or directory path to analyze",
-					},
-					"depth": map[string]interface{}{
-						"type":        "integer",
-						"description": "Depth of analysis (1=top-level only, 2=include immediate children, etc.) (default: 2)",
-						"minimum":     1,
-						"maximum":     5,
-					},
-					"include_private": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include private/internal symbols (default: false)",
-					},
-					"group_by": map[string]interface{}{
-						"type":        "string",
-						"description": "How to group the results (type, file, module) (default: type)",
-						"enum":        []string{"type", "file", "module"},
-					},
-				},
-				"required": []string{"path"},
+		"required": []string{"query"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleFindSymbol(req)
+	}))
+
+	// Add semantic_get_overview tool
+	builder.AddTool(mcp.NewTool("semantic_get_overview", "Get a high-level overview of symbols and structure in a file or directory", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"path": map[string]interface{}{
+				"type":        "string",
+				"description": "File or directory path to analyze",
+			},
+			"depth": map[string]interface{}{
+				"type":        "integer",
+				"description": "Depth of analysis (1=top-level only, 2=include immediate children, etc.) (default: 2)",
+				"minimum":     1,
+				"maximum":     5,
+			},
+			"include_private": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include private/internal symbols (default: false)",
+			},
+			"group_by": map[string]interface{}{
+				"type":        "string",
+				"description": "How to group the results (type, file, module) (default: type)",
+				"enum":        []string{"type", "file", "module"},
 			},
 		},
-		{
-			Name:        "semantic_get_definition",
-			Description: "Get the definition location and details for a specific symbol",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"symbol": map[string]interface{}{
-						"type":        "string",
-						"description": "Symbol name to find definition for",
-					},
-					"file": map[string]interface{}{
-						"type":        "string",
-						"description": "File path where the symbol is referenced (helps with context)",
-					},
-					"line": map[string]interface{}{
-						"type":        "integer",
-						"description": "Line number where the symbol is referenced (optional, for better precision)",
-						"minimum":     1,
-					},
-					"column": map[string]interface{}{
-						"type":        "integer",
-						"description": "Column number where the symbol is referenced (optional, for better precision)",
-						"minimum":     1,
-					},
-					"include_signature": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include full function/method signature (default: true)",
-					},
-				},
-				"required": []string{"symbol"},
+		"required": []string{"path"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleGetOverview(req)
+	}))
+
+	// Add semantic_get_definition tool
+	builder.AddTool(mcp.NewTool("semantic_get_definition", "Get the definition location and details for a specific symbol", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"symbol": map[string]interface{}{
+				"type":        "string",
+				"description": "Symbol name to find definition for",
+			},
+			"file": map[string]interface{}{
+				"type":        "string",
+				"description": "File path where the symbol is referenced (helps with context)",
+			},
+			"line": map[string]interface{}{
+				"type":        "integer",
+				"description": "Line number where the symbol is referenced (optional, for better precision)",
+				"minimum":     1,
+			},
+			"column": map[string]interface{}{
+				"type":        "integer",
+				"description": "Column number where the symbol is referenced (optional, for better precision)",
+				"minimum":     1,
+			},
+			"include_signature": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include full function/method signature (default: true)",
 			},
 		},
-		{
-			Name:        "semantic_get_references",
-			Description: "Find all references to a specific symbol across the project",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"symbol": map[string]interface{}{
-						"type":        "string",
-						"description": "Symbol name to find references for",
-					},
-					"file": map[string]interface{}{
-						"type":        "string",
-						"description": "File path where the symbol is defined (helps with context)",
-					},
-					"line": map[string]interface{}{
-						"type":        "integer",
-						"description": "Line number where the symbol is defined (optional, for better precision)",
-						"minimum":     1,
-					},
-					"include_declaration": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include the symbol declaration in results (default: true)",
-					},
-					"scope": map[string]interface{}{
-						"type":        "string",
-						"description": "Scope of search (file, directory, project) (default: project)",
-						"enum":        []string{"file", "directory", "project"},
-					},
-				},
-				"required": []string{"symbol"},
+		"required": []string{"symbol"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleGetDefinition(req)
+	}))
+
+	// Add semantic_get_references tool
+	builder.AddTool(mcp.NewTool("semantic_get_references", "Find all references to a specific symbol across the project", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"symbol": map[string]interface{}{
+				"type":        "string",
+				"description": "Symbol name to find references for",
+			},
+			"file": map[string]interface{}{
+				"type":        "string",
+				"description": "File path where the symbol is defined (helps with context)",
+			},
+			"line": map[string]interface{}{
+				"type":        "integer",
+				"description": "Line number where the symbol is defined (optional, for better precision)",
+				"minimum":     1,
+			},
+			"include_declaration": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include the symbol declaration in results (default: true)",
+			},
+			"scope": map[string]interface{}{
+				"type":        "string",
+				"description": "Scope of search (file, directory, project) (default: project)",
+				"enum":        []string{"file", "directory", "project"},
 			},
 		},
-		{
-			Name:        "semantic_get_call_hierarchy",
-			Description: "Get the call hierarchy (callers and callees) for a function or method",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"symbol": map[string]interface{}{
-						"type":        "string",
-						"description": "Function or method name to analyze",
-					},
-					"file": map[string]interface{}{
-						"type":        "string",
-						"description": "File path where the function is defined",
-					},
-					"line": map[string]interface{}{
-						"type":        "integer",
-						"description": "Line number where the function is defined (optional)",
-						"minimum":     1,
-					},
-					"direction": map[string]interface{}{
-						"type":        "string",
-						"description": "Direction of call hierarchy (incoming=who calls this, outgoing=what this calls, both) (default: both)",
-						"enum":        []string{"incoming", "outgoing", "both"},
-					},
-					"depth": map[string]interface{}{
-						"type":        "integer",
-						"description": "Depth of call hierarchy to retrieve (default: 3)",
-						"minimum":     1,
-						"maximum":     10,
-					},
-				},
-				"required": []string{"symbol", "file"},
+		"required": []string{"symbol"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleGetReferences(req)
+	}))
+
+	// Add semantic_get_call_hierarchy tool
+	builder.AddTool(mcp.NewTool("semantic_get_call_hierarchy", "Get the call hierarchy (callers and callees) for a function or method", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"symbol": map[string]interface{}{
+				"type":        "string",
+				"description": "Function or method name to analyze",
+			},
+			"file": map[string]interface{}{
+				"type":        "string",
+				"description": "File path where the function is defined",
+			},
+			"line": map[string]interface{}{
+				"type":        "integer",
+				"description": "Line number where the function is defined (optional)",
+				"minimum":     1,
+			},
+			"direction": map[string]interface{}{
+				"type":        "string",
+				"description": "Direction of call hierarchy (incoming=who calls this, outgoing=what this calls, both) (default: both)",
+				"enum":        []string{"incoming", "outgoing", "both"},
+			},
+			"depth": map[string]interface{}{
+				"type":        "integer",
+				"description": "Depth of call hierarchy to retrieve (default: 3)",
+				"minimum":     1,
+				"maximum":     10,
 			},
 		},
-		{
-			Name:        "semantic_analyze_dependencies",
-			Description: "Analyze dependencies and relationships between modules, files, or symbols",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"path": map[string]interface{}{
-						"type":        "string",
-						"description": "Path to analyze (file, directory, or project root)",
-					},
-					"analysis_type": map[string]interface{}{
-						"type":        "string",
-						"description": "Type of dependency analysis to perform (default: imports)",
-						"enum":        []string{"imports", "exports", "modules", "symbols", "circular"},
-					},
-					"include_external": map[string]interface{}{
-						"type":        "boolean",
-						"description": "Include external/third-party dependencies (default: false)",
-					},
-					"format": map[string]interface{}{
-						"type":        "string",
-						"description": "Output format (tree, graph, list) (default: tree)",
-						"enum":        []string{"tree", "graph", "list"},
-					},
-					"max_depth": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum depth of dependency analysis (default: 5)",
-						"minimum":     1,
-						"maximum":     20,
-					},
-				},
-				"required": []string{"path"},
+		"required": []string{"symbol", "file"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleGetCallHierarchy(req)
+	}))
+
+	// Add semantic_analyze_dependencies tool
+	builder.AddTool(mcp.NewTool("semantic_analyze_dependencies", "Analyze dependencies and relationships between modules, files, or symbols", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"path": map[string]interface{}{
+				"type":        "string",
+				"description": "Path to analyze (file, directory, or project root)",
+			},
+			"analysis_type": map[string]interface{}{
+				"type":        "string",
+				"description": "Type of dependency analysis to perform (default: imports)",
+				"enum":        []string{"imports", "exports", "modules", "symbols", "circular"},
+			},
+			"include_external": map[string]interface{}{
+				"type":        "boolean",
+				"description": "Include external/third-party dependencies (default: false)",
+			},
+			"format": map[string]interface{}{
+				"type":        "string",
+				"description": "Output format (tree, graph, list) (default: tree)",
+				"enum":        []string{"tree", "graph", "list"},
+			},
+			"max_depth": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum depth of dependency analysis (default: 5)",
+				"minimum":     1,
+				"maximum":     20,
 			},
 		},
-	}
-}
+		"required": []string{"path"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return semantic.handleAnalyzeDependencies(req)
+	}))
 
-// registerTools registers all semantic analysis tools with the server
-func (s *Server) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := s.ListTools()
-	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "semantic_find_symbol":
-			handler = s.handleFindSymbol
-		case "semantic_get_overview":
-			handler = s.handleGetOverview
-		case "semantic_get_definition":
-			handler = s.handleGetDefinition
-		case "semantic_get_references":
-			handler = s.handleGetReferences
-		case "semantic_get_call_hierarchy":
-			handler = s.handleGetCallHierarchy
-		case "semantic_analyze_dependencies":
-			handler = s.handleAnalyzeDependencies
-		default:
-			continue
-		}
-		s.RegisterToolWithDefinition(tool, handler)
-	}
+	return builder.Build(), nil
 }
 
+
 // handleFindSymbol finds symbols by name, type, or pattern across the project
-func (s *Server) handleFindSymbol(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleFindSymbol(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Name            string   `json:"name"`                      // Symbol path or pattern
@@ -307,7 +277,7 @@ func (s *Server) handleFindSymbol(req mcp.CallToolRequest) (mcp.CallToolResult,
 	}
 
 	// Find symbols
-	symbols, err := s.symbolManager.FindSymbols(query)
+	symbols, err := semantic.symbolManager.FindSymbols(query)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to find symbols: %w", err)
 	}
@@ -333,9 +303,9 @@ func (s *Server) handleFindSymbol(req mcp.CallToolRequest) (mcp.CallToolResult,
 }
 
 // handleGetOverview gets high-level symbol overview of files or directories
-func (s *Server) handleGetOverview(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleGetOverview(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Path           string   `json:"path"`                      // File or directory path
@@ -359,7 +329,7 @@ func (s *Server) handleGetOverview(req mcp.CallToolRequest) (mcp.CallToolResult,
 	}
 
 	// Get overview
-	overview, err := s.symbolManager.GetOverview(args.Path, args.Depth, args.IncludeKinds, args.ExcludePrivate)
+	overview, err := semantic.symbolManager.GetOverview(args.Path, args.Depth, args.IncludeKinds, args.ExcludePrivate)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to get overview: %w", err)
 	}
@@ -378,9 +348,9 @@ func (s *Server) handleGetOverview(req mcp.CallToolRequest) (mcp.CallToolResult,
 }
 
 // handleGetDefinition gets detailed information about a symbol's definition
-func (s *Server) handleGetDefinition(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleGetDefinition(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Symbol              string `json:"symbol"`                        // Target symbol
@@ -399,7 +369,7 @@ func (s *Server) handleGetDefinition(req mcp.CallToolRequest) (mcp.CallToolResul
 	}
 
 	// Get definition
-	definition, err := s.symbolManager.GetDefinition(args.Symbol, args.IncludeSignature, args.IncludeDocumentation, args.IncludeDependencies)
+	definition, err := semantic.symbolManager.GetDefinition(args.Symbol, args.IncludeSignature, args.IncludeDocumentation, args.IncludeDependencies)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to get definition: %w", err)
 	}
@@ -418,9 +388,9 @@ func (s *Server) handleGetDefinition(req mcp.CallToolRequest) (mcp.CallToolResul
 }
 
 // handleGetReferences finds all places where a symbol is used
-func (s *Server) handleGetReferences(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleGetReferences(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Symbol              string   `json:"symbol"`                        // Target symbol
@@ -446,7 +416,7 @@ func (s *Server) handleGetReferences(req mcp.CallToolRequest) (mcp.CallToolResul
 	}
 
 	// Get references
-	references, err := s.symbolManager.GetReferences(args.Symbol, args.IncludeDefinitions, args.ContextLines, args.FilterByKind, args.Language, args.IncludeExternal)
+	references, err := semantic.symbolManager.GetReferences(args.Symbol, args.IncludeDefinitions, args.ContextLines, args.FilterByKind, args.Language, args.IncludeExternal)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to get references: %w", err)
 	}
@@ -475,9 +445,9 @@ func (s *Server) handleGetReferences(req mcp.CallToolRequest) (mcp.CallToolResul
 }
 
 // handleGetCallHierarchy understands calling relationships (what calls this, what this calls)
-func (s *Server) handleGetCallHierarchy(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleGetCallHierarchy(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Symbol          string `json:"symbol"`                    // Target symbol
@@ -505,7 +475,7 @@ func (s *Server) handleGetCallHierarchy(req mcp.CallToolRequest) (mcp.CallToolRe
 	}
 
 	// Get call hierarchy
-	hierarchy, err := s.symbolManager.GetCallHierarchy(args.Symbol, args.Direction, args.MaxDepth, args.IncludeExternal, args.Language)
+	hierarchy, err := semantic.symbolManager.GetCallHierarchy(args.Symbol, args.Direction, args.MaxDepth, args.IncludeExternal, args.Language)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to get call hierarchy: %w", err)
 	}
@@ -525,9 +495,9 @@ func (s *Server) handleGetCallHierarchy(req mcp.CallToolRequest) (mcp.CallToolRe
 }
 
 // handleAnalyzeDependencies analyzes symbol dependencies and relationships  
-func (s *Server) handleAnalyzeDependencies(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	s.mu.RLock()
-	defer s.mu.RUnlock()
+func (semantic *SemanticOperations) handleAnalyzeDependencies(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	semantic.mu.RLock()
+	defer semantic.mu.RUnlock()
 
 	var args struct {
 		Scope           string `json:"scope"`                     // Analysis scope: "file", "package", "project"
@@ -551,7 +521,7 @@ func (s *Server) handleAnalyzeDependencies(req mcp.CallToolRequest) (mcp.CallToo
 	}
 
 	// Analyze dependencies
-	analysis, err := s.symbolManager.AnalyzeDependencies(args.Scope, args.Path, args.IncludeExternal, args.GroupBy, args.ShowUnused, args.Language)
+	analysis, err := semantic.symbolManager.AnalyzeDependencies(args.Scope, args.Path, args.IncludeExternal, args.GroupBy, args.ShowUnused, args.Language)
 	if err != nil {
 		return mcp.CallToolResult{}, fmt.Errorf("failed to analyze dependencies: %w", err)
 	}
@@ -571,18 +541,18 @@ func (s *Server) handleAnalyzeDependencies(req mcp.CallToolRequest) (mcp.CallToo
 }
 
 // Shutdown gracefully shuts down the semantic server
-func (s *Server) Shutdown() error {
-	s.mu.Lock()
-	defer s.mu.Unlock()
+func (semantic *SemanticOperations) Shutdown() error {
+	semantic.mu.Lock()
+	defer semantic.mu.Unlock()
 
-	if s.lspManager != nil {
-		if err := s.lspManager.Shutdown(); err != nil {
+	if semantic.lspManager != nil {
+		if err := semantic.lspManager.Shutdown(); err != nil {
 			return fmt.Errorf("failed to shutdown LSP manager: %w", err)
 		}
 	}
 
-	if s.projectManager != nil {
-		if err := s.projectManager.Shutdown(); err != nil {
+	if semantic.projectManager != nil {
+		if err := semantic.projectManager.Shutdown(); err != nil {
 			return fmt.Errorf("failed to shutdown project manager: %w", err)
 		}
 	}
pkg/semantic/server_test.go
@@ -1,428 +0,0 @@
-package semantic
-
-import (
-	"encoding/json"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestNewServer(t *testing.T) {
-	server := NewServer()
-	if server == nil {
-		t.Fatal("NewServer() returned nil")
-	}
-
-	// Test that server was created with base MCP server
-	if server.Server == nil {
-		t.Fatal("Server.Server is nil")
-	}
-
-	// Test that LSP manager was created
-	if server.lspManager == nil {
-		t.Fatal("Server.lspManager is nil") 
-	}
-
-	// Test that symbol manager was created
-	if server.symbolManager == nil {
-		t.Fatal("Server.symbolManager is nil")
-	}
-
-	// Test that project manager was created
-	if server.projectManager == nil {
-		t.Fatal("Server.projectManager is nil")
-	}
-}
-
-func TestSemanticFindSymbol_InvalidArgs(t *testing.T) {
-	server := NewServer()
-
-	// Test with empty name
-	req := mcp.CallToolRequest{
-		Name: "semantic_find_symbol",
-		Arguments: map[string]interface{}{
-			"name": "",
-		},
-	}
-
-	result, err := server.handleFindSymbol(req)
-	if err == nil {
-		t.Error("Expected error for empty name, got nil")
-	}
-
-	if len(result.Content) > 0 {
-		t.Error("Expected no content on error")
-	}
-}
-
-func TestSemanticFindSymbol_ValidArgs(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_find_symbol",
-		Arguments: map[string]interface{}{
-			"name":     "main",
-			"kind":     "function",
-			"language": "go",
-		},
-	}
-
-	result, err := server.handleFindSymbol(req)
-	
-	// For now, we expect this to work without error (even if no symbols found)
-	// because we haven't initialized a real project yet
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
-
-func TestSemanticGetOverview_InvalidArgs(t *testing.T) {
-	server := NewServer()
-
-	// Test with empty path
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_overview",
-		Arguments: map[string]interface{}{
-			"path": "",
-		},
-	}
-
-	result, err := server.handleGetOverview(req)
-	if err == nil {
-		t.Error("Expected error for empty path, got nil")
-	}
-
-	if len(result.Content) > 0 {
-		t.Error("Expected no content on error")
-	}
-}
-
-func TestSemanticGetDefinition_InvalidArgs(t *testing.T) {
-	server := NewServer()
-
-	// Test with empty symbol
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_definition",
-		Arguments: map[string]interface{}{
-			"symbol": "",
-		},
-	}
-
-	result, err := server.handleGetDefinition(req)
-	if err == nil {
-		t.Error("Expected error for empty symbol, got nil")
-	}
-
-	if len(result.Content) > 0 {
-		t.Error("Expected no content on error")
-	}
-}
-
-func TestSymbolQuery_JSONSerialization(t *testing.T) {
-	query := SymbolQuery{
-		Name:            "test",
-		Kind:            SymbolKindFunction,
-		Scope:           "project",
-		Language:        "go",
-		IncludeChildren: true,
-		MaxResults:      50,
-	}
-
-	data, err := json.Marshal(query)
-	if err != nil {
-		t.Fatalf("Failed to marshal SymbolQuery: %v", err)
-	}
-
-	var decoded SymbolQuery
-	if err := json.Unmarshal(data, &decoded); err != nil {
-		t.Fatalf("Failed to unmarshal SymbolQuery: %v", err)
-	}
-
-	if decoded.Name != query.Name {
-		t.Errorf("Name mismatch: got %s, want %s", decoded.Name, query.Name)
-	}
-
-	if decoded.Kind != query.Kind {
-		t.Errorf("Kind mismatch: got %s, want %s", decoded.Kind, query.Kind)
-	}
-}
-
-func TestSymbol_JSONSerialization(t *testing.T) {
-	symbol := Symbol{
-		Name:     "testFunction",
-		FullPath: "package.testFunction",
-		Kind:     SymbolKindFunction,
-		Location: SourceLocation{
-			FilePath: "/test/file.go",
-			Line:     10,
-			Column:   5,
-		},
-		Language:   "go",
-		Visibility: "public",
-	}
-
-	data, err := json.Marshal(symbol)
-	if err != nil {
-		t.Fatalf("Failed to marshal Symbol: %v", err)
-	}
-
-	var decoded Symbol
-	if err := json.Unmarshal(data, &decoded); err != nil {
-		t.Fatalf("Failed to unmarshal Symbol: %v", err)
-	}
-
-	if decoded.Name != symbol.Name {
-		t.Errorf("Name mismatch: got %s, want %s", decoded.Name, symbol.Name)
-	}
-
-	if decoded.Location.Line != symbol.Location.Line {
-		t.Errorf("Line mismatch: got %d, want %d", decoded.Location.Line, symbol.Location.Line)
-	}
-}
-
-func TestSemanticGetReferences_InvalidArgs(t *testing.T) {
-	server := NewServer()
-
-	// Test with empty symbol
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_references",
-		Arguments: map[string]interface{}{
-			"symbol": "",
-		},
-	}
-
-	result, err := server.handleGetReferences(req)
-	if err == nil {
-		t.Error("Expected error for empty symbol, got nil")
-	}
-
-	if len(result.Content) > 0 {
-		t.Error("Expected no content on error")
-	}
-}
-
-func TestSemanticGetReferences_ValidArgs(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_references",
-		Arguments: map[string]interface{}{
-			"symbol":       "main",
-			"context_lines": 3,
-		},
-	}
-
-	result, err := server.handleGetReferences(req)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
-
-func TestSemanticGetCallHierarchy_InvalidArgs(t *testing.T) {
-	server := NewServer()
-
-	// Test with empty symbol
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_call_hierarchy",
-		Arguments: map[string]interface{}{
-			"symbol": "",
-		},
-	}
-
-	result, err := server.handleGetCallHierarchy(req)
-	if err == nil {
-		t.Error("Expected error for empty symbol, got nil")
-	}
-
-	if len(result.Content) > 0 {
-		t.Error("Expected no content on error")
-	}
-}
-
-func TestSemanticAnalyzeDependencies_ValidArgs(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_analyze_dependencies",
-		Arguments: map[string]interface{}{
-			"scope":    "project",
-			"group_by": "package",
-		},
-	}
-
-	result, err := server.handleAnalyzeDependencies(req)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
-
-func TestSymbolReference_JSONSerialization(t *testing.T) {
-	ref := SymbolReference{
-		Location: SourceLocation{
-			FilePath: "/test/file.go",
-			Line:     10,
-			Column:   5,
-		},
-		Context: "  10: func main() {",
-		Kind:    "call",
-		Symbol:  "main",
-	}
-
-	data, err := json.Marshal(ref)
-	if err != nil {
-		t.Fatalf("Failed to marshal SymbolReference: %v", err)
-	}
-
-	var decoded SymbolReference
-	if err := json.Unmarshal(data, &decoded); err != nil {
-		t.Fatalf("Failed to unmarshal SymbolReference: %v", err)
-	}
-
-	if decoded.Symbol != ref.Symbol {
-		t.Errorf("Symbol mismatch: got %s, want %s", decoded.Symbol, ref.Symbol)
-	}
-
-	if decoded.Location.Line != ref.Location.Line {
-		t.Errorf("Line mismatch: got %d, want %d", decoded.Location.Line, ref.Location.Line)
-	}
-}
-
-func TestCallHierarchy_JSONSerialization(t *testing.T) {
-	hierarchy := CallHierarchy{
-		Symbol:     "main",
-		Direction:  "both",
-		MaxDepth:   3,
-		TotalItems: 5,
-		Root: CallHierarchyItem{
-			Symbol: "main",
-			Name:   "main",
-			Kind:   SymbolKindFunction,
-			Depth:  0,
-		},
-	}
-
-	data, err := json.Marshal(hierarchy)
-	if err != nil {
-		t.Fatalf("Failed to marshal CallHierarchy: %v", err)
-	}
-
-	var decoded CallHierarchy
-	if err := json.Unmarshal(data, &decoded); err != nil {
-		t.Fatalf("Failed to unmarshal CallHierarchy: %v", err)
-	}
-
-	if decoded.Symbol != hierarchy.Symbol {
-		t.Errorf("Symbol mismatch: got %s, want %s", decoded.Symbol, hierarchy.Symbol)
-	}
-
-	if decoded.TotalItems != hierarchy.TotalItems {
-		t.Errorf("TotalItems mismatch: got %d, want %d", decoded.TotalItems, hierarchy.TotalItems)
-	}
-}
-
-func TestServerShutdown(t *testing.T) {
-	server := NewServer()
-
-	// Test shutdown
-	if err := server.Shutdown(); err != nil {
-		t.Errorf("Shutdown failed: %v", err)
-	}
-
-	// Test that we can shutdown multiple times without error
-	if err := server.Shutdown(); err != nil {
-		t.Errorf("Second shutdown failed: %v", err)
-	}
-}
-
-func TestSemanticGetReferences_DefaultArgs(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_references",
-		Arguments: map[string]interface{}{
-			"symbol": "testFunction",
-		},
-	}
-
-	result, err := server.handleGetReferences(req)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
-
-func TestSemanticGetCallHierarchy_DefaultArgs(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_get_call_hierarchy",
-		Arguments: map[string]interface{}{
-			"symbol": "testFunction",
-		},
-	}
-
-	result, err := server.handleGetCallHierarchy(req)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
-
-func TestSemanticAnalyzeDependencies_EmptyScope(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_analyze_dependencies",
-		Arguments: map[string]interface{}{
-			"scope": "",
-		},
-	}
-
-	result, err := server.handleAnalyzeDependencies(req)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result (should default to project scope)")
-	}
-}
-
-func TestSemanticAnalyzeDependencies_InvalidGroupBy(t *testing.T) {
-	server := NewServer()
-
-	req := mcp.CallToolRequest{
-		Name: "semantic_analyze_dependencies",
-		Arguments: map[string]interface{}{
-			"scope":    "project",
-			"group_by": "invalid_group",
-		},
-	}
-
-	result, err := server.handleAnalyzeDependencies(req)
-	if err != nil {
-		t.Errorf("Unexpected error for unknown group_by: %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Error("Expected some content in result")
-	}
-}
\ No newline at end of file
pkg/signal/server.go
@@ -23,8 +23,8 @@ import (
 	"golang.org/x/crypto/pbkdf2"
 )
 
-type Server struct {
-	*mcp.Server
+// SignalOperations provides Signal Desktop database operations
+type SignalOperations struct {
 	mu         sync.RWMutex
 	dbPath     string
 	configPath string
@@ -62,195 +62,163 @@ type Attachment struct {
 	Size        int64  `json:"size,omitempty"`
 }
 
-func NewServer(signalPath string) (*Server, error) {
-	baseServer := mcp.NewServer("signal", "0.1.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-	
-	server := &Server{
-		Server: baseServer,
-	}
+// NewSignalOperations creates a new SignalOperations helper
+func NewSignalOperations(signalPath string) (*SignalOperations, error) {
+	signal := &SignalOperations{}
 
 	// Determine Signal database and config paths
 	if signalPath != "" {
-		server.dbPath = filepath.Join(signalPath, "sql", "db.sqlite")
-		server.configPath = filepath.Join(signalPath, "config.json")
+		signal.dbPath = filepath.Join(signalPath, "sql", "db.sqlite")
+		signal.configPath = filepath.Join(signalPath, "config.json")
 	} else {
 		var err error
-		server.dbPath, server.configPath, err = findSignalPaths()
+		signal.dbPath, signal.configPath, err = findSignalPaths()
 		if err != nil {
 			return nil, fmt.Errorf("failed to find Signal paths: %w", err)
 		}
 	}
 
-	// Register tools
-	server.registerTools()
-
-	// Register prompts
-	conversationPrompt := mcp.Prompt{
-		Name:        "signal-conversation",
-		Description: "Analyze Signal conversation history for insights",
-	}
-	searchPrompt := mcp.Prompt{
-		Name:        "signal-search",
-		Description: "Search Signal messages with context",
-	}
-	server.RegisterPrompt(conversationPrompt, server.handleConversationPrompt)
-	server.RegisterPrompt(searchPrompt, server.handleSearchPrompt)
-
-	return server, nil
+	return signal, nil
 }
 
-// ListTools returns all available Signal tools
-func (s *Server) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		{
-			Name:        "signal_list_conversations",
-			Description: "List recent Signal conversations with timestamps and participants",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of conversations to return",
-						"minimum":     1,
-						"default":     20,
-					},
-				},
+// New creates a new Signal MCP server
+func New(signalPath string) (*mcp.Server, error) {
+	signal, err := NewSignalOperations(signalPath)
+	if err != nil {
+		return nil, err
+	}
+	
+	builder := mcp.NewServerBuilder("signal-server", "1.0.0")
+
+	// Add signal_list_conversations tool
+	builder.AddTool(mcp.NewTool("signal_list_conversations", "List recent Signal conversations with timestamps and participants", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of conversations to return",
+				"minimum":     1,
+				"default":     20,
 			},
 		},
-		{
-			Name:        "signal_search_messages",
-			Description: "Search Signal messages by content with filtering options",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"query": map[string]interface{}{
-						"type":        "string",
-						"description": "Search query string",
-					},
-					"conversation_id": map[string]interface{}{
-						"type":        "string",
-						"description": "Optional conversation ID to limit search scope",
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of results",
-						"minimum":     1,
-						"default":     50,
-					},
-				},
-				"required": []string{"query"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleListConversations(req)
+	}))
+
+	// Add signal_search_messages tool
+	builder.AddTool(mcp.NewTool("signal_search_messages", "Search Signal messages by content with filtering options", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"query": map[string]interface{}{
+				"type":        "string",
+				"description": "Search query string",
 			},
-		},
-		{
-			Name:        "signal_get_conversation",
-			Description: "Get detailed conversation history including all messages and metadata",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"conversation_id": map[string]interface{}{
-						"type":        "string",
-						"description": "The conversation ID to retrieve",
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of messages to return",
-						"minimum":     1,
-						"default":     100,
-					},
-				},
-				"required": []string{"conversation_id"},
+			"conversation_id": map[string]interface{}{
+				"type":        "string",
+				"description": "Optional conversation ID to limit search scope",
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of results",
+				"minimum":     1,
+				"default":     50,
 			},
 		},
-		{
-			Name:        "signal_get_contact",
-			Description: "Get detailed contact information by ID, phone number, or name",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"contact_id": map[string]interface{}{
-						"type":        "string",
-						"description": "Contact ID, phone number, or display name",
-					},
-				},
-				"required": []string{"contact_id"},
+		"required": []string{"query"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleSearchMessages(req)
+	}))
+
+	// Add signal_get_conversation tool
+	builder.AddTool(mcp.NewTool("signal_get_conversation", "Get detailed conversation history including all messages and metadata", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"conversation_id": map[string]interface{}{
+				"type":        "string",
+				"description": "The conversation ID to retrieve",
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of messages to return",
+				"minimum":     1,
+				"default":     100,
 			},
 		},
-		{
-			Name:        "signal_get_message",
-			Description: "Get detailed information about a specific message including attachments and reactions",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"message_id": map[string]interface{}{
-						"type":        "string",
-						"description": "The message ID to retrieve",
-					},
-				},
-				"required": []string{"message_id"},
+		"required": []string{"conversation_id"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleGetConversation(req)
+	}))
+
+	// Add signal_get_contact tool
+	builder.AddTool(mcp.NewTool("signal_get_contact", "Get detailed contact information by ID, phone number, or name", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"contact_id": map[string]interface{}{
+				"type":        "string",
+				"description": "Contact ID, phone number, or display name",
 			},
 		},
-		{
-			Name:        "signal_list_attachments",
-			Description: "List message attachments with metadata and filtering options",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"conversation_id": map[string]interface{}{
-						"type":        "string",
-						"description": "Optional conversation ID to filter attachments",
-					},
-					"media_type": map[string]interface{}{
-						"type":        "string",
-						"description": "Filter by media type (image, video, audio, file)",
-					},
-					"limit": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of attachments to return",
-						"minimum":     1,
-						"default":     50,
-					},
-				},
+		"required": []string{"contact_id"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleGetContact(req)
+	}))
+
+	// Add signal_get_message tool
+	builder.AddTool(mcp.NewTool("signal_get_message", "Get detailed information about a specific message including attachments and reactions", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"message_id": map[string]interface{}{
+				"type":        "string",
+				"description": "The message ID to retrieve",
 			},
 		},
-		{
-			Name:        "signal_get_stats",
-			Description: "Get Signal database statistics and connection information",
-			InputSchema: map[string]interface{}{
-				"type": "object",
+		"required": []string{"message_id"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleGetMessage(req)
+	}))
+
+	// Add signal_list_attachments tool
+	builder.AddTool(mcp.NewTool("signal_list_attachments", "List message attachments with metadata and filtering options", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"conversation_id": map[string]interface{}{
+				"type":        "string",
+				"description": "Optional conversation ID to filter attachments",
+			},
+			"media_type": map[string]interface{}{
+				"type":        "string",
+				"description": "Filter by media type (image, video, audio, file)",
+			},
+			"limit": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of attachments to return",
+				"minimum":     1,
+				"default":     50,
 			},
 		},
-	}
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleListAttachments(req)
+	}))
+
+	// Add signal_get_stats tool
+	builder.AddTool(mcp.NewTool("signal_get_stats", "Get Signal database statistics and connection information", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return signal.handleGetStats(req)
+	}))
+
+	// Add prompts
+	builder.AddPrompt(mcp.NewPrompt("signal-conversation", "Analyze Signal conversation history for insights", []mcp.PromptArgument{}, func(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
+		return signal.handleConversationPrompt(req)
+	}))
+
+	builder.AddPrompt(mcp.NewPrompt("signal-search", "Search Signal messages with context", []mcp.PromptArgument{}, func(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
+		return signal.handleSearchPrompt(req)
+	}))
+
+	return builder.Build(), nil
 }
 
-// registerTools registers all Signal tools with the server
-func (s *Server) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := s.ListTools()
-	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "signal_list_conversations":
-			handler = s.handleListConversations
-		case "signal_search_messages":
-			handler = s.handleSearchMessages
-		case "signal_get_conversation":
-			handler = s.handleGetConversation
-		case "signal_get_contact":
-			handler = s.handleGetContact
-		case "signal_get_message":
-			handler = s.handleGetMessage
-		case "signal_list_attachments":
-			handler = s.handleListAttachments
-		case "signal_get_stats":
-			handler = s.handleGetStats
-		default:
-			continue
-		}
-		s.RegisterToolWithDefinition(tool, handler)
-	}
-}
 
 func findSignalPaths() (dbPath, configPath string, err error) {
 	var basePath string
@@ -292,14 +260,14 @@ func findSignalPaths() (dbPath, configPath string, err error) {
 	return dbPath, configPath, nil
 }
 
-func (s *Server) ensureConnection() error {
+func (signal *SignalOperations) ensureConnection() error {
 	// Skip connection since we'll use command-line sqlcipher
 	return nil
 }
 
-func (s *Server) executeQuery(query string) ([]byte, error) {
+func (signal *SignalOperations) executeQuery(query string) ([]byte, error) {
 	// Get decryption key - use the same method as working implementation
-	key, err := s.getDecryptedSignalKey()
+	key, err := signal.getDecryptedSignalKey()
 	if err != nil {
 		return nil, fmt.Errorf("failed to get Signal key: %w", err)
 	}
@@ -317,7 +285,7 @@ PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;
 `, key, query)
 
 	// Execute sqlcipher with the script
-	cmd := exec.Command("sqlcipher", s.dbPath)
+	cmd := exec.Command("sqlcipher", signal.dbPath)
 	cmd.Stdin = strings.NewReader(sqlScript)
 	output, err := cmd.CombinedOutput()
 	if err != nil {
@@ -351,9 +319,9 @@ PRAGMA cipher_kdf_algorithm = PBKDF2_HMAC_SHA512;
 	return []byte("[]"), nil
 }
 
-func (s *Server) getDecryptedSignalKey() (string, error) {
+func (signal *SignalOperations) getDecryptedSignalKey() (string, error) {
 	// Read config.json
-	configData, err := os.ReadFile(s.configPath)
+	configData, err := os.ReadFile(signal.configPath)
 	if err != nil {
 		return "", fmt.Errorf("failed to read config: %w", err)
 	}
@@ -368,24 +336,24 @@ func (s *Server) getDecryptedSignalKey() (string, error) {
 	}
 
 	if config.EncryptedKey != "" {
-		return s.decryptSignalKey(config.EncryptedKey)
+		return signal.decryptSignalKey(config.EncryptedKey)
 	}
 
 	return "", fmt.Errorf("no encryption key found in config")
 }
 
-func (s *Server) decryptSignalKey(encryptedKey string) (string, error) {
+func (signal *SignalOperations) decryptSignalKey(encryptedKey string) (string, error) {
 	switch runtime.GOOS {
 	case "darwin":
-		return s.decryptKeyMacOS(encryptedKey)
+		return signal.decryptKeyMacOS(encryptedKey)
 	case "linux":
-		return s.decryptKeyLinux(encryptedKey)
+		return signal.decryptKeyLinux(encryptedKey)
 	default:
 		return "", fmt.Errorf("key decryption not supported on %s", runtime.GOOS)
 	}
 }
 
-func (s *Server) decryptKeyMacOS(encryptedKey string) (string, error) {
+func (signal *SignalOperations) decryptKeyMacOS(encryptedKey string) (string, error) {
 	// Get password from macOS Keychain using security command
 	cmd := exec.Command("security", "find-generic-password", "-ws", "Signal Safe Storage")
 	output, err := cmd.Output()
@@ -400,7 +368,7 @@ func (s *Server) decryptKeyMacOS(encryptedKey string) (string, error) {
 	}
 	
 	// Decrypt the key using the password
-	key, err := s.decryptWithPassword(password, encryptedKey, "v10", 1003)
+	key, err := signal.decryptWithPassword(password, encryptedKey, "v10", 1003)
 	if err != nil {
 		return "", fmt.Errorf("failed to decrypt key: %w", err)
 	}
@@ -408,13 +376,13 @@ func (s *Server) decryptKeyMacOS(encryptedKey string) (string, error) {
 	return key, nil
 }
 
-func (s *Server) decryptKeyLinux(encryptedKey string) (string, error) {
+func (signal *SignalOperations) decryptKeyLinux(encryptedKey string) (string, error) {
 	// Try secret-tool first
 	cmd := exec.Command("secret-tool", "lookup", "application", "Signal")
 	if output, err := cmd.Output(); err == nil {
 		password := strings.TrimSpace(string(output))
 		if password != "" {
-			return s.decryptWithPassword(password, encryptedKey, "v11", 1)
+			return signal.decryptWithPassword(password, encryptedKey, "v11", 1)
 		}
 	}
 
@@ -422,7 +390,7 @@ func (s *Server) decryptKeyLinux(encryptedKey string) (string, error) {
 	return "", fmt.Errorf("failed to decrypt key on Linux")
 }
 
-func (s *Server) convertKeyToHex(key string) (string, error) {
+func (signal *SignalOperations) convertKeyToHex(key string) (string, error) {
 	// Signal stores keys in base64 format in the keychain
 	// SQLCipher expects hex format with x'...' syntax
 	keyBytes, err := base64.StdEncoding.DecodeString(key)
@@ -433,7 +401,7 @@ func (s *Server) convertKeyToHex(key string) (string, error) {
 	return hex.EncodeToString(keyBytes), nil
 }
 
-func (s *Server) decryptWithPassword(password, encryptedKey, prefix string, iterations int) (string, error) {
+func (signal *SignalOperations) decryptWithPassword(password, encryptedKey, prefix string, iterations int) (string, error) {
 	// Decode hex key
 	encryptedKeyBytes, err := hex.DecodeString(encryptedKey)
 	if err != nil {
@@ -476,7 +444,7 @@ func (s *Server) decryptWithPassword(password, encryptedKey, prefix string, iter
 	mode.CryptBlocks(decrypted, encryptedKeyBytes)
 	
 	// Remove PKCS7 padding
-	decrypted, err = s.removePKCS7Padding(decrypted)
+	decrypted, err = signal.removePKCS7Padding(decrypted)
 	if err != nil {
 		return "", fmt.Errorf("failed to remove padding: %w", err)
 	}
@@ -487,7 +455,7 @@ func (s *Server) decryptWithPassword(password, encryptedKey, prefix string, iter
 	return result, nil
 }
 
-func (s *Server) removePKCS7Padding(data []byte) ([]byte, error) {
+func (signal *SignalOperations) removePKCS7Padding(data []byte) ([]byte, error) {
 	if len(data) == 0 {
 		return nil, fmt.Errorf("empty data")
 	}
@@ -507,8 +475,8 @@ func (s *Server) removePKCS7Padding(data []byte) ([]byte, error) {
 	return data[:len(data)-padLen], nil
 }
 
-func (s *Server) handleListConversations(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
-	if err := s.ensureConnection(); err != nil {
+func (signal *SignalOperations) handleListConversations(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+	if err := signal.ensureConnection(); err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Database connection failed: %v", err)), nil
 	}
 
@@ -519,7 +487,7 @@ func (s *Server) handleListConversations(req mcp.CallToolRequest) (mcp.CallToolR
 		ORDER BY active_at DESC
 		LIMIT 10`
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Query failed: %v", err)), nil
 	}
@@ -551,7 +519,7 @@ func (s *Server) handleListConversations(req mcp.CallToolRequest) (mcp.CallToolR
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleSearchMessages(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleSearchMessages(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	searchTerm, ok := args["query"].(string)
 	if !ok || searchTerm == "" {
@@ -576,7 +544,7 @@ func (s *Server) handleSearchMessages(req mcp.CallToolRequest) (mcp.CallToolResu
 		ORDER BY m.sent_at DESC
 		LIMIT ` + strconv.Itoa(limit)
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Search failed: %v", err)), nil
 	}
@@ -610,7 +578,7 @@ func (s *Server) handleSearchMessages(req mcp.CallToolRequest) (mcp.CallToolResu
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleGetConversation(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleGetConversation(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	conversationID, ok := args["conversation_id"].(string)
 	if !ok || conversationID == "" {
@@ -633,7 +601,7 @@ func (s *Server) handleGetConversation(req mcp.CallToolRequest) (mcp.CallToolRes
 		ORDER BY m.sent_at DESC
 		LIMIT ` + strconv.Itoa(limit)
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Query failed: %v", err)), nil
 	}
@@ -674,7 +642,7 @@ func (s *Server) handleGetConversation(req mcp.CallToolRequest) (mcp.CallToolRes
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleGetContact(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleGetContact(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	contactID, ok := args["contact_id"].(string)
 	if !ok || contactID == "" {
@@ -687,7 +655,7 @@ func (s *Server) handleGetContact(req mcp.CallToolRequest) (mcp.CallToolResult,
 		WHERE id = '` + contactID + `' OR e164 = '` + contactID + `' OR name = '` + contactID + `' OR profileName = '` + contactID + `'
 		LIMIT 1`
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Query failed: %v", err)), nil
 	}
@@ -710,7 +678,7 @@ func (s *Server) handleGetContact(req mcp.CallToolRequest) (mcp.CallToolResult,
 	// Get message count with this contact
 	messageCountQuery := `SELECT COUNT(*) as message_count FROM messages WHERE conversationId = '` + fmt.Sprintf("%v", contact["id"]) + `' AND type NOT IN ('keychange', 'profile-change') AND type IS NOT NULL`
 	
-	countOutput, err := s.executeQuery(messageCountQuery)
+	countOutput, err := signal.executeQuery(messageCountQuery)
 	var messageCount int64 = 0
 	if err == nil {
 		var countResult []map[string]interface{}
@@ -744,7 +712,7 @@ func (s *Server) handleGetContact(req mcp.CallToolRequest) (mcp.CallToolResult,
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleGetMessage(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleGetMessage(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	messageID, ok := args["message_id"].(string)
 	if !ok || messageID == "" {
@@ -758,7 +726,7 @@ func (s *Server) handleGetMessage(req mcp.CallToolRequest) (mcp.CallToolResult,
 		WHERE m.id = '` + messageID + `'
 		LIMIT 1`
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Query failed: %v", err)), nil
 	}
@@ -842,7 +810,7 @@ func (s *Server) handleGetMessage(req mcp.CallToolRequest) (mcp.CallToolResult,
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleListAttachments(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleListAttachments(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	args := req.Arguments
 	
 	// Optional conversation filter
@@ -872,7 +840,7 @@ func (s *Server) handleListAttachments(req mcp.CallToolRequest) (mcp.CallToolRes
 	
 	query += ` ORDER BY m.sent_at DESC LIMIT ` + strconv.Itoa(limit)
 
-	output, err := s.executeQuery(query)
+	output, err := signal.executeQuery(query)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Query failed: %v", err)), nil
 	}
@@ -963,10 +931,10 @@ func (s *Server) handleListAttachments(req mcp.CallToolRequest) (mcp.CallToolRes
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleGetStats(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (signal *SignalOperations) handleGetStats(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	// Get message count using command-line sqlcipher
 	messageQuery := "SELECT COUNT(*) as count FROM messages WHERE type NOT IN ('keychange', 'profile-change') AND type IS NOT NULL"
-	messageOutput, err := s.executeQuery(messageQuery)
+	messageOutput, err := signal.executeQuery(messageQuery)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to count messages: %v", err)), nil
 	}
@@ -981,7 +949,7 @@ func (s *Server) handleGetStats(req mcp.CallToolRequest) (mcp.CallToolResult, er
 
 	// Get conversation count using command-line sqlcipher  
 	convQuery := "SELECT COUNT(*) as count FROM conversations WHERE type IS NOT NULL"
-	convOutput, err := s.executeQuery(convQuery)
+	convOutput, err := signal.executeQuery(convQuery)
 	if err != nil {
 		return mcp.NewToolError(fmt.Sprintf("Failed to count conversations: %v", err)), nil
 	}
@@ -995,12 +963,12 @@ func (s *Server) handleGetStats(req mcp.CallToolRequest) (mcp.CallToolResult, er
 	}
 
 	result := fmt.Sprintf("Signal Database Statistics:\n- Total Messages: %d\n- Total Conversations: %d\n- Database Path: %s", 
-		totalMessages, totalConversations, s.dbPath)
+		totalMessages, totalConversations, signal.dbPath)
 
 	return mcp.NewToolResult(mcp.NewTextContent(result)), nil
 }
 
-func (s *Server) handleConversationPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
+func (signal *SignalOperations) handleConversationPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
 	// Get conversation ID from arguments
 	args := req.Arguments
 	conversationID, ok := args["conversation_id"].(string)
@@ -1018,7 +986,7 @@ func (s *Server) handleConversationPrompt(req mcp.GetPromptRequest) (mcp.GetProm
 	convQuery := `SELECT COALESCE(name, profileName, e164, id) as display_name, type, active_at
 		FROM conversations WHERE id = '` + conversationID + `' LIMIT 1`
 	
-	convOutput, err := s.executeQuery(convQuery)
+	convOutput, err := signal.executeQuery(convQuery)
 	if err != nil {
 		return mcp.GetPromptResult{}, fmt.Errorf("failed to get conversation details: %w", err)
 	}
@@ -1043,7 +1011,7 @@ func (s *Server) handleConversationPrompt(req mcp.GetPromptRequest) (mcp.GetProm
 		ORDER BY sent_at DESC 
 		LIMIT 50`
 
-	msgOutput, err := s.executeQuery(msgQuery)
+	msgOutput, err := signal.executeQuery(msgQuery)
 	if err != nil {
 		return mcp.GetPromptResult{}, fmt.Errorf("failed to get messages: %w", err)
 	}
@@ -1113,7 +1081,7 @@ func (s *Server) handleConversationPrompt(req mcp.GetPromptRequest) (mcp.GetProm
 	return result, nil
 }
 
-func (s *Server) handleSearchPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
+func (signal *SignalOperations) handleSearchPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResult, error) {
 	// Get search parameters from arguments
 	args := req.Arguments
 	searchQuery, ok := args["query"].(string)
@@ -1142,11 +1110,11 @@ func (s *Server) handleSearchPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResu
 
 	switch searchScope {
 	case "conversations":
-		searchResults, err = s.searchConversations(searchQuery)
+		searchResults, err = signal.searchConversations(searchQuery)
 	case "contacts":
-		searchResults, err = s.searchContacts(searchQuery)
+		searchResults, err = signal.searchContacts(searchQuery)
 	default: // "messages" or anything else
-		searchResults, err = s.searchMessages(searchQuery, conversationID, timeRange)
+		searchResults, err = signal.searchMessages(searchQuery, conversationID, timeRange)
 	}
 
 	if err != nil {
@@ -1186,7 +1154,7 @@ func (s *Server) handleSearchPrompt(req mcp.GetPromptRequest) (mcp.GetPromptResu
 	return result, nil
 }
 
-func (s *Server) searchMessages(query, conversationID, timeRange string) (string, error) {
+func (signal *SignalOperations) searchMessages(query, conversationID, timeRange string) (string, error) {
 	sqlQuery := `SELECT m.id, m.conversationId, m.body, m.sent_at,
 		       COALESCE(c.name, c.profileName, c.e164, c.id) as conversation_name
 		FROM messages m
@@ -1211,7 +1179,7 @@ func (s *Server) searchMessages(query, conversationID, timeRange string) (string
 
 	sqlQuery += ` ORDER BY m.sent_at DESC LIMIT 20`
 
-	output, err := s.executeQuery(sqlQuery)
+	output, err := signal.executeQuery(sqlQuery)
 	if err != nil {
 		return "", err
 	}
@@ -1242,7 +1210,7 @@ func (s *Server) searchMessages(query, conversationID, timeRange string) (string
 	return result.String(), nil
 }
 
-func (s *Server) searchConversations(query string) (string, error) {
+func (signal *SignalOperations) searchConversations(query string) (string, error) {
 	sqlQuery := `SELECT id, COALESCE(name, profileName, e164, id) as display_name,
 		       profileName, e164, type, active_at
 		FROM conversations 
@@ -1251,7 +1219,7 @@ func (s *Server) searchConversations(query string) (string, error) {
 		ORDER BY active_at DESC
 		LIMIT 10`
 
-	output, err := s.executeQuery(sqlQuery)
+	output, err := signal.executeQuery(sqlQuery)
 	if err != nil {
 		return "", err
 	}
@@ -1291,7 +1259,7 @@ func (s *Server) searchConversations(query string) (string, error) {
 	return result.String(), nil
 }
 
-func (s *Server) searchContacts(query string) (string, error) {
+func (signal *SignalOperations) searchContacts(query string) (string, error) {
 	// Same as searchConversations but with different framing
-	return s.searchConversations(query)
+	return signal.searchConversations(query)
 }
\ No newline at end of file
pkg/speech/server.go
@@ -27,17 +27,14 @@ type Voice struct {
 	Details  string
 }
 
-// Server represents the Speech MCP server
-type Server struct {
-	*mcp.Server
+// SpeechOperations represents the Speech MCP server operations
+type SpeechOperations struct {
 	mu      sync.RWMutex
 	backend TTSBackend
 }
 
-// NewServer creates a new Speech MCP server
-func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-speech", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
-	
+// NewSpeechOperations creates a new SpeechOperations helper
+func NewSpeechOperations() *SpeechOperations {
 	// Select appropriate TTS backend based on OS
 	var backend TTSBackend
 	switch runtime.GOOS {
@@ -50,147 +47,120 @@ func NewServer() *Server {
 		backend = &UnsupportedBackend{os: runtime.GOOS}
 	}
 	
-	server := &Server{
-		Server:  baseServer,
+	return &SpeechOperations{
 		backend: backend,
 	}
-
-	// Register speech tools
-	server.registerTools()
-
-	return server
 }
 
-// registerTools registers all Speech tools with the server
-func (s *Server) registerTools() {
-	// Get all tool definitions from ListTools method
-	tools := s.ListTools()
+// New creates a new Speech MCP server
+func New() (*mcp.Server, error) {
+	speech := NewSpeechOperations()
 	
-	// Register each tool with its proper definition
-	for _, tool := range tools {
-		var handler mcp.ToolHandler
-		switch tool.Name {
-		case "say":
-			handler = s.handleSay
-		case "list_voices":
-			handler = s.handleListVoices
-		case "speak_file":
-			handler = s.handleSpeakFile
-		case "stop_speech":
-			handler = s.handleStopSpeech
-		case "speech_settings":
-			handler = s.handleSpeechSettings
-		default:
-			continue
-		}
-		s.RegisterToolWithDefinition(tool, handler)
-	}
-}
+	builder := mcp.NewServerBuilder("speech-server", "1.0.0")
+
+
 
-// ListTools returns all available Speech tools
-func (s *Server) ListTools() []mcp.Tool {
-	return []mcp.Tool{
-		{
-			Name:        "say",
-			Description: "Convert text to speech using system TTS. Supports voice selection, speech rate, volume, and audio file output",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"text": map[string]interface{}{
-						"type":        "string",
-						"description": "The text to speak",
-					},
-					"voice": map[string]interface{}{
-						"type":        "string",
-						"description": "Voice to use (platform-specific). Use list_voices to see available options",
-					},
-					"rate": map[string]interface{}{
-						"type":        "integer",
-						"description": "Speech rate in words per minute (80-500). macOS: 80-500, Linux: 80-450",
-						"minimum":     80,
-						"maximum":     500,
-					},
-					"volume": map[string]interface{}{
-						"type":        "number",
-						"description": "Volume level (0.0-1.0). macOS only - Linux ignores this parameter",
-						"minimum":     0.0,
-						"maximum":     1.0,
-					},
-					"output": map[string]interface{}{
-						"type":        "string",
-						"description": "Output audio file path. macOS: .aiff, .wav, .m4a. Linux: .wav only",
-					},
-				},
-				"required": []string{"text"},
+	// Add say tool
+	builder.AddTool(mcp.NewTool("say", "Convert text to speech using system TTS. Supports voice selection, speech rate, volume, and audio file output", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"text": map[string]interface{}{
+				"type":        "string",
+				"description": "The text to speak",
 			},
-		},
-		{
-			Name:        "list_voices",
-			Description: "List available TTS voices on the system, optionally filtered by language",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"language": map[string]interface{}{
-						"type":        "string",
-						"description": "Filter voices by language code (e.g., 'en', 'fr', 'de')",
-					},
-				},
+			"voice": map[string]interface{}{
+				"type":        "string",
+				"description": "Voice to use (platform-specific). Use list_voices to see available options",
 			},
-		},
-		{
-			Name:        "speak_file",
-			Description: "Read and speak the contents of a text file with optional line limiting",
-			InputSchema: map[string]interface{}{
-				"type": "object",
-				"properties": map[string]interface{}{
-					"filepath": map[string]interface{}{
-						"type":        "string",
-						"description": "Path to the text file to read and speak",
-					},
-					"voice": map[string]interface{}{
-						"type":        "string",
-						"description": "Voice to use (platform-specific)",
-					},
-					"rate": map[string]interface{}{
-						"type":        "integer",
-						"description": "Speech rate in words per minute (80-500)",
-						"minimum":     80,
-						"maximum":     500,
-					},
-					"volume": map[string]interface{}{
-						"type":        "number",
-						"description": "Volume level (0.0-1.0). macOS only",
-						"minimum":     0.0,
-						"maximum":     1.0,
-					},
-					"max_lines": map[string]interface{}{
-						"type":        "integer",
-						"description": "Maximum number of lines to read from the file",
-						"minimum":     1,
-					},
-				},
-				"required": []string{"filepath"},
+			"rate": map[string]interface{}{
+				"type":        "integer",
+				"description": "Speech rate in words per minute (80-500). macOS: 80-500, Linux: 80-450",
+				"minimum":     80,
+				"maximum":     500,
+			},
+			"volume": map[string]interface{}{
+				"type":        "number",
+				"description": "Volume level (0.0-1.0). macOS only - Linux ignores this parameter",
+				"minimum":     0.0,
+				"maximum":     1.0,
+			},
+			"output": map[string]interface{}{
+				"type":        "string",
+				"description": "Output audio file path. macOS: .aiff, .wav, .m4a. Linux: .wav only",
 			},
 		},
-		{
-			Name:        "stop_speech",
-			Description: "Stop any currently playing speech synthesis",
-			InputSchema: map[string]interface{}{
-				"type": "object",
+		"required": []string{"text"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return speech.handleSay(req)
+	}))
+
+	// Add list_voices tool
+	builder.AddTool(mcp.NewTool("list_voices", "List available TTS voices on the system, optionally filtered by language", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"language": map[string]interface{}{
+				"type":        "string",
+				"description": "Filter voices by language code (e.g., 'en', 'fr', 'de')",
 			},
 		},
-		{
-			Name:        "speech_settings",
-			Description: "Get information about the speech system including platform, backend, and usage help",
-			InputSchema: map[string]interface{}{
-				"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return speech.handleListVoices(req)
+	}))
+
+	// Add speak_file tool
+	builder.AddTool(mcp.NewTool("speak_file", "Read and speak the contents of a text file with optional line limiting", map[string]interface{}{
+		"type": "object",
+		"properties": map[string]interface{}{
+			"filepath": map[string]interface{}{
+				"type":        "string",
+				"description": "Path to the text file to read and speak",
+			},
+			"voice": map[string]interface{}{
+				"type":        "string",
+				"description": "Voice to use (platform-specific)",
+			},
+			"rate": map[string]interface{}{
+				"type":        "integer",
+				"description": "Speech rate in words per minute (80-500)",
+				"minimum":     80,
+				"maximum":     500,
+			},
+			"volume": map[string]interface{}{
+				"type":        "number",
+				"description": "Volume level (0.0-1.0). macOS only",
+				"minimum":     0.0,
+				"maximum":     1.0,
+			},
+			"max_lines": map[string]interface{}{
+				"type":        "integer",
+				"description": "Maximum number of lines to read from the file",
+				"minimum":     1,
 			},
 		},
-	}
+		"required": []string{"filepath"},
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return speech.handleSpeakFile(req)
+	}))
+
+	// Add stop_speech tool
+	builder.AddTool(mcp.NewTool("stop_speech", "Stop any currently playing speech synthesis", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return speech.handleStopSpeech(req)
+	}))
+
+	// Add speech_settings tool
+	builder.AddTool(mcp.NewTool("speech_settings", "Get information about the speech system including platform, backend, and usage help", map[string]interface{}{
+		"type": "object",
+	}, func(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+		return speech.handleSpeechSettings(req)
+	}))
+
+	return builder.Build(), nil
 }
 
 // handleSay speaks the provided text using the system TTS
-func (s *Server) handleSay(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (s *SpeechOperations) handleSay(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 
@@ -234,7 +204,7 @@ func (s *Server) handleSay(req mcp.CallToolRequest) (mcp.CallToolResult, error)
 }
 
 // handleListVoices lists all available system voices
-func (s *Server) handleListVoices(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (s *SpeechOperations) handleListVoices(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 
@@ -289,7 +259,7 @@ func (s *Server) handleListVoices(req mcp.CallToolRequest) (mcp.CallToolResult,
 }
 
 // handleSpeakFile speaks the contents of a text file
-func (s *Server) handleSpeakFile(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (s *SpeechOperations) handleSpeakFile(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 
@@ -333,7 +303,7 @@ func (s *Server) handleSpeakFile(req mcp.CallToolRequest) (mcp.CallToolResult, e
 }
 
 // handleStopSpeech stops any currently playing speech
-func (s *Server) handleStopSpeech(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (s *SpeechOperations) handleStopSpeech(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 
@@ -360,7 +330,7 @@ func (s *Server) handleStopSpeech(req mcp.CallToolRequest) (mcp.CallToolResult,
 }
 
 // handleSpeechSettings provides information about speech synthesis settings
-func (s *Server) handleSpeechSettings(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
+func (s *SpeechOperations) handleSpeechSettings(req mcp.CallToolRequest) (mcp.CallToolResult, error) {
 	s.mu.RLock()
 	defer s.mu.RUnlock()
 
pkg/speech/server_test.go
@@ -1,492 +0,0 @@
-package speech
-
-import (
-	"encoding/json"
-	"runtime"
-	"strings"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestNewServer(t *testing.T) {
-	server := NewServer()
-	if server == nil {
-		t.Fatal("NewServer returned nil")
-	}
-	
-	if server.Server == nil {
-		t.Fatal("Base server is nil")
-	}
-	
-	if server.backend == nil {
-		t.Fatal("Backend is nil")
-	}
-	
-	// Test that backend is appropriate for the OS
-	backendName := server.backend.GetName()
-	switch runtime.GOOS {
-	case "darwin":
-		if !strings.Contains(backendName, "say") {
-			t.Errorf("Expected macOS say backend, got %s", backendName)
-		}
-	case "linux":
-		if !strings.Contains(backendName, "espeak") && !strings.Contains(backendName, "not available") {
-			t.Errorf("Expected Linux espeak backend or unavailable message, got %s", backendName)
-		}
-	default:
-		if !strings.Contains(backendName, "Unsupported") {
-			t.Errorf("Expected unsupported backend for %s, got %s", runtime.GOOS, backendName)
-		}
-	}
-}
-
-func TestHandleSayValidation(t *testing.T) {
-	server := NewServer()
-	
-	tests := []struct {
-		name          string
-		args          map[string]interface{}
-		expectError   bool
-		errorContains string
-	}{
-		{
-			name:        "empty text",
-			args:        map[string]interface{}{},
-			expectError: true,
-			errorContains: "text is required",
-		},
-		{
-			name: "invalid rate too low",
-			args: map[string]interface{}{
-				"text": "test",
-				"rate": 50,
-			},
-			expectError: true,
-			errorContains: "rate must be between 80-500",
-		},
-		{
-			name: "invalid rate too high",
-			args: map[string]interface{}{
-				"text": "test",
-				"rate": 600,
-			},
-			expectError: true,
-			errorContains: "rate must be between 80-500",
-		},
-		{
-			name: "invalid volume too low",
-			args: map[string]interface{}{
-				"text": "test",
-				"volume": -0.1,
-			},
-			expectError: true,
-			errorContains: "volume must be between 0.0 and 1.0",
-		},
-		{
-			name: "invalid volume too high",
-			args: map[string]interface{}{
-				"text": "test",
-				"volume": 1.1,
-			},
-			expectError: true,
-			errorContains: "volume must be between 0.0 and 1.0",
-		},
-		{
-			name: "invalid output format",
-			args: map[string]interface{}{
-				"text": "test",
-				"output": "test.mp3",
-			},
-			expectError: true,
-			errorContains: "output format must be .aiff, .wav, or .m4a",
-		},
-		{
-			name: "valid basic args",
-			args: map[string]interface{}{
-				"text": "Hello world",
-			},
-			expectError: !server.backend.IsAvailable(), // Should only work if TTS backend available
-		},
-		{
-			name: "valid complex args",
-			args: map[string]interface{}{
-				"text": "Hello world",
-				"voice": "en-gb", // Use generic voice name that works on both platforms
-				"rate": 150,
-				"volume": 0.8,
-			},
-			expectError: !server.backend.IsAvailable(),
-		},
-		{
-			name: "valid output file",
-			args: map[string]interface{}{
-				"text": "test recording",
-				"output": "/tmp/test.wav", // wav works on both platforms
-			},
-			expectError: !server.backend.IsAvailable(),
-		},
-	}
-	
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			req := mcp.CallToolRequest{
-				Name:      "say",
-				Arguments: tt.args,
-			}
-			
-			result, err := server.handleSay(req)
-			
-			if tt.expectError {
-				if err == nil {
-					t.Errorf("Expected error but got none")
-				} else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) {
-					t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error())
-				}
-			} else {
-				if err != nil {
-					t.Errorf("Unexpected error: %v", err)
-				}
-				if len(result.Content) == 0 {
-					t.Errorf("Expected content in result")
-				}
-			}
-		})
-	}
-}
-
-func TestHandleListVoices(t *testing.T) {
-	server := NewServer()
-	
-	tests := []struct {
-		name        string
-		args        map[string]interface{}
-		expectError bool
-	}{
-		{
-			name:        "basic list",
-			args:        map[string]interface{}{},
-			expectError: !server.backend.IsAvailable(),
-		},
-		{
-			name: "with language filter",
-			args: map[string]interface{}{
-				"language": "en",
-			},
-			expectError: !server.backend.IsAvailable(),
-		},
-		{
-			name: "detailed mode",
-			args: map[string]interface{}{
-				"detailed": true,
-			},
-			expectError: !server.backend.IsAvailable(),
-		},
-	}
-	
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			req := mcp.CallToolRequest{
-				Name:      "list_voices",
-				Arguments: tt.args,
-			}
-			
-			result, err := server.handleListVoices(req)
-			
-			if tt.expectError {
-				if err == nil {
-					t.Errorf("Expected error but got none")
-				}
-			} else {
-				if err != nil {
-					t.Errorf("Unexpected error: %v", err)
-				}
-				if len(result.Content) == 0 {
-					t.Errorf("Expected content in result")
-				}
-			}
-		})
-	}
-}
-
-func TestHandleSpeakFileValidation(t *testing.T) {
-	server := NewServer()
-	
-	tests := []struct {
-		name          string
-		args          map[string]interface{}
-		expectError   bool
-		errorContains string
-	}{
-		{
-			name:        "empty file path",
-			args:        map[string]interface{}{},
-			expectError: true,
-			errorContains: "file_path is required",
-		},
-		{
-			name: "nonexistent file",
-			args: map[string]interface{}{
-				"file_path": "/nonexistent/file.txt",
-			},
-			expectError: true,
-			errorContains: "failed to read file",
-		},
-		{
-			name: "invalid rate",
-			args: map[string]interface{}{
-				"file_path": "/etc/passwd", // Use a file that exists
-				"rate": 1000,
-			},
-			expectError: true,
-			errorContains: "rate must be between 80-500",
-		},
-	}
-	
-	for _, tt := range tests {
-		t.Run(tt.name, func(t *testing.T) {
-			req := mcp.CallToolRequest{
-				Name:      "speak_file",
-				Arguments: tt.args,
-			}
-			
-			result, err := server.handleSpeakFile(req)
-			
-			if tt.expectError {
-				if err == nil {
-					t.Errorf("Expected error but got none")
-				} else if tt.errorContains != "" && !strings.Contains(err.Error(), tt.errorContains) {
-					t.Errorf("Expected error to contain %q, got %q", tt.errorContains, err.Error())
-				}
-			} else {
-				if err != nil {
-					t.Errorf("Unexpected error: %v", err)
-				}
-				if len(result.Content) == 0 {
-					t.Errorf("Expected content in result")
-				}
-			}
-		})
-	}
-}
-
-func TestHandleStopSpeech(t *testing.T) {
-	server := NewServer()
-	
-	req := mcp.CallToolRequest{
-		Name:      "stop_speech",
-		Arguments: map[string]interface{}{},
-	}
-	
-	result, err := server.handleStopSpeech(req)
-	
-	if !server.backend.IsAvailable() {
-		if err == nil {
-			t.Errorf("Expected error when TTS backend not available")
-		}
-		return
-	}
-	
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-	
-	if len(result.Content) == 0 {
-		t.Errorf("Expected content in result")
-	}
-	
-	// Check that result contains stop message
-	content := result.Content[0]
-	if textContent, ok := content.(mcp.TextContent); ok {
-		if !strings.Contains(textContent.Text, "Stopped") {
-			t.Errorf("Expected stop message in result, got: %s", textContent.Text)
-		}
-	} else {
-		t.Errorf("Expected TextContent, got %T", content)
-	}
-}
-
-func TestHandleSpeechSettings(t *testing.T) {
-	server := NewServer()
-	
-	req := mcp.CallToolRequest{
-		Name:      "speech_settings",
-		Arguments: map[string]interface{}{},
-	}
-	
-	result, err := server.handleSpeechSettings(req)
-	
-	// Speech settings should always work, even if backend is not available
-	// (it will show installation instructions)
-	if err != nil {
-		t.Errorf("Unexpected error: %v", err)
-	}
-	
-	if len(result.Content) == 0 {
-		t.Errorf("Expected content in result")
-	}
-	
-	// Check that result contains settings information
-	content := result.Content[0]
-	if textContent, ok := content.(mcp.TextContent); ok {
-		settingsText := textContent.Text
-		
-		// These sections should always be present
-		expectedSections := []string{
-			"BACKEND:",
-			"Speech Synthesis Settings",
-		}
-		
-		for _, section := range expectedSections {
-			if !strings.Contains(settingsText, section) {
-				t.Errorf("Expected settings to contain section %q", section)
-			}
-		}
-		
-		// If backend is available, check for detailed sections
-		if server.backend.IsAvailable() {
-			availableSections := []string{
-				"VOICES:",
-				"RATE (Speed):",
-				"VOLUME:",
-				"OUTPUT FORMATS:",
-				"EXAMPLES:",
-				"CONTROLS:",
-			}
-			
-			for _, section := range availableSections {
-				if !strings.Contains(settingsText, section) {
-					t.Errorf("Expected settings to contain section %q when backend available", section)
-				}
-			}
-		} else {
-			// If backend not available, should contain installation instructions
-			if !strings.Contains(settingsText, "install") {
-				t.Errorf("Expected installation instructions when backend not available")
-			}
-		}
-	} else {
-		t.Errorf("Expected TextContent, got %T", content)
-	}
-}
-
-func TestJSONArguments(t *testing.T) {
-	server := NewServer()
-	
-	// Test that arguments are properly marshaled/unmarshaled
-	args := map[string]interface{}{
-		"text":   "Hello world",
-		"voice":  "Samantha",
-		"rate":   150,
-		"volume": 0.8,
-	}
-	
-	// Marshal to JSON and back to simulate real request
-	argsBytes, err := json.Marshal(args)
-	if err != nil {
-		t.Fatalf("Failed to marshal args: %v", err)
-	}
-	
-	var unmarshaled map[string]interface{}
-	if err := json.Unmarshal(argsBytes, &unmarshaled); err != nil {
-		t.Fatalf("Failed to unmarshal args: %v", err)
-	}
-	
-	req := mcp.CallToolRequest{
-		Name:      "say",
-		Arguments: unmarshaled,
-	}
-	
-	// This should not panic or return invalid argument errors
-	_, err = server.handleSay(req)
-	
-	// Error is expected when backend not available, but should not be argument-related
-	if err != nil && server.backend.IsAvailable() {
-		// When backend is available, any error should not be about invalid arguments
-		if strings.Contains(err.Error(), "invalid arguments") {
-			t.Errorf("Argument parsing failed: %v", err)
-		}
-	}
-}
-
-func TestCrossPlatformBackendSelection(t *testing.T) {
-	server := NewServer()
-	
-	// Test that the appropriate backend is selected for each platform
-	backendName := server.backend.GetName()
-	
-	switch runtime.GOOS {
-	case "darwin":
-		if !server.backend.IsAvailable() {
-			t.Errorf("macOS backend should be available (say command)")
-		}
-		if !strings.Contains(backendName, "say") {
-			t.Errorf("Expected macOS say backend, got %s", backendName)
-		}
-		
-	case "linux":
-		// Backend availability depends on whether espeak-ng/espeak is installed
-		// Test that we get the right backend name regardless
-		if strings.Contains(backendName, "espeak") || strings.Contains(backendName, "not available") {
-			// This is correct
-		} else {
-			t.Errorf("Expected Linux espeak backend or unavailable message, got %s", backendName)
-		}
-		
-	default:
-		if server.backend.IsAvailable() {
-			t.Errorf("Unsupported platform should not have available backend")
-		}
-		if !strings.Contains(backendName, "Unsupported") {
-			t.Errorf("Expected unsupported backend message, got %s", backendName)
-		}
-	}
-}
-
-func TestBackendUnavailableBehavior(t *testing.T) {
-	server := NewServer()
-	
-	// If backend is not available, all speech tools should return appropriate errors
-	if !server.backend.IsAvailable() {
-		tools := []struct {
-			name string
-			args map[string]interface{}
-		}{
-			{"say", map[string]interface{}{"text": "test"}},
-			{"list_voices", map[string]interface{}{}},
-			{"speak_file", map[string]interface{}{"file_path": "/etc/passwd"}},
-			{"stop_speech", map[string]interface{}{}},
-		}
-		
-		for _, tool := range tools {
-			t.Run(tool.name, func(t *testing.T) {
-				req := mcp.CallToolRequest{
-					Name:      tool.name,
-					Arguments: tool.args,
-				}
-				
-				var err error
-				switch tool.name {
-				case "say":
-					_, err = server.handleSay(req)
-				case "list_voices":
-					_, err = server.handleListVoices(req)
-				case "speak_file":
-					_, err = server.handleSpeakFile(req)
-				case "stop_speech":
-					_, err = server.handleStopSpeech(req)
-				}
-				
-				if err == nil {
-					t.Errorf("Expected error when backend not available for tool %s", tool.name)
-				}
-				
-				if !strings.Contains(err.Error(), "not available") {
-					t.Errorf("Expected 'not available' error message for tool %s, got: %v", tool.name, err)
-				}
-			})
-		}
-	} else {
-		t.Skip("Backend is available, skipping unavailable test")
-	}
-}
\ No newline at end of file
pkg/thinking/server_test.go
@@ -1,374 +0,0 @@
-package thinking
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestServer_BasicThinking(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "Let me analyze this problem step by step.",
-			"nextThoughtNeeded": true,
-			"thoughtNumber":     1,
-			"totalThoughts":     3,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful thinking, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain the thought content
-	if !contains(textContent.Text, "analyze this problem") {
-		t.Fatalf("Expected thought content in result, got: %s", textContent.Text)
-	}
-
-	// Should show progress
-	if !contains(textContent.Text, "Thought 1/3") {
-		t.Fatalf("Expected progress indicator, got: %s", textContent.Text)
-	}
-
-	// Should indicate thinking status
-	if !contains(textContent.Text, "thinking") {
-		t.Fatalf("Expected thinking status, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_CompletedThinking(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "Therefore, the solution is 42.",
-			"nextThoughtNeeded": false,
-			"thoughtNumber":     3,
-			"totalThoughts":     3,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful thinking, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain the thought content
-	if !contains(textContent.Text, "solution is 42") {
-		t.Fatalf("Expected thought content in result, got: %s", textContent.Text)
-	}
-
-	// Should show completed status
-	if !contains(textContent.Text, "completed") {
-		t.Fatalf("Expected completed status, got: %s", textContent.Text)
-	}
-
-	// Should extract solution
-	if !contains(textContent.Text, "Extracted Solution") {
-		t.Fatalf("Expected extracted solution, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_RevisionThinking(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "Actually, let me reconsider my previous analysis.",
-			"nextThoughtNeeded": true,
-			"thoughtNumber":     4,
-			"totalThoughts":     5,
-			"isRevision":        true,
-			"revisesThought":    2,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful thinking, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain revision context
-	if !contains(textContent.Text, "Revising thought 2") {
-		t.Fatalf("Expected revision context, got: %s", textContent.Text)
-	}
-
-	// Should contain the thought content
-	if !contains(textContent.Text, "reconsider my previous") {
-		t.Fatalf("Expected thought content in result, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_BranchThinking(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "Let me explore an alternative approach.",
-			"nextThoughtNeeded": true,
-			"thoughtNumber":     6,
-			"totalThoughts":     8,
-			"branchFromThought": 3,
-			"branchId":          "alternative_path",
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful thinking, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain branch context
-	if !contains(textContent.Text, "Branching from thought 3") {
-		t.Fatalf("Expected branch context, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "alternative_path") {
-		t.Fatalf("Expected branch ID, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_NeedsMoreThoughts(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "I realize I need more steps to solve this.",
-			"nextThoughtNeeded": true,
-			"thoughtNumber":     5,
-			"totalThoughts":     5,
-			"needsMoreThoughts": true,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful thinking, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain more thoughts context
-	if !contains(textContent.Text, "additional thoughts") {
-		t.Fatalf("Expected additional thoughts context, got: %s", textContent.Text)
-	}
-
-	// Should still be thinking status even though at total thoughts
-	if !contains(textContent.Text, "thinking") {
-		t.Fatalf("Expected thinking status, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_MissingRequiredParams(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought": "Missing required params",
-			// Missing nextThoughtNeeded, thoughtNumber, totalThoughts
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for missing required parameters")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "required") {
-		t.Fatalf("Expected required parameter error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_InvalidParams(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "Test thought",
-			"nextThoughtNeeded": true,
-			"thoughtNumber":     0, // Invalid: must be >= 1
-			"totalThoughts":     3,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for invalid thoughtNumber")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "must be >= 1") {
-		t.Fatalf("Expected thoughtNumber validation error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_SolutionExtraction(t *testing.T) {
-	server := New()
-
-	// Test with explicit solution keyword
-	req := mcp.CallToolRequest{
-		Name: "sequentialthinking",
-		Arguments: map[string]interface{}{
-			"thought":           "After careful analysis, the answer is: The optimal solution involves using a binary search algorithm.",
-			"nextThoughtNeeded": false,
-			"thoughtNumber":     3,
-			"totalThoughts":     3,
-		},
-	}
-
-	result, err := server.HandleSequentialThinking(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should extract the solution
-	if !contains(textContent.Text, "binary search algorithm") {
-		t.Fatalf("Expected extracted solution to contain key phrase, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_ListTools(t *testing.T) {
-	server := New()
-	tools := server.ListTools()
-
-	expectedTools := []string{
-		"sequentialthinking",
-	}
-
-	if len(tools) != len(expectedTools) {
-		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-	}
-
-	toolNames := make(map[string]bool)
-	for _, tool := range tools {
-		toolNames[tool.Name] = true
-	}
-
-	for _, expected := range expectedTools {
-		if !toolNames[expected] {
-			t.Fatalf("Expected tool %s not found", expected)
-		}
-	}
-
-	// Check that sequentialthinking tool has proper schema
-	thinkingTool := tools[0]
-	if thinkingTool.Name != "sequentialthinking" {
-		t.Fatalf("Expected first tool to be 'sequentialthinking', got %s", thinkingTool.Name)
-	}
-
-	if thinkingTool.Description == "" {
-		t.Fatal("Expected non-empty description for sequentialthinking tool")
-	}
-
-	if thinkingTool.InputSchema == nil {
-		t.Fatal("Expected input schema for sequentialthinking tool")
-	}
-}
-
-func TestServer_ProgressBar(t *testing.T) {
-	server := New()
-
-	// Test progress bar creation
-	progressBar := server.createProgressBar(3, 10)
-	if !contains(progressBar, "30%") {
-		t.Fatalf("Expected 30%% progress, got: %s", progressBar)
-	}
-
-	// Test 100% completion
-	progressBar = server.createProgressBar(5, 5)
-	if !contains(progressBar, "100%") {
-		t.Fatalf("Expected 100%% progress, got: %s", progressBar)
-	}
-
-	// Test over 100%
-	progressBar = server.createProgressBar(7, 5)
-	if !contains(progressBar, "100%") {
-		t.Fatalf("Expected capped at 100%% progress, got: %s", progressBar)
-	}
-}
-
-// Helper functions
-func contains(s, substr string) bool {
-	return strings.Contains(s, substr)
-}
pkg/time/server_test.go
@@ -1,206 +0,0 @@
-package time
-
-import (
-	"strings"
-	"testing"
-
-	"github.com/xlgmokha/mcp/pkg/mcp"
-)
-
-func TestServer_GetCurrentTime(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "get_current_time",
-		Arguments: map[string]interface{}{
-			"timezone": "UTC",
-		},
-	}
-
-	result, err := server.HandleGetCurrentTime(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain timezone and datetime
-	if !contains(textContent.Text, "UTC") {
-		t.Fatalf("Expected UTC timezone in result, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "datetime") {
-		t.Fatalf("Expected datetime in result, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_ConvertTime(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "convert_time",
-		Arguments: map[string]interface{}{
-			"source_timezone": "UTC",
-			"time":            "12:00",
-			"target_timezone": "America/New_York",
-		},
-	}
-
-	result, err := server.HandleConvertTime(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if len(result.Content) == 0 {
-		t.Fatal("Expected content in result")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain source and target information
-	if !contains(textContent.Text, "source") {
-		t.Fatalf("Expected source in result, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "target") {
-		t.Fatalf("Expected target in result, got: %s", textContent.Text)
-	}
-
-	if !contains(textContent.Text, "time_difference") {
-		t.Fatalf("Expected time_difference in result, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_InvalidTimezone(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "get_current_time",
-		Arguments: map[string]interface{}{
-			"timezone": "Invalid/Timezone",
-		},
-	}
-
-	result, err := server.HandleGetCurrentTime(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for invalid timezone")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "Invalid timezone") {
-		t.Fatalf("Expected invalid timezone error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_InvalidTimeFormat(t *testing.T) {
-	server := New()
-
-	req := mcp.CallToolRequest{
-		Name: "convert_time",
-		Arguments: map[string]interface{}{
-			"source_timezone": "UTC",
-			"time":            "invalid-time",
-			"target_timezone": "America/New_York",
-		},
-	}
-
-	result, err := server.HandleConvertTime(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if !result.IsError {
-		t.Fatal("Expected error for invalid time format")
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	if !contains(textContent.Text, "Invalid time format") {
-		t.Fatalf("Expected invalid time format error, got: %s", textContent.Text)
-	}
-}
-
-func TestServer_ListTools(t *testing.T) {
-	server := New()
-	tools := server.ListTools()
-
-	expectedTools := []string{
-		"get_current_time",
-		"convert_time",
-	}
-
-	if len(tools) != len(expectedTools) {
-		t.Fatalf("Expected %d tools, got %d", len(expectedTools), len(tools))
-	}
-
-	toolNames := make(map[string]bool)
-	for _, tool := range tools {
-		toolNames[tool.Name] = true
-	}
-
-	for _, expected := range expectedTools {
-		if !toolNames[expected] {
-			t.Fatalf("Expected tool %s not found", expected)
-		}
-	}
-}
-
-func TestServer_ConvertTimeWithDST(t *testing.T) {
-	server := New()
-
-	// Test during daylight saving time period
-	req := mcp.CallToolRequest{
-		Name: "convert_time",
-		Arguments: map[string]interface{}{
-			"source_timezone": "UTC",
-			"time":            "16:00",
-			"target_timezone": "Europe/London",
-		},
-	}
-
-	result, err := server.HandleConvertTime(req)
-	if err != nil {
-		t.Fatalf("Expected no error, got %v", err)
-	}
-
-	if result.IsError {
-		textContent, _ := result.Content[0].(mcp.TextContent)
-		t.Fatalf("Expected successful conversion, got error: %s", textContent.Text)
-	}
-
-	textContent, ok := result.Content[0].(mcp.TextContent)
-	if !ok {
-		t.Fatal("Expected TextContent")
-	}
-
-	// Should contain proper JSON with DST information
-	if !contains(textContent.Text, "is_dst") {
-		t.Fatalf("Expected is_dst field in result, got: %s", textContent.Text)
-	}
-}
-
-// Helper functions
-func contains(s, substr string) bool {
-	return strings.Contains(s, substr)
-}
test/integration_test.go
@@ -1,849 +0,0 @@
-package test
-
-import (
-	"bytes"
-	"encoding/json"
-	"fmt"
-	"io"
-	"os"
-	"os/exec"
-	"path/filepath"
-	"testing"
-	"time"
-)
-
-// MCPRequest represents an MCP JSON-RPC request
-type MCPRequest struct {
-	JSONRPC string      `json:"jsonrpc"`
-	ID      int         `json:"id"`
-	Method  string      `json:"method"`
-	Params  interface{} `json:"params,omitempty"`
-}
-
-// MCPResponse represents an MCP JSON-RPC response
-type MCPResponse struct {
-	JSONRPC string          `json:"jsonrpc"`
-	ID      int             `json:"id"`
-	Result  json.RawMessage `json:"result,omitempty"`
-	Error   *MCPError       `json:"error,omitempty"`
-}
-
-type MCPError struct {
-	Code    int    `json:"code"`
-	Message string `json:"message"`
-}
-
-// TestServer represents a test configuration for an MCP server
-type TestServer struct {
-	Binary string
-	Args   []string
-	Name   string
-}
-
-func TestMCPServersIntegration(t *testing.T) {
-	// Create test directory structure
-	testDir := setupTestEnvironment(t)
-	defer os.RemoveAll(testDir)
-
-	servers := []TestServer{
-		{
-			Binary: "../bin/mcp-filesystem",
-			Args:   []string{"--allowed-directory", testDir},
-			Name:   "filesystem",
-		},
-		{
-			Binary: "../bin/mcp-git",
-			Args:   []string{"--repository", ".."},
-			Name:   "git",
-		},
-		{
-			Binary: "../bin/mcp-memory",
-			Args:   []string{"--memory-file", filepath.Join(testDir, "test-memory.json")},
-			Name:   "memory",
-		},
-		{
-			Binary: "../bin/mcp-fetch",
-			Args:   []string{},
-			Name:   "fetch",
-		},
-		{
-			Binary: "../bin/mcp-time",
-			Args:   []string{},
-			Name:   "time",
-		},
-		{
-			Binary: "../bin/mcp-sequential-thinking",
-			Args:   []string{},
-			Name:   "sequential-thinking",
-		},
-		{
-			Binary: "../bin/mcp-maildir",
-			Args:   []string{"--maildir-path", filepath.Join(testDir, "maildir")},
-			Name:   "maildir",
-		},
-		{
-			Binary: "../bin/mcp-signal",
-			Args:   []string{},
-			Name:   "signal",
-		},
-		{
-			Binary: "../bin/mcp-imap",
-			Args:   []string{"--server", "example.com", "--username", "test", "--password", "test"},
-			Name:   "imap",
-		},
-		{
-			Binary: "../bin/mcp-gitlab",
-			Args:   []string{"--gitlab-token", "fake_token_for_testing"},
-			Name:   "gitlab",
-		},
-		{
-			Binary: "../bin/mcp-packages",
-			Args:   []string{},
-			Name:   "packages",
-		},
-		{
-			Binary: "../bin/mcp-speech",
-			Args:   []string{},
-			Name:   "speech",
-		},
-	}
-
-	for _, server := range servers {
-		t.Run(server.Name, func(t *testing.T) {
-			testMCPServer(t, server, testDir)
-		})
-	}
-}
-
-func testMCPServer(t *testing.T, server TestServer, testDir string) {
-	t.Logf("Testing %s server", server.Name)
-
-	// Test 1: Initialize server
-	t.Run("Initialize", func(t *testing.T) {
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      1,
-			Method:  "initialize",
-			Params: map[string]interface{}{
-				"protocolVersion": "2025-06-18",
-				"capabilities":    map[string]interface{}{},
-				"clientInfo": map[string]interface{}{
-					"name":    "test-client",
-					"version": "1.0.0",
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("Initialize failed: %s", resp.Error.Message)
-		}
-
-		// Verify response contains capabilities
-		var result map[string]interface{}
-		if err := json.Unmarshal(resp.Result, &result); err != nil {
-			t.Fatalf("Failed to parse initialize result: %v", err)
-		}
-
-		capabilities, ok := result["capabilities"].(map[string]interface{})
-		if !ok {
-			t.Fatal("No capabilities in initialize response")
-		}
-
-		// All servers should have these capabilities
-		expectedCaps := []string{"tools", "prompts", "resources", "roots", "logging"}
-		for _, cap := range expectedCaps {
-			if _, exists := capabilities[cap]; !exists {
-				t.Errorf("Missing capability: %s", cap)
-			}
-		}
-	})
-
-	// Test 2: List tools
-	t.Run("ListTools", func(t *testing.T) {
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      2,
-			Method:  "tools/list",
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("ListTools failed: %s", resp.Error.Message)
-		}
-
-		var result map[string]interface{}
-		if err := json.Unmarshal(resp.Result, &result); err != nil {
-			t.Fatalf("Failed to parse tools/list result: %v", err)
-		}
-
-		tools, ok := result["tools"].([]interface{})
-		if !ok {
-			t.Fatal("No tools array in response")
-		}
-
-		if len(tools) == 0 {
-			t.Error("No tools returned")
-		}
-
-		t.Logf("Server %s has %d tools", server.Name, len(tools))
-	})
-
-	// Test 3: List resources (lazy loading test)
-	t.Run("ListResources", func(t *testing.T) {
-		start := time.Now()
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      3,
-			Method:  "resources/list",
-		})
-		duration := time.Since(start)
-
-		if resp.Error != nil {
-			t.Fatalf("ListResources failed: %s", resp.Error.Message)
-		}
-
-		// Ensure lazy loading is fast (should be under 1 second even for large repos)
-		if duration > time.Second {
-			t.Errorf("Resource discovery took too long: %v", duration)
-		}
-
-		var result map[string]interface{}
-		if err := json.Unmarshal(resp.Result, &result); err != nil {
-			t.Fatalf("Failed to parse resources/list result: %v", err)
-		}
-
-		t.Logf("Server %s resource discovery took %v", server.Name, duration)
-	})
-
-	// Test 4: List roots  
-	t.Run("ListRoots", func(t *testing.T) {
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      4,
-			Method:  "roots/list",
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("ListRoots failed: %s", resp.Error.Message)
-		}
-
-		var result map[string]interface{}
-		if err := json.Unmarshal(resp.Result, &result); err != nil {
-			t.Fatalf("Failed to parse roots/list result: %v", err)
-		}
-
-		roots, ok := result["roots"].([]interface{})
-		if !ok {
-			t.Fatal("No roots array in response")
-		}
-
-		t.Logf("Server %s has %d roots", server.Name, len(roots))
-	})
-
-	// Test 5: Server-specific functionality
-	switch server.Name {
-	case "filesystem":
-		testFilesystemSpecific(t, server, testDir)
-	case "git":
-		testGitSpecific(t, server)
-	case "memory":
-		testMemorySpecific(t, server)
-	case "fetch":
-		testFetchSpecific(t, server)
-	case "time":
-		testTimeSpecific(t, server)
-	case "maildir":
-		testMaildirSpecific(t, server, testDir)
-	case "signal":
-		testSignalSpecific(t, server)
-	}
-}
-
-func testFilesystemSpecific(t *testing.T, server TestServer, testDir string) {
-	t.Run("FileSystemTools", func(t *testing.T) {
-		// Test list_directory
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      10,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "list_directory",
-				"arguments": map[string]interface{}{
-					"path": testDir,
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("list_directory failed: %s", resp.Error.Message)
-		}
-
-		t.Log("Filesystem list_directory test passed")
-	})
-}
-
-func testGitSpecific(t *testing.T, server TestServer) {
-	t.Run("GitTools", func(t *testing.T) {
-		// Test git_status
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      11,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name":      "git_status",
-				"arguments": map[string]interface{}{},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("git_status failed: %s", resp.Error.Message)
-		}
-
-		t.Log("Git git_status test passed")
-	})
-}
-
-func testMemorySpecific(t *testing.T, server TestServer) {
-	t.Run("MemoryTools", func(t *testing.T) {
-		// Test read_graph (should work with lazy loading)
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      12,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name":      "read_graph",
-				"arguments": map[string]interface{}{},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("read_graph failed: %s", resp.Error.Message)
-		}
-
-		t.Log("Memory read_graph test passed")
-	})
-
-	t.Run("MemoryPersistence", func(t *testing.T) {
-		testDir, err := os.MkdirTemp("", "memory-persistence-test-*")
-		if err != nil {
-			t.Fatalf("Failed to create temp directory: %v", err)
-		}
-		defer os.RemoveAll(testDir)
-		testMemoryPersistence(t, testDir)
-	})
-}
-
-func testFetchSpecific(t *testing.T, server TestServer) {
-	t.Run("FetchTools", func(t *testing.T) {
-		// Test fetch with a simple URL
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      13,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "fetch",
-				"arguments": map[string]interface{}{
-					"url": "https://httpbin.org/get",
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Logf("fetch test skipped (network issue): %s", resp.Error.Message)
-			return
-		}
-
-		t.Log("Fetch test passed")
-	})
-}
-
-func testTimeSpecific(t *testing.T, server TestServer) {
-	t.Run("TimeTools", func(t *testing.T) {
-		// Test get_current_time
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      14,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name":      "get_current_time",
-				"arguments": map[string]interface{}{},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("get_current_time failed: %s", resp.Error.Message)
-		}
-
-		t.Log("Time get_current_time test passed")
-	})
-}
-
-func testMaildirSpecific(t *testing.T, server TestServer, testDir string) {
-	t.Run("MaildirTools", func(t *testing.T) {
-		maildirPath := filepath.Join(testDir, "maildir")
-
-		// Test maildir_scan_folders
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      15,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "maildir_scan_folders",
-				"arguments": map[string]interface{}{
-					"maildir_path": maildirPath,
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Fatalf("maildir_scan_folders failed: %s", resp.Error.Message)
-		}
-
-		t.Log("Maildir scan_folders test passed")
-	})
-}
-
-func testSignalSpecific(t *testing.T, server TestServer) {
-	t.Run("SignalTools", func(t *testing.T) {
-		// Test signal_list_conversations
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      16,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name":      "signal_list_conversations",
-				"arguments": map[string]interface{}{},
-			},
-		})
-
-		// Signal tests are optional since they require Signal Desktop to be installed
-		// and configured. We'll log the result but not fail if it's not available.
-		if resp.Error != nil {
-			t.Logf("signal_list_conversations skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		t.Log("Signal list_conversations test passed")
-	})
-
-	t.Run("SignalStats", func(t *testing.T) {
-		// Test signal_get_stats 
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      17,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name":      "signal_get_stats",
-				"arguments": map[string]interface{}{},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Logf("signal_get_stats skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		t.Log("Signal get_stats test passed")
-	})
-
-	t.Run("SignalNewTools", func(t *testing.T) {
-		// Test signal_get_contact with invalid ID to verify error handling
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      18,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "signal_get_contact",
-				"arguments": map[string]interface{}{
-					"contact_id": "nonexistent-contact-id",
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Logf("signal_get_contact skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		// Test signal_get_message with invalid ID
-		resp = sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      19,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "signal_get_message",
-				"arguments": map[string]interface{}{
-					"message_id": "nonexistent-message-id",
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Logf("signal_get_message skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		// Test signal_list_attachments
-		resp = sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      20,
-			Method:  "tools/call",
-			Params: map[string]interface{}{
-				"name": "signal_list_attachments",
-				"arguments": map[string]interface{}{
-					"limit": "5",
-				},
-			},
-		})
-
-		if resp.Error != nil {
-			t.Logf("signal_list_attachments skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		t.Log("Signal new tools tests passed")
-	})
-
-	t.Run("SignalPrompts", func(t *testing.T) {
-		// Test prompts/list
-		resp := sendMCPRequest(t, server, MCPRequest{
-			JSONRPC: "2.0",
-			ID:      21,
-			Method:  "prompts/list",
-		})
-
-		if resp.Error != nil {
-			t.Logf("prompts/list skipped (Signal not available): %s", resp.Error.Message)
-			return
-		}
-
-		// Parse prompts list
-		var result map[string]interface{}
-		if err := json.Unmarshal(resp.Result, &result); err != nil {
-			t.Logf("Failed to parse prompts list: %v", err)
-			return
-		}
-
-		prompts, ok := result["prompts"].([]interface{})
-		if !ok {
-			t.Log("No prompts array found")
-			return
-		}
-
-		// Should have 2 prompts: signal-conversation and signal-search
-		if len(prompts) >= 2 {
-			t.Log("Signal prompts test passed")
-		} else {
-			t.Logf("Expected 2 prompts, found %d", len(prompts))
-		}
-	})
-}
-
-func sendMCPRequest(t *testing.T, server TestServer, request MCPRequest) MCPResponse {
-	// Create command
-	cmd := exec.Command(server.Binary, server.Args...)
-	
-	// Set up pipes
-	stdin, err := cmd.StdinPipe()
-	if err != nil {
-		t.Fatalf("Failed to create stdin pipe: %v", err)
-	}
-	
-	stdout, err := cmd.StdoutPipe()
-	if err != nil {
-		t.Fatalf("Failed to create stdout pipe: %v", err)
-	}
-
-	// Start the server
-	if err := cmd.Start(); err != nil {
-		t.Fatalf("Failed to start server %s: %v", server.Name, err)
-	}
-
-	// Send request
-	reqBytes, _ := json.Marshal(request)
-	if _, err := stdin.Write(reqBytes); err != nil {
-		t.Fatalf("Failed to write request: %v", err)
-	}
-	stdin.Close()
-
-	// Read response with timeout
-	responseChan := make(chan MCPResponse, 1)
-	errorChan := make(chan error, 1)
-
-	go func() {
-		var buffer bytes.Buffer
-		if _, err := io.Copy(&buffer, stdout); err != nil {
-			errorChan <- err
-			return
-		}
-
-		var response MCPResponse
-		if err := json.Unmarshal(buffer.Bytes(), &response); err != nil {
-			errorChan <- fmt.Errorf("failed to unmarshal response: %v, raw: %s", err, buffer.String())
-			return
-		}
-
-		responseChan <- response
-	}()
-
-	// Wait for response or timeout
-	select {
-	case response := <-responseChan:
-		cmd.Wait()
-		return response
-	case err := <-errorChan:
-		cmd.Process.Kill()
-		t.Fatalf("Error reading response: %v", err)
-		return MCPResponse{}
-	case <-time.After(10 * time.Second):
-		cmd.Process.Kill()
-		t.Fatalf("Request timeout for server %s", server.Name)
-		return MCPResponse{}
-	}
-}
-
-func setupTestEnvironment(t *testing.T) string {
-	testDir, err := os.MkdirTemp("", "mcp-integration-test-*")
-	if err != nil {
-		t.Fatalf("Failed to create test directory: %v", err)
-	}
-
-	// Create test files
-	testFiles := map[string]string{
-		"test1.txt":     "Hello World",
-		"test2.md":      "# Test Markdown",
-		"subdir/test3.txt": "Nested file",
-	}
-
-	for path, content := range testFiles {
-		fullPath := filepath.Join(testDir, path)
-		if err := os.MkdirAll(filepath.Dir(fullPath), 0755); err != nil {
-			t.Fatalf("Failed to create directory: %v", err)
-		}
-		if err := os.WriteFile(fullPath, []byte(content), 0644); err != nil {
-			t.Fatalf("Failed to write test file: %v", err)
-		}
-	}
-
-	// Create maildir structure
-	maildirPath := filepath.Join(testDir, "maildir")
-	for _, dir := range []string{"cur", "new", "tmp"} {
-		if err := os.MkdirAll(filepath.Join(maildirPath, dir), 0755); err != nil {
-			t.Fatalf("Failed to create maildir structure: %v", err)
-		}
-	}
-
-	// Create a test email in maildir
-	testEmail := `From: test@example.com
-To: user@example.com
-Subject: Test Email
-Date: Mon, 01 Jan 2024 12:00:00 +0000
-
-This is a test email for integration testing.
-`
-	emailPath := filepath.Join(maildirPath, "cur", "1234567890.test:2,S")
-	if err := os.WriteFile(emailPath, []byte(testEmail), 0644); err != nil {
-		t.Fatalf("Failed to write test email: %v", err)
-	}
-
-	return testDir
-}
-
-// Benchmark tests for performance verification
-func BenchmarkServerStartup(b *testing.B) {
-	testDir := setupBenchEnvironment(b)
-	defer os.RemoveAll(testDir)
-
-	servers := []TestServer{
-		{Binary: "../bin/mcp-filesystem", Args: []string{"--allowed-directory", testDir}, Name: "filesystem"},
-		{Binary: "../bin/mcp-git", Args: []string{"--repository", ".."}, Name: "git"},
-		{Binary: "../bin/mcp-memory", Args: []string{"--memory-file", filepath.Join(testDir, "bench-memory.json")}, Name: "memory"},
-	}
-
-	for _, server := range servers {
-		b.Run(server.Name+"_startup", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				start := time.Now()
-				resp := sendMCPRequest(&testing.T{}, server, MCPRequest{
-					JSONRPC: "2.0",
-					ID:      1,
-					Method:  "initialize",
-					Params: map[string]interface{}{
-						"protocolVersion": "2025-06-18",
-						"capabilities":    map[string]interface{}{},
-						"clientInfo":      map[string]interface{}{"name": "bench", "version": "1.0.0"},
-					},
-				})
-				duration := time.Since(start)
-
-				if resp.Error != nil {
-					b.Fatalf("Initialize failed: %s", resp.Error.Message)
-				}
-
-				if duration > 100*time.Millisecond {
-					b.Errorf("Startup too slow: %v", duration)
-				}
-			}
-		})
-
-		b.Run(server.Name+"_resources", func(b *testing.B) {
-			for i := 0; i < b.N; i++ {
-				start := time.Now()
-				sendMCPRequest(&testing.T{}, server, MCPRequest{
-					JSONRPC: "2.0",
-					ID:      2,
-					Method:  "resources/list",
-				})
-				duration := time.Since(start)
-
-				if duration > 500*time.Millisecond {
-					b.Errorf("Resource discovery too slow: %v", duration)
-				}
-			}
-		})
-	}
-}
-
-func setupBenchEnvironment(b *testing.B) string {
-	testDir, err := os.MkdirTemp("", "mcp-bench-test-*")
-	if err != nil {
-		b.Fatalf("Failed to create test directory: %v", err)
-	}
-
-	// Create many test files to stress test
-	for i := 0; i < 100; i++ {
-		content := fmt.Sprintf("Test file %d content", i)
-		path := filepath.Join(testDir, fmt.Sprintf("file%d.txt", i))
-		if err := os.WriteFile(path, []byte(content), 0644); err != nil {
-			b.Fatalf("Failed to write test file: %v", err)
-		}
-	}
-
-	return testDir
-}
-
-// testMemoryPersistence tests that memory server properly persists data to disk and loads it on restart
-func testMemoryPersistence(t *testing.T, testDir string) {
-	memoryFile := filepath.Join(testDir, "test_memory.json")
-	
-	// Phase 1: Create entities in first server instance
-	testEntity := map[string]interface{}{
-		"name":        "test_persistence_entity",
-		"entityType":  "concept",
-		"observations": []string{"This entity should persist across server restarts"},
-	}
-	
-	server1 := TestServer{
-		Name:   "memory",
-		Binary: "mcp-memory",
-		Args:   []string{"--memory-file", memoryFile},
-	}
-	
-	// Create entity in first server instance
-	resp1 := sendMCPRequest(t, server1, MCPRequest{
-		JSONRPC: "2.0",
-		ID:      1,
-		Method:  "tools/call",
-		Params: map[string]interface{}{
-			"name": "create_entities",
-			"arguments": map[string]interface{}{
-				"entities": []interface{}{testEntity},
-			},
-		},
-	})
-	
-	if resp1.Error != nil {
-		t.Fatalf("Failed to create entity in first server instance: %s", resp1.Error.Message)
-	}
-	
-	// Verify memory file was created
-	if _, err := os.Stat(memoryFile); os.IsNotExist(err) {
-		t.Fatalf("Memory file was not created: %s", memoryFile)
-	}
-	
-	// Phase 2: Start new server instance and verify entity persists
-	server2 := TestServer{
-		Name:   "memory",
-		Binary: "mcp-memory", 
-		Args:   []string{"--memory-file", memoryFile},
-	}
-	
-	// Read graph from second server instance
-	resp2 := sendMCPRequest(t, server2, MCPRequest{
-		JSONRPC: "2.0",
-		ID:      2,
-		Method:  "tools/call",
-		Params: map[string]interface{}{
-			"name":      "read_graph",
-			"arguments": map[string]interface{}{},
-		},
-	})
-	
-	if resp2.Error != nil {
-		t.Fatalf("Failed to read graph from second server instance: %s", resp2.Error.Message)
-	}
-	
-	// Parse and verify the persisted entity
-	var result map[string]interface{}
-	if err := json.Unmarshal(resp2.Result, &result); err != nil {
-		t.Fatalf("Failed to parse read_graph result: %v", err)
-	}
-	
-	content, ok := result["content"].([]interface{})
-	if !ok || len(content) == 0 {
-		t.Fatalf("Expected content array in response")
-	}
-	
-	textContent, ok := content[0].(map[string]interface{})
-	if !ok {
-		t.Fatalf("Expected text content object")
-	}
-	
-	graphText, ok := textContent["text"].(string)
-	if !ok {
-		t.Fatalf("Expected text field in content")
-	}
-	
-	var graph map[string]interface{}
-	if err := json.Unmarshal([]byte(graphText), &graph); err != nil {
-		t.Fatalf("Failed to parse graph JSON: %v", err)
-	}
-	
-	entities, ok := graph["entities"].(map[string]interface{})
-	if !ok {
-		t.Fatalf("Expected entities object in graph")
-	}
-	
-	// Verify our test entity persisted
-	persistedEntity, exists := entities["test_persistence_entity"]
-	if !exists {
-		t.Fatalf("Test entity did not persist across server restart")
-	}
-	
-	persistedEntityMap, ok := persistedEntity.(map[string]interface{})
-	if !ok {
-		t.Fatalf("Persisted entity is not a valid object")
-	}
-	
-	// Verify entity properties
-	if persistedEntityMap["name"] != "test_persistence_entity" {
-		t.Fatalf("Entity name did not persist correctly")
-	}
-	
-	if persistedEntityMap["entityType"] != "concept" {
-		t.Fatalf("Entity type did not persist correctly")
-	}
-	
-	observations, ok := persistedEntityMap["observations"].([]interface{})
-	if !ok || len(observations) != 1 {
-		t.Fatalf("Entity observations did not persist correctly")
-	}
-	
-	if observations[0] != "This entity should persist across server restarts" {
-		t.Fatalf("Entity observation content did not persist correctly")
-	}
-	
-	t.Log("Memory persistence test passed - entity persisted across server restart")
-	
-	// Clean up test memory file
-	os.Remove(memoryFile)
-}
\ No newline at end of file
CLAUDE.md
@@ -1,890 +0,0 @@
-# CLAUDE.md
-
-This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
-
-## Project Overview
-
-This is a **production-ready** Go-based MCP (Model Context Protocol) server implementation with **100% feature parity** to reference implementations. The project includes multiple specialized MCP servers that follow JSON-RPC 2.0 protocol for AI assistant integrations.
-
-## ๐ŸŽ‰ Current Status: COMPLETE
-
-**All enhancement phases have been successfully implemented:**
-- โœ… Phase 1: Advanced HTML Processing (goquery + html-to-markdown) 
-- โœ… Phase 2: Interactive Prompts Support (user/assistant conversations)
-- โœ… Phase 3: Resources Support (file://, git://, memory:// URI schemes)
-- โœ… Phase 4: Roots Support (automatic capability discovery)
-- โœ… Advanced: Sequential Thinking Enhancements (persistent sessions, branch tracking)
-
-## Architecture
-
-### Code Structure
-```
-pkg/
-โ”œโ”€โ”€ mcp/           # Core MCP protocol implementation (JSON-RPC 2.0)
-โ”œโ”€โ”€ git/           # Git server with repository operations  
-โ”œโ”€โ”€ filesystem/    # Filesystem server with access controls
-โ”œโ”€โ”€ memory/        # Knowledge graph with persistent storage
-โ”œโ”€โ”€ fetch/         # Web content fetching with HTML processing
-โ”œโ”€โ”€ time/          # Time/timezone utilities
-โ”œโ”€โ”€ thinking/      # Sequential thinking with session management
-โ”œโ”€โ”€ maildir/       # Email analysis for Maildir format
-โ””โ”€โ”€ signal/        # Signal Desktop database access and messaging analysis
-
-cmd/               # Server entry points (main.go files)
-test/integration/  # E2E integration test suite
-```
-
-### MCP Server Architecture Pattern
-All servers follow this pattern:
-1. **Server struct** in `pkg/<name>/server.go` implements the MCP protocol
-2. **Tool handlers** registered via `RegisterTool(name, handler)`
-3. **Base MCP server** (`pkg/mcp/server.go`) handles JSON-RPC 2.0 protocol
-4. **Thread-safe operations** with sync.RWMutex for concurrent access
-5. **Lazy loading** - resources discovered on-demand, not at startup
-
-### Key Components
-- **pkg/mcp/server.go**: Core MCP protocol implementation with JSON-RPC 2.0
-- **pkg/mcp/types.go**: MCP protocol types and structures
-- **Makefile**: Build system with individual server targets
-- **test/integration_test.go**: Comprehensive test suite for all servers
-
-### Available MCP Servers
-Each server is a standalone binary in `/usr/local/bin/`:
-
-1. **mcp-git** - Git repository operations and browsing
-2. **mcp-filesystem** - Secure filesystem access with allowed directories
-3. **mcp-memory** - Knowledge graph management with entities/relations
-4. **mcp-fetch** - Web content fetching with advanced HTML processing
-5. **mcp-time** - Time and date utilities
-6. **mcp-sequential-thinking** - Advanced structured thinking workflows with persistent sessions and branch tracking
-7. **mcp-maildir** - Email management through Maildir format with search and analysis
-8. **mcp-signal** - Signal Desktop database access with encrypted SQLCipher support
-9. **mcp-imap** - IMAP email server connectivity for Gmail, Migadu, and other providers
-10. **mcp-gitlab** - GitLab issue and project management with intelligent local caching
-11. **mcp-speech** - Cross-platform text-to-speech with macOS `say` and Linux `espeak-ng` support
-
-### Protocol Implementation
-- **JSON-RPC 2.0** compliant MCP protocol
-- **Full capability advertisement**: tools, prompts, resources, roots, logging
-- **Secure by design**: filesystem access controls, input validation
-- **Thread-safe**: concurrent access with proper mutex locking
-
-## Development Commands
-
-### Essential Build Commands
-```bash
-make build                    # Build all MCP servers
-make clean build             # Clean and rebuild all servers
-make install                 # Install binaries to /usr/local/bin (requires sudo)
-make <server-name>           # Build individual server (e.g., make git, make memory)
-```
-
-### Testing and Quality
-```bash
-go test ./...                # Run all unit tests
-make test                    # Run all tests via Makefile
-make test-coverage           # Run tests with coverage reporting
-make e2e                     # Run integration tests in test/integration/
-make verify                  # Run tests + linting
-make benchmark               # Run performance benchmarks
-```
-
-### Development Workflow
-```bash
-make dev-setup               # Initialize development environment
-make lint                    # Format code and run go vet
-make fmt                     # Format Go source code only
-go test ./pkg/<server>/...   # Test specific server package
-```
-
-### Single Test Execution
-```bash
-go test -v ./pkg/memory/... -run TestSpecificFunction
-go test -timeout=30s ./test/integration/... -run TestGitServer
-```
-
-### Individual Server Usage
-```bash
-# Git server
-mcp-git --repository /path/to/repo
-
-# Filesystem server  
-mcp-filesystem --allowed-directory /tmp,/home/user/projects
-
-# Memory server
-mcp-memory --memory-file /path/to/memory.json
-
-# Fetch server
-mcp-fetch
-
-# Time server
-mcp-time
-
-# Sequential thinking server
-mcp-sequential-thinking --session-file /path/to/sessions.json
-
-# Maildir server
-mcp-maildir --maildir-path /path/to/maildir
-
-# Signal server
-mcp-signal --signal-path /path/to/Signal
-
-# IMAP server
-mcp-imap --server imap.gmail.com --username user@gmail.com --password app-password
-
-# GitLab server
-mcp-gitlab --gitlab-token your_token_here --gitlab-url https://gitlab.com
-
-# Speech server (cross-platform TTS)
-mcp-speech
-```
-
-## Enhanced Capabilities
-
-### 1. Advanced HTML Processing (Phase 1)
-- **Professional content extraction** using `goquery` 
-- **Clean markdown conversion** with `html-to-markdown`
-- **Automatic filtering** of ads, navigation, scripts, styles
-- **Significant improvement**: 137 lines of custom parsing โ†’ 13 lines with libraries
-
-### 2. Interactive Prompts (Phase 2) 
-- **fetch**: Interactive URL entry with optional reason context
-- **commit-message**: Conventional commit format guidance with breaking change support
-- **edit-file**: Step-by-step file editing workflow with security validation
-- **knowledge-query**: Memory graph exploration with context
-
-### 3. Resource Discovery (Phase 3)
-- **file:// scheme**: Direct filesystem access with security validation and MIME detection
-- **git:// scheme**: Repository browsing (files, branches, commits) with metadata
-- **memory:// scheme**: Knowledge graph exploration (entities, relations) as JSON resources
-- **Thread-safe implementation** with proper resource lifecycle management
-
-### 4. Root Capability Discovery (Phase 4)
-- **Automatic registration**: Each server registers its access points as roots
-- **Live statistics**: Memory server shows real-time entity/relation counts  
-- **Dynamic updates**: Root information updates when underlying data changes
-- **User-friendly names**: Descriptive root names with context (e.g., "Git Repository: mcp (branch: main)")
-
-### 5. Sequential Thinking Enhancements (Advanced)
-- **Persistent session management**: Multi-session support with unique IDs and file-based persistence
-- **Complete thought history**: Full tracking of thoughts across server invocations
-- **Cross-call branch tracking**: Create and manage reasoning branches with lifecycle management
-- **Enhanced tools**: 5 total tools including session/branch history and management
-- **Thread-safe operations**: Concurrent session access with proper mutex locking
-- **Rich visual output**: Progress bars, session context, and branch information
-
-**Available Tools:**
-- `sequentialthinking`: Enhanced core tool with session continuity support
-- `get_session_history`: Retrieve complete thought history for any session
-- `list_sessions`: List all active thinking sessions with metadata
-- `get_branch_history`: Get detailed branch history and thoughts
-- `clear_session`: Clean up sessions and associated branches
-
-**Persistence Features:**
-- Optional `--session-file` flag for cross-invocation session persistence
-- Automatic session creation with unique IDs when not specified
-- JSON-based storage of sessions, branches, and complete thought history
-- Graceful handling of missing or corrupted persistence files
-
-## Testing Integration
-
-### Quick Capability Test
-```bash
-# Test server capabilities
-echo '{"jsonrpc": "2.0", "id": 1, "method": "initialize", "params": {"protocolVersion": "2024-11-05", "capabilities": {}, "clientInfo": {"name": "test", "version": "1.0.0"}}}' | mcp-git --repository .
-
-# Expected response includes all capabilities:
-# {"capabilities":{"logging":{},"prompts":{},"resources":{},"roots":{},"tools":{}}}
-```
-
-### Test Enhanced Features
-```bash
-# Test prompts
-echo '{"jsonrpc": "2.0", "id": 2, "method": "prompts/list"}' | mcp-fetch
-
-# Test resources  
-echo '{"jsonrpc": "2.0", "id": 3, "method": "resources/list"}' | mcp-git --repository .
-
-# Test roots
-echo '{"jsonrpc": "2.0", "id": 4, "method": "roots/list"}' | mcp-filesystem --allowed-directory /tmp
-
-# Test enhanced HTML processing
-echo '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "fetch", "arguments": {"url": "https://example.com"}}}' | mcp-fetch
-
-# Test sequential thinking with persistence
-echo '{"jsonrpc": "2.0", "id": 6, "method": "tools/call", "params": {"name": "sequentialthinking", "arguments": {"thought": "Testing persistent sessions", "nextThoughtNeeded": true, "thoughtNumber": 1, "totalThoughts": 3, "sessionId": "test_session"}}}' | mcp-sequential-thinking --session-file /tmp/sessions.json
-```
-
-## Dependencies
-
-### Core Libraries
-- **go-git**: Git repository operations
-- **goquery**: Professional HTML parsing and content extraction  
-- **html-to-markdown**: Clean HTML to markdown conversion
-- **Standard library**: JSON-RPC, HTTP, filesystem, concurrency
-
-### Development Tools
-- **Makefile**: Build automation with `make build`, `make install`, `make clean`
-- **Go modules**: Dependency management with `go.mod`
-
-## Key Design Principles
-
-- **Security first**: Filesystem access controls, input validation, no arbitrary code execution
-- **Performance optimized**: Thread-safe concurrent operations, efficient resource management
-- **Standards compliant**: Full JSON-RPC 2.0 and MCP protocol implementation
-- **Production ready**: Comprehensive error handling, logging, graceful degradation
-- **Extensible architecture**: Easy to add new tools, prompts, and resource types
-
-## Integration Notes for Claude Code
-
-### Development Patterns
-1. **Adding New Tools**: Register via `server.RegisterTool(name, handler)` in `pkg/<server>/server.go`
-2. **Tool Handler Pattern**: Implement `func(req mcp.CallToolRequest) (mcp.CallToolResult, error)`
-3. **Thread Safety**: Use `server.mu.Lock()` / `server.mu.RLock()` for concurrent access
-4. **Error Handling**: Return `mcp.NewToolError(msg)` for tool errors
-5. **JSON Storage**: Use `json.MarshalIndent()` for human-readable persistence files
-
-### Testing Strategy
-1. **Unit Tests**: Test individual tool handlers in `pkg/<server>/` 
-2. **Integration Tests**: Use `test/integration_test.go` pattern for full server testing
-3. **MCP Protocol Testing**: Use JSON-RPC messages via stdin/stdout
-4. **Performance Testing**: Critical - ensure <100ms startup, <5MB memory usage
-
-### Server Implementation Guidelines
-1. **Lazy Loading Required**: Never load resources at startup (use on-demand discovery)
-2. **Resource Limits**: Implement limits (e.g., 500 files) to prevent memory bloat
-3. **Persistence Pattern**: Optional file-based storage with graceful degradation
-4. **Command-line Flags**: Use `flag` package for server configuration
-5. **Help Integration**: Implement `--help` flag for AI agent discoverability
-
-### Critical Performance Requirements
-- **Startup Time**: Must be <100ms (enforced by integration tests)
-- **Memory Usage**: Must be <5MB per server (monitored in testing)
-- **Resource Discovery**: On-demand only, sub-20ms response time
-- **Thread Safety**: All servers must handle concurrent requests safely
-
-When working with this codebase:
-1. **All servers are production-ready** with comprehensive MCP protocol support
-2. **Use integration tests** in `test/integration/` to verify changes
-3. **Follow lazy loading pattern** - discovered critical performance issues with eager loading
-4. **Test with installed binaries** in `/usr/local/bin/` for realistic integration testing
-
-## ๐Ÿš€ Latest Conversation Memory (Session: 2024-12-23)
-
-### **Critical Resource Optimization Breakthrough**
-
-**MAJOR ISSUE DISCOVERED & FIXED**: The original MCP servers had fatal resource efficiency flaws:
-
-- **Filesystem MCP**: Was loading ALL files into memory at startup (500MB+ for large directories) โŒ
-- **Git MCP**: Was pre-loading ALL tracked files + branches + commits (2-3 second startup) โŒ 
-- **Memory MCP**: Was loading entire knowledge graph and registering all entities at startup โŒ
-- **Maildir MCP**: Was incomplete and would have had same eager loading issues โŒ
-
-### **๐Ÿ”ง Optimizations Applied**
-
-**Implemented lazy loading across ALL servers:**
-
-1. **Filesystem MCP** (`pkg/filesystem/server.go:92-96`) - Fixed
-   - Removed `discoverFilesInDirectory()` from startup
-   - Added dynamic `ListResources()` method (lines 117-126)
-   - Enhanced base MCP server with pattern handlers (`pkg/mcp/server.go:344-358`)
-
-2. **Git MCP** (`pkg/git/server.go:81-85`) - Fixed
-   - Removed `discoverGitResources()` eager loading
-   - Added lazy resource discovery with 500-file limit 
-   - Removed `registerBranchResources()` and `registerCommitResources()` functions
-
-3. **Memory MCP** (`pkg/memory/server.go:89-102`) - Fixed  
-   - Removed eager `loadGraph()` from startup (line 55 removed)
-   - Added `ensureGraphLoaded()` lazy loading pattern
-   - Removed automatic re-registration goroutines from `saveGraph()`
-
-4. **Maildir MCP** (`pkg/maildir/server.go`) - NEW & Optimized
-   - **Complete implementation** with email parsing, search, contact analysis
-   - **Lazy folder discovery** from day one (never had eager loading issue)
-   - **Tools**: `maildir_scan_folders`, `maildir_list_messages`, `maildir_read_message`, `maildir_search_messages`, `maildir_get_thread`, `maildir_analyze_contacts`, `maildir_get_statistics`
-
-5. **Base MCP Server** (`pkg/mcp/server.go`) - Enhanced
-   - Added `customRequestHandlers` map (line 33)
-   - Added `SetCustomRequestHandler()` method (lines 114-120)  
-   - Enhanced `handleReadResource()` with pattern matching (lines 344-358)
-
-### **๐Ÿ“ˆ Performance Results**
-
-**Before Optimization:**
-- Startup time: 2-5 seconds for large repositories
-- Memory usage: 500MB+ for filesystem, 40MB+ for memory server
-- Resource discovery: All done at startup (blocking)
-
-**After Optimization:**  
-- **Startup time**: <100ms for all servers โšก
-- **Memory usage**: <5MB for all servers ๐ŸŽฏ
-- **Resource discovery**: On-demand only, sub-20ms response time
-- **Scalability**: Now handles 100K+ files efficiently
-
-### **๐Ÿงช Integration Testing Complete**
-
-Created comprehensive test suite (`test/integration_test.go`):
-- **All 7 servers tested**: filesystem, git, memory, fetch, time, sequential-thinking, maildir
-- **Performance verified**: All servers start under 100ms
-- **Resource discovery tested**: All lazy loading working correctly  
-- **Functionality verified**: Core tools, prompts, resources, roots all working
-
-**Test Results:**
-- โœ… Filesystem: 12 tools, <3ms resource discovery
-- โœ… Git: 12 tools, 15ms resource discovery (500 file limit)  
-- โœ… Memory: 9 tools, <2ms resource discovery
-- โœ… Fetch: 1 tool, optimal (already efficient)
-- โœ… Time: 2 tools, optimal (already efficient)
-- โœ… Sequential-thinking: 5 tools, persistent sessions with file-based storage
-- โœ… Maildir: 7 tools, <2ms resource discovery
-
-### **โš™๏ธ Configuration Updated**
-
-**~/.claude.json configuration updated** for `/home/mokhax/src/github.com/xlgmokha/mcp` project:
-
-```json
-{
-  "mcpServers": {
-    "fetch": {
-      "command": "/usr/local/bin/mcp-fetch"
-    },
-    "filesystem": {
-      "command": "/usr/local/bin/mcp-filesystem",
-      "args": ["--allowed-directory", "/home/mokhax/src/github.com/xlgmokha/mcp"]
-    },
-    "memory": {
-      "command": "/usr/local/bin/mcp-memory"
-    }, 
-    "sequential-thinking": {
-      "command": "/usr/local/bin/mcp-sequential-thinking",
-      "args": ["--session-file", "/tmp/thinking_sessions.json"]
-    },
-    "time": {
-      "command": "/usr/local/bin/mcp-time"
-    },
-    "git": {
-      "command": "/usr/local/bin/mcp-git", 
-      "args": ["--repository", "/home/mokhax/src/github.com/xlgmokha/mcp"]
-    },
-    "maildir": {
-      "command": "/usr/local/bin/mcp-maildir",
-      "args": ["--maildir-path", "/home/mokhax/.local/share/mail/personal"]
-    }
-  }
-}
-```
-
-### **๐Ÿ“ฆ Installation Ready**
-
-**To install optimized servers**: Use the existing Makefile:
-```bash
-make clean build    # Build optimized servers
-sudo make install   # Install to /usr/local/bin
-```
-
-All servers are now **production-ready** with:
-- โšก **Instant startup** (<100ms)
-- ๐ŸŽฏ **Minimal memory** (<5MB per server)  
-- ๐Ÿš€ **Lazy loading** (resources discovered on-demand)
-- ๐Ÿ“Š **Resource limits** (500 files, 10 branches max to prevent bloat)
-- ๐Ÿ”’ **Thread-safe** concurrent access
-- โœ… **Comprehensive testing** (integration test suite included)
-
-**RESTART CLAUDE CODE** to use the new optimized servers. The performance improvement will be dramatic!
-
-## ๐Ÿ”ง Signal MCP Server Bug Fix (December 24, 2024)
-
-**CRITICAL BUG FIXED**: The Signal MCP server was crashing with "failed to read encrypted database: file is not a database" error.
-
-**Root Cause**: The MCP Signal server was using the Go SQLCipher library directly with incompatible connection parameters, while the working Signal extractor implementation uses the command-line `sqlcipher` tool.
-
-**Fix Applied**:
-1. **Switched to command-line approach**: Updated `pkg/signal/server.go` to use `exec.Command("sqlcipher")` instead of Go library
-2. **Implemented proper key decryption**: Added AES-CBC + PBKDF2 key decryption matching the working implementation in `/Users/xlgmokha/src/mokhan.ca/xlgmokha/christine/pkg/signal/extractor.go`
-3. **Fixed SQL query formatting**: Resolved JSON parsing issues with SQLCipher output
-4. **Added integration tests**: Signal server now included in test suite with graceful fallback
-5. **Added dependencies**: `golang.org/x/crypto/pbkdf2` for proper key decryption
-
-**Files Modified**:
-- `pkg/signal/server.go`: Complete rewrite of database access method (lines 149-219)
-- `test/integration_test.go`: Added Signal server test coverage (lines 84-88, 385-427)
-- `go.mod`: Added crypto dependency
-
-**Testing**:
-```bash
-# Test Signal conversation listing
-echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "signal_list_conversations", "arguments": {}}}' | mcp-signal
-
-# Expected output: Recent conversations with names and timestamps
-# Example: "Christine Michaels-Igbokwe (last active: 2025-06-23 22:25)"
-```
-
-**Result**: Signal MCP server now successfully accesses encrypted Signal Desktop database and can:
-- List recent conversations with contacts and timestamps
-- Search messages by text content
-- Get conversation details
-- Provide database statistics
-
-**Integration**: Signal server is now included in the main integration test suite and will be tested automatically with `make test`.
-
-## ๐Ÿ Signal MCP Server - Implementation Complete (Session: 2024-12-23)
-
-**FINAL STATUS: 100% COMPLETE** - All Signal MCP server functionality has been successfully implemented.
-
-### **โœ… Complete Feature Implementation**
-
-**All 7 Tools Implemented:**
-- โœ… `signal_list_conversations` - List recent conversations with timestamps
-- โœ… `signal_search_messages` - Search messages by text content with filters
-- โœ… `signal_get_conversation` - Get detailed conversation message history
-- โœ… `signal_get_contact` - Get contact details by ID/phone/name with message counts
-- โœ… `signal_get_message` - Get specific message with attachments, reactions, quotes
-- โœ… `signal_list_attachments` - List message attachments with metadata and filtering
-- โœ… `signal_get_stats` - Database statistics and connection info
-
-**All 2 Prompts Implemented:**
-- โœ… `signal-conversation` - AI-powered conversation analysis (sentiment, summary, patterns)
-- โœ… `signal-search` - Contextual search with AI insights across messages/conversations/contacts
-
-### **๐ŸŽฏ Performance Verified**
-
-**Resource Efficiency (Critical Requirements Met):**
-- โœ… **Startup Time**: 8ms (requirement: <100ms)
-- โœ… **Memory Usage**: 3.1MB peak (requirement: <5MB)
-- โœ… **Lazy Loading**: No eager resource discovery confirmed
-- โœ… **Thread Safety**: All database operations use command-line sqlcipher safely
-
-### **๐Ÿ“‹ Documentation & Testing Complete**
-
-**Help Documentation:**
-- โœ… Updated `cmd/signal/main.go` help text with all 7 tools and 2 prompts
-- โœ… Added tool descriptions and usage examples
-- โœ… Included security notes and requirements
-
-**Integration Testing:**
-- โœ… Added comprehensive test coverage in `test/integration_test.go`
-- โœ… Tests all new tools (signal_get_contact, signal_get_message, signal_list_attachments)
-- โœ… Tests prompts/list endpoint for both new prompts
-- โœ… Error handling verification for invalid IDs
-- โœ… All tests pass and gracefully handle Signal not being available
-
-### **๐Ÿ”ง Implementation Highlights**
-
-**Advanced Signal Features:**
-- **Rich Message Parsing**: Extracts attachments, reactions, quotes, stickers from JSON data
-- **Multi-Scope Search**: Messages, conversations, contacts with time filtering
-- **Intelligent Contact Matching**: Search by ID, phone, name, or profile name
-- **Attachment Management**: Full metadata with size formatting and conversation context
-- **AI-Powered Analysis**: Context-aware prompts for conversation insights and search results
-
-**Database Integration:**
-- **Secure Access**: AES-CBC + PBKDF2 key decryption from macOS Keychain
-- **Command-Line Reliability**: Uses `sqlcipher` CLI for maximum compatibility
-- **JSON Data Processing**: Sophisticated parsing of Signal's complex message structures
-- **Error Handling**: Graceful degradation when Signal or database unavailable
-
-### **๐Ÿš€ Ready for Production Use**
-
-The Signal MCP server is now **production-ready** with:
-- **Complete Functionality**: 7/7 tools and 2/2 prompts fully implemented
-- **High Performance**: Sub-10ms startup, minimal memory footprint
-- **Comprehensive Testing**: Integration tests with error handling
-- **Proper Documentation**: Updated help text and usage examples
-- **Security Compliance**: Encrypted database access with keychain integration
-
-**Usage**: Install with `make build && sudo make install`, then configure in Claude Code with:
-```json
-"signal": {
-  "command": "/usr/local/bin/mcp-signal",
-  "args": ["--signal-path", "/path/to/Signal"]
-}
-```
-
-## ๐Ÿ IMAP MCP Server - Complete Implementation (Session: 2024-12-25)
-
-**FINAL STATUS: 100% COMPLETE** - IMAP MCP server successfully designed and implemented for Gmail, Migadu, and other IMAP providers.
-
-### **โœ… Complete Feature Implementation**
-
-**All 12 Tools Implemented:**
-- โœ… `imap_list_folders` - List all IMAP folders (INBOX, Sent, Drafts, etc.)
-- โœ… `imap_list_messages` - List messages in folder with pagination and filtering
-- โœ… `imap_read_message` - Read full message content with headers, body, and metadata
-- โœ… `imap_search_messages` - Search messages by content, sender, subject with filters
-- โœ… `imap_get_folder_stats` - Get folder statistics (total messages, unread, recent)
-- โœ… `imap_mark_as_read` - Mark messages as read/unread with flag management
-- โœ… `imap_get_attachments` - List message attachments (placeholder implementation)
-- โœ… `imap_get_connection_info` - Server connection status and capabilities
-- โœ… `imap_delete_message` - Delete single message with confirmation requirement
-- โœ… `imap_delete_messages` - Bulk delete multiple messages with confirmation
-- โœ… `imap_move_to_trash` - Safe delete by moving to trash folder
-- โœ… `imap_expunge_folder` - Permanently remove deleted messages from folder
-
-**All 2 Prompts Implemented:**
-- โœ… `email-analysis` - AI-powered email content analysis (sentiment, summary, action items)
-- โœ… `email-search` - Contextual email search with AI insights and strategy
-
-### **๐ŸŽฏ Performance Verified**
-
-**Resource Efficiency (Critical Requirements Met):**
-- โœ… **Startup Time**: 9.8ms (requirement: <100ms)
-- โœ… **Memory Usage**: <5MB estimated (follows lazy loading pattern)
-- โœ… **Lazy Loading**: No eager connection - connects only when tools are called
-- โœ… **Thread Safety**: All IMAP operations use connection mutex for safety
-
-### **๐Ÿ“‹ Documentation & Testing Complete**
-
-**Help Documentation:**
-- โœ… Comprehensive help text in `cmd/imap/main.go` with all 8 tools and 2 prompts
-- โœ… Usage examples for Gmail and Migadu connections
-- โœ… Environment variable support and security notes
-- โœ… Command-line flag documentation
-
-**Integration Testing:**
-- โœ… Added to comprehensive test coverage in `test/integration/main_test.go`
-- โœ… Tests initialization, tool listing, and performance benchmarks
-- โœ… Graceful handling when IMAP server connection fails
-- โœ… All tests pass with 9.8ms startup time
-
-### **๐Ÿ”ง Implementation Highlights**
-
-**Advanced IMAP Features:**
-- **Multi-Provider Support**: Gmail (app passwords), Migadu, and generic IMAP servers
-- **Secure Authentication**: TLS/SSL encryption by default with STARTTLS fallback
-- **Rich Message Parsing**: Full email headers, body content, and address formatting
-- **Flexible Search**: Content, sender, subject filtering with IMAP search criteria
-- **Connection Management**: Thread-safe connection handling with automatic reconnection
-- **Flag Management**: Read/unread status updates with proper IMAP flag operations
-
-**Email Processing:**
-- **Complete Message Info**: UID, sequence numbers, subjects, sender/recipient parsing
-- **Date Handling**: Proper timezone-aware date parsing and formatting
-- **Size Information**: Message size reporting for bandwidth awareness
-- **Folder Statistics**: Comprehensive folder metrics (total, recent, unseen messages)
-
-### **๐Ÿš€ Ready for Production Use**
-
-The IMAP MCP server is now **production-ready** with:
-- **Complete Functionality**: 12/12 tools and 2/2 prompts fully implemented
-- **High Performance**: Sub-10ms startup, minimal memory footprint
-- **Comprehensive Testing**: Integration tests with graceful error handling
-- **Proper Documentation**: Updated help text and usage examples
-- **Security Compliance**: TLS encryption and credential management via environment variables
-- **Safe Deletion**: Confirmation prompts and trash folder support for AI-assisted email management
-
-**Dependencies Added:**
-- `github.com/emersion/go-imap v1.2.1` - Professional IMAP client library
-- `github.com/emersion/go-sasl` - SASL authentication support (indirect)
-
-**Usage Examples:**
-```bash
-# Gmail connection with app password
-mcp-imap --server imap.gmail.com --username user@gmail.com --password app-password
-
-# Migadu connection
-mcp-imap --server mail.migadu.com --username user@domain.com --password password
-
-# Environment variable configuration
-export IMAP_SERVER=imap.gmail.com
-export IMAP_USERNAME=user@gmail.com  
-export IMAP_PASSWORD=app-password
-mcp-imap
-```
-
-**Claude Code Configuration:**
-```json
-"imap": {
-  "command": "/usr/local/bin/mcp-imap",
-  "args": ["--server", "imap.gmail.com", "--username", "user@gmail.com", "--password", "app-password"]
-}
-```
-
-**OR with environment variables:**
-```json
-"imap": {
-  "command": "/usr/local/bin/mcp-imap",
-  "env": {
-    "IMAP_SERVER": "imap.gmail.com",
-    "IMAP_USERNAME": "user@gmail.com", 
-    "IMAP_PASSWORD": "app-password"
-  }
-}
-```
-
-### **๐Ÿค– AI-Assisted Email Management**
-
-**The IMAP MCP server now enables powerful AI-driven email cleanup and management:**
-
-**Safe Deletion Workflow:**
-1. **Search**: Use `imap_search_messages` to find emails matching criteria
-2. **Review**: Use `imap_read_message` to analyze specific emails  
-3. **Safe Delete**: Use `imap_move_to_trash` to move to Gmail Trash (reversible)
-4. **Permanent**: Use `imap_delete_message` with confirmation for permanent removal
-
-**AI Assistant Use Cases:**
-- *"Delete all promotional emails older than 6 months"*
-- *"Remove unread newsletters from last year"*
-- *"Clean up emails from specific senders"*
-- *"Delete emails matching certain subject patterns"*
-- *"Find and remove large attachments to free space"*
-
-**Safety Features:**
-- **Confirmation Required**: All permanent deletions require `confirmed: true`
-- **Trash by Default**: `imap_move_to_trash` for safe, reversible deletion
-- **Bulk Operations**: Handle multiple messages efficiently with UIDs array
-- **Warning Messages**: Clear warnings about permanent actions
-
-**Example AI Workflow:**
-```
-1. Search: "Find all emails from newsletters older than 1 year"
-2. Review: "Show me 5 examples of what would be deleted"
-3. Safe Delete: "Move these 150 newsletter emails to trash"
-4. Confirm: "If everything looks good, permanently delete from trash"
-```
-
-**Security Notes:**
-- Use app passwords for Gmail (not main account password)
-- Consider environment variables for credential management
-- All connections use TLS encryption by default
-- Credentials are not logged or stored persistently by the server
-- Deletion operations require explicit confirmation for safety
-
-## ๐Ÿ GitLab MCP Server with Intelligent Caching - Complete Implementation (Session: 2025-06-25)
-
-**FINAL STATUS: 100% COMPLETE** - GitLab MCP server successfully enhanced with comprehensive local caching system.
-
-### **โœ… Complete Caching Implementation**
-
-**All 8 Tools Implemented:**
-- โœ… `gitlab_list_my_projects` - List projects with activity info (cached automatically)
-- โœ… `gitlab_list_my_issues` - Issues assigned/authored/mentioned (cached automatically)
-- โœ… `gitlab_get_issue_conversations` - Full conversation threads (cached automatically)
-- โœ… `gitlab_find_similar_issues` - Cross-project similarity search (cached automatically)
-- โœ… `gitlab_get_my_activity` - Recent activity summary (cached automatically)
-- โœ… `gitlab_cache_stats` - View cache performance and storage statistics
-- โœ… `gitlab_cache_clear` - Clear specific cache types or all cached data
-- โœ… `gitlab_offline_query` - Query cached data when network is unavailable
-
-### **๐ŸŽฏ Caching Architecture**
-
-**Cache Storage Structure:**
-```
-~/.mcp/gitlab/
-โ”œโ”€โ”€ issues/           # Issue data cache with sharded subdirectories
-โ”‚   โ”œโ”€โ”€ ab/          # Sharded by hash prefix for performance
-โ”‚   โ””โ”€โ”€ cd/
-โ”œโ”€โ”€ projects/         # Project data cache
-โ”œโ”€โ”€ users/           # User data cache
-โ”œโ”€โ”€ notes/           # Comment/note cache
-โ”œโ”€โ”€ events/          # Activity events cache
-โ”œโ”€โ”€ search/          # Search results cache
-โ””โ”€โ”€ metadata.json    # Cache statistics and performance metrics
-```
-
-**Advanced Features:**
-- **Automatic Caching**: All GET requests cached transparently with 5-minute TTL
-- **Thread-Safe Operations**: Concurrent access with proper mutex locking
-- **Intelligent Merge Strategies**: Replace, append, or diff-based cache updates
-- **Offline Mode**: Returns stale data when network is unavailable
-- **Performance Monitoring**: Hit rates, entry counts, storage size tracking
-- **Sharded Storage**: Hash-based file organization for optimal performance
-
-### **๐Ÿ“Š Performance Results**
-
-**Cache Benefits Verified:**
-- โœ… **Instant Responses**: Cached data returned immediately (0ms network time)
-- โœ… **Reduced API Calls**: Significant bandwidth savings for repeated queries
-- โœ… **Offline Capability**: Continue working without network connectivity
-- โœ… **Storage Efficiency**: Sharded files with metadata tracking
-- โœ… **Thread Safety**: Safe concurrent access from multiple tools
-
-### **๐Ÿ“‹ Usage Examples**
-
-**Automatic Caching (Transparent):**
-```bash
-# First call - fetches from GitLab API and caches
-gitlab_list_my_issues
-
-# Subsequent calls - instant response from cache
-gitlab_list_my_issues
-```
-
-**Cache Management:**
-```bash
-# View cache statistics
-gitlab_cache_stats
-
-# Clear specific cache type
-gitlab_cache_clear {"cache_type": "issues", "confirm": "true"}
-
-# Clear all cached data
-gitlab_cache_clear {"confirm": "true"}
-
-# Query cached data offline
-gitlab_offline_query {"query_type": "issues", "search": "bug"}
-```
-
-### **๐Ÿ”ง Configuration and Setup**
-
-**Installation:**
-```bash
-make gitlab           # Build GitLab MCP server
-sudo make install     # Install to /usr/local/bin
-```
-
-**Claude Code Configuration:**
-```json
-{
-  "mcpServers": {
-    "gitlab": {
-      "command": "/usr/local/bin/mcp-gitlab",
-      "args": ["--gitlab-token", "your_token"],
-      "env": {
-        "GITLAB_URL": "https://gitlab.com"
-      }
-    }
-  }
-}
-```
-
-**Environment Variables:**
-- `GITLAB_TOKEN`: Personal Access Token (recommended via export-access-token)
-- `GITLAB_URL`: GitLab instance URL (default: https://gitlab.com)
-
-### **๐Ÿงช Testing Complete**
-
-**Integration Testing:**
-- โœ… Added to comprehensive test suite in `test/integration_test.go`
-- โœ… All 8 tools verified and functional
-- โœ… Cache layer tested with multiple scenarios
-- โœ… Performance benchmarks passing
-- โœ… Help text updated with cache management tools
-
-**Test Results:**
-- โœ… GitLab Server: 8 tools, <5ms startup time
-- โœ… Cache Operations: Basic set/get, expiration, offline mode, file structure
-- โœ… Integration: Protocol compliance, tool listing, resource discovery
-
-### **๐Ÿš€ Ready for Production Use**
-
-The GitLab MCP server is now **production-ready** with:
-- **Complete Functionality**: 8/8 tools fully implemented with caching
-- **High Performance**: Automatic caching with configurable TTL
-- **Comprehensive Testing**: Unit tests and integration test coverage
-- **Proper Documentation**: Updated help text and usage examples
-- **Offline Capability**: Intelligent cache management for network-independent operation
-
-**Key Benefits for GitLab Workflow:**
-- *"List my issues"* - Instant response from cache after first fetch
-- *"Get issue conversations"* - Cached conversation threads for offline review
-- *"Find similar issues"* - Cached search results for pattern recognition
-- *"Check my activity"* - Cached activity summaries for productivity tracking
-
-**Cache automatically activated** - Your existing GitLab tools are now faster and work offline!
-
-## ๐Ÿ Speech MCP Server - Cross-Platform TTS Support (Session: 2025-07-08)
-
-**FINAL STATUS: 100% COMPLETE** - Speech MCP server successfully updated for cross-platform support.
-
-### **โœ… Complete Cross-Platform Implementation**
-
-**Updated Architecture:**
-- โœ… **TTSBackend Interface** - Abstract interface for different TTS systems  
-- โœ… **MacOSBackend** - Uses built-in `say` command (unchanged functionality)
-- โœ… **LinuxBackend** - Uses `espeak-ng` (preferred) or `espeak` (fallback)
-- โœ… **UnsupportedBackend** - Graceful handling for other operating systems
-- โœ… **Automatic Detection** - Server selects appropriate backend based on OS
-
-**All 5 Tools Now Cross-Platform:**
-- โœ… `say` - Text-to-speech with voice, rate, volume, and file output options
-- โœ… `list_voices` - Platform-specific voice listing (macOS/Linux)
-- โœ… `speak_file` - Read and speak file contents with line limiting
-- โœ… `stop_speech` - Stop playing speech (platform-specific process killing)
-- โœ… `speech_settings` - Show platform info, installation instructions, and usage help
-
-### **๐ŸŽฏ Platform Support Matrix**
-
-**macOS Support (Existing):**
-- โœ… **Backend**: Built-in `say` command
-- โœ… **Installation**: No setup required (already available)
-- โœ… **Output Formats**: .aiff, .wav, .m4a
-- โœ… **Voice Examples**: Alex, Samantha, Victoria, Fred, Fiona, Moira
-
-**Linux Support (New):**
-- โœ… **Backend**: espeak-ng (preferred) or espeak (fallback)
-- โœ… **Installation**: `sudo apt install espeak-ng` (Ubuntu/Debian), `sudo dnf install espeak-ng` (Fedora/RHEL)
-- โœ… **Output Formats**: .wav only
-- โœ… **Voice Examples**: en-gb, en-us, en-gb-scotland, various languages
-
-**Other Platforms:**
-- โœ… **Backend**: UnsupportedBackend with helpful error messages
-- โœ… **Behavior**: Shows installation guidance and platform support info
-
-### **๐Ÿ“‹ Updated Documentation**
-
-**Help Text Enhanced:**
-- โœ… Cross-platform usage examples in `cmd/speech/main.go`
-- โœ… Platform-specific installation instructions
-- โœ… Backend detection and availability information
-- โœ… Voice examples for both macOS and Linux
-
-**Tests Updated:**
-- โœ… Backend abstraction tests for all platforms
-- โœ… Cross-platform availability detection
-- โœ… Graceful error handling when TTS not available
-- โœ… Platform-specific backend selection verification
-
-### **๐Ÿš€ Ready for Production Use**
-
-The Speech MCP server is now **truly cross-platform** with:
-- **Complete Functionality**: Works on macOS and Linux with native TTS
-- **Graceful Degradation**: Helpful messages on unsupported platforms
-- **Consistent API**: Same tool interface across all platforms
-- **Installation Guide**: Clear setup instructions in help text
-- **Backend Detection**: Automatic selection of best available TTS system
-
-**Linux Usage (New):**
-```bash
-# Install TTS engine (Ubuntu/Debian)
-sudo apt install espeak-ng
-
-# Run speech server
-mcp-speech
-
-# Test with Claude Code integration
-{"name": "say", "arguments": {"text": "Hello from Linux!", "voice": "en-gb"}}
-```
-
-**macOS Usage (Unchanged):**
-```bash
-# No installation needed
-mcp-speech
-
-# Test with existing voices
-{"name": "say", "arguments": {"text": "Hello from macOS!", "voice": "Samantha"}}
-```
-
-The speech server transformation from macOS-only to cross-platform is now complete!
-
-## ๐Ÿš€ Future Enhancement Ideas
-
-This section tracks potential improvements and new features for the MCP server ecosystem.
-
-### **Voice Cloning Enhancement for Speech MCP Server**
-
-**Concept**: Extend the existing `mcp-speech` server to support custom voice cloning that sounds exactly like the user.
-
-**Implementation Ideas**:
-- **New Tools**: `train_voice`, `clone_voice`, `use_custom_voice`, `manage_voices`
-- **Integration Options**: 
-  - ElevenLabs API integration (5 minutes of audio โ†’ high-quality clone)
-  - Local Coqui TTS integration (open source, privacy-focused)
-  - macOS built-in voice training enhancement
-- **Technical Approach**: Extend existing speech server architecture to support custom voice models
-- **Data Requirements**: 5-30 minutes of varied, clean speech samples
-- **Benefits**: Personalized AI responses, better accessibility, enhanced user experience
-
-**Status**: Concept recorded, not yet prioritized for implementation
-
-*Add new ideas below this section as they arise...*
\ No newline at end of file
DESIGN.md
@@ -1,286 +0,0 @@
-# MCP Design Document: Go Implementation
-
-**Project Name:** `mcp`
-**Target Repository:** `https://github.com/xlgmokha/mcp`
-**Primary Language:** Go
-**Structure:** Multi-command architecture using `cmd/` folder to organize multiple protocol servers.
-
----
-
-## ๐Ÿ“ Overall Project Structure
-
-```
-mcp/
-โ”œโ”€โ”€ go.mod
-โ”œโ”€โ”€ go.sum
-โ”œโ”€โ”€ README.md
-โ”œโ”€โ”€ internal/
-โ”‚   โ””โ”€โ”€ shared/          # Reusable logic/utilities across commands
-โ”œโ”€โ”€ pkg/
-โ”‚   โ””โ”€โ”€ mcp/             # Shared MCP protocol tooling (e.g., request/response structs)
-โ””โ”€โ”€ cmd/
-    โ”œโ”€โ”€ git/
-    โ”‚   โ”œโ”€โ”€ main.go
-    โ”‚   โ”œโ”€โ”€ handler.go
-    โ”‚   โ””โ”€โ”€ gitutils.go
-    โ”œโ”€โ”€ <future-server>/
-    โ”‚   โ””โ”€โ”€ main.go
-```
-
----
-
-## ๐Ÿš€ Phase 1: Implementing `cmd/git`
-
-### Objective
-
-Replicate the functionality of the [Python git server](https://github.com/modelcontextprotocol/servers/tree/main/src/git) from the MCP project in Go.
-
-### Features
-
-- Accept stdin JSON requests conforming to the MCP protocol.
-- Supported commands:
-  - `clone`: Clone a remote Git repository.
-  - `list`: List files in the repo (default to `HEAD`).
-  - `read`: Read file contents at specific paths.
-  - `head`: Show latest commit hash.
-- Return responses as JSON to stdout.
-- Print logs and errors to stderr.
-- Work as a CLI tool or JSON-RPC-compatible stdin/stdout process.
-
-### External Dependencies
-
-- `go-git` ([https://github.com/go-git/go-git](https://github.com/go-git/go-git)) for Git operations.
-- Optional: `spf13/cobra` if CLI argument parsing is needed in the future.
-
-### Internal Structure for Git Server
-
-```
-cmd/git/
-โ”œโ”€โ”€ main.go          # Entry point
-โ”œโ”€โ”€ handler.go       # Routes incoming MCP requests
-โ”œโ”€โ”€ gitutils.go      # Handles Git-specific operations
-```
-
-### JSON Message Format
-
-#### Request (stdin)
-```json
-{
-  "action": "list",
-  "repo": "https://github.com/modelcontextprotocol/servers",
-  "ref": "HEAD"
-}
-```
-
-#### Response (stdout)
-```json
-{
-  "status": "ok",
-  "files": ["README.md", "src/git/main.py"]
-}
-```
-
-#### Error (stdout)
-```json
-{
-  "status": "error",
-  "error": "failed to clone repository"
-}
-```
-
----
-
-## ๐Ÿง  Design Principles
-
-- **Modular Design**: Each server lives in `cmd/` as an isolated command.
-- **Stream IO**: Communicate via stdin/stdout with JSON.
-- **Minimal Dependencies**: Use Go standard library and `go-git`.
-- **Testable**: Extract core logic into testable units.
-
----
-
-## ๐Ÿ”ฎ Future Commands
-
-- `cmd/fs/main.go`: Local filesystem server.
-- `cmd/bash/main.go`: Bash command interpreter.
-- `cmd/sql/main.go`: SQL query server.
-- `cmd/http/main.go`: HTTP API interaction.
-- `cmd/gpt/main.go`: LLM invocation.
-- `cmd/py/main.go`: Python code runner.
-
----
-
-## โœ… Acceptance Criteria
-
-- `go run cmd/git/main.go` processes JSON stdin and performs Git tasks correctly.
-- Errors are written to stderr and returned as JSON.
-- Repo contains:
-  - `README.md` with instructions.
-  - Example JSON inputs/outputs.
-  - Template to add new MCP command servers under `cmd/`.
-
----
-
-## ๐Ÿ“ Bootstrap Code: Go MCP Git Server
-
-### `cmd/git/main.go`
-```go
-package main
-
-import (
-	"encoding/json"
-	"fmt"
-	"os"
-
-	"mcp/cmd/git"
-)
-
-func main() {
-	var req git.Request
-	decoder := json.NewDecoder(os.Stdin)
-	encoder := json.NewEncoder(os.Stdout)
-
-	if err := decoder.Decode(&req); err != nil {
-		fmt.Fprintf(os.Stderr, "decode error: %v\n", err)
-		encoder.Encode(git.ErrorResponse("invalid JSON input"))
-		return
-	}
-
-	resp := git.HandleRequest(req)
-	encoder.Encode(resp)
-}
-```
-
-### `cmd/git/handler.go`
-```go
-package git
-
-type Request struct {
-	Action string `json:"action"`
-	Repo   string `json:"repo"`
-	Path   string `json:"path,omitempty"`
-	Ref    string `json:"ref,omitempty"`
-}
-
-type Response map[string]interface{}
-
-func ErrorResponse(msg string) Response {
-	return Response{"status": "error", "error": msg}
-}
-
-func OkResponse(data map[string]interface{}) Response {
-	data["status"] = "ok"
-	return data
-}
-
-func HandleRequest(req Request) Response {
-	switch req.Action {
-	case "clone":
-		return handleClone(req)
-	case "list":
-		return handleList(req)
-	case "read":
-		return handleRead(req)
-	case "head":
-		return handleHead(req)
-	default:
-		return ErrorResponse("unsupported action")
-	}
-}
-```
-
-### `cmd/git/gitutils.go`
-```go
-package git
-
-import (
-	"fmt"
-	"io/ioutil"
-	"os"
-	"path/filepath"
-
-	gitlib "github.com/go-git/go-git/v5"
-)
-
-func cloneRepo(url string) (string, error) {
-	dir, err := ioutil.TempDir("", "mcp-git-")
-	if err != nil {
-		return "", err
-	}
-
-	_, err = gitlib.PlainClone(dir, false, &gitlib.CloneOptions{
-		URL:      url,
-		Progress: os.Stderr,
-	})
-	return dir, err
-}
-
-func handleClone(req Request) Response {
-	dir, err := cloneRepo(req.Repo)
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("clone failed: %v", err))
-	}
-	return OkResponse(map[string]interface{}{ "path": dir })
-}
-
-func handleList(req Request) Response {
-	dir, err := cloneRepo(req.Repo)
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("clone failed: %v", err))
-	}
-	defer os.RemoveAll(dir)
-
-	var files []string
-	err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
-		if err != nil {
-			return err
-		}
-		if !info.IsDir() {
-			rel, _ := filepath.Rel(dir, path)
-			files = append(files, rel)
-		}
-		return nil
-	})
-
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("walk failed: %v", err))
-	}
-
-	return OkResponse(map[string]interface{}{ "files": files })
-}
-
-func handleRead(req Request) Response {
-	dir, err := cloneRepo(req.Repo)
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("clone failed: %v", err))
-	}
-	defer os.RemoveAll(dir)
-
-	content, err := os.ReadFile(filepath.Join(dir, req.Path))
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("read failed: %v", err))
-	}
-
-	return OkResponse(map[string]interface{}{ "path": req.Path, "content": string(content) })
-}
-
-func handleHead(req Request) Response {
-	dir, err := cloneRepo(req.Repo)
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("clone failed: %v", err))
-	}
-	defer os.RemoveAll(dir)
-
-	r, err := gitlib.PlainOpen(dir)
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("open failed: %v", err))
-	}
-
-	h, err := r.Head()
-	if err != nil {
-		return ErrorResponse(fmt.Sprintf("head failed: %v", err))
-	}
-
-	return OkResponse(map[string]interface{}{ "ref": h.Name().String(), "hash": h.Hash().String() })
-}
-```
install.sh
@@ -1,203 +0,0 @@
-#!/bin/bash
-
-# Go MCP Servers Installation Script
-# Builds and installs all MCP servers for drop-in replacement
-
-set -e
-
-# Colors for output
-RED='\033[0;31m'
-GREEN='\033[0;32m'
-YELLOW='\033[1;33m'
-BLUE='\033[0;34m'
-NC='\033[0m' # No Color
-
-# Configuration
-INSTALL_DIR="${INSTALL_DIR:-/usr/local/bin}"
-BUILD_DIR="bin"
-SERVERS=("git" "filesystem" "fetch" "memory" "sequential-thinking" "time")
-
-# Print colored output
-print_status() {
-    echo -e "${BLUE}[INFO]${NC} $1"
-}
-
-print_success() {
-    echo -e "${GREEN}[SUCCESS]${NC} $1"
-}
-
-print_warning() {
-    echo -e "${YELLOW}[WARNING]${NC} $1"
-}
-
-print_error() {
-    echo -e "${RED}[ERROR]${NC} $1"
-}
-
-# Check if Go is installed
-check_go() {
-    if ! command -v go &> /dev/null; then
-        print_error "Go is not installed. Please install Go 1.21 or later."
-        print_status "Visit: https://golang.org/doc/install"
-        exit 1
-    fi
-    
-    go_version=$(go version | cut -d' ' -f3 | sed 's/go//')
-    print_status "Found Go version: $go_version"
-}
-
-# Check if we have write permissions to install directory
-check_permissions() {
-    if [[ ! -w "$INSTALL_DIR" ]]; then
-        print_warning "No write permission to $INSTALL_DIR"
-        print_status "You may need to run with sudo or set INSTALL_DIR to a writable location"
-        print_status "Example: INSTALL_DIR=~/.local/bin $0"
-        
-        if [[ $EUID -ne 0 ]]; then
-            print_status "Re-running with sudo..."
-            exec sudo "$0" "$@"
-        fi
-    fi
-}
-
-# Build all servers
-build_servers() {
-    print_status "Building MCP servers..."
-    
-    if ! make build; then
-        print_error "Build failed"
-        exit 1
-    fi
-    
-    print_success "All servers built successfully"
-}
-
-# Install servers
-install_servers() {
-    print_status "Installing servers to $INSTALL_DIR..."
-    
-    mkdir -p "$INSTALL_DIR"
-    
-    for server in "${SERVERS[@]}"; do
-        binary="$BUILD_DIR/mcp-$server"
-        target="$INSTALL_DIR/mcp-$server"
-        
-        if [[ ! -f "$binary" ]]; then
-            print_error "Binary $binary not found"
-            exit 1
-        fi
-        
-        cp "$binary" "$target"
-        chmod +x "$target"
-        print_status "Installed mcp-$server"
-    done
-    
-    print_success "All servers installed to $INSTALL_DIR"
-}
-
-# Test installations
-test_installation() {
-    print_status "Testing installations..."
-    
-    for server in "${SERVERS[@]}"; do
-        binary="$INSTALL_DIR/mcp-$server"
-        if [[ -x "$binary" ]]; then
-            print_success "โœ“ mcp-$server is executable"
-        else
-            print_error "โœ— mcp-$server is not executable"
-            exit 1
-        fi
-    done
-}
-
-# Show configuration example
-show_config() {
-    print_status "Installation complete!"
-    echo
-    print_status "Add these servers to your Claude Code configuration (~/.claude.json):"
-    echo
-    cat << 'EOF'
-{
-  "mcpServers": {
-    "git": {
-      "command": "mcp-git",
-      "args": ["--repository", "/path/to/your/repo"]
-    },
-    "filesystem": {
-      "command": "mcp-filesystem",
-      "args": ["/path/to/allowed/directory"]
-    },
-    "fetch": {
-      "command": "mcp-fetch"
-    },
-    "memory": {
-      "command": "mcp-memory"
-    },
-    "sequential-thinking": {
-      "command": "mcp-sequential-thinking"
-    },
-    "time": {
-      "command": "mcp-time"
-    }
-  }
-}
-EOF
-    echo
-    print_status "For more configuration options, see: README.md"
-}
-
-# Main installation flow
-main() {
-    echo "๐Ÿš€ Go MCP Servers Installation"
-    echo "=================================="
-    echo
-    
-    check_go
-    check_permissions
-    build_servers
-    install_servers
-    test_installation
-    show_config
-    
-    echo
-    print_success "๐ŸŽ‰ Installation completed successfully!"
-    print_status "Servers are installed in: $INSTALL_DIR"
-}
-
-# Handle command line arguments
-case "${1:-}" in
-    --help|-h)
-        echo "Go MCP Servers Installation Script"
-        echo
-        echo "Usage: $0 [OPTIONS]"
-        echo
-        echo "Options:"
-        echo "  --help, -h     Show this help message"
-        echo "  --uninstall    Uninstall MCP servers"
-        echo
-        echo "Environment Variables:"
-        echo "  INSTALL_DIR    Installation directory (default: /usr/local/bin)"
-        echo
-        echo "Examples:"
-        echo "  $0                           # Install to /usr/local/bin"
-        echo "  INSTALL_DIR=~/.local/bin $0  # Install to user directory"
-        exit 0
-        ;;
-    --uninstall)
-        print_status "Uninstalling MCP servers from $INSTALL_DIR..."
-        for server in "${SERVERS[@]}"; do
-            rm -f "$INSTALL_DIR/mcp-$server"
-            print_status "Removed mcp-$server"
-        done
-        print_success "Uninstall complete!"
-        exit 0
-        ;;
-    "")
-        main
-        ;;
-    *)
-        print_error "Unknown option: $1"
-        print_status "Use --help for usage information"
-        exit 1
-        ;;
-esac
\ No newline at end of file
Makefile
@@ -11,7 +11,7 @@ BINDIR = bin
 INSTALLDIR = /usr/local/bin
 
 # Server binaries
-SERVERS = git filesystem fetch memory sequential-thinking time maildir signal gitlab imap bash packages speech semantic
+SERVERS = git filesystem fetch memory sequential-thinking time maildir signal gitlab imap bash speech semantic
 BINARIES = $(addprefix $(BINDIR)/mcp-,$(SERVERS))
 
 # Build flags
@@ -110,7 +110,6 @@ signal: $(BINDIR)/mcp-signal ## Build signal server only
 gitlab: $(BINDIR)/mcp-gitlab ## Build gitlab server only
 imap: $(BINDIR)/mcp-imap ## Build imap server only
 bash: $(BINDIR)/mcp-bash ## Build bash server only
-packages: $(BINDIR)/mcp-packages ## Build packages server only
 speech: $(BINDIR)/mcp-speech ## Build speech server only
 semantic: $(BINDIR)/mcp-semantic ## Build semantic server only
 
@@ -120,4 +119,4 @@ help: ## Show this help message
 	@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "  \033[36m%-20s\033[0m %s\n", $$1, $$2}'
 	@echo ""
 	@echo "Individual servers:"
-	@echo "  git, filesystem, fetch, memory, sequential-thinking, time, maildir, signal, gitlab, imap, bash, packages, speech, semantic"
+	@echo "  git, filesystem, fetch, memory, sequential-thinking, time, maildir, signal, gitlab, imap, bash, speech, semantic"
PLAN.md
@@ -1,199 +0,0 @@
-# MCP Go Implementation Enhancement Plan
-
-This plan tracks the implementation of advanced features to achieve better feature parity with the reference MCP implementations.
-
-## Status Legend
-- โŒ Not Started
-- ๐ŸŸก In Progress  
-- โœ… Completed
-- ๐Ÿšซ Blocked
-
----
-
-## Phase 1: Advanced HTML Processing โœ…
-
-**Goal**: Improve content extraction quality in fetch server
-
-### Tasks:
-- [x] Add goquery dependency (`go get github.com/PuerkitoBio/goquery`)
-- [x] Add html-to-markdown dependency (`go get github.com/JohannesKaufmann/html-to-markdown`)
-- [x] Create `pkg/htmlprocessor/processor.go` with ContentExtractor
-- [x] Implement `ExtractReadableContent()` method using goquery
-- [x] Implement `ToMarkdown()` method with better conversion
-- [x] Update `cmd/fetch/main.go` to use new HTML processor
-- [x] Test with various HTML content types
-
-### Files Created/Modified:
-- โœ… `pkg/htmlprocessor/processor.go` (new)
-- โœ… `pkg/htmlprocessor/processor_test.go` (new) 
-- โœ… `pkg/fetch/server.go` (modified to use new processor)
-- โœ… `go.mod` (dependencies added)
-
-### Results:
-- Significantly improved HTML content extraction
-- Better markdown conversion with proper formatting
-- Automatic filtering of ads, navigation, scripts, styles
-- Comprehensive test coverage
-- 137 lines of old HTML processing code removed and replaced with 13 lines using new processor
-
----
-
-## Phase 2: Prompts Support โœ…
-
-**Goal**: Enable interactive prompts across servers
-
-### Tasks:
-- [x] Create `pkg/mcp/prompts.go` with base prompt structures
-- [x] Add prompt support to `pkg/mcp/server.go` BaseServer
-- [x] Implement `list_prompts` method in BaseServer
-- [x] Implement `get_prompt` method in BaseServer
-- [x] Add `fetch` prompt to fetch server for manual URL entry
-- [x] Add `commit-message` prompt to git server
-- [x] Add `edit-file` prompt to filesystem server
-- [x] Test prompt functionality with sample clients
-
-### Files Created/Modified:
-- โœ… `pkg/mcp/prompts_test.go` (new - comprehensive tests)
-- โœ… `pkg/mcp/server_prompts_test.go` (new - server prompt tests)
-- โœ… `pkg/mcp/server.go` (modified - added prompt infrastructure)
-- โœ… `pkg/fetch/server.go` (modified - added fetch prompt)
-- โœ… `pkg/git/server.go` (modified - added commit-message prompt)
-- โœ… `pkg/filesystem/server.go` (modified - added edit-file prompt)
-
-### Results:
-- **3 Interactive Prompts** implemented across all servers
-- **Complete MCP protocol compliance** for prompts capability
-- **Comprehensive error handling** and argument validation
-- **User/assistant conversation flows** for guided interactions
-- **Security validation** for filesystem prompts (allowed directories)
-
-### Prompts Implemented:
-1. **fetch**: Interactive URL entry with optional reason context
-2. **commit-message**: Conventional commit format guidance with breaking change support  
-3. **edit-file**: Step-by-step file editing workflow with security validation
-
----
-
-## Phase 3: Resources Support โœ…
-
-**Goal**: Enable resource discovery and access
-
-### Tasks:
-- [x] Create `pkg/mcp/resources.go` with Resource structures
-- [x] Add ResourceHandler interface and support to BaseServer
-- [x] Implement `list_resources` method in BaseServer
-- [x] Implement `read_resource` method in BaseServer
-- [x] Add `file://` resource support to filesystem server
-- [x] Add `git://` resource support to git server
-- [x] Add `memory://` resource support to memory server
-- [x] Test resource discovery and access
-
-### Files Created/Modified:
-- โœ… `pkg/mcp/resources.go` (new - Resource structures and types)
-- โœ… `pkg/mcp/resources_test.go` (new - comprehensive resource tests)
-- โœ… `pkg/mcp/server_resources_test.go` (new - server resource tests)
-- โœ… `pkg/mcp/server.go` (modified - added resource infrastructure)
-- โœ… `pkg/filesystem/server.go` (modified - added file:// resources)
-- โœ… `pkg/git/server.go` (modified - added git:// resources)
-- โœ… `pkg/memory/server.go` (modified - added memory:// resources and knowledge-query prompt)
-
-### Results:
-- **Complete MCP Resources capability** implemented with 3 URI schemes
-- **file:// resources**: Automatic discovery of files in allowed directories with MIME type detection
-- **git:// resources**: Repository files, branches, and commits accessible as resources
-- **memory:// resources**: Knowledge graph entities and relations exposed as JSON resources
-- **Thread-safe implementation** with proper mutex locking
-- **Security validation** for all resource access
-- **Comprehensive test coverage** including invalid URI handling
-
-### Resources Implemented:
-1. **file:// scheme**: Direct filesystem access with security validation
-2. **git:// scheme**: Repository browsing including files, branches, and commits
-3. **memory:// scheme**: Knowledge graph exploration with entity and relation access
-
----
-
-## Phase 4: Roots Support โœ…
-
-**Goal**: Enable root capability negotiation
-
-### Tasks:
-- [x] Create `pkg/mcp/roots.go` with Root structures
-- [x] Add roots support to BaseServer
-- [x] Implement `list_roots` method in BaseServer
-- [x] Add filesystem root configuration
-- [x] Add git repository root discovery
-- [x] Add memory graph root support
-- [x] Test root capability negotiation
-- [x] Update server initialization to announce roots
-
-### Files Created/Modified:
-- โœ… `pkg/mcp/roots.go` (new - Root helper functions and interfaces)
-- โœ… `pkg/mcp/roots_test.go` (new - comprehensive root tests)
-- โœ… `pkg/mcp/roots_integration_test.go` (new - integration tests across all servers)
-- โœ… `pkg/mcp/server_roots_test.go` (new - server root functionality tests)
-- โœ… `pkg/mcp/types.go` (modified - added Roots to ServerCapabilities)
-- โœ… `pkg/mcp/server.go` (modified - added complete roots infrastructure)
-- โœ… `pkg/filesystem/server.go` (modified - added filesystem root registration)
-- โœ… `pkg/git/server.go` (modified - added git repository root registration)
-- โœ… `pkg/memory/server.go` (modified - added memory graph root registration with dynamic updates)
-
-### Results:
-- **Complete MCP Roots capability** implemented with automatic discovery
-- **Filesystem roots**: Each allowed directory registered as `file://` root with user-friendly names
-- **Git repository roots**: Repository path and current branch info exposed as `git://` root
-- **Memory graph roots**: Knowledge graph statistics exposed as `memory://` root with live updates
-- **Dynamic root updates**: Memory server automatically updates root info when graph changes
-- **Thread-safe implementation** with proper mutex locking for concurrent access
-- **Comprehensive testing** including integration tests and concurrency tests
-
-### Roots Implemented:
-1. **file:// scheme**: Directory-based roots for filesystem access points
-2. **git:// scheme**: Repository-based roots with branch information
-3. **memory:// scheme**: Knowledge graph roots with live entity/relation counts
-
----
-
-## Implementation Notes
-
-### Dependencies to Add:
-```bash
-go get github.com/PuerkitoBio/goquery
-go get github.com/JohannesKaufmann/html-to-markdown
-```
-
-### Key Design Principles:
-1. Maintain backward compatibility with existing tools
-2. Follow existing code patterns in the codebase
-3. Add proper error handling for all new features
-4. Keep JSON-RPC protocol compliance
-5. Test each phase independently
-
-### Testing Strategy:
-- Unit tests for each new package
-- Integration tests with sample MCP clients
-- Manual testing with reference implementations
-- Regression testing of existing functionality
-
----
-
-## Progress Tracking
-
-**Overall Progress**: 4/4 phases completed (100%) ๐ŸŽ‰
-
-**Last Updated**: Phase 4 completed - Roots Support fully implemented and tested
-**Status**: โœ… COMPLETE - All planned MCP enhancements successfully implemented
-**Achievement**: Full feature parity with reference implementations for the requested capabilities (1.3, 2.1, 2.2, 2.3)
-
----
-
-## Notes for Continuation
-
-If Claude Code needs to be restarted:
-1. Check this PLAN.md for current progress status
-2. Review the "Last Updated" section for context
-3. Continue from the current phase's next unchecked task
-4. Update progress markers (โŒ/๐ŸŸก/โœ…) as work completes
-5. Update "Last Updated" section with current status
-
-This plan focuses on the core MCP protocol enhancements that will make the Go implementation a powerful development tool, skipping enterprise features like robots.txt compliance and proxy support that aren't needed for personal development productivity.
\ No newline at end of file
README.md
@@ -4,30 +4,6 @@
 
 A pure Go implementation of Model Context Protocol (MCP) servers, providing drop-in replacements for the Python MCP servers with zero dependencies and static linking.
 
-## Features
-
-- **Zero Dependencies**: Statically linked binaries with no runtime dependencies
-- **Drop-in Replacement**: Compatible with existing Python MCP server configurations
-- **Test-Driven Development**: Comprehensive test coverage for all servers
-- **Security First**: Built-in access controls and validation
-- **High Performance**: Native Go performance and concurrency
-
-## Available Servers
-
-| Server                  | Description                                                | Status   |
-| ------                  | -----------                                                | ------   |
-| **fetch**               | Web content fetching with HTML to Markdown conversion      | Complete |
-| **filesystem**          | Secure file operations with access controls                | Complete |
-| **git**                 | Git repository operations (status, add, commit, log, etc.) | Complete |
-| **imap**                | IMAP email server connectivity for Gmail, Migadu, etc.    | Complete |
-| **maildir**             | Email archive analysis for maildir format                 | Complete |
-| **memory**              | Knowledge graph persistent memory system                   | Complete |
-| **packages**            | Package management for Cargo, Homebrew, and more          | Complete |
-| **sequential-thinking** | Dynamic problem-solving with thought sequences             | Complete |
-| **signal**              | Signal Desktop database access with encrypted SQLCipher   | Complete |
-| **speech**              | Text-to-speech synthesis using macOS say command          | Complete |
-| **time**                | Time and timezone conversion utilities                     | Complete |
-
 ## Quick Start
 
 ### Installation
@@ -44,279 +20,6 @@ make build
 sudo make install
 ```
 
-### Binary Releases
-
-Pre-built binaries will be available from the [releases page](https://github.com/xlgmokha/mcp/releases) once the first release is published.
-
-### Claude Code Configuration
-
-Replace Python MCP servers in your `~/.claude.json` configuration:
-
-```json
-{
-  "mcpServers": {
-    "git": {
-      "command": "mcp-git"
-    },
-    "filesystem": {
-      "command": "mcp-filesystem"
-    },
-    "fetch": {
-      "command": "mcp-fetch"
-    },
-    "imap": {
-      "command": "mcp-imap",
-      "args": ["--server", "imap.gmail.com", "--username", "user@gmail.com", "--password", "app-password"]
-    },
-    "memory": {
-      "command": "mcp-memory"
-    },
-    "packages": {
-      "command": "mcp-packages"
-    },
-    "sequential-thinking": {
-      "command": "mcp-sequential-thinking"
-    },
-    "speech": {
-      "command": "mcp-speech"
-    },
-    "time": {
-      "command": "mcp-time"
-    }
-  }
-}
-```
-
-## Server Documentation
-
-### Git Server (`mcp-git`)
-
-Provides Git repository operations with safety checks.
-
-**Tools:**
-- `git_status` - Show repository status
-- `git_add` - Stage files for commit
-- `git_commit` - Create commits
-- `git_log` - View commit history
-- `git_diff` - Show differences
-- `git_show` - Show commit details
-
-**Usage:**
-```bash
-# Git server runs without additional arguments
-# Repository path is specified per-tool call
-mcp-git
-```
-
-### Filesystem Server (`mcp-filesystem`)
-
-Secure file operations with configurable access controls.
-
-**Tools:**
-- `read_file` - Read file contents
-- `write_file` - Write file contents
-- `edit_file` - Edit files with line-based operations
-- `list_directory` - List directory contents
-- `create_directory` - Create directories
-- `move_file` - Move/rename files
-- `search_files` - Search for files by pattern
-
-**Usage:**
-```bash
-# Filesystem server runs without additional arguments
-# Allowed paths are validated per-tool call
-mcp-filesystem
-```
-
-### Fetch Server (`mcp-fetch`)
-
-Web content fetching with intelligent HTML to Markdown conversion.
-
-**Tools:**
-- `fetch` - Fetch and convert web content
-
-**Features:**
-- Automatic HTML to Markdown conversion
-- Content truncation and pagination
-- Raw HTML mode
-- Custom User-Agent headers
-
-**Usage:**
-```bash
-mcp-fetch
-```
-
-### IMAP Server (`mcp-imap`)
-
-Connect to IMAP email servers like Gmail, Migadu, and other providers for email management.
-
-**Tools:**
-- `imap_list_folders` - List all IMAP folders (INBOX, Sent, Drafts, etc.)
-- `imap_list_messages` - List messages in folder with pagination
-- `imap_read_message` - Read full message content with headers and body
-- `imap_search_messages` - Search messages by content, sender, subject
-- `imap_get_folder_stats` - Get folder statistics (total, unread, recent)
-- `imap_mark_as_read` - Mark messages as read/unread
-- `imap_get_attachments` - List message attachments
-- `imap_get_connection_info` - Server connection status and capabilities
-- `imap_delete_message` - Delete single message (requires confirmation)
-- `imap_delete_messages` - Delete multiple messages (requires confirmation)
-- `imap_move_to_trash` - Move messages to trash folder (safe delete)
-- `imap_expunge_folder` - Permanently remove deleted messages
-
-**Prompts:**
-- `email-analysis` - AI-powered email content analysis
-- `email-search` - Contextual email search with AI insights
-
-**Features:**
-- Multi-provider support (Gmail, Migadu, generic IMAP)
-- Secure TLS/SSL encryption by default
-- Environment variable credential management
-- Thread-safe connection handling
-- Rich message parsing and formatting
-- Safe email deletion with confirmation prompts
-- Move to trash vs permanent delete options
-- Bulk operations for managing multiple messages
-
-**Usage:**
-```bash
-# Gmail with app password
-mcp-imap --server imap.gmail.com --username user@gmail.com --password app-password
-
-# Migadu
-mcp-imap --server mail.migadu.com --username user@domain.com --password password
-
-# Environment variables
-export IMAP_SERVER=imap.gmail.com
-export IMAP_USERNAME=user@gmail.com
-export IMAP_PASSWORD=app-password
-mcp-imap
-```
-
-### Memory Server (`mcp-memory`)
-
-Persistent knowledge graph for maintaining context across sessions.
-
-**Tools:**
-- `create_entities` - Create entities in the knowledge graph
-- `create_relations` - Create relationships between entities
-- `add_observations` - Add observations to entities
-- `read_graph` - Read the entire knowledge graph
-- `search_nodes` - Search entities and observations
-- `open_nodes` - Retrieve specific entities
-- `delete_entities` - Delete entities and their relations
-- `delete_observations` - Delete specific observations
-- `delete_relations` - Delete relationships
-
-**Usage:**
-```bash
-# Uses ~/.mcp_memory.json by default
-mcp-memory
-
-# Custom memory file
-MEMORY_FILE=/path/to/memory.json mcp-memory
-```
-
-### Sequential Thinking Server (`mcp-sequential-thinking`)
-
-Dynamic problem-solving through structured thought sequences with persistent session management.
-
-**Tools:**
-- `sequentialthinking` - Process thoughts with session persistence and branching
-- `get_session_history` - Retrieve complete session thought history
-- `list_sessions` - List all active thinking sessions  
-- `get_branch_history` - Get thought history for specific branches
-- `clear_session` - Clear completed sessions and branches
-
-**Features:**
-- **Session Persistence**: Sessions survive server restarts with JSON storage
-- **Branch Tracking**: Create alternative reasoning paths from any thought
-- **Revision Support**: Mark and track thought revisions with context
-- **Progress Indicators**: Visual progress bars and status tracking
-- **Solution Extraction**: Automatic extraction from final thoughts
-- **Thread-Safe Operations**: Concurrent access to sessions and branches
-
-**Usage:**
-```bash
-# Without persistence (sessions lost on restart)
-mcp-sequential-thinking
-
-# With session persistence
-mcp-sequential-thinking --session-file /path/to/sessions.json
-```
-
-### Packages Server (`mcp-packages`)
-
-Package management tools for multiple ecosystems including Rust (Cargo) and macOS (Homebrew).
-
-**Cargo Tools:**
-- `cargo_build` - Build Rust projects with optional flags
-- `cargo_run` - Run Rust applications with arguments  
-- `cargo_test` - Execute test suites with filtering
-- `cargo_add` - Add dependencies to Cargo.toml
-- `cargo_update` - Update dependencies to latest versions
-- `cargo_check` - Quick compile check without building
-- `cargo_clippy` - Run Rust linter with suggestions
-
-**Homebrew Tools:**
-- `brew_install` - Install packages or casks
-- `brew_uninstall` - Remove packages or casks
-- `brew_search` - Search for available packages
-- `brew_update` - Update Homebrew itself
-- `brew_upgrade` - Upgrade installed packages
-- `brew_doctor` - Check system health
-- `brew_list` - List installed packages
-
-**Cross-Platform Tools:**
-- `check_vulnerabilities` - Scan for security vulnerabilities
-- `outdated_packages` - Find packages needing updates
-- `package_info` - Get detailed package information
-
-**Usage:**
-```bash
-mcp-packages
-```
-
-### Speech Server (`mcp-speech`)
-
-Text-to-speech synthesis using the macOS `say` command. Enables LLMs to speak their responses with customizable voices and settings.
-
-**Tools:**
-- `say` - Speak text with customizable voice, rate, and volume
-- `list_voices` - List all available system voices (100+ voices)
-- `speak_file` - Read and speak the contents of a text file
-- `stop_speech` - Stop any currently playing speech synthesis
-- `speech_settings` - Get detailed information about speech options
-
-**Features:**
-- Voice selection from 100+ built-in voices (Alex, Samantha, Victoria, etc.)
-- Adjustable speech rate (80-500 words per minute)
-- Volume control (0.0 to 1.0)
-- Audio file output (.wav, .aiff, .m4a formats)
-- File reading with line limits
-- Instant speech control and interruption
-
-**Usage:**
-```bash
-mcp-speech
-```
-
-**Requirements:**
-- macOS (uses the built-in `say` command)
-
-### Time Server (`mcp-time`)
-
-Time and timezone utilities for temporal operations.
-
-**Tools:**
-- `get_current_time` - Get current time in specified timezone
-- `convert_time` - Convert between timezones
-
-**Usage:**
-```bash
-mcp-time
-```
-
 ## Development
 
 ### Prerequisites
@@ -328,14 +31,6 @@ mcp-time
 
 ```bash
 # Build all servers
-go build -o bin/mcp-git ./cmd/git
-go build -o bin/mcp-filesystem ./cmd/filesystem
-go build -o bin/mcp-fetch ./cmd/fetch
-go build -o bin/mcp-memory ./cmd/memory
-go build -o bin/mcp-sequential-thinking ./cmd/sequential-thinking
-go build -o bin/mcp-time ./cmd/time
-
-# Or use make
 make build
 ```
 
@@ -343,10 +38,10 @@ make build
 
 ```bash
 # Run all tests
-go test ./...
+make test
 
 # Run tests with coverage
-go test -cover ./...
+make test-coverage
 
 # Run specific server tests
 go test ./pkg/git/...
@@ -371,33 +66,9 @@ gofmt -l .
 4. Update this README
 5. Add build targets to Makefile
 
-## Architecture
-
-The project follows a clean, modular architecture with:
-
-- **`pkg/mcp/`**: Core MCP protocol implementation and shared utilities
-- **`pkg/*/`**: Individual MCP server implementations (reusable packages)
-  - `pkg/git/`: Git operations server
-  - `pkg/filesystem/`: Filesystem operations server  
-  - `pkg/fetch/`: Web content fetching server
-  - `pkg/memory/`: Knowledge graph memory server
-  - `pkg/thinking/`: Sequential thinking server
-  - `pkg/time/`: Time and timezone server
-- **`cmd/*/`**: Minimal CLI entry points (thin wrappers around packages)
-- **Standard Go project layout** with clear separation of concerns
-- **Test-driven development** ensuring reliability
-
-Each server is a standalone binary that communicates via JSON-RPC over stdin/stdout, following the MCP specification. The modular package structure allows for easy reuse and testing of server implementations.
-
-## Performance
-
-Benchmarks comparing to Python implementations (approximate):
-
-| Metric | Python | Go | Improvement |
-|--------|--------|----| ----------- |
-| Memory Usage | ~50MB | ~8MB | 6x less |
-| Startup Time | ~200ms | ~5ms | 40x faster |
-| Binary Size | N/A | ~15MB | Single binary |
+Each server is a standalone binary that communicates via JSON-RPC over
+stdin/stdout, following the MCP specification. The modular package
+structure allows for easy reuse and testing of server implementations.
 
 ## License
 
@@ -417,5 +88,4 @@ Quick start:
 ## Acknowledgments
 
 - Inspired by the [Python MCP servers](https://github.com/modelcontextprotocol/servers)
-- Built for compatibility with [Claude Code](https://claude.ai/code)
 - Follows the [Model Context Protocol](https://modelcontextprotocol.io/) specification
SIGNAL_DEMO.md
@@ -1,160 +0,0 @@
-# Signal MCP Server Examples
-
-## ๐ŸŽฏ **What the Signal MCP Server Can Do**
-
-Based on your christine project's proven Signal database access, here are examples of what the Signal MCP server provides:
-
-### **1. Database Statistics**
-```bash
-echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/call", "params": {"name": "signal_get_stats", "arguments": {}}}' | mcp-signal
-```
-
-**Example Output:**
-```json
-{
-  "jsonrpc": "2.0",
-  "id": 1,
-  "result": {
-    "content": [{
-      "type": "text",
-      "text": "Signal Database Statistics:\n- Total Messages: 23,847\n- Total Conversations: 156\n- Database Path: /Users/xlgmokha/Library/Application Support/Signal/sql/db.sqlite"
-    }],
-    "isError": false
-  }
-}
-```
-
-### **2. List Conversations**
-```bash
-echo '{"jsonrpc": "2.0", "id": 2, "method": "tools/call", "params": {"name": "signal_list_conversations", "arguments": {}}}' | mcp-signal
-```
-
-**Example Output:**
-```json
-{
-  "jsonrpc": "2.0", 
-  "id": 2,
-  "result": {
-    "content": [{
-      "type": "text",
-      "text": "Found 156 conversations:\n\n1. Adia Khan (+15876643389)\n2. Allison Khan (+14036143389)\n3. Better ppl of the fam (Group)\n4. Christine Michaels-Igbokwe (+14035854029)\n5. Cyberdelia - Personal Disaster (Group)\n..."
-    }],
-    "isError": false
-  }
-}
-```
-
-### **3. Search Messages**
-```bash
-echo '{"jsonrpc": "2.0", "id": 3, "method": "tools/call", "params": {"name": "signal_search_messages", "arguments": {"query": "dinner", "limit": "5"}}}' | mcp-signal
-```
-
-**Example Output:**
-```json
-{
-  "jsonrpc": "2.0",
-  "id": 3, 
-  "result": {
-    "content": [{
-      "type": "text",
-      "text": "Found 23 messages containing 'dinner':\n\n**Adia Khan** (2024-06-15 18:30)\nWant to grab dinner tonight?\n---\n**Christine Michaels-Igbokwe** (2024-06-10 12:15)\nDinner at 7pm works for me\n---\n**Better ppl of the fam** (2024-06-08 16:45)\nFamily dinner this Sunday?\n---"
-    }],
-    "isError": false
-  }
-}
-```
-
-### **4. Get Conversation History**
-```bash
-echo '{"jsonrpc": "2.0", "id": 4, "method": "tools/call", "params": {"name": "signal_get_conversation", "arguments": {"conversation_id": "uuid-of-adia-conversation", "limit": "10"}}}' | mcp-signal
-```
-
-**Example Output:**
-```json
-{
-  "jsonrpc": "2.0",
-  "id": 4,
-  "result": {
-    "content": [{
-      "type": "text", 
-      "text": "Conversation with Adia Khan (10 recent messages):\n\n2024-06-23 14:22: Hey! How was your day?\n2024-06-23 14:18: Just finished work, heading home\n2024-06-22 19:45: Thanks for the photos! [1 attachment]\n2024-06-22 18:30: See you tomorrow\n2024-06-21 20:15: Movie was great, we should go again\n..."
-    }],
-    "isError": false
-  }
-}
-```
-
-### **5. Contact Lookup**
-```bash
-echo '{"jsonrpc": "2.0", "id": 5, "method": "tools/call", "params": {"name": "signal_search_messages", "arguments": {"query": "Adia", "limit": "1"}}}' | mcp-signal
-```
-
-## ๐Ÿ” **Real Data from Your Signal Database** 
-
-From your actual Signal contacts (based on christine project extraction):
-
-- **Adia Khan**: +15876643389
-- **Allison Khan**: +14036143389  
-- **Christine Michaels-Igbokwe**: +14035854029
-- **Better ppl of the fam**: Group conversation
-- **Cyberdelia - Personal Disaster**: Group conversation
-- **GitLab Calgary lunch group**: Group conversation
-- And 150+ more contacts and groups
-
-## ๐Ÿ›  **Database Access Method**
-
-The Signal MCP server uses the same proven approach as your christine project:
-
-1. **Automatic Signal Detection**: Finds Signal installation across platforms
-2. **Key Decryption**: Handles macOS keychain and Linux credential stores
-3. **SQLCipher Integration**: Uses command-line sqlcipher for reliable database access
-4. **Schema Knowledge**: Direct access to conversations, messages, and attachments tables
-
-## ๐Ÿ“‹ **Schema Structure**
-
-The server understands Signal's database schema:
-
-```sql
--- Conversations table
-SELECT id, name, profileName, e164, type FROM conversations;
-
--- Messages table  
-SELECT conversationId, type, json, body, sourceServiceId, sent_at 
-FROM messages 
-WHERE type NOT IN ('keychange', 'profile-change');
-
--- Rich message data in JSON column includes:
--- - attachments[]
--- - reactions[]  
--- - quote{}
--- - sticker{}
-```
-
-## ๐Ÿ” **Security Features**
-
-- **Read-only access**: No modification of Signal data
-- **Encrypted database support**: Full SQLCipher compatibility
-- **Keychain integration**: Secure key retrieval from system stores
-- **Signal-app-closed requirement**: Prevents database conflicts
-
-## ๐ŸŽ‰ **Integration Ready**
-
-Add to your Claude configuration:
-
-```json
-{
-  "mcpServers": {
-    "signal": {
-      "command": "/usr/local/bin/mcp-signal"
-    }
-  }
-}
-```
-
-Then ask Claude things like:
-- "What was my last conversation with Adia?"
-- "Search my Signal messages for 'dinner plans'"  
-- "How many Signal conversations do I have?"
-- "Show me all group conversations"
-
-The Signal MCP server provides the same comprehensive database access as your proven christine project, but exposed through the MCP protocol for seamless AI assistant integration!
\ No newline at end of file