diff --git a/test/e2e/fetch_mcp_server_test.go b/test/e2e/fetch_mcp_server_test.go index ee9c401c9..a92e906e2 100644 --- a/test/e2e/fetch_mcp_server_test.go +++ b/test/e2e/fetch_mcp_server_test.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "net/http" + "os" "strings" "time" @@ -110,6 +111,215 @@ var _ = Describe("FetchMcpServer", Label("mcp", "e2e"), func() { }) }) + Context("when starting the server from registry with tools override", Label("override"), func() { + var ( + toolsOverrideFile string + tempDir string + ) + + BeforeEach(func() { + // Create temporary directory for tool override files + tempDir = GinkgoT().TempDir() + }) + + It("should start with valid tool override and show overridden tool names", func() { + By("Creating a valid tool override JSON file") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "custom_fetch_tool", + "description": "A customized fetch tool with overridden name and description" + } + } + }` + toolsOverrideFile = tempDir + "/tools_override.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create tool override file") + + By("Starting the fetch MCP server with tool override") + stdout, stderr := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", toolsOverrideFile).ExpectSuccess() + + // The command should indicate success + Expect(stdout+stderr).To(ContainSubstring("fetch"), "Output should mention the fetch server") + + By("Waiting for the server to be running") + err = e2e.WaitForMCPServer(config, serverName, 60*time.Second) + Expect(err).ToNot(HaveOccurred(), "Server should be running within 60 seconds") + + By("Verifying the server appears in the list") + stdout, _ = e2e.NewTHVCommand(config, "list").ExpectSuccess() + Expect(stdout).To(ContainSubstring(serverName), "Server should appear in the list") + Expect(stdout).To(ContainSubstring("running"), "Server should be in running state") + + By("Verifying tool override is applied by listing tools") + stdout, _ = e2e.NewTHVCommand(config, "mcp", "list", "tools", "--server", serverName, "--timeout", "60s").ExpectSuccess() + Expect(stdout).To(ContainSubstring("custom_fetch_tool"), "Should show overridden tool name") + Expect(stdout).To(ContainSubstring("customized fetch tool"), "Should show overridden tool description") + }) + + It("should start with tool override that only changes description", func() { + By("Creating a tool override JSON file with only description override") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "description": "An enhanced fetch tool with custom description only" + } + } + }` + toolsOverrideFile = tempDir + "/tools_override_desc_only.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create tool override file") + + By("Starting the fetch MCP server with description-only tool override") + stdout, stderr := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", toolsOverrideFile).ExpectSuccess() + + // The command should indicate success + Expect(stdout+stderr).To(ContainSubstring("fetch"), "Output should mention the fetch server") + + By("Waiting for the server to be running") + err = e2e.WaitForMCPServer(config, serverName, 60*time.Second) + Expect(err).ToNot(HaveOccurred(), "Server should be running within 60 seconds") + + By("Verifying tool override is applied by listing tools") + stdout, _ = e2e.NewTHVCommand(config, "mcp", "list", "tools", "--server", serverName, "--timeout", "60s").ExpectSuccess() + Expect(stdout).To(ContainSubstring("fetch"), "Should still show original tool name") + Expect(stdout).To(ContainSubstring("enhanced fetch tool"), "Should show overridden tool description") + }) + + It("should start with tool override that only changes name", func() { + By("Creating a tool override JSON file with only name override") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "renamed_fetch" + } + } + }` + toolsOverrideFile = tempDir + "/tools_override_name_only.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create tool override file") + + By("Starting the fetch MCP server with name-only tool override") + stdout, stderr := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", toolsOverrideFile).ExpectSuccess() + + // The command should indicate success + Expect(stdout+stderr).To(ContainSubstring("fetch"), "Output should mention the fetch server") + + By("Waiting for the server to be running") + err = e2e.WaitForMCPServer(config, serverName, 60*time.Second) + Expect(err).ToNot(HaveOccurred(), "Server should be running within 60 seconds") + + By("Verifying tool override is applied by listing tools") + stdout, _ = e2e.NewTHVCommand(config, "mcp", "list", "tools", "--server", serverName, "--timeout", "60s").ExpectSuccess() + Expect(stdout).To(ContainSubstring("renamed_fetch"), "Should show overridden tool name") + }) + + It("should fail when tool override file has invalid JSON", func() { + By("Creating an invalid tool override JSON file") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "invalid_json" + } + // Missing closing brace + }` + toolsOverrideFile = tempDir + "/invalid_tools_override.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create invalid tool override file") + + By("Attempting to start the fetch MCP server with invalid tool override") + _, _, err = e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", toolsOverrideFile).ExpectFailure() + Expect(err).To(HaveOccurred(), "Should fail with invalid JSON") + }) + + It("should fail when tool override file does not exist", func() { + By("Attempting to start the fetch MCP server with non-existent tool override file") + _, _, err := e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", "/non/existent/file.json").ExpectFailure() + Expect(err).To(HaveOccurred(), "Should fail with non-existent file") + }) + + It("should fail when tool override has empty name and description", func() { + By("Creating a tool override JSON file with empty override") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "", + "description": "" + } + } + }` + toolsOverrideFile = tempDir + "/empty_tools_override.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create empty tool override file") + + By("Attempting to start the fetch MCP server with empty tool override") + _, _, err = e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools-override", toolsOverrideFile).ExpectFailure() + Expect(err).To(HaveOccurred(), "Should fail with empty tool override") + }) + }) + + Context("when combining tools filter with tools override", Label("override", "filter"), func() { + var ( + toolsOverrideFile string + tempDir string + ) + + BeforeEach(func() { + // Create temporary directory for tool override files + tempDir = GinkgoT().TempDir() + }) + + It("should apply both filter and override correctly", func() { + By("Creating a tool override JSON file") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "filtered_and_overridden_fetch", + "description": "A fetch tool that is both filtered and overridden" + } + } + }` + toolsOverrideFile = tempDir + "/combined_tools_override.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create tool override file") + + By("Starting the fetch MCP server with both tools filter and override") + stdout, stderr := e2e.NewTHVCommand( + config, "run", "--name", serverName, "fetch", "--tools", "filtered_and_overridden_fetch", "--tools-override", toolsOverrideFile).ExpectSuccess() + + // The command should indicate success + Expect(stdout+stderr).To(ContainSubstring("fetch"), "Output should mention the fetch server") + + By("Waiting for the server to be running") + err = e2e.WaitForMCPServer(config, serverName, 60*time.Second) + Expect(err).ToNot(HaveOccurred(), "Server should be running within 60 seconds") + + By("Verifying both filter and override are applied by listing tools") + stdout, _ = e2e.NewTHVCommand(config, "mcp", "list", "tools", "--server", serverName, "--timeout", "60s").ExpectSuccess() + Expect(stdout).To(ContainSubstring("filtered_and_overridden_fetch"), "Should show overridden tool name") + Expect(stdout).To(ContainSubstring("filtered and overridden"), "Should show overridden tool description") + }) + + It("should fail when filtering out a tool that has an override", func() { + By("Creating a tool override JSON file for a tool that will be filtered out") + toolsOverrideContent := `{ + "toolsOverride": { + "fetch": { + "name": "overridden_but_filtered_out", + "description": "This tool will be filtered out despite having an override" + } + } + }` + toolsOverrideFile = tempDir + "/filtered_out_override.json" + err := os.WriteFile(toolsOverrideFile, []byte(toolsOverrideContent), 0644) + Expect(err).ToNot(HaveOccurred(), "Should be able to create tool override file") + + By("Attempting to start server with tool filter that excludes the overridden tool") + _, _, err = e2e.NewTHVCommand(config, "run", "--name", serverName, "fetch", "--tools", "non-existent-tool", "--tools-override", toolsOverrideFile).ExpectFailure() + Expect(err).To(HaveOccurred(), "Should fail when filtering out overridden tool") + }) + }) + Context("when managing the server lifecycle", func() { BeforeEach(func() { // Start a server for lifecycle tests