From a46615abb0e43ced0e06686f3913bdbfed9dc86b Mon Sep 17 00:00:00 2001 From: Ed Zynda Date: Tue, 24 Jun 2025 17:50:18 +0300 Subject: [PATCH] cleanup --- example-config.yml | 46 --------------- internal/config/config.go | 51 ++++++++++++---- internal/config/config_test.go | 104 +++++++++++++++++++++++++++++++++ 3 files changed, 143 insertions(+), 58 deletions(-) delete mode 100644 example-config.yml diff --git a/example-config.yml b/example-config.yml deleted file mode 100644 index 84a38e71..00000000 --- a/example-config.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Example MCPHost Configuration with Simplified Schema -# This demonstrates the new simplified local/remote/builtin server configuration - -mcpServers: - # Local MCP server - runs a command locally - filesystem-local: - type: "local" - command: ["npx", "@modelcontextprotocol/server-filesystem", "/tmp"] - environment: - DEBUG: "true" - LOG_LEVEL: "info" - - # Builtin MCP server - runs in-process for optimal performance - filesystem-builtin: - type: "builtin" - name: "fs" - options: - allowed_directories: ["/tmp", "/home/user/documents"] - allowedTools: ["read_file", "write_file", "list_directory"] - - # Minimal builtin server - defaults to current working directory - filesystem-cwd: - type: "builtin" - name: "fs" - - # Another local server with different command - sqlite: - type: "local" - command: ["uvx", "mcp-server-sqlite", "--db-path", "/tmp/example.db"] - environment: - SQLITE_DEBUG: "1" - - # Remote MCP server - connects via StreamableHTTP - websearch: - type: "remote" - url: "https://api.example.com/mcp" - - # Another remote server - weather: - type: "remote" - url: "https://weather-mcp.example.com" - -# Application settings -model: "anthropic:claude-sonnet-4-20250514" -max-steps: 10 -debug: false \ No newline at end of file diff --git a/internal/config/config.go b/internal/config/config.go index 60118fd7..4abdb275 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -222,39 +222,66 @@ func createDefaultConfig(homeDir string) error { } defer file.Close() - // Write a clean YAML template + // Write a comprehensive YAML template with examples content := `# MCPHost Configuration File # All command-line flags can be configured here +# This demonstrates the simplified local/remote/builtin server configuration # MCP Servers configuration # Add your MCP servers here # Examples for different server types: # mcpServers: -# # Local servers - run commands locally -# filesystem: +# # Local MCP servers - run commands locally via stdio transport +# filesystem-local: # type: "local" -# command: ["npx", "@modelcontextprotocol/server-filesystem", "/path/to/allowed/files"] +# command: ["npx", "@modelcontextprotocol/server-filesystem", "/tmp"] # environment: -# MY_ENV_VAR: "my_env_var_value" -# sqlite: -# type: "local" -# command: ["uvx", "mcp-server-sqlite", "--db-path", "/tmp/example.db"] +# DEBUG: "true" +# LOG_LEVEL: "info" # -# # Builtin servers - run in-process for optimal performance +# sqlite: +# type: "local" +# command: ["uvx", "mcp-server-sqlite", "--db-path", "/tmp/example.db"] +# environment: +# SQLITE_DEBUG: "1" +# +# # Builtin MCP servers - run in-process for optimal performance # filesystem-builtin: # type: "builtin" # name: "fs" # options: # allowed_directories: ["/tmp", "/home/user/documents"] +# allowedTools: ["read_file", "write_file", "list_directory"] +# +# # Minimal builtin server - defaults to current working directory # filesystem-cwd: # type: "builtin" -# name: "fs" # Defaults to current working directory if no options specified +# name: "fs" # -# # Remote servers - connect via StreamableHTTP +# # Bash server for shell commands +# bash: +# type: "builtin" +# name: "bash" +# +# # Todo server for task management +# todo: +# type: "builtin" +# name: "todo" +# +# # Fetch server for web content +# fetch: +# type: "builtin" +# name: "fetch" +# +# # Remote MCP servers - connect via StreamableHTTP transport # websearch: # type: "remote" # url: "https://api.example.com/mcp" # +# weather: +# type: "remote" +# url: "https://weather-mcp.example.com" +# # # Legacy format still supported for backward compatibility: # # legacy-server: # # command: npx @@ -266,7 +293,7 @@ mcpServers: # Application settings (all optional) # model: "anthropic:claude-sonnet-4-20250514" # Default model to use -# max-steps: 20 # Maximum agent steps (0 for unlimited) +# max-steps: 10 # Maximum agent steps (0 for unlimited) # debug: false # Enable debug logging # system-prompt: "/path/to/system-prompt.txt" # System prompt text file diff --git a/internal/config/config_test.go b/internal/config/config_test.go index f8825fd3..9b9173e0 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -2,6 +2,9 @@ package config import ( "encoding/json" + "os" + "path/filepath" + "strings" "testing" ) @@ -196,3 +199,104 @@ func TestConfig_Validate(t *testing.T) { t.Errorf("Validation failed: %v", err) } } + +func TestEnsureConfigExists(t *testing.T) { + // Create a temporary directory for testing + tempDir, err := os.MkdirTemp("", "mcphost_config_test") + if err != nil { + t.Fatalf("Error creating temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + // Set HOME to temp directory + oldHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", oldHome) + + // Test config creation + err = EnsureConfigExists() + if err != nil { + t.Fatalf("Error creating config: %v", err) + } + + // Verify the config file was created + configPath := filepath.Join(tempDir, ".mcphost.yml") + if _, err := os.Stat(configPath); os.IsNotExist(err) { + t.Fatalf("Config file was not created at %s", configPath) + } + + // Read and verify the content + content, err := os.ReadFile(configPath) + if err != nil { + t.Fatalf("Error reading config: %v", err) + } + + contentStr := string(content) + + // Verify it contains the expected sections + expectedSections := []string{ + "# MCPHost Configuration File", + "mcpServers:", + "# Local MCP servers", + "# Builtin MCP servers", + "# Remote MCP servers", + "filesystem-builtin:", + "bash:", + "todo:", + "fetch:", + "type: \"builtin\"", + "type: \"local\"", + "type: \"remote\"", + "# Application settings", + "# Model generation parameters", + } + + for _, expected := range expectedSections { + if !strings.Contains(contentStr, expected) { + t.Errorf("Config content missing expected section: %s", expected) + } + } + + // Verify it's valid YAML structure (basic check) + if !strings.Contains(contentStr, "mcpServers:") { + t.Error("Config should contain mcpServers section") + } +} + +func TestEnsureConfigExistsWhenFileExists(t *testing.T) { + // Create a temporary directory for testing + tempDir, err := os.MkdirTemp("", "mcphost_config_test") + if err != nil { + t.Fatalf("Error creating temp dir: %v", err) + } + defer os.RemoveAll(tempDir) + + // Set HOME to temp directory + oldHome := os.Getenv("HOME") + os.Setenv("HOME", tempDir) + defer os.Setenv("HOME", oldHome) + + // Create an existing config file + configPath := filepath.Join(tempDir, ".mcphost.yml") + existingContent := "# Existing config\nmcpServers:\n test: {}\n" + err = os.WriteFile(configPath, []byte(existingContent), 0644) + if err != nil { + t.Fatalf("Error creating existing config: %v", err) + } + + // Test that EnsureConfigExists doesn't overwrite + err = EnsureConfigExists() + if err != nil { + t.Fatalf("Error in EnsureConfigExists: %v", err) + } + + // Verify the content wasn't changed + content, err := os.ReadFile(configPath) + if err != nil { + t.Fatalf("Error reading config: %v", err) + } + + if string(content) != existingContent { + t.Error("Existing config file was modified when it shouldn't have been") + } +}