Textwell MCP Server
by worldnine
- tools
package tools
import (
"context"
"fmt"
"os"
"strings"
"github.com/joho/godotenv"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"github.com/nguyenvanduocit/all-in-one-model-context-protocol/services"
"github.com/nguyenvanduocit/all-in-one-model-context-protocol/util"
"github.com/sashabaranov/go-openai"
)
var envFilePath string
func RegisterToolManagerTool(s *server.MCPServer, envFile string) {
envFilePath = envFile
tool := mcp.NewTool("tool_manager",
mcp.WithDescription("Manage MCP tools - enable or disable tools"),
mcp.WithString("action", mcp.Required(), mcp.Description("Action to perform: list, enable, disable")),
mcp.WithString("tool_name", mcp.Description("Tool name to enable/disable")),
)
s.AddTool(tool, util.ErrorGuard(toolManagerHandler))
planTool := mcp.NewTool("tool_use_plan",
mcp.WithDescription("Tạo kế hoạch sử dụng các công cụ đang kích hoạt để giải quyết yêu cầu"),
mcp.WithString("request", mcp.Required(), mcp.Description("Yêu cầu cần lập kế hoạch")),
mcp.WithString("context", mcp.Required(), mcp.Description("Ngữ cảnh liên quan đến yêu cầu")),
)
s.AddTool(planTool, util.ErrorGuard(toolUsePlanHandler))
}
func toolManagerHandler(arguments map[string]interface{}) (*mcp.CallToolResult, error) {
action, ok := arguments["action"].(string)
if !ok {
return mcp.NewToolResultError("action must be a string"), nil
}
env, err := godotenv.Read(envFilePath)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to read env file %s: %v", envFilePath, err)), nil
}
enableTools := env["ENABLE_TOOLS"]
toolList := strings.Split(enableTools, ",")
switch action {
case "list":
response := fmt.Sprintf("Environment file: %s\n\n", envFilePath)
allEnabled := enableTools == ""
// List all available tools with status
response += "Available tools:\n"
tools := []struct {
name string
desc string
}{
{"tool_manager", "Tool management"},
{"gemini", "AI tools: web search"},
{"fetch", "Web content fetching"},
{"confluence", "Confluence integration"},
{"youtube", "YouTube transcript"},
{"jira", "Jira issue management"},
{"gitlab", "GitLab integration"},
{"script", "Script execution"},
{"rag", "RAG memory tools"},
{"gmail", "Gmail tools"},
{"calendar", "Google Calendar tools"},
{"youtube_channel", "YouTube channel tools"},
{"sequential_thinking", "Sequential thinking tool"},
{"deepseek", "Deepseek reasoning tool"},
}
for _, t := range tools {
status := "disabled"
if allEnabled || contains(toolList, t.name) {
status = "enabled"
}
response += fmt.Sprintf("- %s (%s) [%s]\n", t.name, t.desc, status)
}
response += "\n"
// List enabled tools
response += "Currently enabled tools:\n"
if allEnabled {
response += "All tools are enabled (ENABLE_TOOLS is empty)\n"
} else {
for _, tool := range toolList {
if tool != "" {
response += fmt.Sprintf("- %s\n", tool)
}
}
}
return mcp.NewToolResultText(response), nil
case "enable", "disable":
toolName, ok := arguments["tool_name"].(string)
if !ok || toolName == "" {
return mcp.NewToolResultError("tool_name is required for enable/disable actions"), nil
}
// Nếu ENABLE_TOOLS trống, tạo một list mới
if enableTools == "" {
toolList = []string{}
}
if action == "enable" {
// Kiểm tra xem tool đã được enable chưa
if !contains(toolList, toolName) {
toolList = append(toolList, toolName)
}
} else {
// Disable tool bằng cách xóa khỏi list
toolList = removeString(toolList, toolName)
}
// Cập nhật ENABLE_TOOLS trong env
env["ENABLE_TOOLS"] = strings.Join(toolList, ",")
// Ghi lại vào file .env
content := ""
for key, value := range env {
content += fmt.Sprintf("%s=%s\n", key, value)
}
if err := os.WriteFile(envFilePath, []byte(content), 0644); err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to write env file %s: %v", envFilePath, err)), nil
}
return mcp.NewToolResultText(fmt.Sprintf("Successfully %sd tool: %s in %s", action, toolName, envFilePath)), nil
default:
return mcp.NewToolResultError("Invalid action. Use 'list', 'enable', or 'disable'"), nil
}
}
func contains(slice []string, item string) bool {
for _, s := range slice {
if s == item {
return true
}
}
return false
}
func removeString(slice []string, item string) []string {
result := []string{}
for _, s := range slice {
if s != item {
result = append(result, s)
}
}
return result
}
func toolUsePlanHandler(arguments map[string]interface{}) (*mcp.CallToolResult, error) {
request, _ := arguments["request"].(string)
contextString, _ := arguments["context"].(string)
env, err := godotenv.Read(envFilePath)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("Failed to read env file: %v", err)), nil
}
enabledTools := strings.Split(env["ENABLE_TOOLS"], ",")
if !contains(enabledTools, "deepseek") {
return mcp.NewToolResultError("Deepseek tool must be enabled to generate plans"), nil
}
// Create a more detailed system prompt
systemPrompt := fmt.Sprintf(`You are a tool usage planning assistant. Create a detailed execution plan using the currently enabled tools: %s
Context: %s
Output format:
1. [Tool Name] - Purpose: ... (Expected result: ...)
2. [Tool Name] - Purpose: ... (Expected result: ...)
...`, strings.Join(enabledTools, ", "), contextString)
messages := []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleSystem,
Content: systemPrompt,
},
{
Role: openai.ChatMessageRoleUser,
Content: request,
},
}
// Sử dụng client chung từ deepseek.go
resp, err := services.DefaultDeepseekClient().CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: "deepseek-reasoner", // Sử dụng model R1
Messages: messages,
Temperature: 0.3, // Giảm temperature để kế hoạch ổn định hơn
},
)
if err != nil {
return mcp.NewToolResultError(fmt.Sprintf("API call failed: %v", err)), nil
}
if len(resp.Choices) == 0 {
return mcp.NewToolResultError("No response from Deepseek"), nil
}
// Format kết quả
content := strings.TrimSpace(resp.Choices[0].Message.Content)
return mcp.NewToolResultText("📝 **Execution Plan:**\n" + content), nil
}