mirror of
https://github.com/0xJacky/nginx-ui.git
synced 2025-05-11 10:25:52 +02:00
feat: mcp server
This commit is contained in:
parent
c4a9d03bb3
commit
e8ee931e16
51 changed files with 2749 additions and 1526 deletions
114
mcp/config/config_add.go
Normal file
114
mcp/config/config_add.go
Normal file
|
@ -0,0 +1,114 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"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"
|
||||
)
|
||||
|
||||
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(
|
||||
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")),
|
||||
)
|
||||
|
||||
func handleNginxConfigAdd(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.Params.Arguments
|
||||
name := args["name"].(string)
|
||||
content := args["content"].(string)
|
||||
baseDir := args["base_dir"].(string)
|
||||
overwrite := args["overwrite"].(bool)
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dir := nginx.GetConfPath(baseDir)
|
||||
path := filepath.Join(dir, name)
|
||||
if !helper.IsUnderDirectory(path, nginx.GetConfPath()) {
|
||||
return nil, config.ErrPathIsNotUnderTheNginxConfDir
|
||||
}
|
||||
|
||||
if !overwrite && helper.FileExists(path) {
|
||||
return nil, ErrFileAlreadyExists
|
||||
}
|
||||
|
||||
// Check if the directory exists, if not, create it
|
||||
if !helper.FileExists(dir) {
|
||||
err := os.MkdirAll(dir, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
err := os.WriteFile(path, []byte(content), 0644)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := nginx.Reload()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if nginx.GetLogLevel(output) >= nginx.Warn {
|
||||
return nil, config.ErrNginxReloadFailed
|
||||
}
|
||||
|
||||
q := query.Config
|
||||
_, err = q.Where(q.Filepath.Eq(path)).Delete()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg := &model.Config{
|
||||
Name: name,
|
||||
Filepath: path,
|
||||
SyncNodeIds: syncNodeIds,
|
||||
SyncOverwrite: overwrite,
|
||||
}
|
||||
|
||||
err = q.Create(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = config.SyncToRemoteServer(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"name": name,
|
||||
"content": content,
|
||||
"file_path": path,
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
27
mcp/config/config_base_path.go
Normal file
27
mcp/config/config_base_path.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigBasePathToolName = "nginx_config_base_path"
|
||||
|
||||
var nginxConfigBasePathTool = mcp.NewTool(
|
||||
nginxConfigBasePathToolName,
|
||||
mcp.WithDescription("Get the base path of Nginx configurations"),
|
||||
)
|
||||
|
||||
func handleNginxConfigBasePath(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
basePath := nginx.GetConfPath()
|
||||
|
||||
result := map[string]interface{}{
|
||||
"base_path": basePath,
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
67
mcp/config/config_get.go
Normal file
67
mcp/config/config_get.go
Normal file
|
@ -0,0 +1,67 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigGetToolName = "nginx_config_get"
|
||||
|
||||
var nginxConfigGetTool = mcp.NewTool(
|
||||
nginxConfigGetToolName,
|
||||
mcp.WithDescription("Get a specific Nginx configuration file"),
|
||||
mcp.WithString("relative_path", mcp.Description("The relative path to the configuration file")),
|
||||
)
|
||||
|
||||
func handleNginxConfigGet(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
relativePath := request.Params.Arguments["relative_path"].(string)
|
||||
|
||||
absPath := nginx.GetConfPath(relativePath)
|
||||
if !helper.IsUnderDirectory(absPath, nginx.GetConfPath()) {
|
||||
return nil, config.ErrPathIsNotUnderTheNginxConfDir
|
||||
}
|
||||
|
||||
stat, err := os.Stat(absPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(absPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
q := query.Config
|
||||
g := query.ChatGPTLog
|
||||
chatgpt, err := g.Where(g.Name.Eq(absPath)).FirstOrCreate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg, err := q.Where(q.Filepath.Eq(absPath)).FirstOrInit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"name": stat.Name(),
|
||||
"content": string(content),
|
||||
"chat_gpt_messages": chatgpt.Content,
|
||||
"file_path": absPath,
|
||||
"modified_at": stat.ModTime(),
|
||||
"dir": filepath.Dir(relativePath),
|
||||
"sync_node_ids": cfg.SyncNodeIds,
|
||||
"sync_overwrite": cfg.SyncOverwrite,
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
30
mcp/config/config_history.go
Normal file
30
mcp/config/config_history.go
Normal file
|
@ -0,0 +1,30 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/query"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigHistoryToolName = "nginx_config_history"
|
||||
|
||||
var nginxConfigHistoryTool = mcp.NewTool(
|
||||
nginxConfigHistoryToolName,
|
||||
mcp.WithDescription("Get history of Nginx configuration changes"),
|
||||
mcp.WithString("filepath", mcp.Description("The file path to get history for")),
|
||||
)
|
||||
|
||||
func handleNginxConfigHistory(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
filepath := request.Params.Arguments["filepath"].(string)
|
||||
|
||||
q := query.ConfigBackup
|
||||
var histories, err = q.Where(q.FilePath.Eq(filepath)).Order(q.ID.Desc()).Find()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(histories)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
32
mcp/config/config_list.go
Normal file
32
mcp/config/config_list.go
Normal file
|
@ -0,0 +1,32 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigListToolName = "nginx_config_list"
|
||||
|
||||
var nginxConfigListTool = mcp.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")),
|
||||
)
|
||||
|
||||
func handleNginxConfigList(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
relativePath := request.Params.Arguments["relative_path"].(string)
|
||||
filterByName := request.Params.Arguments["filter_by_name"].(string)
|
||||
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
|
||||
}
|
45
mcp/config/config_mkdir.go
Normal file
45
mcp/config/config_mkdir.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxConfigMkdirToolName = "nginx_config_mkdir"
|
||||
|
||||
var nginxConfigMkdirTool = mcp.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")),
|
||||
)
|
||||
|
||||
func handleNginxConfigMkdir(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.Params.Arguments
|
||||
basePath := args["base_path"].(string)
|
||||
folderName := args["folder_name"].(string)
|
||||
|
||||
fullPath := nginx.GetConfPath(basePath, folderName)
|
||||
if !helper.IsUnderDirectory(fullPath, nginx.GetConfPath()) {
|
||||
return nil, config.ErrPathIsNotUnderTheNginxConfDir
|
||||
}
|
||||
|
||||
err := os.Mkdir(fullPath, 0755)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"message": "Directory created successfully",
|
||||
"path": fullPath,
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
96
mcp/config/config_modify.go
Normal file
96
mcp/config/config_modify.go
Normal file
|
@ -0,0 +1,96 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"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"
|
||||
"gorm.io/gen/field"
|
||||
)
|
||||
|
||||
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(
|
||||
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")),
|
||||
)
|
||||
|
||||
func handleNginxConfigModify(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.Params.Arguments
|
||||
relativePath := args["relative_path"].(string)
|
||||
content := args["content"].(string)
|
||||
syncOverwrite := args["sync_overwrite"].(bool)
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
absPath := nginx.GetConfPath(relativePath)
|
||||
if !helper.IsUnderDirectory(absPath, nginx.GetConfPath()) {
|
||||
return nil, config.ErrPathIsNotUnderTheNginxConfDir
|
||||
}
|
||||
|
||||
if !helper.FileExists(absPath) {
|
||||
return nil, ErrFileNotFound
|
||||
}
|
||||
|
||||
q := query.Config
|
||||
cfg, err := q.Assign(field.Attrs(&model.Config{
|
||||
Filepath: absPath,
|
||||
})).Where(q.Filepath.Eq(absPath)).FirstOrCreate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Update database record
|
||||
_, err = q.Where(q.Filepath.Eq(absPath)).
|
||||
Select(q.SyncNodeIds, q.SyncOverwrite).
|
||||
Updates(&model.Config{
|
||||
SyncNodeIds: syncNodeIds,
|
||||
SyncOverwrite: syncOverwrite,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cfg.SyncNodeIds = syncNodeIds
|
||||
cfg.SyncOverwrite = syncOverwrite
|
||||
|
||||
err = config.Save(absPath, content, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"name": filepath.Base(absPath),
|
||||
"content": content,
|
||||
"file_path": absPath,
|
||||
"dir": filepath.Dir(relativePath),
|
||||
"sync_node_ids": cfg.SyncNodeIds,
|
||||
"sync_overwrite": cfg.SyncOverwrite,
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
120
mcp/config/config_rename.go
Normal file
120
mcp/config/config_rename.go
Normal file
|
@ -0,0 +1,120 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/config"
|
||||
"github.com/0xJacky/Nginx-UI/internal/helper"
|
||||
"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"
|
||||
)
|
||||
|
||||
const nginxConfigRenameToolName = "nginx_config_rename"
|
||||
|
||||
var nginxConfigRenameTool = mcp.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")),
|
||||
)
|
||||
|
||||
func handleNginxConfigRename(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
args := request.Params.Arguments
|
||||
basePath := args["base_path"].(string)
|
||||
origName := args["orig_name"].(string)
|
||||
newName := args["new_name"].(string)
|
||||
|
||||
// Convert sync_node_ids from []interface{} to []uint64
|
||||
syncNodeIdsInterface, ok := args["sync_node_ids"].([]interface{})
|
||||
syncNodeIds := make([]uint64, 0)
|
||||
if ok {
|
||||
for _, id := range syncNodeIdsInterface {
|
||||
if idFloat, ok := id.(float64); ok {
|
||||
syncNodeIds = append(syncNodeIds, uint64(idFloat))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if origName == newName {
|
||||
result := map[string]interface{}{
|
||||
"message": "No changes needed, names are identical",
|
||||
}
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
||||
|
||||
origFullPath := nginx.GetConfPath(basePath, origName)
|
||||
newFullPath := nginx.GetConfPath(basePath, newName)
|
||||
if !helper.IsUnderDirectory(origFullPath, nginx.GetConfPath()) ||
|
||||
!helper.IsUnderDirectory(newFullPath, nginx.GetConfPath()) {
|
||||
return nil, config.ErrPathIsNotUnderTheNginxConfDir
|
||||
}
|
||||
|
||||
stat, err := os.Stat(origFullPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if helper.FileExists(newFullPath) {
|
||||
return nil, ErrFileAlreadyExists
|
||||
}
|
||||
|
||||
err = os.Rename(origFullPath, newFullPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// update ChatGPT records
|
||||
g := query.ChatGPTLog
|
||||
q := query.Config
|
||||
cfg, err := q.Where(q.Filepath.Eq(origFullPath)).FirstOrInit()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !stat.IsDir() {
|
||||
_, _ = g.Where(g.Name.Eq(newFullPath)).Delete()
|
||||
_, _ = g.Where(g.Name.Eq(origFullPath)).Update(g.Name, newFullPath)
|
||||
// for file, the sync policy for this file is used
|
||||
syncNodeIds = cfg.SyncNodeIds
|
||||
} else {
|
||||
// is directory, update all records under the directory
|
||||
_, _ = g.Where(g.Name.Like(origFullPath+"%")).Update(g.Name, g.Name.Replace(origFullPath, newFullPath))
|
||||
}
|
||||
|
||||
_, err = q.Where(q.Filepath.Eq(origFullPath)).Updates(&model.Config{
|
||||
Filepath: newFullPath,
|
||||
Name: newName,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
b := query.ConfigBackup
|
||||
_, _ = b.Where(b.FilePath.Eq(origFullPath)).Updates(map[string]interface{}{
|
||||
"filepath": newFullPath,
|
||||
"name": newName,
|
||||
})
|
||||
|
||||
if len(syncNodeIds) > 0 {
|
||||
err = config.SyncRenameOnRemoteServer(origFullPath, newFullPath, syncNodeIds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
result := map[string]interface{}{
|
||||
"path": strings.TrimLeft(filepath.Join(basePath, newName), "/"),
|
||||
}
|
||||
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
16
mcp/config/register.go
Normal file
16
mcp/config/register.go
Normal file
|
@ -0,0 +1,16 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
mcp.AddTool(nginxConfigAddTool, handleNginxConfigAdd)
|
||||
mcp.AddTool(nginxConfigBasePathTool, handleNginxConfigBasePath)
|
||||
mcp.AddTool(nginxConfigGetTool, handleNginxConfigGet)
|
||||
mcp.AddTool(nginxConfigHistoryTool, handleNginxConfigHistory)
|
||||
mcp.AddTool(nginxConfigListTool, handleNginxConfigList)
|
||||
mcp.AddTool(nginxConfigMkdirTool, handleNginxConfigMkdir)
|
||||
mcp.AddTool(nginxConfigModifyTool, handleNginxConfigModify)
|
||||
mcp.AddTool(nginxConfigRenameTool, handleNginxConfigRename)
|
||||
}
|
11
mcp/nginx/register.go
Normal file
11
mcp/nginx/register.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package nginx
|
||||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
mcp.AddTool(nginxReloadTool, handleNginxReload)
|
||||
mcp.AddTool(nginxRestartTool, handleNginxRestart)
|
||||
mcp.AddTool(statusTool, handleNginxStatus)
|
||||
}
|
24
mcp/nginx/reload.go
Normal file
24
mcp/nginx/reload.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package nginx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxReloadToolName = "reload_nginx"
|
||||
|
||||
var nginxReloadTool = mcp.NewTool(
|
||||
nginxReloadToolName,
|
||||
mcp.WithDescription("Perform a graceful reload of the Nginx configuration"),
|
||||
)
|
||||
|
||||
func handleNginxReload(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
output, err := nginx.Reload()
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(output + "\n" + err.Error()), err
|
||||
}
|
||||
|
||||
return mcp.NewToolResultText(output), nil
|
||||
}
|
24
mcp/nginx/restart.go
Normal file
24
mcp/nginx/restart.go
Normal file
|
@ -0,0 +1,24 @@
|
|||
package nginx
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxRestartToolName = "restart_nginx"
|
||||
|
||||
var nginxRestartTool = mcp.NewTool(
|
||||
nginxRestartToolName,
|
||||
mcp.WithDescription("Perform a graceful restart of the Nginx configuration"),
|
||||
)
|
||||
|
||||
func handleNginxRestart(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
nginx.Restart()
|
||||
output, err := nginx.GetLastOutput()
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(output + "\n" + err.Error()), err
|
||||
}
|
||||
return mcp.NewToolResultText(output), nil
|
||||
}
|
41
mcp/nginx/status.go
Normal file
41
mcp/nginx/status.go
Normal file
|
@ -0,0 +1,41 @@
|
|||
package nginx
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/0xJacky/Nginx-UI/internal/nginx"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mark3labs/mcp-go/mcp"
|
||||
)
|
||||
|
||||
const nginxStatusToolName = "nginx_status"
|
||||
|
||||
// statusResource is the status of the Nginx server
|
||||
var statusTool = mcp.NewTool(
|
||||
nginxStatusToolName,
|
||||
mcp.WithDescription("This is the status of the Nginx server"),
|
||||
)
|
||||
|
||||
// handleNginxStatus handles the Nginx status request
|
||||
func handleNginxStatus(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
||||
lastOutput, err := nginx.GetLastOutput()
|
||||
if err != nil {
|
||||
return mcp.NewToolResultError(lastOutput + "\n" + err.Error()), err
|
||||
}
|
||||
|
||||
running := nginx.IsNginxRunning()
|
||||
level := nginx.GetLogLevel(lastOutput)
|
||||
|
||||
// build result
|
||||
result := gin.H{
|
||||
"running": running,
|
||||
"message": lastOutput,
|
||||
"level": level,
|
||||
}
|
||||
|
||||
// marshal to json and return text result
|
||||
jsonResult, _ := json.Marshal(result)
|
||||
|
||||
return mcp.NewToolResultText(string(jsonResult)), nil
|
||||
}
|
11
mcp/register.go
Normal file
11
mcp/register.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package mcp
|
||||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/mcp/config"
|
||||
"github.com/0xJacky/Nginx-UI/mcp/nginx"
|
||||
)
|
||||
|
||||
func init() {
|
||||
config.Init()
|
||||
nginx.Init()
|
||||
}
|
18
mcp/router.go
Normal file
18
mcp/router.go
Normal file
|
@ -0,0 +1,18 @@
|
|||
package mcp
|
||||
|
||||
import (
|
||||
"github.com/0xJacky/Nginx-UI/internal/mcp"
|
||||
"github.com/0xJacky/Nginx-UI/internal/middleware"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitRouter(r *gin.Engine) {
|
||||
r.Any("/mcp", middleware.IPWhiteList(), middleware.AuthRequired(),
|
||||
func(c *gin.Context) {
|
||||
mcp.ServeHTTP(c)
|
||||
})
|
||||
r.Any("/mcp_message", middleware.IPWhiteList(),
|
||||
func(c *gin.Context) {
|
||||
mcp.ServeHTTP(c)
|
||||
})
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue