fix(mcp): harden tool handlers
This commit is contained in:
parent
ab1aa0cad0
commit
0b63f9cd22
3 changed files with 69 additions and 1 deletions
|
|
@ -6,6 +6,7 @@ package mcp
|
|||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"iter"
|
||||
"net/http"
|
||||
"os"
|
||||
|
|
@ -506,6 +507,10 @@ type EditDiffOutput struct {
|
|||
// Tool handlers
|
||||
|
||||
func (s *Service) readFile(ctx context.Context, req *mcp.CallToolRequest, input ReadFileInput) (*mcp.CallToolResult, ReadFileOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, ReadFileOutput{}, log.E("mcp.readFile", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
content, err := s.medium.Read(input.Path)
|
||||
if err != nil {
|
||||
return nil, ReadFileOutput{}, log.E("mcp.readFile", "failed to read file", err)
|
||||
|
|
@ -518,6 +523,10 @@ func (s *Service) readFile(ctx context.Context, req *mcp.CallToolRequest, input
|
|||
}
|
||||
|
||||
func (s *Service) writeFile(ctx context.Context, req *mcp.CallToolRequest, input WriteFileInput) (*mcp.CallToolResult, WriteFileOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, WriteFileOutput{}, log.E("mcp.writeFile", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
// Medium.Write creates parent directories automatically
|
||||
if err := s.medium.Write(input.Path, input.Content); err != nil {
|
||||
return nil, WriteFileOutput{}, log.E("mcp.writeFile", "failed to write file", err)
|
||||
|
|
@ -526,6 +535,10 @@ func (s *Service) writeFile(ctx context.Context, req *mcp.CallToolRequest, input
|
|||
}
|
||||
|
||||
func (s *Service) listDirectory(ctx context.Context, req *mcp.CallToolRequest, input ListDirectoryInput) (*mcp.CallToolResult, ListDirectoryOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, ListDirectoryOutput{}, log.E("mcp.listDirectory", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
entries, err := s.medium.List(input.Path)
|
||||
if err != nil {
|
||||
return nil, ListDirectoryOutput{}, log.E("mcp.listDirectory", "failed to list directory", err)
|
||||
|
|
@ -563,6 +576,10 @@ func directoryEntryPath(dir, name string) string {
|
|||
}
|
||||
|
||||
func (s *Service) createDirectory(ctx context.Context, req *mcp.CallToolRequest, input CreateDirectoryInput) (*mcp.CallToolResult, CreateDirectoryOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, CreateDirectoryOutput{}, log.E("mcp.createDirectory", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
if err := s.medium.EnsureDir(input.Path); err != nil {
|
||||
return nil, CreateDirectoryOutput{}, log.E("mcp.createDirectory", "failed to create directory", err)
|
||||
}
|
||||
|
|
@ -570,6 +587,10 @@ func (s *Service) createDirectory(ctx context.Context, req *mcp.CallToolRequest,
|
|||
}
|
||||
|
||||
func (s *Service) deleteFile(ctx context.Context, req *mcp.CallToolRequest, input DeleteFileInput) (*mcp.CallToolResult, DeleteFileOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, DeleteFileOutput{}, log.E("mcp.deleteFile", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
if err := s.medium.Delete(input.Path); err != nil {
|
||||
return nil, DeleteFileOutput{}, log.E("mcp.deleteFile", "failed to delete file", err)
|
||||
}
|
||||
|
|
@ -577,6 +598,10 @@ func (s *Service) deleteFile(ctx context.Context, req *mcp.CallToolRequest, inpu
|
|||
}
|
||||
|
||||
func (s *Service) renameFile(ctx context.Context, req *mcp.CallToolRequest, input RenameFileInput) (*mcp.CallToolResult, RenameFileOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, RenameFileOutput{}, log.E("mcp.renameFile", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
if err := s.medium.Rename(input.OldPath, input.NewPath); err != nil {
|
||||
return nil, RenameFileOutput{}, log.E("mcp.renameFile", "failed to rename file", err)
|
||||
}
|
||||
|
|
@ -584,10 +609,17 @@ func (s *Service) renameFile(ctx context.Context, req *mcp.CallToolRequest, inpu
|
|||
}
|
||||
|
||||
func (s *Service) fileExists(ctx context.Context, req *mcp.CallToolRequest, input FileExistsInput) (*mcp.CallToolResult, FileExistsOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, FileExistsOutput{}, log.E("mcp.fileExists", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
info, err := s.medium.Stat(input.Path)
|
||||
if err != nil {
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return nil, FileExistsOutput{Exists: false, IsDir: false, Path: input.Path}, nil
|
||||
}
|
||||
return nil, FileExistsOutput{}, log.E("mcp.fileExists", "failed to stat path", err)
|
||||
}
|
||||
return nil, FileExistsOutput{
|
||||
Exists: true,
|
||||
IsDir: info.IsDir(),
|
||||
|
|
@ -605,6 +637,10 @@ func (s *Service) getSupportedLanguages(ctx context.Context, req *mcp.CallToolRe
|
|||
}
|
||||
|
||||
func (s *Service) editDiff(ctx context.Context, req *mcp.CallToolRequest, input EditDiffInput) (*mcp.CallToolResult, EditDiffOutput, error) {
|
||||
if s.medium == nil {
|
||||
return nil, EditDiffOutput{}, log.E("mcp.editDiff", "workspace medium unavailable", nil)
|
||||
}
|
||||
|
||||
if input.OldString == "" {
|
||||
return nil, EditDiffOutput{}, log.E("mcp.editDiff", "old_string cannot be empty", nil)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -176,6 +176,10 @@ func (s *Service) registerProcessTools(server *mcp.Server) bool {
|
|||
|
||||
// processStart handles the process_start tool call.
|
||||
func (s *Service) processStart(ctx context.Context, req *mcp.CallToolRequest, input ProcessStartInput) (*mcp.CallToolResult, ProcessStartOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessStartOutput{}, log.E("processStart", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Security("MCP tool execution", "tool", "process_start", "command", input.Command, "args", input.Args, "dir", input.Dir, "user", log.Username())
|
||||
|
||||
if input.Command == "" {
|
||||
|
|
@ -222,6 +226,10 @@ func (s *Service) processStart(ctx context.Context, req *mcp.CallToolRequest, in
|
|||
|
||||
// processStop handles the process_stop tool call.
|
||||
func (s *Service) processStop(ctx context.Context, req *mcp.CallToolRequest, input ProcessStopInput) (*mcp.CallToolResult, ProcessStopOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessStopOutput{}, log.E("processStop", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Security("MCP tool execution", "tool", "process_stop", "id", input.ID, "user", log.Username())
|
||||
|
||||
if input.ID == "" {
|
||||
|
|
@ -260,6 +268,10 @@ func (s *Service) processStop(ctx context.Context, req *mcp.CallToolRequest, inp
|
|||
|
||||
// processKill handles the process_kill tool call.
|
||||
func (s *Service) processKill(ctx context.Context, req *mcp.CallToolRequest, input ProcessKillInput) (*mcp.CallToolResult, ProcessKillOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessKillOutput{}, log.E("processKill", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Security("MCP tool execution", "tool", "process_kill", "id", input.ID, "user", log.Username())
|
||||
|
||||
if input.ID == "" {
|
||||
|
|
@ -296,6 +308,10 @@ func (s *Service) processKill(ctx context.Context, req *mcp.CallToolRequest, inp
|
|||
|
||||
// processList handles the process_list tool call.
|
||||
func (s *Service) processList(ctx context.Context, req *mcp.CallToolRequest, input ProcessListInput) (*mcp.CallToolResult, ProcessListOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessListOutput{}, log.E("processList", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Info("MCP tool execution", "tool", "process_list", "running_only", input.RunningOnly, "user", log.Username())
|
||||
|
||||
var procs []*process.Process
|
||||
|
|
@ -329,6 +345,10 @@ func (s *Service) processList(ctx context.Context, req *mcp.CallToolRequest, inp
|
|||
|
||||
// processOutput handles the process_output tool call.
|
||||
func (s *Service) processOutput(ctx context.Context, req *mcp.CallToolRequest, input ProcessOutputInput) (*mcp.CallToolResult, ProcessOutputOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessOutputOutput{}, log.E("processOutput", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Info("MCP tool execution", "tool", "process_output", "id", input.ID, "user", log.Username())
|
||||
|
||||
if input.ID == "" {
|
||||
|
|
@ -349,6 +369,10 @@ func (s *Service) processOutput(ctx context.Context, req *mcp.CallToolRequest, i
|
|||
|
||||
// processInput handles the process_input tool call.
|
||||
func (s *Service) processInput(ctx context.Context, req *mcp.CallToolRequest, input ProcessInputInput) (*mcp.CallToolResult, ProcessInputOutput, error) {
|
||||
if s.processService == nil {
|
||||
return nil, ProcessInputOutput{}, log.E("processInput", "process service unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Security("MCP tool execution", "tool", "process_input", "id", input.ID, "user", log.Username())
|
||||
|
||||
if input.ID == "" {
|
||||
|
|
|
|||
|
|
@ -64,6 +64,10 @@ func (s *Service) registerWSTools(server *mcp.Server) bool {
|
|||
|
||||
// wsStart handles the ws_start tool call.
|
||||
func (s *Service) wsStart(ctx context.Context, req *mcp.CallToolRequest, input WSStartInput) (*mcp.CallToolResult, WSStartOutput, error) {
|
||||
if s.wsHub == nil {
|
||||
return nil, WSStartOutput{}, log.E("wsStart", "websocket hub unavailable", nil)
|
||||
}
|
||||
|
||||
addr := input.Addr
|
||||
if addr == "" {
|
||||
addr = ":8080"
|
||||
|
|
@ -119,6 +123,10 @@ func (s *Service) wsStart(ctx context.Context, req *mcp.CallToolRequest, input W
|
|||
|
||||
// wsInfo handles the ws_info tool call.
|
||||
func (s *Service) wsInfo(ctx context.Context, req *mcp.CallToolRequest, input WSInfoInput) (*mcp.CallToolResult, WSInfoOutput, error) {
|
||||
if s.wsHub == nil {
|
||||
return nil, WSInfoOutput{}, log.E("wsInfo", "websocket hub unavailable", nil)
|
||||
}
|
||||
|
||||
s.logger.Info("MCP tool execution", "tool", "ws_info", "user", log.Username())
|
||||
|
||||
stats := s.wsHub.Stats()
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue