Commit 8d76079

mo khan <mo@mokhan.ca>
2025-08-16 14:27:57
refactor: provide roots to ctor
1 parent 56c7693
pkg/bash/server.go
@@ -91,7 +91,7 @@ func NewBashServer(config *Config) (*BashServer, error) {
 	}
 
 	// Create base MCP server
-	baseServer := mcp.NewServer("bash", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("bash", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 	
 	server := &BashServer{
 		Server:         baseServer,
pkg/fetch/server.go
@@ -33,7 +33,7 @@ type FetchResult struct {
 
 // New creates a new Fetch MCP server
 func New() *Server {
-	server := mcp.NewServer("mcp-fetch", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("mcp-fetch", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	fetchServer := &Server{
 		Server: server,
pkg/filesystem/server.go
@@ -161,19 +161,18 @@ func New(allowedDirs []string) *Server {
 		))
 	}
 
-	fsServer.Server = mcp.NewServer("filesystem", "0.2.0", tools, resources)
-
+	var roots []mcp.Root
 	for _, dir := range allowedDirectories {
 		fileURI := "file://" + dir
 		dirName := filepath.Base(dir)
 		if dirName == "." || dirName == "/" {
 			dirName = dir
 		}
-
-		root := mcp.NewRoot(fileURI, fmt.Sprintf("Directory: %s", dirName))
-		fsServer.RegisterRoot(root)
+		roots = append(roots, mcp.NewRoot(fileURI, fmt.Sprintf("Directory: %s", dirName)))
 	}
 
+	fsServer.Server = mcp.NewServer("filesystem", "0.2.0", tools, resources, roots)
+
 	handlers := map[string]func(mcp.JSONRPCRequest) mcp.JSONRPCResponse{
 		"resources/list": func(req mcp.JSONRPCRequest) mcp.JSONRPCResponse {
 			resources := fsServer.ListResources()
pkg/filesystem/server_test.go
@@ -30,7 +30,16 @@ func TestFilesystemServer_ReadFile(t *testing.T) {
     },
   }
 
-  result, err := server.HandleReadFile(req)
+  // 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)
   }
@@ -68,7 +77,16 @@ func TestFilesystemServer_WriteFile(t *testing.T) {
     },
   }
 
-  result, err := server.HandleWriteFile(req)
+  // 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)
   }
@@ -103,7 +121,16 @@ func TestFilesystemServer_SecurityValidation(t *testing.T) {
     },
   }
 
-  result, err := server.HandleReadFile(req)
+  // 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)
   }
pkg/git/server.go
@@ -20,7 +20,7 @@ type Server struct {
 
 // New creates a new Git MCP server
 func New(repoPath string) *Server {
-	server := mcp.NewServer("mcp-git", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("mcp-git", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	gitServer := &Server{
 		Server:   server,
pkg/gitlab/server.go
@@ -97,7 +97,7 @@ type GitLabUser struct {
 }
 
 func NewServer(gitlabURL, token string) (*Server, error) {
-	baseServer := mcp.NewServer("gitlab", "0.1.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("gitlab", "0.1.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 	
 	// Initialize cache with default configuration
 	cache, err := NewCache(CacheConfig{
pkg/imap/server.go
@@ -67,7 +67,7 @@ type ConnectionInfo struct {
 
 func NewServer(server, username, password string, port int, useTLS bool) *Server {
 	s := &Server{
-		Server:   mcp.NewServer("imap", "IMAP email server for reading and managing emails", []mcp.Tool{}, []mcp.Resource{}),
+		Server:   mcp.NewServer("imap", "IMAP email server for reading and managing emails", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{}),
 		server:   server,
 		username: username,
 		password: password,
pkg/maildir/server.go
@@ -67,7 +67,7 @@ type ContactInfo struct {
 
 // New creates a new Maildir MCP server
 func New(allowedPaths []string) *Server {
-	server := mcp.NewServer("maildir-server", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("maildir-server", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	// Normalize and validate allowed paths
 	normalizedPaths := make([]string, len(allowedPaths))
pkg/mcp/roots_integration_test.go
@@ -12,7 +12,7 @@ import (
 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")
+	server := NewServer("test-filesystem", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Simulate filesystem server registering roots
 	tempDir := t.TempDir()
@@ -51,7 +51,7 @@ func TestRootsIntegration_FilesystemServer(t *testing.T) {
 }
 
 func TestRootsIntegration_GitServer(t *testing.T) {
-	server := NewServer("test-git", "1.0.0")
+	server := NewServer("test-git", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Simulate git server registering repository root
 	repoPath := "/path/to/repository"
@@ -80,7 +80,7 @@ func TestRootsIntegration_GitServer(t *testing.T) {
 }
 
 func TestRootsIntegration_MemoryServer(t *testing.T) {
-	server := NewServer("test-memory", "1.0.0")
+	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)")
@@ -111,7 +111,7 @@ func TestRootsIntegration_MemoryServer(t *testing.T) {
 
 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")
+	server := NewServer("multi-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Filesystem roots
 	server.RegisterRoot(NewRoot("file:///home/user", "Home Directory"))
@@ -155,7 +155,7 @@ func TestRootsIntegration_MultipleServers(t *testing.T) {
 }
 
 func TestRootsIntegration_DynamicUpdates(t *testing.T) {
-	server := NewServer("dynamic-server", "1.0.0")
+	server := NewServer("dynamic-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Initially no roots
 	roots := server.ListRoots()
@@ -187,7 +187,7 @@ func TestRootsIntegration_DynamicUpdates(t *testing.T) {
 }
 
 func TestRootsIntegration_Concurrency(t *testing.T) {
-	server := NewServer("concurrent-server", "1.0.0")
+	server := NewServer("concurrent-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Test concurrent root registration
 	done := make(chan bool, 10)
pkg/mcp/server.go
@@ -40,7 +40,7 @@ type PromptHandler func(GetPromptRequest) (GetPromptResult, error)
 type ResourceHandler func(ReadResourceRequest) (ReadResourceResult, error)
 
 // NewServer creates a new MCP server
-func NewServer(name, version string, tools []Tool, resources []Resource) *Server {
+func NewServer(name, version string, tools []Tool, resources []Resource, roots []Root) *Server {
 	server := &Server{
 		name:                  name,
 		version:               version,
@@ -69,6 +69,10 @@ func NewServer(name, version string, tools []Tool, resources []Resource) *Server
 		server.resourceDefinitions[resource.URI] = resource
 	}
 
+	for _, root := range roots {
+		server.rootDefinitions[root.URI] = root
+	}
+
 	return server
 }
 
pkg/mcp/server_prompts_test.go
@@ -5,7 +5,7 @@ import (
 )
 
 func TestServer_RegisterPrompt(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	prompt := Prompt{
 		Name:        "test-prompt",
@@ -61,7 +61,7 @@ func TestServer_RegisterPrompt(t *testing.T) {
 }
 
 func TestServer_ListPrompts_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	prompts := server.ListPrompts()
 	if len(prompts) != 0 {
@@ -70,7 +70,7 @@ func TestServer_ListPrompts_Empty(t *testing.T) {
 }
 
 func TestServer_MultiplePrompts(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	prompt1 := Prompt{
 		Name:        "prompt1",
pkg/mcp/server_resources_test.go
@@ -5,7 +5,7 @@ import (
 )
 
 func TestServer_RegisterResource(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	// Create a test resource handler
 	handler := func(req ReadResourceRequest) (ReadResourceResult, error) {
@@ -31,7 +31,7 @@ func TestServer_RegisterResource(t *testing.T) {
 }
 
 func TestServer_ListResources_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	resources := server.ListResources()
 	if len(resources) != 0 {
@@ -40,7 +40,7 @@ func TestServer_ListResources_Empty(t *testing.T) {
 }
 
 func TestServer_MultipleResources(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	handler := func(req ReadResourceRequest) (ReadResourceResult, error) {
 		return ReadResourceResult{}, nil
@@ -76,7 +76,7 @@ func TestServer_MultipleResources(t *testing.T) {
 }
 
 func TestServer_RegisterResourceWithDefinition(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	resource := Resource{
 		URI:         "file:///docs/readme.md",
pkg/mcp/server_roots_test.go
@@ -5,7 +5,7 @@ import (
 )
 
 func TestServer_RegisterRoot(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	root := Root{
 		URI:  "file:///home/user/projects",
@@ -29,7 +29,7 @@ func TestServer_RegisterRoot(t *testing.T) {
 }
 
 func TestServer_ListRoots_Empty(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	roots := server.ListRoots()
 	if len(roots) != 0 {
@@ -38,7 +38,7 @@ func TestServer_ListRoots_Empty(t *testing.T) {
 }
 
 func TestServer_MultipleRoots(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	roots := []Root{
 		{URI: "file:///home/user", Name: "Home"},
@@ -75,7 +75,7 @@ func TestServer_MultipleRoots(t *testing.T) {
 }
 
 func TestServer_RootCapability(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	if server.capabilities.Roots == nil {
 		t.Error("Expected server to have roots capability")
@@ -83,7 +83,7 @@ func TestServer_RootCapability(t *testing.T) {
 }
 
 func TestServer_DuplicateRoots(t *testing.T) {
-	server := NewServer("test-server", "1.0.0")
+	server := NewServer("test-server", "1.0.0", []Tool{}, []Resource{}, []Root{})
 
 	root1 := Root{
 		URI:  "file:///home/user",
pkg/memory/server.go
@@ -41,7 +41,7 @@ type Relation struct {
 
 // New creates a new Memory MCP server
 func New(memoryFile string) *Server {
-	server := mcp.NewServer("mcp-memory", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("mcp-memory", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	memoryServer := &Server{
 		Server:     server,
pkg/packages/server.go
@@ -20,7 +20,7 @@ type Server struct {
 
 // NewServer creates a new Package Manager MCP server
 func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-packages", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("mcp-packages", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	server := &Server{
 		Server: baseServer,
pkg/semantic/server.go
@@ -19,7 +19,7 @@ type Server struct {
 
 // NewServer creates a new Semantic MCP server
 func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-semantic", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("mcp-semantic", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 	
 	lspManager := NewLSPManager()
 	projectManager := NewProjectManager()
pkg/signal/server.go
@@ -63,7 +63,7 @@ type Attachment struct {
 }
 
 func NewServer(signalPath string) (*Server, error) {
-	baseServer := mcp.NewServer("signal", "0.1.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("signal", "0.1.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 	
 	server := &Server{
 		Server: baseServer,
pkg/speech/server.go
@@ -36,7 +36,7 @@ type Server struct {
 
 // NewServer creates a new Speech MCP server
 func NewServer() *Server {
-	baseServer := mcp.NewServer("mcp-speech", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	baseServer := mcp.NewServer("mcp-speech", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 	
 	// Select appropriate TTS backend based on OS
 	var backend TTSBackend
pkg/thinking/server.go
@@ -75,7 +75,7 @@ func New() *Server {
 
 // NewWithPersistence creates a new Sequential Thinking MCP server with optional persistence
 func NewWithPersistence(persistFile string) *Server {
-	server := mcp.NewServer("mcp-sequential-thinking", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("mcp-sequential-thinking", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	thinkingServer := &Server{
 		Server:      server,
pkg/time/server.go
@@ -32,7 +32,7 @@ type TimeConversionResult struct {
 
 // New creates a new Time MCP server
 func New() *Server {
-	server := mcp.NewServer("mcp-time", "1.0.0", []mcp.Tool{}, []mcp.Resource{})
+	server := mcp.NewServer("mcp-time", "1.0.0", []mcp.Tool{}, []mcp.Resource{}, []mcp.Root{})
 
 	// Get local timezone
 	localTZ := getLocalTimezone()