diff --git a/Remote/README.md b/Remote/README.md new file mode 100644 index 0000000..8eb4743 --- /dev/null +++ b/Remote/README.md @@ -0,0 +1,45 @@ +# 使用说明 + +本模块分为两部分:**RPC 调用** 和 **MCP 调用**。 + +## 启动方式 + +### 1. RPC 调用 + +运行以下命令启动 RPC 服务: + +```bash +go run remote.go -api 127.0.0.1:8080 -sercet xxxx +``` + +- `-api`:指定监听地址和端口。 +- `-sercet`:指定访问密钥。 + +### 2. MCP 调用 + +直接运行: + +```bash +go run remote.go +``` + +- 默认使用 `stdio` 协议。 +- 如需指定使用 `sse` 协议,请添加 `-transport sse` 参数: + +```bash +go run remote.go -transport sse +``` + +## 开发调试 + +在开发 MCP 时,可以借助 [Model Context Protocol Inspector](https://www.npmjs.com/package/@modelcontextprotocol/inspector) 进行调试。 + +使用以下命令启动调试器: + +```bash +npx @modelcontextprotocol/inspector +``` + +启动后即可在终端中进行调试。 + + diff --git a/Remote/remote.go b/Remote/remote.go index e59157d..869367d 100644 --- a/Remote/remote.go +++ b/Remote/remote.go @@ -19,9 +19,9 @@ func main() { flag.StringVar(&secret, "secret", "", "RPC调用使用的秘钥") flag.StringVar(&transport, "transport", "stdio", "MCP传输协议:stdio 或 sse") Common.Flag(&Info) - + //模式参数,因为在initoutput中会使用,所以需要在这里设置 + Common.ApiAddr = "remote" if apiURL != "" { - Common.ApiAddr = apiURL server.StartApiServer(apiURL, secret) } else { server.StartMcpServer(transport) diff --git a/Remote/server/mcp.go b/Remote/server/mcp.go index 72321c1..ddc9f6a 100644 --- a/Remote/server/mcp.go +++ b/Remote/server/mcp.go @@ -1,9 +1,57 @@ package server -import "fmt" +import ( + "fmt" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/mark3labs/mcp-go/server" + "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/Remote/service" +) + +func StartMcpServer(transport string) { + mcpServer := NewFscanMCPServer() + + if transport == "sse" { + Common.LogSuccess("🚀 启动 MCP SSE 服务器,监听地址: http://localhost:8080") + sseServer := server.NewSSEServer(mcpServer, server.WithBaseURL("http://localhost:8080")) + if err := sseServer.Start(":8080"); err != nil { + Common.LogError(fmt.Sprintf("❌ 启动 SSE 服务器失败: %v", err)) + panic(err) + } + } else { + Common.LogSuccess("🚀 启动 MCP Stdio 服务器(标准输入输出模式)") + if err := server.ServeStdio(mcpServer); err != nil { + Common.LogError(fmt.Sprintf("❌ 启动 Stdio 服务器失败: %v", err)) + panic(err) + } + } +} + +func NewFscanMCPServer() *server.MCPServer { + s := server.NewMCPServer( + "Fscan MCP", + "1.0.0", + ) + toolHandler := service.NewFscanMCPTool() + + // 添加工具处理器 + s.AddTool( + mcp.NewTool("StartScan", + mcp.WithDescription("开始扫描"), + mcp.WithString("target", + mcp.Required(), + mcp.Description("扫描目标"), + ), + ), + toolHandler.StartScan, + ) + s.AddTool( + mcp.NewTool("GetScanResults", + mcp.WithDescription("获取扫描结果"), + ), + toolHandler.GetScanResults, + ) + return s -func StartMcpServer(transport string) error { - fmt.Println("Starting MCP server...") - fmt.Println("Transport protocol:", transport) - return nil } diff --git a/Remote/service/mcp.go b/Remote/service/mcp.go new file mode 100644 index 0000000..e4d50a0 --- /dev/null +++ b/Remote/service/mcp.go @@ -0,0 +1,94 @@ +package service + +import ( + "context" + "errors" + "fmt" + "sync" + "sync/atomic" + "time" + + "github.com/mark3labs/mcp-go/mcp" + "github.com/shadow1ng/fscan/Common" + "github.com/shadow1ng/fscan/Core" +) + +type FscanMCPTool struct { + scanMutex sync.Mutex + isScanning int32 + scanStartTime time.Time +} + +func NewFscanMCPTool() *FscanMCPTool { + return &FscanMCPTool{} +} + +func (s *FscanMCPTool) StartScan(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + target, ok := request.Params.Arguments["target"].(string) + if !ok { + return nil, errors.New("name must be a string") + } + //构造扫描字符串 + arg := fmt.Sprintf("-h %s", target) + + if !atomic.CompareAndSwapInt32(&s.isScanning, 0, 1) { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + mcp.TextContent{ + Type: "text", + Text: "已有扫描任务正在运行,请稍后重试", + }, + }, + }, nil + } + + s.scanStartTime = time.Now() // 记录任务开始时间 + + go func() { + defer atomic.StoreInt32(&s.isScanning, 0) + + s.scanMutex.Lock() + defer s.scanMutex.Unlock() + + Common.LogDebug("异步执行扫描请求,目标: " + arg) + + var info Common.HostInfo + if err := Common.FlagFromRemote(&info, arg); err != nil { + return + } + if err := Common.Parse(&info); err != nil { + return + } + if err := Common.CloseOutput(); err != nil { + Common.LogError(fmt.Sprintf("关闭输出系统失败: %v", err)) + return + } + if err := Common.InitOutput(); err != nil { + Common.LogError(fmt.Sprintf("初始化输出系统失败: %v", err)) + return + } + + Core.Scan(info) + + Common.LogDebug("扫描任务完成") + }() + return &mcp.CallToolResult{ + Content: []mcp.Content{ + mcp.TextContent{ + Type: "text", + Text: fmt.Sprintf("扫描任务开始,扫描参数: %s", arg), + }, + }, + }, nil +} + +func (s *FscanMCPTool) GetScanResults(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) { + return &mcp.CallToolResult{ + Content: []mcp.Content{ + mcp.TextContent{ + Type: "text", + Text: Common.OutputFormat + Common.Outputfile, + }, + }, + }, nil +} diff --git a/Remote/service/fscan.go b/Remote/service/rpc.go similarity index 100% rename from Remote/service/fscan.go rename to Remote/service/rpc.go diff --git a/go.mod b/go.mod index a3a5320..ae8e16a 100644 --- a/go.mod +++ b/go.mod @@ -17,6 +17,7 @@ require ( github.com/hirochachacha/go-smb2 v1.1.0 github.com/jlaffaye/ftp v0.2.0 github.com/lib/pq v1.10.9 + github.com/mark3labs/mcp-go v0.23.1 github.com/mitchellh/go-vnc v0.0.0-20150629162542-723ed9867aed github.com/neo4j/neo4j-go-driver/v4 v4.4.7 github.com/rabbitmq/amqp091-go v1.10.0 @@ -74,7 +75,9 @@ require ( github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect + github.com/spf13/cast v1.7.1 // indirect github.com/stoewer/go-strcase v1.2.0 // indirect + github.com/yosida95/uritemplate/v3 v3.0.2 // indirect golang.org/x/term v0.29.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect ) diff --git a/go.sum b/go.sum index a1ce73e..d9ea337 100644 --- a/go.sum +++ b/go.sum @@ -67,6 +67,8 @@ github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= +github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= +github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.5.1/go.mod h1:T3375wBYaZdLLcVNkcVbzGHY7f1l/uK5T5Ai1i3InKU= @@ -229,13 +231,16 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc= github.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mark3labs/mcp-go v0.23.1 h1:RzTzZ5kJ+HxwnutKA4rll8N/pKV6Wh5dhCmiJUu5S9I= +github.com/mark3labs/mcp-go v0.23.1/go.mod h1:rXqOudj/djTORU/ThxYx8fqEVj/5pvTuuebQ2RC7uk4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -330,6 +335,8 @@ github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4k github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= +github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= @@ -358,6 +365,8 @@ github.com/tfriedel6/canvas v0.12.1/go.mod h1:WIe1YgsQiKA1awmU6tSs8e5DkceDHC5MHg github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/veandco/go-sdl2 v0.4.0/go.mod h1:FB+kTpX9YTE+urhYiClnRzpOXbiWgaU3+5F2AB78DPg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4= +github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=