This commit is contained in:
Ed Zynda
2025-06-24 17:50:18 +03:00
parent 3fcee53836
commit a46615abb0
3 changed files with 143 additions and 58 deletions
-46
View File
@@ -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
+39 -12
View File
@@ -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
+104
View File
@@ -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")
}
}