Files

215 lines
6.5 KiB
Go

package tools
import (
"context"
"fmt"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func registerNetworkTools(s *mcp.Server) {
// net_stat
s.AddTool(&mcp.Tool{
Name: "net_stat",
Description: "Check listening ports (ss/netstat)",
InputSchema: schema(map[string]any{
"port": intProps("Filter by specific port", false),
"target": strProps("Connection alias (default: primary)", false),
}, nil),
}, netStatHandler)
// search_files
s.AddTool(&mcp.Tool{
Name: "search_files",
Description: "Find files using POSIX find",
InputSchema: schema(map[string]any{
"pattern": strProps("File name pattern (supports wildcards)", true),
"path": strProps("Search path (default: /)", false),
"max_depth": intProps("Maximum directory depth", false),
"type": strProps("Filter by type: f (file), d (directory)", false, "f", "d"),
"target": strProps("Connection alias (default: primary)", false),
}, []string{"pattern"}),
}, searchFilesHandler)
// search_text
s.AddTool(&mcp.Tool{
Name: "search_text",
Description: "Search text in files using grep",
InputSchema: schema(map[string]any{
"pattern": strProps("Search pattern", true),
"path": strProps("File or directory path", true),
"recursive": boolProps("Search recursively", false),
"ignore_case": boolProps("Case-insensitive search", false),
"context": intProps("Lines of context around matches", false),
"target": strProps("Connection alias (default: primary)", false),
}, []string{"pattern", "path"}),
}, searchTextHandler)
// package_manage
s.AddTool(&mcp.Tool{
Name: "package_manage",
Description: "Install/remove/check packages (apt, apk, dnf, yum)",
InputSchema: schema(map[string]any{
"action": strProps("Package management action", true, "install", "remove", "check", "list"),
"package": strProps("Package name (required for install/remove/check)", false),
"target": strProps("Connection alias (default: primary)", false),
}, []string{"action"}),
}, packageManageHandler)
}
func netStatHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
m := manager()
if m == nil {
return errorResult("no SSH manager initialized"), nil
}
args := requestArgs(req)
port := getInt(args, "port", 0)
target := getString(args, "target", "primary")
var cmd string
if port > 0 {
cmd = fmt.Sprintf("ss -tlnp 2>/dev/null | grep ':%d ' || netstat -an 2>/dev/null | grep -i listen | grep '[\\.: ]%d '", port, port)
} else {
cmd = "ss -tlnp 2>/dev/null || netstat -an 2>/dev/null | grep -i listen"
}
output, err := m.Execute(ctx, cmd, target)
if err != nil {
return errorResult(err.Error()), nil
}
return textResult(output), nil
}
func searchFilesHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
m := manager()
if m == nil {
return errorResult("no SSH manager initialized"), nil
}
args := requestArgs(req)
pattern, _ := requireString(args, "pattern")
path := getString(args, "path", "/")
maxDepth := getInt(args, "max_depth", 0)
fileType := getString(args, "type", "")
target := getString(args, "target", "primary")
cmd := fmt.Sprintf("find %s", shellQuote(path))
if maxDepth > 0 {
cmd += fmt.Sprintf(" -maxdepth %d", maxDepth)
}
if fileType == "f" || fileType == "d" {
cmd += fmt.Sprintf(" -type %s", fileType)
}
cmd += fmt.Sprintf(" -name %s 2>/dev/null | head -100", shellQuote(pattern))
output, err := m.Execute(ctx, cmd, target)
if err != nil {
return errorResult(err.Error()), nil
}
return textResult(output), nil
}
func searchTextHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
m := manager()
if m == nil {
return errorResult("no SSH manager initialized"), nil
}
args := requestArgs(req)
pattern, _ := requireString(args, "pattern")
path, _ := requireString(args, "path")
recursive := getBool(args, "recursive", false)
ignoreCase := getBool(args, "ignore_case", false)
ctxLines := getInt(args, "context", 0)
target := getString(args, "target", "primary")
cmd := "grep"
if recursive {
cmd += " -r"
}
if ignoreCase {
cmd += " -i"
}
if ctxLines > 0 {
cmd += fmt.Sprintf(" -C %d", ctxLines)
}
cmd += fmt.Sprintf(" -n %s %s 2>/dev/null | head -100", shellQuote(pattern), shellQuote(path))
output, err := m.Execute(ctx, cmd, target)
if err != nil {
return errorResult(err.Error()), nil
}
return textResult(output), nil
}
func packageManageHandler(ctx context.Context, req *mcp.CallToolRequest) (*mcp.CallToolResult, error) {
m := manager()
if m == nil {
return errorResult("no SSH manager initialized"), nil
}
args := requestArgs(req)
action, _ := requireString(args, "action")
pkg := getString(args, "package", "")
target := getString(args, "target", "primary")
if action != "list" && pkg == "" {
return errorResult("Package name required for this action"), nil
}
detectCmd := `
if command -v apt-get >/dev/null 2>&1; then echo "apt"
elif command -v apk >/dev/null 2>&1; then echo "apk"
elif command -v dnf >/dev/null 2>&1; then echo "dnf"
elif command -v yum >/dev/null 2>&1; then echo "yum"
else echo "unknown"
fi`
pkgMgr, err := m.Execute(ctx, detectCmd, target)
if err != nil {
return errorResult(err.Error()), nil
}
pkgMgr = trimOutput(pkgMgr)
var cmd string
switch pkgMgr {
case "apt":
switch action {
case "install":
cmd = fmt.Sprintf("apt-get update && apt-get install -y %s", shellQuote(pkg))
case "remove":
cmd = fmt.Sprintf("apt-get remove -y %s", shellQuote(pkg))
case "check":
cmd = fmt.Sprintf("dpkg -s %s 2>/dev/null", shellQuote(pkg))
case "list":
cmd = "dpkg -l | head -50"
}
case "apk":
switch action {
case "install":
cmd = fmt.Sprintf("apk add %s", shellQuote(pkg))
case "remove":
cmd = fmt.Sprintf("apk del %s", shellQuote(pkg))
case "check":
cmd = fmt.Sprintf("apk info %s 2>/dev/null", shellQuote(pkg))
case "list":
cmd = "apk list --installed | head -50"
}
case "dnf", "yum":
switch action {
case "install":
cmd = fmt.Sprintf("%s install -y %s", pkgMgr, shellQuote(pkg))
case "remove":
cmd = fmt.Sprintf("%s remove -y %s", pkgMgr, shellQuote(pkg))
case "check":
cmd = fmt.Sprintf("rpm -qi %s 2>/dev/null", shellQuote(pkg))
case "list":
cmd = fmt.Sprintf("%s list installed 2>/dev/null | head -50", pkgMgr)
}
default:
return errorResult("No supported package manager found"), nil
}
output, err := m.Execute(ctx, cmd, target)
if err != nil {
return errorResult(err.Error()), nil
}
return textResult(output), nil
}