mirror of
https://github.com/mickael-kerjean/filestash.git
synced 2025-10-28 04:05:21 +08:00
341 lines
8.2 KiB
Go
341 lines
8.2 KiB
Go
package impl
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
. "github.com/mickael-kerjean/filestash/server/common"
|
|
. "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/config"
|
|
. "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/types"
|
|
. "github.com/mickael-kerjean/filestash/server/plugin/plg_handler_mcp/utils"
|
|
)
|
|
|
|
func init() {
|
|
Hooks.Register.Onload(func() {
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "ls",
|
|
Description: "list directory contents",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{},
|
|
}),
|
|
},
|
|
Exec: ToolFSLs,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "cat",
|
|
Description: "read a file at a specified path.",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSCat,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "pwd",
|
|
Description: "print name of current/working directory",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{},
|
|
"required": []string{},
|
|
}),
|
|
},
|
|
Exec: ToolFSPwd,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "cd",
|
|
Description: "change the working directory",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSCd,
|
|
})
|
|
|
|
if !CanEdit() {
|
|
return
|
|
}
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "mv",
|
|
Description: "move (rename) files",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"from": map[string]string{
|
|
"type": "string",
|
|
"description": "origin path",
|
|
},
|
|
"to": map[string]string{
|
|
"type": "string",
|
|
"description": "destination path",
|
|
},
|
|
},
|
|
"required": []string{"from", "to"},
|
|
}),
|
|
},
|
|
Exec: ToolFSMv,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "mkdir",
|
|
Description: "make directories",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSMkdir,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "touch",
|
|
Description: "create file",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSTouch,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "rm",
|
|
Description: "remove files or directories",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSRm,
|
|
})
|
|
|
|
RegisterTool(ToolDefinition{
|
|
Tool: Tool{
|
|
Name: "save",
|
|
Description: "save a file",
|
|
InputSchema: JsonSchema(map[string]interface{}{
|
|
"type": "object",
|
|
"properties": map[string]interface{}{
|
|
"path": map[string]string{
|
|
"type": "string",
|
|
"description": "path where the query is made",
|
|
},
|
|
"content": map[string]string{
|
|
"type": "string",
|
|
"description": "content of the file",
|
|
},
|
|
},
|
|
"required": []string{"path"},
|
|
}),
|
|
},
|
|
Exec: ToolFSSave,
|
|
})
|
|
})
|
|
}
|
|
|
|
func ToolFSLs(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
files, err := userSession.Backend.Ls(EnforceDirectory(getPath(params, userSession, "path")))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var b bytes.Buffer
|
|
for _, file := range files {
|
|
if file.IsDir() {
|
|
b.Write([]byte("[DIR] "))
|
|
} else {
|
|
b.Write([]byte("[FILE] "))
|
|
}
|
|
b.Write([]byte(file.Name()))
|
|
b.Write([]byte("\n"))
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: b.String(),
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSCat(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "path") {
|
|
return nil, ErrNotValid
|
|
}
|
|
r, err := userSession.Backend.Cat(getPath(params, userSession, "path"))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
b, err := io.ReadAll(r)
|
|
r.Close()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: string(b),
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSPwd(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: userSession.CurrDir,
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSCd(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
path := EnforceDirectory(getPath(params, userSession, "path"))
|
|
if _, err := userSession.Backend.Ls(path); err != nil {
|
|
return nil, errors.New("No such file or directory")
|
|
}
|
|
userSession.CurrDir = EnforceDirectory(path)
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: userSession.CurrDir,
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSMv(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "from") || isArgEmpty(params, "to") {
|
|
return nil, ErrNotValid
|
|
}
|
|
if err := userSession.Backend.Mv(
|
|
getPath(params, userSession, "from"),
|
|
getPath(params, userSession, "to"),
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: "",
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSMkdir(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "path") {
|
|
return nil, ErrNotValid
|
|
}
|
|
if err := userSession.Backend.Mkdir(EnforceDirectory(getPath(params, userSession, "path"))); err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: "",
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSTouch(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "path") {
|
|
return nil, ErrNotValid
|
|
}
|
|
if err := userSession.Backend.Touch(getPath(params, userSession, "path")); err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: "",
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSRm(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "path") {
|
|
return nil, ErrNotValid
|
|
}
|
|
if err := userSession.Backend.Rm(getPath(params, userSession, "path")); err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: "",
|
|
}, nil
|
|
}
|
|
|
|
func ToolFSSave(params map[string]any, userSession *UserSession) (*TextContent, error) {
|
|
if isArgEmpty(params, "path") {
|
|
return nil, ErrNotValid
|
|
}
|
|
if err := userSession.Backend.Save(
|
|
getPath(params, userSession, "path"),
|
|
NewReadCloserFromBytes([]byte(GetArgumentsString(params, "content"))),
|
|
); err != nil {
|
|
return nil, err
|
|
}
|
|
return &TextContent{
|
|
Type: "text",
|
|
Text: "",
|
|
}, nil
|
|
}
|
|
|
|
func getPath(params map[string]any, userSession *UserSession, name string) string {
|
|
path := GetArgumentsString(params, name)
|
|
currDir := ""
|
|
if path == "" {
|
|
currDir = userSession.CurrDir
|
|
} else if strings.HasPrefix(path, "~/") {
|
|
currDir = "." + strings.TrimPrefix(path, "~")
|
|
currDir = JoinPath(userSession.HomeDir, currDir)
|
|
} else if strings.HasPrefix(path, "/") {
|
|
currDir = path
|
|
} else {
|
|
currDir = filepath.Join(userSession.CurrDir, ToString(path, "./"))
|
|
}
|
|
return currDir
|
|
}
|
|
|
|
func isArgEmpty(params map[string]any, name string) bool {
|
|
if arg := GetArgumentsString(params, name); arg == "" {
|
|
return true
|
|
}
|
|
return false
|
|
}
|