mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2026-05-06 14:03:40 +08:00
fix(mcp): prevent panic on nil interface conversion for tool arguments (#1622)
* fix(mcp): prevent panic on nil interface conversion for tool arguments
Add safe argument extraction helper functions in internal/mcp/args.go
that handle nil values gracefully instead of panicking on direct type
assertions.
This fixes the issue where MCP config tools panic with:
'interface conversion: interface {} is nil, not string'
when called via Claude Desktop with protocol version 2025-11-25.
Affected tools:
- nginx_config_list
- nginx_config_get
- nginx_config_add
- nginx_config_modify
- nginx_config_rename
- nginx_config_mkdir
- nginx_config_history
- nginx_config_enable
Fixes #36ec
Co-authored-by: Jacky <me@jackyu.cn>
* Add required argument validation to MCP config handlers to prevent data loss
The safe argument extraction helpers (mcp.GetString, etc.) return zero values
for nil/missing arguments, which could cause silent data loss. This adds
explicit validation for required arguments in:
- config_modify: validate relative_path and content
- config_add: validate name and content
- config_rename: validate orig_name and new_name
- config_mkdir: validate folder_name
This follows the same pattern already used in handleNginxConfigEnable.
* Add required argument validation to config_get and config_history handlers
---------
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
34
internal/mcp/args.go
Normal file
34
internal/mcp/args.go
Normal file
@@ -0,0 +1,34 @@
|
||||
package mcp
|
||||
|
||||
// GetString safely extracts a string value from the arguments map.
|
||||
// Returns an empty string if the key doesn't exist or the value is nil.
|
||||
func GetString(args map[string]interface{}, key string) string {
|
||||
if v, ok := args[key]; ok && v != nil {
|
||||
if s, ok := v.(string); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// GetBool safely extracts a boolean value from the arguments map.
|
||||
// Returns false if the key doesn't exist or the value is nil.
|
||||
func GetBool(args map[string]interface{}, key string) bool {
|
||||
if v, ok := args[key]; ok && v != nil {
|
||||
if b, ok := v.(bool); ok {
|
||||
return b
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// GetSlice safely extracts a slice of interface{} from the arguments map.
|
||||
// Returns nil if the key doesn't exist or the value is nil.
|
||||
func GetSlice(args map[string]interface{}, key string) []interface{} {
|
||||
if v, ok := args[key]; ok && v != nil {
|
||||
if s, ok := v.([]interface{}); ok {
|
||||
return s
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -4,14 +4,16 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigAddToolName = "nginx_config_add"
|
||||
@@ -19,31 +21,36 @@ const nginxConfigAddToolName = "nginx_config_add"
|
||||
// ErrFileAlreadyExists is returned when trying to create a file that already exists
|
||||
var ErrFileAlreadyExists = errors.New("file already exists")
|
||||
|
||||
var nginxConfigAddTool = mcp.NewTool(
|
||||
var nginxConfigAddTool = mcpgo.NewTool(
|
||||
nginxConfigAddToolName,
|
||||
mcp.WithDescription("Add or create a new Nginx configuration file"),
|
||||
mcp.WithString("name", mcp.Description("The name of the configuration file to create")),
|
||||
mcp.WithString("content", mcp.Description("The content of the configuration file")),
|
||||
mcp.WithString("base_dir", mcp.Description("The base directory for the configuration")),
|
||||
mcp.WithBoolean("overwrite", mcp.Description("Whether to overwrite an existing file")),
|
||||
mcp.WithArray("sync_node_ids", mcp.Description("IDs of nodes to sync the configuration to")),
|
||||
mcpgo.WithDescription("Add or create a new Nginx configuration file"),
|
||||
mcpgo.WithString("name", mcpgo.Description("The name of the configuration file to create")),
|
||||
mcpgo.WithString("content", mcpgo.Description("The content of the configuration file")),
|
||||
mcpgo.WithString("base_dir", mcpgo.Description("The base directory for the configuration")),
|
||||
mcpgo.WithBoolean("overwrite", mcpgo.Description("Whether to overwrite an existing file")),
|
||||
mcpgo.WithArray("sync_node_ids", mcpgo.Description("IDs of nodes to sync the configuration to")),
|
||||
)
|
||||
|
||||
func handleNginxConfigAdd(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigAdd(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
name := args["name"].(string)
|
||||
content := args["content"].(string)
|
||||
baseDir := args["base_dir"].(string)
|
||||
overwrite := args["overwrite"].(bool)
|
||||
name := mcp.GetString(args, "name")
|
||||
content := mcp.GetString(args, "content")
|
||||
baseDir := mcp.GetString(args, "base_dir")
|
||||
overwrite := mcp.GetBool(args, "overwrite")
|
||||
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("argument 'name' is required")
|
||||
}
|
||||
if _, exists := args["content"]; !exists || args["content"] == nil {
|
||||
return nil, fmt.Errorf("argument 'content' is required")
|
||||
}
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIdsInterface := mcp.GetSlice(args, "sync_node_ids")
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,5 +116,5 @@ func handleNginxConfigAdd(ctx context.Context, request mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
@@ -9,25 +9,26 @@ import (
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigEnableToolName = "nginx_config_enable"
|
||||
|
||||
var nginxConfigEnableTool = mcp.NewTool(
|
||||
var nginxConfigEnableTool = mcpgo.NewTool(
|
||||
nginxConfigEnableToolName,
|
||||
mcp.WithDescription("Enable a previously created Nginx configuration (creates symlink in sites-enabled)"),
|
||||
mcp.WithString("name", mcp.Description("The name of the configuration file to enable")),
|
||||
mcp.WithString("base_dir", mcp.Description("The source directory (default: sites-available)")),
|
||||
mcp.WithBoolean("overwrite", mcp.Description("Whether to overwrite an existing enabled configuration")),
|
||||
mcpgo.WithDescription("Enable a previously created Nginx configuration (creates symlink in sites-enabled)"),
|
||||
mcpgo.WithString("name", mcpgo.Description("The name of the configuration file to enable")),
|
||||
mcpgo.WithString("base_dir", mcpgo.Description("The source directory (default: sites-available)")),
|
||||
mcpgo.WithBoolean("overwrite", mcpgo.Description("Whether to overwrite an existing enabled configuration")),
|
||||
)
|
||||
|
||||
func handleNginxConfigEnable(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigEnable(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
name := args["name"].(string)
|
||||
baseDir := args["base_dir"].(string)
|
||||
overwrite := args["overwrite"].(bool)
|
||||
name := mcp.GetString(args, "name")
|
||||
baseDir := mcp.GetString(args, "base_dir")
|
||||
overwrite := mcp.GetBool(args, "overwrite")
|
||||
|
||||
if name == "" {
|
||||
return nil, fmt.Errorf("argument 'name' is required")
|
||||
@@ -110,6 +111,6 @@ func handleNginxConfigEnable(ctx context.Context, request mcp.CallToolRequest) (
|
||||
}
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
|
||||
}
|
||||
|
||||
@@ -3,25 +3,31 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigGetToolName = "nginx_config_get"
|
||||
|
||||
var nginxConfigGetTool = mcp.NewTool(
|
||||
var nginxConfigGetTool = mcpgo.NewTool(
|
||||
nginxConfigGetToolName,
|
||||
mcp.WithDescription("Get a specific Nginx configuration file"),
|
||||
mcp.WithString("relative_path", mcp.Description("The relative path to the configuration file")),
|
||||
mcpgo.WithDescription("Get a specific Nginx configuration file"),
|
||||
mcpgo.WithString("relative_path", mcpgo.Description("The relative path to the configuration file")),
|
||||
)
|
||||
|
||||
func handleNginxConfigGet(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigGet(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
relativePath := args["relative_path"].(string)
|
||||
relativePath := mcp.GetString(args, "relative_path")
|
||||
|
||||
if relativePath == "" {
|
||||
return nil, fmt.Errorf("argument 'relative_path' is required")
|
||||
}
|
||||
|
||||
absPath, err := config.ResolveAbsoluteOrRelativeConfPath(relativePath)
|
||||
if err != nil {
|
||||
@@ -55,5 +61,5 @@ func handleNginxConfigGet(ctx context.Context, request mcp.CallToolRequest) (*mc
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
@@ -3,22 +3,28 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigHistoryToolName = "nginx_config_history"
|
||||
|
||||
var nginxConfigHistoryTool = mcp.NewTool(
|
||||
var nginxConfigHistoryTool = mcpgo.NewTool(
|
||||
nginxConfigHistoryToolName,
|
||||
mcp.WithDescription("Get history of Nginx configuration changes"),
|
||||
mcp.WithString("filepath", mcp.Description("The file path to get history for")),
|
||||
mcpgo.WithDescription("Get history of Nginx configuration changes"),
|
||||
mcpgo.WithString("filepath", mcpgo.Description("The file path to get history for")),
|
||||
)
|
||||
|
||||
func handleNginxConfigHistory(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigHistory(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
filepath := args["filepath"].(string)
|
||||
filepath := mcp.GetString(args, "filepath")
|
||||
|
||||
if filepath == "" {
|
||||
return nil, fmt.Errorf("argument 'filepath' is required")
|
||||
}
|
||||
|
||||
q := query.ConfigBackup
|
||||
var histories, err = q.Where(q.FilePath.Eq(filepath)).Order(q.ID.Desc()).Find()
|
||||
@@ -27,5 +33,5 @@ func handleNginxConfigHistory(ctx context.Context, request mcp.CallToolRequest)
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(histories)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
@@ -7,27 +7,28 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigListToolName = "nginx_config_list"
|
||||
|
||||
var nginxConfigListTool = mcp.NewTool(
|
||||
var nginxConfigListTool = mcpgo.NewTool(
|
||||
nginxConfigListToolName,
|
||||
mcp.WithDescription("This is the list of Nginx configurations"),
|
||||
mcp.WithString("relative_path", mcp.Description("The relative path to the Nginx configurations")),
|
||||
mcp.WithString("filter_by_name", mcp.Description("Filter the Nginx configurations by name")),
|
||||
mcpgo.WithDescription("This is the list of Nginx configurations"),
|
||||
mcpgo.WithString("relative_path", mcpgo.Description("The relative path to the Nginx configurations")),
|
||||
mcpgo.WithString("filter_by_name", mcpgo.Description("Filter the Nginx configurations by name")),
|
||||
)
|
||||
|
||||
func handleNginxConfigList(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigList(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
relativePath := args["relative_path"].(string)
|
||||
filterByName := args["filter_by_name"].(string)
|
||||
relativePath := mcp.GetString(args, "relative_path")
|
||||
filterByName := mcp.GetString(args, "filter_by_name")
|
||||
configs, err := config.GetConfigList(relativePath, func(file os.FileInfo) bool {
|
||||
return filterByName == "" || strings.Contains(file.Name(), filterByName)
|
||||
})
|
||||
|
||||
jsonResult, _ := json.Marshal(configs)
|
||||
|
||||
return mcp.NewToolResultText(string(jsonResult)), err
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), err
|
||||
}
|
||||
|
||||
@@ -3,25 +3,31 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigMkdirToolName = "nginx_config_mkdir"
|
||||
|
||||
var nginxConfigMkdirTool = mcp.NewTool(
|
||||
var nginxConfigMkdirTool = mcpgo.NewTool(
|
||||
nginxConfigMkdirToolName,
|
||||
mcp.WithDescription("Create a new directory in the Nginx configuration path"),
|
||||
mcp.WithString("base_path", mcp.Description("The base path where to create the directory")),
|
||||
mcp.WithString("folder_name", mcp.Description("The name of the folder to create")),
|
||||
mcpgo.WithDescription("Create a new directory in the Nginx configuration path"),
|
||||
mcpgo.WithString("base_path", mcpgo.Description("The base path where to create the directory")),
|
||||
mcpgo.WithString("folder_name", mcpgo.Description("The name of the folder to create")),
|
||||
)
|
||||
|
||||
func handleNginxConfigMkdir(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigMkdir(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
basePath := args["base_path"].(string)
|
||||
folderName := args["folder_name"].(string)
|
||||
basePath := mcp.GetString(args, "base_path")
|
||||
folderName := mcp.GetString(args, "folder_name")
|
||||
|
||||
if folderName == "" {
|
||||
return nil, fmt.Errorf("argument 'folder_name' is required")
|
||||
}
|
||||
|
||||
fullPath, err := config.ResolveConfPath(basePath, folderName)
|
||||
if err != nil {
|
||||
@@ -39,5 +45,5 @@ func handleNginxConfigMkdir(ctx context.Context, request mcp.CallToolRequest) (*
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
@@ -4,13 +4,15 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
"gorm.io/gen/field"
|
||||
)
|
||||
|
||||
@@ -19,29 +21,34 @@ const nginxConfigModifyToolName = "nginx_config_modify"
|
||||
// ErrFileNotFound is returned when a file is not found
|
||||
var ErrFileNotFound = errors.New("file not found")
|
||||
|
||||
var nginxConfigModifyTool = mcp.NewTool(
|
||||
var nginxConfigModifyTool = mcpgo.NewTool(
|
||||
nginxConfigModifyToolName,
|
||||
mcp.WithDescription("Modify an existing Nginx configuration file"),
|
||||
mcp.WithString("relative_path", mcp.Description("The relative path to the configuration file")),
|
||||
mcp.WithString("content", mcp.Description("The new content of the configuration file")),
|
||||
mcp.WithBoolean("sync_overwrite", mcp.Description("Whether to overwrite existing files when syncing")),
|
||||
mcp.WithArray("sync_node_ids", mcp.Description("IDs of nodes to sync the configuration to")),
|
||||
mcpgo.WithDescription("Modify an existing Nginx configuration file"),
|
||||
mcpgo.WithString("relative_path", mcpgo.Description("The relative path to the configuration file")),
|
||||
mcpgo.WithString("content", mcpgo.Description("The new content of the configuration file")),
|
||||
mcpgo.WithBoolean("sync_overwrite", mcpgo.Description("Whether to overwrite existing files when syncing")),
|
||||
mcpgo.WithArray("sync_node_ids", mcpgo.Description("IDs of nodes to sync the configuration to")),
|
||||
)
|
||||
|
||||
func handleNginxConfigModify(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigModify(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
relativePath := args["relative_path"].(string)
|
||||
content := args["content"].(string)
|
||||
syncOverwrite := args["sync_overwrite"].(bool)
|
||||
relativePath := mcp.GetString(args, "relative_path")
|
||||
content := mcp.GetString(args, "content")
|
||||
syncOverwrite := mcp.GetBool(args, "sync_overwrite")
|
||||
|
||||
if relativePath == "" {
|
||||
return nil, fmt.Errorf("argument 'relative_path' is required")
|
||||
}
|
||||
if _, exists := args["content"]; !exists || args["content"] == nil {
|
||||
return nil, fmt.Errorf("argument 'content' is required")
|
||||
}
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIdsInterface := mcp.GetSlice(args, "sync_node_ids")
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,5 +99,5 @@ func handleNginxConfigModify(ctx context.Context, request mcp.CallToolRequest) (
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
@@ -3,42 +3,49 @@ package config
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/model"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
mcpgo "github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigRenameToolName = "nginx_config_rename"
|
||||
|
||||
var nginxConfigRenameTool = mcp.NewTool(
|
||||
var nginxConfigRenameTool = mcpgo.NewTool(
|
||||
nginxConfigRenameToolName,
|
||||
mcp.WithDescription("Rename a file or directory in the Nginx configuration path"),
|
||||
mcp.WithString("base_path", mcp.Description("The base path where the file or directory is located")),
|
||||
mcp.WithString("orig_name", mcp.Description("The original name of the file or directory")),
|
||||
mcp.WithString("new_name", mcp.Description("The new name for the file or directory")),
|
||||
mcp.WithArray("sync_node_ids", mcp.Description("IDs of nodes to sync the rename operation to")),
|
||||
mcpgo.WithDescription("Rename a file or directory in the Nginx configuration path"),
|
||||
mcpgo.WithString("base_path", mcpgo.Description("The base path where the file or directory is located")),
|
||||
mcpgo.WithString("orig_name", mcpgo.Description("The original name of the file or directory")),
|
||||
mcpgo.WithString("new_name", mcpgo.Description("The new name for the file or directory")),
|
||||
mcpgo.WithArray("sync_node_ids", mcpgo.Description("IDs of nodes to sync the rename operation to")),
|
||||
)
|
||||
|
||||
func handleNginxConfigRename(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
func handleNginxConfigRename(ctx context.Context, request mcpgo.CallToolRequest) (*mcpgo.CallToolResult, error) {
|
||||
args := request.GetArguments()
|
||||
basePath := args["base_path"].(string)
|
||||
origName := args["orig_name"].(string)
|
||||
newName := args["new_name"].(string)
|
||||
basePath := mcp.GetString(args, "base_path")
|
||||
origName := mcp.GetString(args, "orig_name")
|
||||
newName := mcp.GetString(args, "new_name")
|
||||
|
||||
if origName == "" {
|
||||
return nil, fmt.Errorf("argument 'orig_name' is required")
|
||||
}
|
||||
if newName == "" {
|
||||
return nil, fmt.Errorf("argument 'new_name' is required")
|
||||
}
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIdsInterface := mcp.GetSlice(args, "sync_node_ids")
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +54,7 @@ func handleNginxConfigRename(ctx context.Context, request mcp.CallToolRequest) (
|
||||
"message": "No changes needed, names are identical",
|
||||
}
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
origFullPath, err := config.ResolveConfPath(basePath, origName)
|
||||
@@ -118,5 +125,5 @@ func handleNginxConfigRename(ctx context.Context, request mcp.CallToolRequest) (
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
return mcpgo.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user